Bug 1189137 - Don't treat integer stores to unboxed objects as truncated, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 04 Aug 2015 15:41:26 -0700
changeset 287905 dd7436fa536c40d4fdbfb7cdad414d6a00c7a549
parent 287904 13c1bf72e32fc040a70e7df79c97207c7bba61b5
child 287906 2a0e43caef9b8edbf03fc20d1a220c63f0164f5d
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1189137
milestone42.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 1189137 - Don't treat integer stores to unboxed objects as truncated, r=jandem.
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
@@ -9386,17 +9386,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);
 }
@@ -11655,26 +11656,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;
 
@@ -13028,17 +13032,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
@@ -2900,17 +2900,17 @@ IonBuilder::inlineAtomicsStore(CallInfo&
 
     MDefinition* toWrite = value;
     if (value->type() != MIRType_Int32) {
         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;
 }
@@ -3472,17 +3472,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
@@ -9630,27 +9630,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();
@@ -9660,22 +9672,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 {
@@ -9691,16 +9704,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;
 }