Bug 1245627: Canonicalize before storing a floating point value in deterministic mode; r=nbp
authorJeff Walden <jwalden@mit.edu>
Mon, 23 May 2016 22:49:56 +0200
changeset 299593 2c86039b1c6868c2606d343e0906c645468c3fcc
parent 299592 65f18c79794bad38e16519fa97e39a2483c7fab5
child 299594 d813f7d5f90cba36d4401e970f4b559cb3153f72
push id77640
push userbbouvier@mozilla.com
push dateTue, 31 May 2016 07:25:50 +0000
treeherdermozilla-inbound@2c86039b1c68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1245627
milestone49.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 1245627: Canonicalize before storing a floating point value in deterministic mode; r=nbp Patch written by :Waldo and :bbouvier. MozReview-Commit-ID: 6vjiqfk0jvj
js/src/jit-test/tests/asm.js/testSIMD-load-store.js
js/src/jit/MacroAssembler-inl.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/MacroAssembler-arm-inl.h
js/src/jit/arm64/MacroAssembler-arm64-inl.h
js/src/jit/mips32/MacroAssembler-mips32-inl.h
js/src/jit/mips64/MacroAssembler-mips64-inl.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x86-shared/Assembler-x86-shared.h
js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
js/src/jit/x86-shared/CodeGenerator-x86-shared.h
js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/vm/TypedArrayObject.cpp
--- a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js
@@ -166,30 +166,35 @@ var code = `
 `;
 assertThrowsInstanceOf(() => asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), RangeError);
 
 // Float32x4.store
 function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); };
 
 var vec  = SIMD.Float32x4(5,6,7,8);
 var vec2 = SIMD.Float32x4(0,1,2,3);
+var vecWithNaN = SIMD.Float32x4(NaN, 2, NaN, 4);
 
 reset();
 f32s(0, vec);
 assertEqX4(vec, slice(F32, 0, 4));
 
 reset();
 f32s(0, vec2);
 assertEqX4(vec2, slice(F32, 0, 4));
 
 reset();
 f32s(4, vec);
 assertEqX4(vec, slice(F32, 4, 4));
 
 reset();
+f32s(4, vecWithNaN);
+assertEqX4(vecWithNaN, slice(F32, 4, 4));
+
+reset();
 m.f32scst(vec2);
 assertEqX4(vec2, slice(F32, CONSTANT_INDEX, 4));
 
 reset();
 m.f32sbndcheck(CONSTANT_BYTE_INDEX, vec);
 assertEqX4(vec, slice(F32, CONSTANT_INDEX, 4));
 
 //      OOB
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -546,16 +546,76 @@ MacroAssembler::branchTestNeedsIncrement
 void
 MacroAssembler::branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                                      Label* label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     branchTestValue(cond, val, MagicValue(why), label);
 }
 
+// ========================================================================
+// Canonicalization primitives.
+void
+MacroAssembler::canonicalizeFloat(FloatRegister reg)
+{
+    Label notNaN;
+    branchFloat(DoubleOrdered, reg, reg, &notNaN);
+    loadConstantFloat32(float(JS::GenericNaN()), reg);
+    bind(&notNaN);
+}
+
+void
+MacroAssembler::canonicalizeFloatIfDeterministic(FloatRegister reg)
+{
+#ifdef JS_MORE_DETERMINISTIC
+    // See the comment in TypedArrayObjectTemplate::getIndexValue.
+    canonicalizeFloat(reg);
+#endif // JS_MORE_DETERMINISTIC
+}
+
+void
+MacroAssembler::canonicalizeDouble(FloatRegister reg)
+{
+    Label notNaN;
+    branchDouble(DoubleOrdered, reg, reg, &notNaN);
+    loadConstantDouble(JS::GenericNaN(), reg);
+    bind(&notNaN);
+}
+
+void
+MacroAssembler::canonicalizeDoubleIfDeterministic(FloatRegister reg)
+{
+#ifdef JS_MORE_DETERMINISTIC
+    // See the comment in TypedArrayObjectTemplate::getIndexValue.
+    canonicalizeDouble(reg);
+#endif // JS_MORE_DETERMINISTIC
+}
+
+// ========================================================================
+// Memory access primitives.
+template<class T> void
+MacroAssembler::storeDouble(FloatRegister src, const T& dest)
+{
+    canonicalizeDoubleIfDeterministic(src);
+    storeUncanonicalizedDouble(src, dest);
+}
+
+template void MacroAssembler::storeDouble(FloatRegister src, const Address& dest);
+template void MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest);
+
+template<class T> void
+MacroAssembler::storeFloat32(FloatRegister src, const T& dest)
+{
+    canonicalizeFloatIfDeterministic(src);
+    storeUncanonicalizedFloat32(src, dest);
+}
+
+template void MacroAssembler::storeFloat32(FloatRegister src, const Address& dest);
+template void MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& dest);
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 #ifndef JS_CODEGEN_ARM64
 
 template <typename T>
 void
 MacroAssembler::branchTestStackPtr(Condition cond, T t, Label* label)
@@ -586,34 +646,16 @@ MacroAssembler::addToStackPtr(T t)
 template <typename T> void
 MacroAssembler::addStackPtrTo(T t)
 {
     addPtr(getStackPointer(), t);
 }
 
 #endif // !JS_CODEGEN_ARM64
 
-void
-MacroAssembler::canonicalizeFloat(FloatRegister reg)
-{
-    Label notNaN;
-    branchFloat(DoubleOrdered, reg, reg, &notNaN);
-    loadConstantFloat32(float(JS::GenericNaN()), reg);
-    bind(&notNaN);
-}
-
-void
-MacroAssembler::canonicalizeDouble(FloatRegister reg)
-{
-    Label notNaN;
-    branchDouble(DoubleOrdered, reg, reg, &notNaN);
-    loadConstantDouble(JS::GenericNaN(), reg);
-    bind(&notNaN);
-}
-
 template <typename T>
 void
 MacroAssembler::storeObjectOrNull(Register src, const T& dest)
 {
     Label notNull, done;
     branchTestPtr(Assembler::NonZero, src, src, &notNull);
     storeValue(NullValue(), dest);
     jump(&done);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -233,20 +233,16 @@ static void
 StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, const T& dest,
                        unsigned numElems)
 {
     switch (arrayType) {
       case Scalar::Float32:
         masm.storeFloat32(value, dest);
         break;
       case Scalar::Float64:
-#ifdef JS_MORE_DETERMINISTIC
-        // See the comment in TypedArrayObjectTemplate::doubleToNative.
-        masm.canonicalizeDouble(value);
-#endif
         masm.storeDouble(value, dest);
         break;
       case Scalar::Float32x4:
         switch (numElems) {
           case 1:
             masm.storeFloat32(value, dest);
             break;
           case 2:
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1117,28 +1117,48 @@ class MacroAssembler : public MacroAssem
     inline void branchTestPrimitiveImpl(Condition cond, const T& t, Label* label)
         DEFINED_ON(arm, arm64, x86_shared);
     template <typename T, class L>
     inline void branchTestMagicImpl(Condition cond, const T& t, L label)
         DEFINED_ON(arm, arm64, x86_shared);
 
   public:
     // ========================================================================
+    // Canonicalization primitives.
+    inline void canonicalizeDouble(FloatRegister reg);
+    inline void canonicalizeDoubleIfDeterministic(FloatRegister reg);
+
+    inline void canonicalizeFloat(FloatRegister reg);
+    inline void canonicalizeFloatIfDeterministic(FloatRegister reg);
+
+    inline void canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch)
+        DEFINED_ON(x86_shared);
+
+  public:
+    // ========================================================================
     // Memory access primitives.
-    inline void storeDouble(FloatRegister src, const Address& dest)
-        DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
-    inline void storeDouble(FloatRegister src, const BaseIndex& dest)
+    inline void storeUncanonicalizedDouble(FloatRegister src, const Address& dest)
         DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
-    inline void storeDouble(FloatRegister src, const Operand& dest) DEFINED_ON(x86_shared);
-
-    inline void storeFloat32(FloatRegister src, const Address& dest)
+    inline void storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& dest)
         DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
-    inline void storeFloat32(FloatRegister src, const BaseIndex& dest)
+    inline void storeUncanonicalizedDouble(FloatRegister src, const Operand& dest)
+        DEFINED_ON(x86_shared);
+
+    template<class T>
+    inline void storeDouble(FloatRegister src, const T& dest);
+
+    inline void storeUncanonicalizedFloat32(FloatRegister src, const Address& dest)
         DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
-    inline void storeFloat32(FloatRegister src, const Operand& dest) DEFINED_ON(x86_shared);
+    inline void storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& dest)
+        DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
+    inline void storeUncanonicalizedFloat32(FloatRegister src, const Operand& dest)
+        DEFINED_ON(x86_shared);
+
+    template<class T>
+    inline void storeFloat32(FloatRegister src, const T& dest);
 
     inline void storeFloat32x3(FloatRegister src, const Address& dest) PER_SHARED_ARCH;
     inline void storeFloat32x3(FloatRegister src, const BaseIndex& dest) PER_SHARED_ARCH;
 
     template <typename T>
     void storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const T& dest,
                            MIRType slotType) PER_ARCH;
 
@@ -1327,20 +1347,16 @@ class MacroAssembler : public MacroAssem
 
         callPreBarrier(address, type);
         jump(&done);
 
         haltingAlign(8);
         bind(&done);
     }
 
-    inline void canonicalizeDouble(FloatRegister reg);
-
-    inline void canonicalizeFloat(FloatRegister reg);
-
     template<typename T>
     void loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp, Label* fail,
                             bool canonicalizeDoubles = true, unsigned numElems = 0);
 
     template<typename T>
     void loadFromTypedArray(Scalar::Type arrayType, const T& src, const ValueOperand& dest, bool allowDouble,
                             Register temp, Label* fail);
 
@@ -1589,17 +1605,17 @@ class MacroAssembler : public MacroAssem
     void tracelogStartId(Register logger, Register textId);
     void tracelogStartEvent(Register logger, Register event);
     void tracelogStopId(Register logger, uint32_t textId, bool force = false);
     void tracelogStopId(Register logger, Register textId);
 #endif
 
 #define DISPATCH_FLOATING_POINT_OP(method, type, arg1d, arg1f, arg2)    \
     MOZ_ASSERT(IsFloatingPointType(type));                              \
-    if (type == MIRType::Double)                                         \
+    if (type == MIRType::Double)                                        \
         method##Double(arg1d, arg2);                                    \
     else                                                                \
         method##Float32(arg1f, arg2);                                   \
 
     void loadConstantFloatingPoint(double d, float f, FloatRegister dest, MIRType destType) {
         DISPATCH_FLOATING_POINT_OP(loadConstant, destType, d, f, dest);
     }
     void boolValueToFloatingPoint(ValueOperand value, FloatRegister dest, MIRType destType) {
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2325,37 +2325,38 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
     if (ptr->isConstant()) {
         MOZ_ASSERT(!mir->needsBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         MOZ_ASSERT(ptrImm >= 0);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
             Address addr(HeapReg, ptrImm);
             if (size == 32)
-                masm.ma_vstr(vd.singleOverlay(), addr, Assembler::Always);
+                masm.storeFloat32(vd, addr);
             else
-                masm.ma_vstr(vd, addr, Assembler::Always);
+                masm.storeDouble(vd, addr);
         } else {
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
                                   ToRegister(ins->value()), Offset, Assembler::Always);
         }
         memoryBarrier(mir->barrierAfter());
         return;
     }
 
     Register ptrReg = ToRegister(ptr);
 
     if (!mir->needsBoundsCheck()) {
         Register ptrReg = ToRegister(ptr);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
+            BaseIndex addr(HeapReg, ptrReg, TimesOne, 0);
             if (size == 32)
-                masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, 0, Assembler::Always);
+                masm.storeFloat32(vd, addr);
             else
-                masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Always);
+                masm.storeDouble(vd, addr);
         } else {
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                                   ToRegister(ins->value()), Offset, Assembler::Always);
         }
         memoryBarrier(mir->barrierAfter());
         return;
     }
 
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -1201,34 +1201,34 @@ MacroAssembler::branchTestMagic(Conditio
 {
     branchTestMagic(cond, valaddr, label);
     branch32(cond, ToPayload(valaddr), Imm32(why), label);
 }
 
 // ========================================================================
 // Memory access primitives.
 void
-MacroAssembler::storeDouble(FloatRegister src, const Address& addr)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
 {
     ma_vstr(src, addr);
 }
 void
-MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
 {
     uint32_t scale = Imm32::ShiftOf(addr.scale).value;
     ma_vstr(src, addr.base, addr.index, scale, addr.offset);
 }
 
 void
-MacroAssembler::storeFloat32(FloatRegister src, const Address& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
 {
     ma_vstr(src.asSingle(), addr);
 }
 void
-MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
     uint32_t scale = Imm32::ShiftOf(addr.scale).value;
     ma_vstr(src.asSingle(), addr.base, addr.index, scale, addr.offset);
 }
 
 void
 MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest)
 {
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -1296,33 +1296,33 @@ MacroAssembler::branchTestMagic(Conditio
     uint64_t magic = MagicValue(why).asRawBits();
     cmpPtr(valaddr, ImmWord(magic));
     B(label, cond);
 }
 
 // ========================================================================
 // Memory access primitives.
 void
-MacroAssembler::storeDouble(FloatRegister src, const Address& dest)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& dest)
 {
     Str(ARMFPRegister(src, 64), MemOperand(ARMRegister(dest.base, 64), dest.offset));
 }
 void
-MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& dest)
 {
     doBaseIndex(ARMFPRegister(src, 64), dest, vixl::STR_d);
 }
 
 void
-MacroAssembler::storeFloat32(FloatRegister src, const Address& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
 {
     Str(ARMFPRegister(src, 32), MemOperand(ARMRegister(addr.base, 64), addr.offset));
 }
 void
-MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
     doBaseIndex(ARMFPRegister(src, 32), addr, vixl::STR_s);
 }
 
 void
 MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest)
 {
     MOZ_CRASH("NYI");
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -418,34 +418,34 @@ MacroAssembler::branchTestMagic(Conditio
 {
     branchTestMagic(cond, valaddr, label);
     branch32(cond, ToPayload(valaddr), Imm32(why), label);
 }
 
 // ========================================================================
 // Memory access primitives.
 void
-MacroAssembler::storeDouble(FloatRegister src, const Address& addr)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
 {
     ma_sd(src, addr);
 }
 void
-MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
 {
     MOZ_ASSERT(addr.offset == 0);
     ma_sd(src, addr);
 }
 
 void
-MacroAssembler::storeFloat32(FloatRegister src, const Address& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
 {
     ma_ss(src, addr);
 }
 void
-MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
     MOZ_ASSERT(addr.offset == 0);
     ma_ss(src, addr);
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -375,34 +375,34 @@ MacroAssembler::branchTestMagic(Conditio
     ScratchRegisterScope scratch(*this);
     loadPtr(valaddr, scratch);
     ma_b(scratch, ImmWord(magic), label, cond);
 }
 
 // ========================================================================
 // Memory access primitives.
 void
-MacroAssembler::storeDouble(FloatRegister src, const Address& addr)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
 {
     ma_sd(src, addr);
 }
 void
-MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& addr)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
 {
     MOZ_ASSERT(addr.offset == 0);
     ma_sd(src, addr);
 }
 
 void
-MacroAssembler::storeFloat32(FloatRegister src, const Address& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
 {
     ma_ss(src, addr);
 }
 void
-MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& addr)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
     MOZ_ASSERT(addr.offset == 0);
     ma_ss(src, addr);
 }
 
 //}}} check_macroassembler_style
 // ===============================================================
 
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -720,19 +720,19 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAs
 void
 CodeGeneratorX64::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in,
                             const Operand& dstAddr)
 {
     switch (type) {
       case Scalar::Float32x4: {
         switch (numElems) {
           // In memory-to-register mode, movss zeroes out the high lanes.
-          case 1: masm.storeFloat32(in, dstAddr); break;
+          case 1: masm.storeUncanonicalizedFloat32(in, dstAddr); break;
           // See comment above, which also applies to movsd.
-          case 2: masm.storeDouble(in, dstAddr); break;
+          case 2: masm.storeUncanonicalizedDouble(in, dstAddr); break;
           case 4: masm.storeUnalignedSimd128Float(in, dstAddr); break;
           default: MOZ_CRASH("unexpected size for partial load");
         }
         break;
       }
       case Scalar::Int32x4: {
         switch (numElems) {
           // In memory-to-register mode, movd zeroes out the high lanes.
@@ -811,21 +811,23 @@ CodeGeneratorX64::emitSimdStore(LAsmJSSt
         cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
 }
 
 void
 CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     const MAsmJSStoreHeap* mir = ins->mir();
     Scalar::Type accessType = mir->accessType();
+    const LAllocation* value = ins->value();
+
+    canonicalizeIfDeterministic(accessType, value);
 
     if (Scalar::isSimdType(accessType))
         return emitSimdStore(ins);
 
-    const LAllocation* value = ins->value();
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(HeapReg, mir->offset())
                       : Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
     memoryBarrier(mir->barrierBefore());
 
     Label* rejoin;
@@ -846,28 +848,39 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
           case Scalar::Int32x4:
           case Scalar::Uint8Clamped:
           case Scalar::MaxTypedArrayViewType:
               MOZ_CRASH("unexpected array type");
         }
     } else {
         switch (accessType) {
           case Scalar::Int8:
-          case Scalar::Uint8:        masm.movb(ToRegister(value), dstAddr); break;
+          case Scalar::Uint8:
+            masm.movb(ToRegister(value), dstAddr);
+            break;
           case Scalar::Int16:
-          case Scalar::Uint16:       masm.movw(ToRegister(value), dstAddr); break;
+          case Scalar::Uint16:
+            masm.movw(ToRegister(value), dstAddr);
+            break;
           case Scalar::Int32:
-          case Scalar::Uint32:       masm.movl(ToRegister(value), dstAddr); break;
-          case Scalar::Float32:      masm.storeFloat32(ToFloatRegister(value), dstAddr); break;
-          case Scalar::Float64:      masm.storeDouble(ToFloatRegister(value), dstAddr); break;
+          case Scalar::Uint32:
+            masm.movl(ToRegister(value), dstAddr);
+            break;
+          case Scalar::Float32:
+            masm.storeUncanonicalizedFloat32(ToFloatRegister(value), dstAddr);
+            break;
+          case Scalar::Float64:
+            masm.storeUncanonicalizedDouble(ToFloatRegister(value), dstAddr);
+            break;
           case Scalar::Float32x4:
-          case Scalar::Int32x4:      MOZ_CRASH("SIMD stores must be handled in emitSimdStore");
+          case Scalar::Int32x4:
+            MOZ_CRASH("SIMD stores must be handled in emitSimdStore");
           case Scalar::Uint8Clamped:
           case Scalar::MaxTypedArrayViewType:
-              MOZ_CRASH("unexpected array type");
+            MOZ_CRASH("unexpected array type");
         }
     }
     uint32_t after = masm.size();
 
     verifyHeapAccessDisassembly(before, after, /*isLoad=*/false, accessType, 0, dstAddr, *value);
 
     if (rejoin) {
         cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -2327,16 +2327,19 @@ class AssemblerX86Shared : public Assemb
         vcmpps(X86Encoding::ConditionCmp_LE, src1, src0, dest);
     }
     void vcmpunordps(const Operand& src1, FloatRegister src0, FloatRegister dest) {
         vcmpps(X86Encoding::ConditionCmp_UNORD, src1, src0, dest);
     }
     void vcmpneqps(const Operand& src1, FloatRegister src0, FloatRegister dest) {
         vcmpps(X86Encoding::ConditionCmp_NEQ, src1, src0, dest);
     }
+    void vcmpordps(const Operand& src1, FloatRegister src0, FloatRegister dest) {
+        vcmpps(X86Encoding::ConditionCmp_ORD, src1, src0, dest);
+    }
     void vrcpps(const Operand& src, FloatRegister dest) {
         MOZ_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::FPREG:
             masm.vrcpps_rr(src.fpu(), dest.encoding());
             break;
           case Operand::MEM_REG_DISP:
             masm.vrcpps_mr(src.disp(), src.base(), dest.encoding());
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -4062,10 +4062,42 @@ CodeGeneratorX86Shared::emitWasmSignedTr
         masm.vcvttss2si(input, output);
     else
         MOZ_CRASH("unexpected type in emitWasmSignedTruncateToInt32");
 
     masm.cmp32(output, Imm32(1));
     masm.j(Assembler::Overflow, ool->entry());
 }
 
+void
+CodeGeneratorX86Shared::canonicalizeIfDeterministic(Scalar::Type type, const LAllocation* value)
+{
+#ifdef JS_MORE_DETERMINISTIC
+    switch (type) {
+      case Scalar::Float32: {
+        FloatRegister in = ToFloatRegister(value);
+        masm.canonicalizeFloatIfDeterministic(in);
+        break;
+      }
+      case Scalar::Float64: {
+        FloatRegister in = ToFloatRegister(value);
+        masm.canonicalizeDoubleIfDeterministic(in);
+        break;
+      }
+      case Scalar::Float32x4: {
+        FloatRegister in = ToFloatRegister(value);
+        MOZ_ASSERT(in.isSimd128());
+        FloatRegister scratch = in != xmm0.asSimd128() ? xmm0 : xmm1;
+        masm.push(scratch);
+        masm.canonicalizeFloat32x4(in, scratch);
+        masm.pop(scratch);
+        break;
+      }
+      default: {
+        // Other types don't need canonicalization.
+        break;
+      }
+    }
+#endif // JS_MORE_DETERMINISTIC
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -334,16 +334,18 @@ class CodeGeneratorX86Shared : public Co
     void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                     const T& mem, Register temp1, Register temp2, AnyRegister output);
 
     // Generating no result.
     template<typename S, typename T>
     void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value, const T& mem);
 
     void setReturnDoubleRegs(LiveRegisterSet* regs);
+
+    void canonicalizeIfDeterministic(Scalar::Type type, const LAllocation* value);
 };
 
 // An out-of-line bailout thunk.
 class OutOfLineBailout : public OutOfLineCodeBase<CodeGeneratorX86Shared>
 {
     LSnapshot* snapshot_;
 
   public:
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h
@@ -906,67 +906,94 @@ template <typename T, class L>
 void
 MacroAssembler::branchTestMagicImpl(Condition cond, const T& t, L label)
 {
     cond = testMagic(cond, t);
     j(cond, label);
 }
 
 // ========================================================================
+// Canonicalization primitives.
+void
+MacroAssembler::canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch)
+{
+    ScratchSimd128Scope scratch2(*this);
+
+    MOZ_ASSERT(scratch.asSimd128() != scratch2.asSimd128());
+    MOZ_ASSERT(reg.asSimd128() != scratch2.asSimd128());
+    MOZ_ASSERT(reg.asSimd128() != scratch.asSimd128());
+
+    FloatRegister mask = scratch;
+    vcmpordps(Operand(reg), reg, mask);
+
+    FloatRegister ifFalse = scratch2;
+    float nanf = float(JS::GenericNaN());
+    loadConstantSimd128Float(SimdConstant::SplatX4(nanf), ifFalse);
+
+    bitwiseAndX4(Operand(mask), reg);
+    bitwiseAndNotX4(Operand(ifFalse), mask);
+    bitwiseOrX4(Operand(mask), reg);
+}
+
+// ========================================================================
 // Memory access primitives.
 void
-MacroAssembler::storeDouble(FloatRegister src, const Address& dest)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& dest)
 {
     vmovsd(src, dest);
 }
 void
-MacroAssembler::storeDouble(FloatRegister src, const BaseIndex& dest)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& dest)
 {
     vmovsd(src, dest);
 }
 void
-MacroAssembler::storeDouble(FloatRegister src, const Operand& dest)
+MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Operand& dest)
 {
     switch (dest.kind()) {
       case Operand::MEM_REG_DISP:
-        storeDouble(src, dest.toAddress());
+        storeUncanonicalizedDouble(src, dest.toAddress());
         break;
       case Operand::MEM_SCALE:
-        storeDouble(src, dest.toBaseIndex());
+        storeUncanonicalizedDouble(src, dest.toBaseIndex());
         break;
       default:
         MOZ_CRASH("unexpected operand kind");
     }
 }
 
+template void MacroAssembler::storeDouble(FloatRegister src, const Operand& dest);
+
 void
-MacroAssembler::storeFloat32(FloatRegister src, const Address& dest)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& dest)
 {
     vmovss(src, dest);
 }
 void
-MacroAssembler::storeFloat32(FloatRegister src, const BaseIndex& dest)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& dest)
 {
     vmovss(src, dest);
 }
 void
-MacroAssembler::storeFloat32(FloatRegister src, const Operand& dest)
+MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Operand& dest)
 {
     switch (dest.kind()) {
       case Operand::MEM_REG_DISP:
-        storeFloat32(src, dest.toAddress());
+        storeUncanonicalizedFloat32(src, dest.toAddress());
         break;
       case Operand::MEM_SCALE:
-        storeFloat32(src, dest.toBaseIndex());
+        storeUncanonicalizedFloat32(src, dest.toBaseIndex());
         break;
       default:
         MOZ_CRASH("unexpected operand kind");
     }
 }
 
+template void MacroAssembler::storeFloat32(FloatRegister src, const Operand& dest);
+
 void
 MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest)
 {
     Address destZ(dest);
     destZ.offset += 2 * sizeof(int32_t);
     storeDouble(src, dest);
     ScratchSimd128Scope scratch(*this);
     vmovhlps(src, scratch, scratch);
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -468,38 +468,58 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
 }
 
 void
 CodeGeneratorX86::store(Scalar::Type accessType, const LAllocation* value, const Operand& dstAddr)
 {
     switch (accessType) {
       case Scalar::Int8:
       case Scalar::Uint8Clamped:
-      case Scalar::Uint8:        masm.movbWithPatch(ToRegister(value), dstAddr); break;
+      case Scalar::Uint8:
+        masm.movbWithPatch(ToRegister(value), dstAddr);
+        break;
+
       case Scalar::Int16:
-      case Scalar::Uint16:       masm.movwWithPatch(ToRegister(value), dstAddr); break;
+      case Scalar::Uint16:
+        masm.movwWithPatch(ToRegister(value), dstAddr);
+        break;
+
       case Scalar::Int32:
-      case Scalar::Uint32:       masm.movlWithPatch(ToRegister(value), dstAddr); break;
-      case Scalar::Float32:      masm.vmovssWithPatch(ToFloatRegister(value), dstAddr); break;
-      case Scalar::Float64:      masm.vmovsdWithPatch(ToFloatRegister(value), dstAddr); break;
+      case Scalar::Uint32:
+        masm.movlWithPatch(ToRegister(value), dstAddr);
+        break;
+
+      case Scalar::Float32:
+        masm.vmovssWithPatch(ToFloatRegister(value), dstAddr);
+        break;
+
+      case Scalar::Float64:
+        masm.vmovsdWithPatch(ToFloatRegister(value), dstAddr);
+        break;
+
       case Scalar::Float32x4:
-      case Scalar::Int32x4:      MOZ_CRASH("SIMD stores should be handled in emitSimdStore");
-      case Scalar::MaxTypedArrayViewType: MOZ_CRASH("unexpected type");
+      case Scalar::Int32x4:
+        MOZ_CRASH("SIMD stores should be handled in emitSimdStore");
+
+      case Scalar::MaxTypedArrayViewType:
+        MOZ_CRASH("unexpected type");
     }
 }
 
 void
 CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins)
 {
     MStoreTypedArrayElementStatic* mir = ins->mir();
     Scalar::Type accessType = mir->accessType();
     Register ptr = ToRegister(ins->ptr());
     const LAllocation* value = ins->value();
+
+    canonicalizeIfDeterministic(accessType, value);
+
     uint32_t offset = mir->offset();
-
     if (!mir->needsBoundsCheck()) {
         Operand dstAddr(ptr, int32_t(mir->base().asValue()) + int32_t(offset));
         store(accessType, value, dstAddr);
         return;
     }
 
     MOZ_ASSERT(offset == 0);
     masm.cmpPtr(ptr, ImmWord(mir->length()));
@@ -600,21 +620,23 @@ CodeGeneratorX86::emitSimdStore(LAsmJSSt
         cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
 }
 
 void
 CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     const MAsmJSStoreHeap* mir = ins->mir();
     Scalar::Type accessType = mir->accessType();
+    const LAllocation* value = ins->value();
+
+    canonicalizeIfDeterministic(accessType, value);
 
     if (Scalar::isSimdType(accessType))
         return emitSimdStore(ins);
 
-    const LAllocation* value = ins->value();
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(PatchedAbsoluteAddress(mir->offset()))
                       : Operand(ToRegister(ptr), mir->offset());
 
     memoryBarrier(mir->barrierBefore());
 
     Label* rejoin;
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -44,22 +44,17 @@
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
-using mozilla::IsNaN;
-using mozilla::NegativeInfinity;
-using mozilla::PodCopy;
-using mozilla::PositiveInfinity;
 using JS::CanonicalizeNaN;
-using JS::GenericNaN;
 using JS::ToInt32;
 using JS::ToUint32;
 
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by