Bug 1189137 - Don't treat integer stores to unboxed objects as truncated, r=jandem. a=ritu
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 04 Aug 2015 15:41:26 -0700
changeset 281884 5329b78bfdb3a554985d4317147834bcd0d3cdeb
parent 281883 aa576e40d2ae04174da1f363b464339c5d884362
child 281885 5440624ee3a8c76fc102ff26f92b5decad9c07dc
push id4943
push userkwierso@gmail.com
push dateTue, 11 Aug 2015 18:01:41 +0000
treeherdermozilla-beta@5440624ee3a8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, ritu
bugs1189137
milestone41.0
Bug 1189137 - Don't treat integer stores to unboxed objects as truncated, r=jandem. a=ritu
js/src/jit-test/tests/ion/bug1189137.js
js/src/jit/IonBuilder.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1189137.js
@@ -0,0 +1,12 @@
+
+var arr = [];
+for (var i=0; i<2000; i++)
+    arr.push({amount: i > 1900 ? 1987654321 : 1});
+
+function f() {
+    for (var i=0; i<arr.length; i++) {
+	arr[i].amount += 1987654321;
+	assertEq(arr[i].amount, i > 1900 ? 3975308642 : 1987654322);
+    }
+}
+f();
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -9267,17 +9267,18 @@ IonBuilder::jsop_setelem_typed(Scalar::T
     }
 
     // Store the value.
     MInstruction* ins;
     if (expectOOB) {
         ins = MStoreTypedArrayElementHole::New(alloc(), elements, length, id, toWrite, arrayType);
     } else {
         MStoreUnboxedScalar* store =
-            MStoreUnboxedScalar::New(alloc(), elements, id, toWrite, arrayType);
+            MStoreUnboxedScalar::New(alloc(), elements, id, toWrite, arrayType,
+                                     MStoreUnboxedScalar::TruncateInput);
         ins = store;
     }
 
     current->add(ins);
     current->push(value);
 
     return resumeAfter(ins);
 }
@@ -11522,26 +11523,29 @@ MInstruction*
 IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
                               MDefinition* scaledOffset, JSValueType unboxedType,
                               MDefinition* value, bool preBarrier /* = true */)
 {
     MInstruction* store;
     switch (unboxedType) {
       case JSVAL_TYPE_BOOLEAN:
         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
+                                         MStoreUnboxedScalar::DontTruncateInput,
                                          DoesNotRequireMemoryBarrier, elementsOffset);
         break;
 
       case JSVAL_TYPE_INT32:
         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32,
+                                         MStoreUnboxedScalar::DontTruncateInput,
                                          DoesNotRequireMemoryBarrier, elementsOffset);
         break;
 
       case JSVAL_TYPE_DOUBLE:
         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
+                                         MStoreUnboxedScalar::DontTruncateInput,
                                          DoesNotRequireMemoryBarrier, elementsOffset);
         break;
 
       case JSVAL_TYPE_STRING:
         store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value,
                                          elementsOffset, preBarrier);
         break;
 
@@ -12900,17 +12904,18 @@ IonBuilder::storeScalarTypedObjectValue(
     MDefinition* toWrite = value;
     if (type == Scalar::Uint8Clamped) {
         toWrite = MClampToUint8::New(alloc(), value);
         current->add(toWrite->toInstruction());
     }
 
     MStoreUnboxedScalar* store =
         MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, toWrite,
-                                 type, DoesNotRequireMemoryBarrier, adjustment);
+                                 type, MStoreUnboxedScalar::TruncateInput,
+                                 DoesNotRequireMemoryBarrier, adjustment);
     current->add(store);
 
     return true;
 }
 
 bool
 IonBuilder::storeReferenceTypedObjectValue(MDefinition* typedObj,
                                            const LinearSum& byteOffset,
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -2869,17 +2869,17 @@ IonBuilder::inlineAtomicsStore(CallInfo&
 
     MDefinition* toWrite = value;
     if (value->type() == MIRType_Double) {
         toWrite = MTruncateToInt32::New(alloc(), value);
         current->add(toWrite->toInstruction());
     }
     MStoreUnboxedScalar* store =
         MStoreUnboxedScalar::New(alloc(), elements, index, toWrite, arrayType,
-                                 DoesRequireMemoryBarrier);
+                                 MStoreUnboxedScalar::TruncateInput, DoesRequireMemoryBarrier);
     current->add(store);
     current->push(value);
 
     if (!resumeAfter(store))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
@@ -3399,17 +3399,18 @@ IonBuilder::inlineSimdStore(CallInfo& ca
     MDefinition* index = nullptr;
     MInstruction* elements = nullptr;
     Scalar::Type arrayType;
     if (!prepareForSimdLoadStore(callInfo, simdType, &elements, &index, &arrayType))
         return InliningStatus_NotInlined;
 
     MDefinition* valueToWrite = callInfo.getArg(2);
     MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(alloc(), elements, index,
-                                                          valueToWrite, arrayType);
+                                                          valueToWrite, arrayType,
+                                                          MStoreUnboxedScalar::TruncateInput);
     store->setSimdWrite(simdType, numElems);
 
     current->add(store);
     current->push(valueToWrite);
 
     callInfo.setImplicitlyUsedUnchecked();
 
     if (!resumeAfter(store))
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9624,27 +9624,39 @@ class StoreUnboxedScalarBase
 };
 
 // Store an unboxed scalar value to a typed array or other object.
 class MStoreUnboxedScalar
   : public MTernaryInstruction,
     public StoreUnboxedScalarBase,
     public StoreUnboxedScalarPolicy::Data
 {
+  public:
+    enum TruncateInputKind {
+        DontTruncateInput,
+        TruncateInput
+    };
+
+  private:
     Scalar::Type storageType_;
+
+    // Whether this store truncates out of range inputs, for use by range analysis.
+    TruncateInputKind truncateInput_;
+
     bool requiresBarrier_;
     int32_t offsetAdjustment_;
     unsigned numElems_; // used only for SIMD
 
     MStoreUnboxedScalar(MDefinition* elements, MDefinition* index, MDefinition* value,
-                        Scalar::Type storageType, MemoryBarrierRequirement requiresBarrier,
-                        int32_t offsetAdjustment)
+                        Scalar::Type storageType, TruncateInputKind truncateInput,
+                        MemoryBarrierRequirement requiresBarrier, int32_t offsetAdjustment)
       : MTernaryInstruction(elements, index, value),
         StoreUnboxedScalarBase(storageType),
         storageType_(storageType),
+        truncateInput_(truncateInput),
         requiresBarrier_(requiresBarrier == DoesRequireMemoryBarrier),
         offsetAdjustment_(offsetAdjustment),
         numElems_(1)
     {
         if (requiresBarrier_)
             setGuard();         // Not removable or movable
         else
             setMovable();
@@ -9654,22 +9666,23 @@ class MStoreUnboxedScalar
     }
 
   public:
     INSTRUCTION_HEADER(StoreUnboxedScalar)
 
     static MStoreUnboxedScalar* New(TempAllocator& alloc,
                                     MDefinition* elements, MDefinition* index,
                                     MDefinition* value, Scalar::Type storageType,
+                                    TruncateInputKind truncateInput,
                                     MemoryBarrierRequirement requiresBarrier =
                                         DoesNotRequireMemoryBarrier,
                                     int32_t offsetAdjustment = 0)
     {
         return new(alloc) MStoreUnboxedScalar(elements, index, value, storageType,
-                                              requiresBarrier, offsetAdjustment);
+                                              truncateInput, requiresBarrier, offsetAdjustment);
     }
 
     void setSimdWrite(Scalar::Type writeType, unsigned numElems) {
         MOZ_ASSERT(Scalar::isSimdType(writeType));
         setWriteType(writeType);
         numElems_ = numElems;
     }
     unsigned numElems() const {
@@ -9685,16 +9698,19 @@ class MStoreUnboxedScalar
         return getOperand(1);
     }
     MDefinition* value() const {
         return getOperand(2);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::UnboxedElement);
     }
+    TruncateInputKind truncateInput() const {
+        return truncateInput_;
+    }
     bool requiresMemoryBarrier() const {
         return requiresBarrier_;
     }
     int32_t offsetAdjustment() const {
         return offsetAdjustment_;
     }
     TruncateKind operandTruncateKind(size_t index) const override;
 
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2678,18 +2678,18 @@ MToDouble::operandTruncateKind(size_t in
 {
     // MToDouble propagates its truncate kind to its operand.
     return truncateKind();
 }
 
 MDefinition::TruncateKind
 MStoreUnboxedScalar::operandTruncateKind(size_t index) const
 {
-    // An integer store truncates the stored value.
-    return index == 2 && isIntegerWrite() ? Truncate : NoTruncate;
+    // Some receiver objects, such as typed arrays, will truncate out of range integer inputs.
+    return (truncateInput() && index == 2 && isIntegerWrite()) ? Truncate : NoTruncate;
 }
 
 MDefinition::TruncateKind
 MStoreTypedArrayElementHole::operandTruncateKind(size_t index) const
 {
     // An integer store truncates the stored value.
     return index == 3 && isIntegerWrite() ? Truncate : NoTruncate;
 }