Bug 888109: Float32 general optimizations for IonMonkey: framework and arithmetic operations; r=sstangl,nbp
authorBenjamin Bouvier <bbouvier@mozilla.com>
Thu, 18 Jul 2013 15:13:15 -0700
changeset 160120 a43cf13bd6a653461d3987f775a4d1060d8d912f
parent 160119 4a6ac4c0ab583efad5e4854876e6088b37a2be38
child 160121 e9a2e269aa9e34fff22d6f1a71a2e281fb908304
push id407
push userlsblakk@mozilla.com
push dateTue, 03 Dec 2013 03:32:50 +0000
treeherdermozilla-release@babf8c9ebc52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstangl, nbp
bugs888109
milestone26.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 888109: Float32 general optimizations for IonMonkey: framework and arithmetic operations; r=sstangl,nbp
js/public/Value.h
js/src/assembler/assembler/X86Assembler.h
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonFrames.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/IonTypes.h
js/src/jit/LICM.cpp
js/src/jit/LIR-Common.h
js/src/jit/LIR.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/ParallelSafetyAnalysis.cpp
js/src/jit/RangeAnalysis.cpp
js/src/jit/SnapshotReader.h
js/src/jit/SnapshotWriter.h
js/src/jit/Snapshots.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/TypePolicy.h
js/src/jit/arm/Lowering-arm.h
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/shared/Assembler-x86-shared.h
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.h
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/shared/Lowering-shared.h
js/src/jit/shared/Lowering-x86-shared.cpp
js/src/jit/shared/Lowering-x86-shared.h
js/src/jit/shared/MacroAssembler-x86-shared.h
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/Assembler-x86.cpp
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
js/src/jit/x86/MacroAssembler-x86.cpp
js/src/jit/x86/MacroAssembler-x86.h
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1269,16 +1269,24 @@ static inline Value
 DoubleValue(double dbl)
 {
     Value v;
     v.setDouble(dbl);
     return v;
 }
 
 static inline Value
+Float32Value(float f)
+{
+    Value v;
+    v.setDouble(f);
+    return v;
+}
+
+static inline Value
 StringValue(JSString *str)
 {
     Value v;
     v.setString(str);
     return v;
 }
 
 static inline Value
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -2175,32 +2175,55 @@ public:
     void addsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("addsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void addss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("addss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void addsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("addsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void addss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("addss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
 #if !WTF_CPU_X86_64
     void addsd_mr(const void* address, XMMRegisterID dst)
     {
         spew("addsd      %p, %s",
              address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
     }
+    void addss_mr(const void* address, XMMRegisterID dst)
+    {
+        spew("addss      %p, %s",
+             address, nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
+    }
 #endif
 
     void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("cvtss2sd   %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_CVTSS2SD_VsdEd, (RegisterID)dst, (RegisterID)src);
@@ -2209,16 +2232,24 @@ public:
     void cvtsd2ss_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("cvtsd2ss   %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSD2SS_VsdEd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void cvtsi2ss_rr(RegisterID src, XMMRegisterID dst)
+    {
+        spew("cvtsi2ss   %s, %s",
+             nameIReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
+    }
+
     void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst)
     {
         spew("cvtsi2sd   %s, %s",
              nameIReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
     }
 
@@ -2243,16 +2274,32 @@ public:
     void cvtsi2sd_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
     {
         spew("cvtsi2sd   %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
     }
 
+    void cvtsi2ss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("cvtsi2ss   %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset);
+    }
+
+    void cvtsi2ss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
+    {
+        spew("cvtsi2ss   %d(%s,%s,%d), %s",
+             offset, nameIReg(base), nameIReg(index), 1<<scale, nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
+    }
+
 #if !WTF_CPU_X86_64
     void cvtsi2sd_mr(const void* address, XMMRegisterID dst)
     {
         spew("cvtsi2sd   %p, %s",
              address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, address);
     }
@@ -2261,16 +2308,24 @@ public:
     void cvttsd2si_rr(XMMRegisterID src, RegisterID dst)
     {
         spew("cvttsd2si  %s, %s",
              nameFPReg(src), nameIReg(4, dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
 
+    void cvttss2si_rr(XMMRegisterID src, RegisterID dst)
+    {
+        spew("cvttss2si  %s, %s",
+             nameFPReg(src), nameIReg(4, dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
+    }
+
 #if WTF_CPU_X86_64
     void cvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
     {
         spew("cvttsd2sq  %s, %s",
              nameFPReg(src), nameIReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp64(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
     }
@@ -2386,52 +2441,32 @@ public:
     void movss_rm_disp32(XMMRegisterID src, int offset, RegisterID base)
     {
         spew("movss      %s, %s0x%x(%s)",
              nameFPReg(src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
     }
 
-#if !WTF_CPU_X86_64
-    void movss_rm(XMMRegisterID src, const void* addr)
-    {
-        spew("movss      %s, %p",
-             nameFPReg(src), addr);
-        m_formatter.prefix(PRE_SSE_F3);
-        m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, addr);
-    }
-#endif
-
     void movss_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("movss      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
     void movss_mr_disp32(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("movss      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F3);
         m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
-#if !WTF_CPU_X86_64
-    void movss_mr(const void* addr, XMMRegisterID dst)
-    {
-        spew("movss      %p, %s",
-             addr, nameFPReg(dst));
-        m_formatter.prefix(PRE_SSE_F3);
-        m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, addr);
-    }
-#endif
-
     void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("movsd      %s, %d(%s,%s,%d)",
              nameFPReg(src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, base, index, scale, offset);
     }
 
@@ -2478,32 +2513,56 @@ public:
     void movsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("movsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void movss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("movss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
 #if !WTF_CPU_X86_64
     void movsd_mr(const void* address, XMMRegisterID dst)
     {
         spew("movsd      %p, %s",
              address, nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
     }
 
+    void movss_mr(const void* address, XMMRegisterID dst)
+    {
+        spew("movss      %p, %s",
+             address, nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
+    }
+
     void movsd_rm(XMMRegisterID src, const void* address)
     {
         spew("movsd      %s, %p",
              nameFPReg(src), address);
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
     }
+
+    void movss_rm(XMMRegisterID src, const void* address)
+    {
+        spew("movss      %s, %p",
+             nameFPReg(src), address);
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
+    }
 #else
     JmpSrc movsd_ripr(XMMRegisterID dst)
     {
         spew("movsd      \?(%%rip), %s",
              nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteRipOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, 0);
         return JmpSrc(m_formatter.size());
@@ -2553,48 +2612,87 @@ public:
     void mulsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("mulsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void mulss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("mulss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("mulsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void mulss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("mulss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
     void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst)
     {
         FIXME_INSN_PRINTING;
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_PEXTRW_GdUdIb, (RegisterID)dst, (RegisterID)src);
         m_formatter.immediate8(whichWord);
     }
 
     void subsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("subsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void subss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("subss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void subsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("subsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void subss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("subss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
+    void ucomiss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("ucomiss    %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("ucomisd    %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
@@ -2609,32 +2707,55 @@ public:
     void divsd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("divsd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void divss_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("divss      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void divsd_mr(int offset, RegisterID base, XMMRegisterID dst)
     {
         spew("divsd      %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_F2);
         m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
     }
 
+    void divss_mr(int offset, RegisterID base, XMMRegisterID dst)
+    {
+        spew("divss      %s0x%x(%s), %s",
+             PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_F3);
+        m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
+    }
+
     void xorpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("xorpd      %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void xorps_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        spew("xorps      %s, %s",
+             nameFPReg(src), nameFPReg(dst));
+        m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void orpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         spew("orpd       %s, %s",
              nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_ORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
     }
 
@@ -2789,16 +2910,21 @@ public:
         m_formatter.jumpTablePointer(ptr);
     }
 
     void doubleConstant(double d)
     {
         spew(".double %.20f", d);
         m_formatter.doubleConstant(d);
     }
+    void floatConstant(float f)
+    {
+        spew(".float %.20f", f);
+        m_formatter.floatConstant(f);
+    }
 
     void int64Constant(int64_t i)
     {
         spew(".quad %lld", (long long)i);
         m_formatter.int64Constant(i);
     }
 
     // Linking & patching:
@@ -3429,16 +3555,27 @@ private:
             union {
                 uint64_t u64;
                 double d;
             } u;
             u.d = d;
             m_buffer.putInt64Unchecked(u.u64);
         }
 
+        void floatConstant(float f)
+        {
+            m_buffer.ensureSpace(sizeof(float));
+            union {
+                uint32_t u32;
+                float f;
+            } u;
+            u.f = f;
+            m_buffer.putIntUnchecked(u.u32);
+        }
+
         void int64Constant(int64_t i)
         {
             m_buffer.ensureSpace(sizeof(int64_t));
             m_buffer.putInt64Unchecked(i);
         }
 
         // Administrative methods:
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -10,16 +10,17 @@
 #include "jslibmath.h"
 
 #include "builtin/Eval.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/BaselineHelpers.h"
 #include "jit/BaselineJIT.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
+#include "jit/Lowering.h"
 #include "jit/PerfSpewer.h"
 #include "jit/VMFunctions.h"
 
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/ScopeObject-inl.h"
@@ -5163,17 +5164,24 @@ ICSetElem_TypedArray::Compiler::generate
     regs = availableGeneralRegs(0);
     regs.takeUnchecked(obj);
     regs.takeUnchecked(key);
     regs.take(scratchReg);
     Register secondScratch = regs.takeAny();
 
     if (type_ == ScalarTypeRepresentation::TYPE_FLOAT32 || type_ == ScalarTypeRepresentation::TYPE_FLOAT64) {
         masm.ensureDouble(value, FloatReg0, &failure);
-        masm.storeToTypedFloatArray(type_, FloatReg0, dest);
+        if (LIRGenerator::allowFloat32Optimizations() &&
+            type_ == ScalarTypeRepresentation::TYPE_FLOAT32)
+        {
+            masm.convertDoubleToFloat(FloatReg0, ScratchFloatReg);
+            masm.storeToTypedFloatArray(type_, ScratchFloatReg, dest);
+        } else {
+            masm.storeToTypedFloatArray(type_, FloatReg0, dest);
+        }
         EmitReturnFromIC(masm);
     } else if (type_ == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
         Label notInt32;
         masm.branchTestInt32(Assembler::NotEqual, value, &notInt32);
         masm.unboxInt32(value, secondScratch);
         masm.clampIntToUint8(secondScratch, secondScratch);
 
         Label clamped;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -298,23 +298,105 @@ CodeGenerator::visitValueToDouble(LValue
     masm.bind(&isDouble);
     masm.unboxDouble(operand, output);
     masm.bind(&done);
 
     return true;
 }
 
 bool
+CodeGenerator::visitValueToFloat32(LValueToFloat32 *lir)
+{
+    MToFloat32 *mir = lir->mir();
+    ValueOperand operand = ToValue(lir, LValueToFloat32::Input);
+    FloatRegister output = ToFloatRegister(lir->output());
+
+    Register tag = masm.splitTagForTest(operand);
+
+    Label isDouble, isInt32, isBool, isNull, isUndefined, done;
+    bool hasBoolean = false, hasNull = false, hasUndefined = false;
+
+    masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
+    masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
+
+    if (mir->conversion() != MToFloat32::NumbersOnly) {
+        masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
+        masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
+        hasBoolean = true;
+        hasUndefined = true;
+        if (mir->conversion() != MToFloat32::NonNullNonStringPrimitives) {
+            masm.branchTestNull(Assembler::Equal, tag, &isNull);
+            hasNull = true;
+        }
+    }
+
+    if (!bailout(lir->snapshot()))
+        return false;
+
+    if (hasNull) {
+        static float DoubleZeroFloat = DoubleZero;
+        masm.bind(&isNull);
+        masm.loadStaticFloat32(&DoubleZeroFloat, output);
+        masm.jump(&done);
+    }
+
+    if (hasUndefined) {
+        static float js_NaN_float = js_NaN;
+        masm.bind(&isUndefined);
+        masm.loadStaticFloat32(&js_NaN_float, output);
+        masm.jump(&done);
+    }
+
+    if (hasBoolean) {
+        masm.bind(&isBool);
+        masm.boolValueToFloat32(operand, output);
+        masm.jump(&done);
+    }
+
+    masm.bind(&isInt32);
+    masm.int32ValueToFloat32(operand, output);
+    masm.jump(&done);
+
+    masm.bind(&isDouble);
+    masm.unboxDouble(operand, output);
+    masm.convertDoubleToFloat(output, output);
+    masm.bind(&done);
+
+    return true;
+}
+
+bool
 CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
 {
     masm.convertInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
     return true;
 }
 
 bool
+CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir)
+{
+    masm.convertFloatToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
+    return true;
+}
+
+bool
+CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir)
+{
+    masm.convertDoubleToFloat(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
+    return true;
+}
+
+bool
+CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir)
+{
+    masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
+    return true;
+}
+
+bool
 CodeGenerator::visitDoubleToInt32(LDoubleToInt32 *lir)
 {
     Label fail;
     FloatRegister input = ToFloatRegister(lir->input());
     Register output = ToRegister(lir->output());
     masm.convertDoubleToInt32(input, output, &fail, lir->mir()->canBeNegativeZero());
     if (!bailoutFrom(&fail, lir->snapshot()))
         return false;
@@ -7435,16 +7517,29 @@ CodeGenerator::visitAssertRangeD(LAssert
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister temp = ToFloatRegister(ins->temp());
     Range *r = ins->range();
 
     return emitAssertRangeD(r, input, temp);
 }
 
 bool
+CodeGenerator::visitAssertRangeF(LAssertRangeF *ins)
+{
+    FloatRegister input = ToFloatRegister(ins->input());
+    FloatRegister temp = ToFloatRegister(ins->temp());
+    Range *r = ins->range();
+
+    masm.convertFloatToDouble(input, input);
+    bool success = emitAssertRangeD(r, input, temp);
+    masm.convertDoubleToFloat(input, input);
+    return success;
+}
+
+bool
 CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
 {
     Range *r = ins->range();
     const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
     Register tag = masm.splitTagForTest(value);
     Label done;
 
     {
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -66,16 +66,20 @@ class CodeGenerator : public CodeGenerat
     bool visitDefFun(LDefFun *lir);
     bool visitOsrEntry(LOsrEntry *lir);
     bool visitOsrScopeChain(LOsrScopeChain *lir);
     bool visitStackArgT(LStackArgT *lir);
     bool visitStackArgV(LStackArgV *lir);
     bool visitMoveGroup(LMoveGroup *group);
     bool visitValueToInt32(LValueToInt32 *lir);
     bool visitValueToDouble(LValueToDouble *lir);
+    bool visitValueToFloat32(LValueToFloat32 *lir);
+    bool visitFloat32ToDouble(LFloat32ToDouble *lir);
+    bool visitDoubleToFloat32(LDoubleToFloat32 *lir);
+    bool visitInt32ToFloat32(LInt32ToFloat32 *lir);
     bool visitInt32ToDouble(LInt32ToDouble *lir);
     void emitOOLTestObject(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch);
     bool visitTestOAndBranch(LTestOAndBranch *lir);
     bool visitTestVAndBranch(LTestVAndBranch *lir);
     bool visitFunctionDispatch(LFunctionDispatch *lir);
     bool visitTypeObjectDispatch(LTypeObjectDispatch *lir);
     bool visitIntToString(LIntToString *lir);
     bool visitDoubleToString(LDoubleToString *lir);
@@ -300,16 +304,17 @@ class CodeGenerator : public CodeGenerat
     bool visitGetElementParIC(OutOfLineUpdateCache *ool, DataPtr<GetElementParIC> &ic);
     bool visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic);
     bool visitBindNameIC(OutOfLineUpdateCache *ool, DataPtr<BindNameIC> &ic);
     bool visitNameIC(OutOfLineUpdateCache *ool, DataPtr<NameIC> &ic);
     bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, DataPtr<CallsiteCloneIC> &ic);
 
     bool visitAssertRangeI(LAssertRangeI *ins);
     bool visitAssertRangeD(LAssertRangeD *ins);
+    bool visitAssertRangeF(LAssertRangeF *ins);
     bool visitAssertRangeV(LAssertRangeV *ins);
 
     IonScriptCounts *extractUnassociatedScriptCounts() {
         IonScriptCounts *counts = unassociatedScriptCounts_;
         unassociatedScriptCounts_ = NULL;  // prevent delete in dtor
         return counts;
     }
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -7,16 +7,17 @@
 #include "jit/IonAnalysis.h"
 
 #include "jsanalyze.h"
 
 #include "jit/BaselineInspector.h"
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
 #include "jit/LIR.h"
+#include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 
 #include "jsinferinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
@@ -395,57 +396,75 @@ class TypeAnalyzer
     bool respecialize(MPhi *phi, MIRType type);
     bool propagateSpecialization(MPhi *phi);
     bool specializePhis();
     void replaceRedundantPhi(MPhi *phi);
     void adjustPhiInputs(MPhi *phi);
     bool adjustInputs(MDefinition *def);
     bool insertConversions();
 
+    bool graphContainsFloat32();
+    bool markPhiConsumers();
+    bool markPhiProducers();
+    bool specializeValidFloatOps();
+    bool tryEmitFloatOperations();
+
   public:
     TypeAnalyzer(MIRGenerator *mir, MIRGraph &graph)
       : mir(mir), graph(graph)
     { }
 
     bool analyze();
 };
 
 } /* anonymous namespace */
 
 // Try to specialize this phi based on its non-cyclic inputs.
 static MIRType
 GuessPhiType(MPhi *phi)
 {
     MIRType type = MIRType_None;
+    bool convertibleToFloat32 = false;
     for (size_t i = 0, e = phi->numOperands(); i < e; i++) {
         MDefinition *in = phi->getOperand(i);
         if (in->isPhi()) {
             if (!in->toPhi()->triedToSpecialize())
                 continue;
             if (in->type() == MIRType_None) {
                 // The operand is a phi we tried to specialize, but we were
                 // unable to guess its type. propagateSpecialization will
                 // propagate the type to this phi when it becomes known.
                 continue;
             }
         }
         if (type == MIRType_None) {
             type = in->type();
+            if (in->isConstant())
+                convertibleToFloat32 = true;
             continue;
         }
         if (type != in->type()) {
             // Ignore operands which we've never observed.
             if (in->resultTypeSet() && in->resultTypeSet()->empty())
                 continue;
 
-            // Specialize phis with int32 and double operands as double.
-            if (IsNumberType(type) && IsNumberType(in->type()))
+            if (IsFloatType(type) && IsFloatType(in->type())) {
+                // Specialize phis with int32 and float32 operands as float32.
+                type = MIRType_Float32;
+            } else if (convertibleToFloat32 && in->type() == MIRType_Float32) {
+                // If we only saw constants before and encounter a Float32 value, promote previous
+                // constants to Float32
+                type = MIRType_Float32;
+            } else if (IsNumberType(type) && IsNumberType(in->type())) {
+                // Specialize phis with int32 and double operands as double.
                 type = MIRType_Double;
-            else
+                convertibleToFloat32 &= in->isConstant();
+            } else {
                 return MIRType_Value;
+            }
         }
     }
     return type;
 }
 
 bool
 TypeAnalyzer::respecialize(MPhi *phi, MIRType type)
 {
@@ -471,16 +490,23 @@ TypeAnalyzer::propagateSpecialization(MP
             // We tried to specialize this phi, but were unable to guess its
             // type. Now that we know the type of one of its operands, we can
             // specialize it.
             if (!respecialize(use, phi->type()))
                 return false;
             continue;
         }
         if (use->type() != phi->type()) {
+            // Specialize phis with int32 and float operands as floats.
+            if (IsFloatType(use->type()) && IsFloatType(phi->type())) {
+                if (!respecialize(use, MIRType_Float32))
+                    return false;
+                continue;
+            }
+
             // Specialize phis with int32 and double operands as double.
             if (IsNumberType(use->type()) && IsNumberType(phi->type())) {
                 if (!respecialize(use, MIRType_Double))
                     return false;
                 continue;
             }
 
             // This phi in our use chain can now no longer be specialized.
@@ -541,19 +567,34 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
             if (in->type() == phiType)
                 continue;
 
             if (in->isBox() && in->toBox()->input()->type() == phiType) {
                 phi->replaceOperand(i, in->toBox()->input());
             } else {
                 MInstruction *replacement;
 
-                if (phiType == MIRType_Double && in->type() == MIRType_Int32) {
+                if (phiType == MIRType_Double && IsFloatType(in->type())) {
                     // Convert int32 operands to double.
                     replacement = MToDouble::New(in);
+                } else if (phiType == MIRType_Float32) {
+                    if (in->type() == MIRType_Int32 || in->type() == MIRType_Double) {
+                        replacement = MToFloat32::New(in);
+                    } else {
+                        // See comment below
+                        if (in->type() != MIRType_Value) {
+                            MBox *box = MBox::New(in);
+                            in->block()->insertBefore(in->block()->lastIns(), box);
+                            in = box;
+                        }
+
+                        MUnbox *unbox = MUnbox::New(in, MIRType_Double, MUnbox::Fallible);
+                        in->block()->insertBefore(in->block()->lastIns(), unbox);
+                        replacement = MToFloat32::New(in);
+                    }
                 } else {
                     // If we know this branch will fail to convert to phiType,
                     // insert a box that'll immediately fail in the fallible unbox
                     // below.
                     if (in->type() != MIRType_Value) {
                         MBox *box = MBox::New(in);
                         in->block()->insertBefore(in->block()->lastIns(), box);
                         in = box;
@@ -578,18 +619,17 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
         if (in->type() == MIRType_Value)
             continue;
 
         if (in->isUnbox() && phi->typeIncludes(in->toUnbox()->input())) {
             // The input is being explicitly unboxed, so sneak past and grab
             // the original box.
             phi->replaceOperand(i, in->toUnbox()->input());
         } else {
-            MBox *box = MBox::New(in);
-            in->block()->insertBefore(in->block()->lastIns(), box);
+            MDefinition *box = BoxInputsPolicy::alwaysBoxAt(in->block()->lastIns(), in);
             phi->replaceOperand(i, box);
         }
     }
 }
 
 bool
 TypeAnalyzer::adjustInputs(MDefinition *def)
 {
@@ -645,19 +685,232 @@ TypeAnalyzer::insertConversions()
         for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) {
             if (!adjustInputs(*iter))
                 return false;
         }
     }
     return true;
 }
 
+// This function tries to emit Float32 specialized operations whenever it's possible.
+// MIR nodes are flagged as:
+// - Producers, when they can create Float32 that might need to be coerced into a Double.
+//   Loads in Float32 arrays and conversions to Float32 are producers.
+// - Consumers, when they can have Float32 as inputs and validate a legal use of a Float32.
+//   Stores in Float32 arrays and conversions to Float32 are consumers.
+// - Float32 commutative, when using the Float32 instruction instead of the Double instruction
+//   does not result in a compound loss of precision. This is the case for +, -, /, * with 2
+//   operands, for instance. However, an addition with 3 operands is not commutative anymore,
+//   so an intermediate coercion is needed.
+// Except for phis, all these flags are known after Ion building, so they cannot change during
+// the process.
+//
+// The idea behind the algorithm is easy: whenever we can prove that a commutative operation
+// has only producers as inputs and consumers as uses, we can specialize the operation as a
+// float32 operation. Otherwise, we have to convert all float32 inputs to doubles. Even
+// if a lot of conversions are produced, GVN will take care of eliminating the redundant ones.
+//
+// Phis have a special status. Phis need to be flagged as producers or consumers as they can
+// be inputs or outputs of commutative instructions. Fortunately, producers and consumers
+// properties are such that we can deduce the property using all non phis inputs first (which form
+// an initial phi graph) and then propagate all properties from one phi to another using a
+// fixed point algorithm. The algorithm is ensured to terminate as each iteration has less or as
+// many flagged phis as the previous iteration (so the worst steady state case is all phis being
+// flagged as false).
+//
+// In a nutshell, the algorithm applies three passes:
+// 1 - Determine which phis are consumers. Each phi gets an initial value by making a global AND on
+// all its non-phi inputs. Then each phi propagates its value to other phis. If after propagation,
+// the flag value changed, we have to reapply the algorithm on all phi operands, as a phi is a
+// consumer if all of its uses are consumers.
+// 2 - Determine which phis are producers. It's the same algorithm, except that we have to reapply
+// the algorithm on all phi uses, as a phi is a producer if all of its operands are producers.
+// 3 - Go through all commutative operations and ensure their inputs are all producers and their
+// uses are all consumers.
+bool
+TypeAnalyzer::markPhiConsumers()
+{
+    JS_ASSERT(phiWorklist_.empty());
+
+    // Iterate in postorder so worklist is initialized to RPO.
+    for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Initial state"))
+            return false;
+
+        for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
+            JS_ASSERT(!phi->isInWorklist());
+            bool canConsumeFloat32 = true;
+            for (MUseDefIterator use(*phi); canConsumeFloat32 && use; use++) {
+                MDefinition *usedef = use.def();
+                canConsumeFloat32 &= usedef->isPhi() || usedef->canConsumeFloat32();
+            }
+            phi->setCanConsumeFloat32(canConsumeFloat32);
+            if (canConsumeFloat32 && !addPhiToWorklist(*phi))
+                return false;
+        }
+    }
+
+    while (!phiWorklist_.empty()) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Fixed point"))
+            return false;
+
+        MPhi *phi = popPhi();
+        JS_ASSERT(phi->canConsumeFloat32());
+
+        bool validConsumer = true;
+        for (MUseDefIterator use(phi); use; use++) {
+            MDefinition *def = use.def();
+            if (def->isPhi() && !def->canConsumeFloat32()) {
+                validConsumer = false;
+                break;
+            }
+        }
+
+        if (validConsumer)
+            continue;
+
+        // Propagate invalidated phis
+        phi->setCanConsumeFloat32(false);
+        for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
+            MDefinition *input = phi->getOperand(i);
+            if (input->isPhi() && !input->isInWorklist() && input->canConsumeFloat32())
+            {
+                if (!addPhiToWorklist(input->toPhi()))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool
+TypeAnalyzer::markPhiProducers()
+{
+    JS_ASSERT(phiWorklist_.empty());
+
+    // Iterate in reverse postorder so worklist is initialized to PO.
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - initial state"))
+            return false;
+
+        for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
+            JS_ASSERT(!phi->isInWorklist());
+            bool canProduceFloat32 = true;
+            for (size_t i = 0, e = phi->numOperands(); canProduceFloat32 && i < e; ++i) {
+                MDefinition *input = phi->getOperand(i);
+                canProduceFloat32 &= input->isPhi() || input->canProduceFloat32();
+            }
+            phi->setCanProduceFloat32(canProduceFloat32);
+            if (canProduceFloat32 && !addPhiToWorklist(*phi))
+                return false;
+        }
+    }
+
+    while (!phiWorklist_.empty()) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - Fixed point"))
+            return false;
+
+        MPhi *phi = popPhi();
+        JS_ASSERT(phi->canProduceFloat32());
+
+        bool validProducer = true;
+        for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
+            MDefinition *input = phi->getOperand(i);
+            if (input->isPhi() && !input->canProduceFloat32()) {
+                validProducer = false;
+                break;
+            }
+        }
+
+        if (validProducer)
+            continue;
+
+        // Propagate invalidated phis
+        phi->setCanProduceFloat32(false);
+        for (MUseDefIterator use(phi); use; use++) {
+            MDefinition *def = use.def();
+            if (def->isPhi() && !def->isInWorklist() && def->canProduceFloat32())
+            {
+                if (!addPhiToWorklist(def->toPhi()))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool
+TypeAnalyzer::specializeValidFloatOps()
+{
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Instructions"))
+            return false;
+
+        for (MInstructionIterator ins(block->begin()); ins != block->end(); ++ins) {
+            if (!ins->isFloat32Commutative())
+                continue;
+
+            if (ins->type() == MIRType_Float32)
+                continue;
+
+            // This call will try to specialize the instruction iff all uses are consumers and
+            // all inputs are producers.
+            ins->trySpecializeFloat32();
+        }
+    }
+    return true;
+}
+
+bool
+TypeAnalyzer::graphContainsFloat32()
+{
+    for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
+        if (mir->shouldCancel("Ensure Float32 commutativity - Graph contains Float32"))
+            return false;
+
+        for (MDefinitionIterator def(*block); def; def++) {
+            if (def->type() == MIRType_Float32)
+                return true;
+        }
+    }
+    return false;
+}
+
+bool
+TypeAnalyzer::tryEmitFloatOperations()
+{
+    // Backends that currently don't know how to generate Float32 specialized instructions
+    // shouldn't run this pass and just let all instructions as specialized for Double.
+    if (!LIRGenerator::allowFloat32Optimizations())
+        return true;
+
+    // Asm.js uses the ahead of time type checks to specialize operations, no need to check
+    // them again at this point.
+    if (mir->compilingAsmJS())
+        return true;
+
+    // Check ahead of time that there is at least one definition typed as Float32, otherwise we
+    // don't need this pass.
+    if (!graphContainsFloat32())
+        return true;
+
+    if (!markPhiConsumers())
+       return false;
+    if (!markPhiProducers())
+       return false;
+    if (!specializeValidFloatOps())
+       return false;
+    return true;
+}
+
 bool
 TypeAnalyzer::analyze()
 {
+    if (!tryEmitFloatOperations())
+        return false;
     if (!specializePhis())
         return false;
     if (!insertConversions())
         return false;
     return true;
 }
 
 bool
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6300,16 +6300,17 @@ bool
 jit::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *inputTypes)
 {
     switch (input) {
       case MIRType_Undefined:
       case MIRType_Null:
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
+      case MIRType_Float32:
       case MIRType_String:
       case MIRType_Magic:
         return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input)));
 
       case MIRType_Object:
         return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
 
       case MIRType_Value:
@@ -7018,16 +7019,18 @@ IonBuilder::jsop_getelem_typed(MDefiniti
           case ScalarTypeRepresentation::TYPE_UINT16:
           case ScalarTypeRepresentation::TYPE_INT32:
             knownType = MIRType_Int32;
             break;
           case ScalarTypeRepresentation::TYPE_UINT32:
             knownType = allowDouble ? MIRType_Double : MIRType_Int32;
             break;
           case ScalarTypeRepresentation::TYPE_FLOAT32:
+            knownType = (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double;
+            break;
           case ScalarTypeRepresentation::TYPE_FLOAT64:
             knownType = MIRType_Double;
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unknown typed array type");
         }
 
         // Get the length.
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -1236,16 +1236,32 @@ SnapshotIterator::slotReadable(const Slo
 
 Value
 SnapshotIterator::slotValue(const Slot &slot)
 {
     switch (slot.mode()) {
       case SnapshotReader::DOUBLE_REG:
         return DoubleValue(machine_.read(slot.floatReg()));
 
+      case SnapshotReader::FLOAT32_REG:
+      {
+        double asDouble = machine_.read(slot.floatReg());
+        // The register contains the encoding of a float32. We just read
+        // the bits without making any conversion.
+        float asFloat = *(float*) &asDouble;
+        return DoubleValue(asFloat);
+      }
+
+      case SnapshotReader::FLOAT32_STACK:
+      {
+        double asDouble = ReadFrameDoubleSlot(fp_, slot.stackSlot());
+        float asFloat = *(float*) &asDouble; // no conversion, see comment above.
+        return DoubleValue(asFloat);
+      }
+
       case SnapshotReader::TYPED_REG:
         return FromTypedPayload(slot.knownType(), machine_.read(slot.reg()));
 
       case SnapshotReader::TYPED_STACK:
       {
         JSValueType type = slot.knownType();
         if (type == JSVAL_TYPE_DOUBLE)
             return DoubleValue(ReadFrameDoubleSlot(fp_, slot.stackSlot()));
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -10,16 +10,17 @@
 #include "jsprf.h"
 
 #include "builtin/TypeRepresentation.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/BaselineRegisters.h"
+#include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "vm/ForkJoin.h"
 
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 
 #include "vm/Shape-inl.h"
 
@@ -299,16 +300,51 @@ MacroAssembler::moveNurseryPtr(const Imm
 {
 #ifdef JSGC_GENERATIONAL
     if (ptr.value && gc::IsInsideNursery(GetIonContext()->cx->runtime(), (void *)ptr.value))
         embedsNurseryPointers_ = true;
 #endif
     movePtr(ptr, reg);
 }
 
+template<typename S, typename T>
+static void
+StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest) {
+    switch (arrayType) {
+      case ScalarTypeRepresentation::TYPE_FLOAT32:
+        if (LIRGenerator::allowFloat32Optimizations()) {
+            masm.storeFloat(value, dest);
+        } else {
+#ifdef JS_MORE_DETERMINISTIC
+            // See the comment in ToDoubleForTypedArray.
+            masm.canonicalizeDouble(value);
+#endif
+            masm.convertDoubleToFloat(value, ScratchFloatReg);
+            masm.storeFloat(ScratchFloatReg, dest);
+        }
+        break;
+      case ScalarTypeRepresentation::TYPE_FLOAT64:
+#ifdef JS_MORE_DETERMINISTIC
+        // See the comment in ToDoubleForTypedArray.
+        masm.canonicalizeDouble(value);
+#endif
+        masm.storeDouble(value, dest);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
+    }
+}
+
+void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest) {
+    StoreToTypedFloatArray(*this, arrayType, value, dest);
+}
+void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest) {
+    StoreToTypedFloatArray(*this, arrayType, value, dest);
+}
+
 template<typename T>
 void
 MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp,
                                    Label *fail)
 {
     switch (arrayType) {
       case ScalarTypeRepresentation::TYPE_INT8:
         load8SignExtend(src, dest.gpr());
@@ -332,21 +368,26 @@ MacroAssembler::loadFromTypedArray(int a
             convertUInt32ToDouble(temp, dest.fpu());
         } else {
             load32(src, dest.gpr());
             test32(dest.gpr(), dest.gpr());
             j(Assembler::Signed, fail);
         }
         break;
       case ScalarTypeRepresentation::TYPE_FLOAT32:
-      case ScalarTypeRepresentation::TYPE_FLOAT64:
-        if (arrayType == ScalarTypeRepresentation::TYPE_FLOAT32)
+        if (LIRGenerator::allowFloat32Optimizations()) {
+            loadFloat(src, dest.fpu());
+            canonicalizeFloat(dest.fpu());
+        } else {
             loadFloatAsDouble(src, dest.fpu());
-        else
-            loadDouble(src, dest.fpu());
+            canonicalizeDouble(dest.fpu());
+        }
+        break;
+      case ScalarTypeRepresentation::TYPE_FLOAT64:
+        loadDouble(src, dest.fpu());
         canonicalizeDouble(dest.fpu());
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
     }
 }
 
 template void MacroAssembler::loadFromTypedArray(int arrayType, const Address &src, AnyRegister dest,
@@ -390,16 +431,21 @@ MacroAssembler::loadFromTypedArray(int a
             bind(&done);
         } else {
             // Bailout if the value does not fit in an int32.
             j(Assembler::Signed, fail);
             tagValue(JSVAL_TYPE_INT32, temp, dest);
         }
         break;
       case ScalarTypeRepresentation::TYPE_FLOAT32:
+        loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
+        if (LIRGenerator::allowFloat32Optimizations())
+            convertFloatToDouble(ScratchFloatReg, ScratchFloatReg);
+        boxDouble(ScratchFloatReg, dest);
+        break;
       case ScalarTypeRepresentation::TYPE_FLOAT64:
         loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
         boxDouble(ScratchFloatReg, dest);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
     }
 }
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -425,17 +425,17 @@ class MacroAssembler : public MacroAssem
             size_t idbits = JSID_BITS(id);
             Push(ImmWord(idbits));
         }
     }
 
     void Push(TypedOrValueRegister v) {
         if (v.hasValue())
             Push(v.valueReg());
-        else if (v.type() == MIRType_Double)
+        else if (IsFloatingPointType(v.type()))
             Push(v.typedReg().fpu());
         else
             Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
     }
 
     void Push(ConstantOrRegister v) {
         if (v.constant())
             Push(v.value());
@@ -556,16 +556,24 @@ class MacroAssembler : public MacroAssem
 
     void canonicalizeDouble(FloatRegister reg) {
         Label notNaN;
         branchDouble(DoubleOrdered, reg, reg, &notNaN);
         loadStaticDouble(&js_NaN, reg);
         bind(&notNaN);
     }
 
+    void canonicalizeFloat(FloatRegister reg) {
+        static float js_NaN_float = js_NaN;
+        Label notNaN;
+        branchFloat(DoubleOrdered, reg, reg, &notNaN);
+        loadStaticFloat32(&js_NaN_float, reg);
+        bind(&notNaN);
+    }
+
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);
 
     template<typename T>
     void loadFromTypedArray(int arrayType, const T &src, const ValueOperand &dest, bool allowDouble,
                             Register temp, Label *fail);
 
     template<typename S, typename T>
@@ -584,34 +592,18 @@ class MacroAssembler : public MacroAssem
           case ScalarTypeRepresentation::TYPE_UINT32:
             store32(value, dest);
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
         }
     }
 
-    template<typename T>
-    void storeToTypedFloatArray(int arrayType, FloatRegister value, const T &dest) {
-#ifdef JS_MORE_DETERMINISTIC
-        // See the comment in ToDoubleForTypedArray.
-        canonicalizeDouble(value);
-#endif
-        switch (arrayType) {
-          case ScalarTypeRepresentation::TYPE_FLOAT32:
-            convertDoubleToFloat(value, ScratchFloatReg);
-            storeFloat(ScratchFloatReg, dest);
-            break;
-          case ScalarTypeRepresentation::TYPE_FLOAT64:
-            storeDouble(value, dest);
-            break;
-          default:
-            MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
-        }
-    }
+    void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest);
+    void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest);
 
     Register extractString(const Address &address, Register scratch) {
         return extractObject(address, scratch);
     }
     Register extractString(const ValueOperand &value, Register scratch) {
         return extractObject(value, scratch);
     }
 
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -78,16 +78,17 @@ BailoutKindString(BailoutKind kind)
 // a number.
 enum MIRType
 {
     MIRType_Undefined,
     MIRType_Null,
     MIRType_Boolean,
     MIRType_Int32,
     MIRType_Double,
+    MIRType_Float32,
     MIRType_String,
     MIRType_Object,
     MIRType_Magic,
     MIRType_Value,
     MIRType_None,         // Invalid, used as a placeholder.
     MIRType_Slots,        // A slots vector
     MIRType_Elements,     // An elements vector
     MIRType_Pointer,      // An opaque pointer that receives no special treatment
@@ -129,16 +130,17 @@ ValueTypeFromMIRType(MIRType type)
     case MIRType_Undefined:
       return JSVAL_TYPE_UNDEFINED;
     case MIRType_Null:
       return JSVAL_TYPE_NULL;
     case MIRType_Boolean:
       return JSVAL_TYPE_BOOLEAN;
     case MIRType_Int32:
       return JSVAL_TYPE_INT32;
+    case MIRType_Float32: // Fall through, there's no JSVAL for Float32
     case MIRType_Double:
       return JSVAL_TYPE_DOUBLE;
     case MIRType_String:
       return JSVAL_TYPE_STRING;
     case MIRType_Magic:
       return JSVAL_TYPE_MAGIC;
     default:
       JS_ASSERT(type == MIRType_Object);
@@ -161,16 +163,18 @@ StringFromMIRType(MIRType type)
     case MIRType_Null:
       return "Null";
     case MIRType_Boolean:
       return "Bool";
     case MIRType_Int32:
       return "Int32";
     case MIRType_Double:
       return "Double";
+    case MIRType_Float32:
+      return "Float32";
     case MIRType_String:
       return "String";
     case MIRType_Object:
       return "Object";
     case MIRType_Magic:
       return "Magic";
     case MIRType_Value:
       return "Value";
@@ -187,17 +191,29 @@ StringFromMIRType(MIRType type)
     default:
       MOZ_ASSUME_UNREACHABLE("Unknown MIRType.");
   }
 }
 
 static inline bool
 IsNumberType(MIRType type)
 {
-    return type == MIRType_Int32 || type == MIRType_Double;
+    return type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32;
+}
+
+static inline bool
+IsFloatType(MIRType type)
+{
+    return type == MIRType_Int32 || type == MIRType_Float32;
+}
+
+static inline bool
+IsFloatingPointType(MIRType type)
+{
+    return type == MIRType_Double || type == MIRType_Float32;
 }
 
 static inline bool
 IsNullOrUndefined(MIRType type)
 {
     return type == MIRType_Null || type == MIRType_Undefined;
 }
 
--- a/js/src/jit/LICM.cpp
+++ b/js/src/jit/LICM.cpp
@@ -243,17 +243,17 @@ Loop::requiresHoistedUse(const MDefiniti
 {
     if (ins->isConstantElements() || ins->isBox())
         return true;
 
     // Integer constants can often be folded as immediates and aren't worth
     // hoisting on their own, in general. Floating-point constants typically
     // are worth hoisting, unless they'll end up being spilled (eg. due to a
     // call).
-    if (ins->isConstant() && (ins->type() != MIRType_Double || containsPossibleCall_))
+    if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
         return true;
 
     return false;
 }
 
 bool
 Loop::hoistInstructions(InstructionQueue &toHoist)
 {
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -183,16 +183,32 @@ class LDouble : public LInstructionHelpe
 
     LDouble(double d) : d_(d)
     { }
     double getDouble() const {
         return d_;
     }
 };
 
+// Constant float32.
+class LFloat32 : public LInstructionHelper<1, 0, 0>
+{
+    float f_;
+  public:
+    LIR_HEADER(Float32);
+
+    LFloat32(float f)
+      : f_(f)
+    { }
+
+    float getFloat() const {
+        return f_;
+    }
+};
+
 // A constant Value.
 class LValue : public LInstructionHelper<BOX_PIECES, 0, 0>
 {
     Value v_;
 
   public:
     LIR_HEADER(Value)
 
@@ -2097,16 +2113,26 @@ class LNegD : public LInstructionHelper<
 {
   public:
     LIR_HEADER(NegD)
     LNegD(const LAllocation &num) {
         setOperand(0, num);
     }
 };
 
+// Negative of a float32.
+class LNegF : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(NegF)
+    LNegF(const LAllocation &num) {
+        setOperand(0, num);
+    }
+};
+
 // Absolute value of an integer.
 class LAbsI : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(AbsI)
     LAbsI(const LAllocation &num) {
         setOperand(0, num);
     }
@@ -2297,16 +2323,33 @@ class LMathD : public LBinaryMath<0>
       : jsop_(jsop)
     { }
 
     JSOp jsop() const {
         return jsop_;
     }
 };
 
+// Performs an add, sub, mul, or div on two double values.
+class LMathF: public LBinaryMath<0>
+{
+    JSOp jsop_;
+
+  public:
+    LIR_HEADER(MathF)
+
+    LMathF(JSOp jsop)
+      : jsop_(jsop)
+    { }
+
+    JSOp jsop() const {
+        return jsop_;
+    }
+};
+
 class LModD : public LBinaryMath<1>
 {
   public:
     LIR_HEADER(ModD)
 
     LModD(const LAllocation &lhs, const LAllocation &rhs, const LDefinition &temp) {
         setOperand(0, lhs);
         setOperand(1, rhs);
@@ -2453,28 +2496,73 @@ class LInt32ToDouble : public LInstructi
   public:
     LIR_HEADER(Int32ToDouble)
 
     LInt32ToDouble(const LAllocation &input) {
         setOperand(0, input);
     }
 };
 
+// Convert a 32-bit float to a double.
+class LFloat32ToDouble : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Float32ToDouble)
+
+    LFloat32ToDouble(const LAllocation &input) {
+        setOperand(0, input);
+    }
+};
+
+// Convert a double to a 32-bit float.
+class LDoubleToFloat32 : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(DoubleToFloat32)
+
+    LDoubleToFloat32(const LAllocation &input) {
+        setOperand(0, input);
+    }
+};
+
+// Convert a 32-bit integer to a float32.
+class LInt32ToFloat32 : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(Int32ToFloat32)
+
+    LInt32ToFloat32(const LAllocation &input) {
+        setOperand(0, input);
+    }
+};
+
 // Convert a value to a double.
 class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(ValueToDouble)
     static const size_t Input = 0;
 
     MToDouble *mir() {
         return mir_->toToDouble();
     }
 };
 
+// Convert a value to a float32.
+class LValueToFloat32 : public LInstructionHelper<1, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(ValueToFloat32)
+    static const size_t Input = 0;
+
+    MToFloat32 *mir() {
+        return mir_->toToFloat32();
+    }
+};
+
 // Convert a value to an int32.
 //   Input: components of a Value
 //   Output: 32-bit integer
 //   Bailout: undefined, string, object, or non-int32 double
 //   Temps: one float register, one GP register
 //
 // This instruction requires a temporary float register.
 class LValueToInt32 : public LInstructionHelper<1, BOX_PIECES, 2>
@@ -4999,16 +5087,42 @@ class LAssertRangeD : public LInstructio
     MAssertRange *mir() {
         return mir_->toAssertRange();
     }
     Range *range() {
         return mir()->range();
     }
 };
 
+class LAssertRangeF : public LInstructionHelper<0, 1, 1>
+{
+  public:
+    LIR_HEADER(AssertRangeF)
+
+    LAssertRangeF(const LAllocation &input, const LDefinition &temp) {
+        setOperand(0, input);
+        setTemp(0, temp);
+    }
+
+    const LAllocation *input() {
+        return getOperand(0);
+    }
+
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
+
+    MAssertRange *mir() {
+        return mir_->toAssertRange();
+    }
+    Range *range() {
+        return mir()->range();
+    }
+};
+
 class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3>
 {
   public:
     LIR_HEADER(AssertRangeV)
 
     LAssertRangeV(const LDefinition &temp, const LDefinition &floatTemp1,
                   const LDefinition &floatTemp2)
     {
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -535,16 +535,17 @@ class LDefinition
         switch (type) {
           case MIRType_Boolean:
           case MIRType_Int32:
             return LDefinition::GENERAL;
           case MIRType_String:
           case MIRType_Object:
             return LDefinition::OBJECT;
           case MIRType_Double:
+          case MIRType_Float32:
             return LDefinition::DOUBLE;
 #if defined(JS_PUNBOX64)
           case MIRType_Value:
             return LDefinition::BOX;
 #endif
           case MIRType_Slots:
           case MIRType_Elements:
             return LDefinition::SLOTS;
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -10,16 +10,17 @@
 #define LIR_COMMON_OPCODE_LIST(_)   \
     _(Label)                        \
     _(Nop)                          \
     _(OsiPoint)                     \
     _(MoveGroup)                    \
     _(Integer)                      \
     _(Pointer)                      \
     _(Double)                       \
+    _(Float32)                      \
     _(Value)                        \
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
     _(NewParallelArray)             \
     _(NewArray)                     \
@@ -87,41 +88,47 @@
     _(IsNullOrLikeUndefined)        \
     _(IsNullOrLikeUndefinedAndBranch)\
     _(EmulatesUndefined)            \
     _(EmulatesUndefinedAndBranch)   \
     _(MinMaxI)                      \
     _(MinMaxD)                      \
     _(NegI)                         \
     _(NegD)                         \
+    _(NegF)                         \
     _(AbsI)                         \
     _(AbsD)                         \
     _(SqrtD)                        \
     _(Atan2D)                       \
     _(PowI)                         \
     _(PowD)                         \
     _(Random)                       \
     _(MathFunctionD)                \
     _(NotI)                         \
     _(NotD)                         \
     _(NotO)                         \
     _(NotV)                         \
     _(AddI)                         \
     _(SubI)                         \
     _(MulI)                         \
     _(MathD)                        \
+    _(MathF)                        \
     _(ModD)                         \
     _(BinaryV)                      \
     _(Concat)                       \
     _(ConcatPar)                    \
     _(CharCodeAt)                   \
     _(FromCharCode)                 \
     _(Int32ToDouble)                \
+    _(Float32ToDouble)              \
+    _(DoubleToFloat32)              \
+    _(Int32ToFloat32)               \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
+    _(ValueToFloat32)               \
     _(DoubleToInt32)                \
     _(TruncateDToInt32)             \
     _(IntToString)                  \
     _(DoubleToString)               \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
@@ -244,16 +251,17 @@
     _(AsmJSReturn)                  \
     _(AsmJSVoidReturn)              \
     _(AsmJSPassStackArg)            \
     _(AsmJSCall)                    \
     _(AsmJSCheckOverRecursed)       \
     _(CheckInterruptPar)            \
     _(AssertRangeI)                 \
     _(AssertRangeD)                 \
+    _(AssertRangeF)                 \
     _(AssertRangeV)
 
 #if defined(JS_CPU_X86)
 # include "jit/x86/LOpcodes-x86.h"
 #elif defined(JS_CPU_X64)
 # include "jit/x64/LOpcodes-x64.h"
 #elif defined(JS_CPU_ARM)
 # include "jit/arm/LOpcodes-arm.h"
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1236,16 +1236,22 @@ LIRGenerator::visitAdd(MAdd *ins)
     }
 
     if (ins->specialization() == MIRType_Double) {
         JS_ASSERT(lhs->type() == MIRType_Double);
         ReorderCommutative(&lhs, &rhs);
         return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
     }
 
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        ReorderCommutative(&lhs, &rhs);
+        return lowerForFPU(new LMathF(JSOP_ADD), ins, lhs, rhs);
+    }
+
     return lowerBinaryV(JSOP_ADD, ins);
 }
 
 bool
 LIRGenerator::visitSub(MSub *ins)
 {
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
@@ -1264,16 +1270,20 @@ LIRGenerator::visitSub(MSub *ins)
 
         MaybeSetRecoversInput(ins, lir);
         return true;
     }
     if (ins->specialization() == MIRType_Double) {
         JS_ASSERT(lhs->type() == MIRType_Double);
         return lowerForFPU(new LMathD(JSOP_SUB), ins, lhs, rhs);
     }
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        return lowerForFPU(new LMathF(JSOP_SUB), ins, lhs, rhs);
+    }
 
     return lowerBinaryV(JSOP_SUB, ins);
 }
 
 bool
 LIRGenerator::visitMul(MMul *ins)
 {
     MDefinition *lhs = ins->lhs();
@@ -1290,16 +1300,28 @@ LIRGenerator::visitMul(MMul *ins)
         ReorderCommutative(&lhs, &rhs);
 
         // If our RHS is a constant -1.0, we can optimize to an LNegD.
         if (rhs->isConstant() && rhs->toConstant()->value() == DoubleValue(-1.0))
             return defineReuseInput(new LNegD(useRegisterAtStart(lhs)), ins, 0);
 
         return lowerForFPU(new LMathD(JSOP_MUL), ins, lhs, rhs);
     }
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        ReorderCommutative(&lhs, &rhs);
+
+        // We apply the same optimizations as for doubles
+        if (lhs->isConstant() && lhs->toConstant()->value() == JS::Float32Value(-1.0f))
+            return defineReuseInput(new LNegF(useRegisterAtStart(rhs)), ins, 0);
+        if (rhs->isConstant() && rhs->toConstant()->value() == JS::Float32Value(-1.0f))
+            return defineReuseInput(new LNegF(useRegisterAtStart(lhs)), ins, 0);
+
+        return lowerForFPU(new LMathF(JSOP_MUL), ins, lhs, rhs);
+    }
 
     return lowerBinaryV(JSOP_MUL, ins);
 }
 
 bool
 LIRGenerator::visitDiv(MDiv *ins)
 {
     MDefinition *lhs = ins->lhs();
@@ -1309,16 +1331,20 @@ LIRGenerator::visitDiv(MDiv *ins)
     if (ins->specialization() == MIRType_Int32) {
         JS_ASSERT(lhs->type() == MIRType_Int32);
         return lowerDivI(ins);
     }
     if (ins->specialization() == MIRType_Double) {
         JS_ASSERT(lhs->type() == MIRType_Double);
         return lowerForFPU(new LMathD(JSOP_DIV), ins, lhs, rhs);
     }
+    if (ins->specialization() == MIRType_Float32) {
+        JS_ASSERT(lhs->type() == MIRType_Float32);
+        return lowerForFPU(new LMathF(JSOP_DIV), ins, lhs, rhs);
+    }
 
     return lowerBinaryV(JSOP_DIV, ins);
 }
 
 bool
 LIRGenerator::visitMod(MMod *ins)
 {
     JS_ASSERT(ins->lhs()->type() == ins->rhs()->type());
@@ -1501,27 +1527,83 @@ LIRGenerator::visitToDouble(MToDouble *c
         /* FALLTHROUGH */
 
       case MIRType_Int32:
       {
         LInt32ToDouble *lir = new LInt32ToDouble(useRegister(opd));
         return define(lir, convert);
       }
 
+      case MIRType_Float32:
+      {
+        LFloat32ToDouble *lir = new LFloat32ToDouble(useRegister(opd));
+        return define(lir, convert);
+      }
+
       case MIRType_Double:
         return redefine(convert, opd);
 
       default:
         // Objects might be effectful.
         // Strings are complicated - we don't handle them yet.
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
 bool
+LIRGenerator::visitToFloat32(MToFloat32 *convert)
+{
+    MDefinition *opd = convert->input();
+    mozilla::DebugOnly<MToFloat32::ConversionKind> conversion = convert->conversion();
+
+    switch (opd->type()) {
+      case MIRType_Value:
+      {
+        LValueToFloat32 *lir = new LValueToFloat32();
+        if (!useBox(lir, LValueToFloat32::Input, opd))
+            return false;
+        return assignSnapshot(lir) && define(lir, convert);
+      }
+
+      case MIRType_Null:
+        JS_ASSERT(conversion != MToFloat32::NonStringPrimitives);
+        return lowerConstantFloat32(0, convert);
+
+      case MIRType_Undefined:
+        JS_ASSERT(conversion != MToFloat32::NumbersOnly);
+        return lowerConstantFloat32(js_NaN, convert);
+
+      case MIRType_Boolean:
+        JS_ASSERT(conversion != MToFloat32::NumbersOnly);
+        /* FALLTHROUGH */
+
+      case MIRType_Int32:
+      {
+        LInt32ToFloat32 *lir = new LInt32ToFloat32(useRegister(opd));
+        return define(lir, convert);
+      }
+
+      case MIRType_Double:
+      {
+        LDoubleToFloat32 *lir = new LDoubleToFloat32(useRegister(opd));
+        return define(lir, convert);
+      }
+
+      case MIRType_Float32:
+        return redefine(convert, opd);
+
+      default:
+        // Objects might be effectful.
+        // Strings are complicated - we don't handle them yet.
+        MOZ_ASSUME_UNREACHABLE("unexpected type");
+        return false;
+    }
+}
+
+bool
 LIRGenerator::visitToInt32(MToInt32 *convert)
 {
     MDefinition *opd = convert->input();
 
     switch (opd->type()) {
       case MIRType_Value:
       {
         LValueToInt32 *lir = new LValueToInt32(tempFloat(), temp(), LValueToInt32::NORMAL);
@@ -1817,16 +1899,19 @@ LIRGenerator::visitStoreSlot(MStoreSlot 
         lir = new LStoreSlotV(useRegister(ins->slots()));
         if (!useBox(lir, LStoreSlotV::Value, ins->value()))
             return false;
         return add(lir, ins);
 
       case MIRType_Double:
         return add(new LStoreSlotT(useRegister(ins->slots()), useRegister(ins->value())), ins);
 
+      case MIRType_Float32:
+        MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be stored in a slot.");
+
       default:
         return add(new LStoreSlotT(useRegister(ins->slots()), useRegisterOrConstant(ins->value())),
                    ins);
     }
 
     return true;
 }
 
@@ -2247,17 +2332,17 @@ LIRGenerator::visitLoadTypedArrayElement
 
     const LUse elements = useRegister(ins->elements());
     const LAllocation index = useRegisterOrConstant(ins->index());
 
     JS_ASSERT(IsNumberType(ins->type()));
 
     // We need a temp register for Uint32Array with known double result.
     LDefinition tempDef = LDefinition::BogusTemp();
-    if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && ins->type() == MIRType_Double)
+    if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && IsFloatingPointType(ins->type()))
         tempDef = temp();
 
     LLoadTypedArrayElement *lir = new LLoadTypedArrayElement(elements, index, tempDef);
     if (ins->fallible() && !assignSnapshot(lir))
         return false;
     return define(lir, ins);
 }
 
@@ -2495,16 +2580,20 @@ LIRGenerator::visitAssertRange(MAssertRa
       case MIRType_Int32:
           lir = new LAssertRangeI(useRegisterAtStart(input));
         break;
 
       case MIRType_Double:
         lir = new LAssertRangeD(useRegister(input), tempFloat());
         break;
 
+      case MIRType_Float32:
+        lir = new LAssertRangeF(useRegister(input), tempFloat());
+        break;
+
       case MIRType_Value:
         lir = new LAssertRangeV(tempToUnbox(), tempFloat(), tempFloat());
         if (!useBox(lir, LAssertRangeV::Input, input))
             return false;
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected Range for MIRType");
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -148,16 +148,17 @@ class LIRGenerator : public LIRGenerator
     bool visitCharCodeAt(MCharCodeAt *ins);
     bool visitFromCharCode(MFromCharCode *ins);
     bool visitStart(MStart *start);
     bool visitOsrEntry(MOsrEntry *entry);
     bool visitNop(MNop *nop);
     bool visitOsrValue(MOsrValue *value);
     bool visitOsrScopeChain(MOsrScopeChain *object);
     bool visitToDouble(MToDouble *convert);
+    bool visitToFloat32(MToFloat32 *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitToString(MToString *convert);
     bool visitRegExp(MRegExp *ins);
     bool visitRegExpTest(MRegExpTest *ins);
     bool visitLambda(MLambda *ins);
     bool visitLambdaPar(MLambdaPar *ins);
     bool visitImplicitThis(MImplicitThis *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -787,19 +787,19 @@ IonBuilder::inlineMathImul(CallInfo &cal
 {
     if (callInfo.argc() != 2 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MIRType returnType = getInlineReturnType();
     if (returnType != MIRType_Int32)
         return InliningStatus_NotInlined;
 
-    if (!IsNumberType(callInfo.getArg(0)->type()))
+    if (!IsNumberType(callInfo.getArg(0)->type()) || callInfo.getArg(0)->type() == MIRType_Float32)
         return InliningStatus_NotInlined;
-    if (!IsNumberType(callInfo.getArg(1)->type()))
+    if (!IsNumberType(callInfo.getArg(1)->type()) || callInfo.getArg(1)->type() == MIRType_Float32)
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     MInstruction *first = MTruncateToInt32::New(callInfo.getArg(0));
     current->add(first);
 
     MInstruction *second = MTruncateToInt32::New(callInfo.getArg(1));
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -447,16 +447,22 @@ MConstant::printOpcode(FILE *fp) const
         fprintf(fp, value().toBoolean() ? "true" : "false");
         break;
       case MIRType_Int32:
         fprintf(fp, "0x%x", value().toInt32());
         break;
       case MIRType_Double:
         fprintf(fp, "%f", value().toDouble());
         break;
+      case MIRType_Float32:
+      {
+        float val = value().toDouble();
+        fprintf(fp, "%f", val);
+        break;
+      }
       case MIRType_Object:
         if (value().toObject().is<JSFunction>()) {
             JSFunction *fun = &value().toObject().as<JSFunction>();
             if (fun->displayAtom()) {
                 fputs("function ", fp);
                 FileEscapedString(fp, fun->displayAtom(), 0);
             } else {
                 fputs("unnamed function", fp);
@@ -478,16 +484,38 @@ MConstant::printOpcode(FILE *fp) const
       case MIRType_Magic:
         fprintf(fp, "magic");
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
+// Needs a static function to avoid overzealous optimizations by certain compilers (MSVC).
+static bool
+IsFloat32Representable(double x)
+{
+    float asFloat = static_cast<float>(x);
+    double floatAsDouble = static_cast<double>(asFloat);
+    return floatAsDouble == x;
+}
+
+bool
+MConstant::canProduceFloat32() const
+{
+    if (!IsNumberType(type()))
+        return false;
+
+    if (type() == MIRType_Int32)
+        return IsFloat32Representable(static_cast<double>(value_.toInt32()));
+    if (type() == MIRType_Double)
+        return IsFloat32Representable(value_.toDouble());
+    return true;
+}
+
 void
 MControlInstruction::printOpcode(FILE *fp) const
 {
     MDefinition::printOpcode(fp);
     for (size_t j = 0; j < numSuccessors(); j++)
         fprintf(fp, " block%d", getSuccessor(j)->id());
 }
 
@@ -741,17 +769,19 @@ MakeMIRTypeSet(MIRType type)
 
 void
 jit::MergeTypes(MIRType *ptype, types::StackTypeSet **ptypeSet,
                 MIRType newType, types::StackTypeSet *newTypeSet)
 {
     if (newTypeSet && newTypeSet->empty())
         return;
     if (newType != *ptype) {
-        if (IsNumberType(newType) && IsNumberType(*ptype)) {
+        if (IsFloatType(newType) && IsFloatType(*ptype)) {
+            *ptype = MIRType_Float32;
+        } else if (IsNumberType(newType) && IsNumberType(*ptype)) {
             *ptype = MIRType_Double;
         } else if (*ptype != MIRType_Value) {
             if (!*ptypeSet)
                 *ptypeSet = MakeMIRTypeSet(*ptype);
             *ptype = MIRType_Value;
         } else if (*ptypeSet && (*ptypeSet)->empty()) {
             *ptype = newType;
         }
@@ -1165,16 +1195,53 @@ MBinaryArithInstruction::foldsTo(bool us
         return this;
 
     if (IsConstant(lhs, getIdentity()))
         return rhs; // x op id => x
 
     return this;
 }
 
+template<size_t Op> static void
+ConvertDefinitionToDouble(MDefinition *def, MInstruction *consumer)
+{
+    MInstruction *replace = MToDouble::New(def);
+    consumer->replaceOperand(Op, replace);
+    consumer->block()->insertBefore(consumer, replace);
+}
+
+static bool
+CheckUsesAreFloat32Consumers(MInstruction *ins)
+{
+    bool allConsumerUses = true;
+    for (MUseDefIterator use(ins); allConsumerUses && use; use++)
+        allConsumerUses &= use.def()->canConsumeFloat32();
+    return allConsumerUses;
+}
+
+void
+MBinaryArithInstruction::trySpecializeFloat32()
+{
+    MDefinition *left = lhs();
+    MDefinition *right = rhs();
+
+    if (!left->canProduceFloat32() || !right->canProduceFloat32()
+        || !CheckUsesAreFloat32Consumers(this))
+    {
+        if (left->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<0>(left, this);
+        if (right->type() == MIRType_Float32)
+            ConvertDefinitionToDouble<1>(right, this);
+        return;
+    }
+
+    specialization_ = MIRType_Float32;
+    setResultType(MIRType_Float32);
+}
+
 bool
 MAbs::fallible() const
 {
     return !implicitTruncate_ && (!range() || !range()->isInt32());
 }
 
 MDefinition *
 MDiv::foldsTo(bool useValueNumbers)
@@ -1403,17 +1470,19 @@ MBinaryArithInstruction::infer(BaselineI
     // unless baseline type hints suggest it might be profitable
     if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1)))
         return inferFallback(inspector, pc);
 
     // Guess a result type based on the inputs.
     // Don't specialize for neither-integer-nor-double results.
     if (lhs == MIRType_Int32 && rhs == MIRType_Int32)
         setResultType(MIRType_Int32);
-    else if (lhs == MIRType_Double || rhs == MIRType_Double)
+    // Double operations are prioritary over float32 operations (i.e. if any operand needs
+    // a double as an input, convert all operands to doubles)
+    else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs))
         setResultType(MIRType_Double);
     else
         return inferFallback(inspector, pc);
 
     // If the operation has ever overflowed, use a double specialization.
     if (inspector->hasSeenDoubleResult(pc))
         setResultType(MIRType_Double);
 
@@ -1428,17 +1497,17 @@ MBinaryArithInstruction::infer(BaselineI
 
     JS_ASSERT(lhs < MIRType_String || lhs == MIRType_Value);
     JS_ASSERT(rhs < MIRType_String || rhs == MIRType_Value);
 
     MIRType rval = this->type();
 
     // Don't specialize values when result isn't double
     if (lhs == MIRType_Value || rhs == MIRType_Value) {
-        if (rval != MIRType_Double) {
+        if (!IsFloatingPointType(rval)) {
             specialization_ = MIRType_None;
             return;
         }
     }
 
     // Don't specialize as int32 if one of the operands is undefined,
     // since ToNumber(undefined) is NaN.
     if (rval == MIRType_Int32 && (lhs == MIRType_Undefined || rhs == MIRType_Undefined)) {
@@ -1984,16 +2053,38 @@ MToDouble::foldsTo(bool useValueNumbers)
     // Fold unnecessary numeric conversions.
     if (input()->isToInt32())
         replaceOperand(0, input()->getOperand(0));
 
     return this;
 }
 
 MDefinition *
+MToFloat32::foldsTo(bool useValueNumbers)
+{
+    if (input()->type() == MIRType_Float32)
+        return input();
+
+    // If x is a Float32, Float32(Double(x)) == x
+    if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32)
+        return input()->toToDouble()->input();
+
+    if (input()->isConstant()) {
+        const Value &v = input()->toConstant()->value();
+        if (v.isNumber()) {
+            float out = v.toNumber();
+            MConstant *c = MConstant::New(DoubleValue(out));
+            c->setResultType(MIRType_Float32);
+            return c;
+        }
+    }
+    return this;
+}
+
+MDefinition *
 MToString::foldsTo(bool useValueNumbers)
 {
     MDefinition *in = input();
     if (in->type() == MIRType_String)
         return in;
     return this;
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -448,16 +448,23 @@ class MDefinition : public MNode
             return true;
 
         if (MIRType_Value != this->type())
             return false;
 
         return !resultTypeSet() || resultTypeSet()->mightBeType(ValueTypeFromMIRType(type));
     }
 
+    // Float32 specialization operations (see big comment in IonAnalysis before the Float32
+    // specialization algorithm).
+    virtual bool isFloat32Commutative() const { return false; }
+    virtual bool canProduceFloat32() const { return false; }
+    virtual bool canConsumeFloat32() const { return false; }
+    virtual void trySpecializeFloat32() {}
+
     // Returns the beginning of this definition's use chain.
     MUseIterator usesBegin() const {
         return uses_.begin();
     }
 
     // Returns the end of this definition's use chain.
     MUseIterator usesEnd() const {
         return uses_.end();
@@ -943,18 +950,31 @@ class MConstant : public MNullaryInstruc
 
     HashNumber valueHash() const;
     bool congruentTo(MDefinition *ins) const;
 
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 
+    bool updateForReplacement(MDefinition *def) {
+        MConstant *c = def->toConstant();
+        // During constant folding, we don't want to replace a float32
+        // value by a double value.
+        if (type() == MIRType_Float32)
+            return c->type() == MIRType_Float32;
+        if (type() == MIRType_Double)
+            return c->type() != MIRType_Float32;
+        return true;
+    }
+
     void computeRange();
     bool truncate();
+
+    bool canProduceFloat32() const;
 };
 
 class MParameter : public MNullaryInstruction
 {
     int32_t index_;
 
   public:
     static const int32_t THIS_SLOT = -1;
@@ -1718,16 +1738,20 @@ class MCall
     }
 
     void addArg(size_t argnum, MPassArg *arg);
 
     MDefinition *getArg(uint32_t index) const {
         return getOperand(NumNonArgumentOperands + index);
     }
 
+    void replaceArg(uint32_t index, MDefinition *def) {
+        replaceOperand(NumNonArgumentOperands + index, def);
+    }
+
     void rootTargetScript(JSFunction *target) {
         targetScript_.setRoot(target->nonLazyScript());
     }
     bool hasRootedScript() {
         return targetScript_ != NULL;
     }
 
     // For TI-informed monomorphic callsites.
@@ -2636,16 +2660,75 @@ class MToDouble
         return AliasSet::None();
     }
 
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
 };
 
+// Converts a primitive (either typed or untyped) to a float32. If the input is
+// not primitive at runtime, a bailout occurs.
+class MToFloat32
+  : public MUnaryInstruction,
+    public ToDoublePolicy
+{
+  public:
+    // Types of values which can be converted.
+    enum ConversionKind {
+        NonStringPrimitives,
+        NonNullNonStringPrimitives,
+        NumbersOnly
+    };
+
+  protected:
+    ConversionKind conversion_;
+
+    MToFloat32(MDefinition *def, ConversionKind conversion)
+      : MUnaryInstruction(def), conversion_(conversion)
+    {
+        setResultType(MIRType_Float32);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(ToFloat32)
+    static MToFloat32 *New(MDefinition *def, ConversionKind conversion = NonStringPrimitives) {
+        return new MToFloat32(def, conversion);
+    }
+    static MToFloat32 *NewAsmJS(MDefinition *def) {
+        return new MToFloat32(def, NonStringPrimitives);
+    }
+
+    ConversionKind conversion() const {
+        return conversion_;
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    virtual MDefinition *foldsTo(bool useValueNumbers);
+    bool congruentTo(MDefinition *ins) const {
+        if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion())
+            return false;
+        return congruentIfOperandsEqual(ins);
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+
+    void computeRange();
+    bool truncate();
+    bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return true; }
+    bool canProduceFloat32() const { return true; }
+};
+
 // Converts a uint32 to a double (coming from asm.js).
 class MAsmJSUnsignedToDouble
   : public MUnaryInstruction
 {
     MAsmJSUnsignedToDouble(MDefinition *def)
       : MUnaryInstruction(def)
     {
         setResultType(MIRType_Double);
@@ -3101,16 +3184,18 @@ class MBinaryArithInstruction
     void infer(BaselineInspector *inspector,
                jsbytecode *pc, bool overflowed);
 
     void setInt32() {
         specialization_ = MIRType_Int32;
         setResultType(MIRType_Int32);
     }
 
+    virtual void trySpecializeFloat32();
+
     bool congruentTo(MDefinition *ins) const {
         return MBinaryInstruction::congruentTo(ins);
     }
     AliasSet getAliasSet() const {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
@@ -3476,16 +3561,18 @@ class MAdd : public MBinaryArithInstruct
         add->setResultType(type);
         if (type == MIRType_Int32) {
             add->setTruncated(true);
             add->setCommutative();
         }
         return add;
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     double getIdentity() {
         return 0;
     }
 
     bool fallible();
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
@@ -3512,16 +3599,18 @@ class MSub : public MBinaryArithInstruct
             sub->setTruncated(true);
         return sub;
     }
 
     double getIdentity() {
         return 0;
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     bool fallible();
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
 };
 
 class MMul : public MBinaryArithInstruction
 {
@@ -3584,16 +3673,18 @@ class MMul : public MBinaryArithInstruct
     }
 
     bool updateForReplacement(MDefinition *ins);
 
     bool fallible() {
         return canBeNegativeZero_ || canOverflow();
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     void computeRange();
     bool truncate();
     bool isOperandTruncated(size_t index) const;
 
     Mode mode() { return mode_; }
 };
 
 class MDiv : public MBinaryArithInstruction
@@ -3652,16 +3743,18 @@ class MDiv : public MBinaryArithInstruct
     bool canBeDivideByZero() {
         return canBeDivideByZero_;
     }
 
     bool isUnsigned() {
         return unsigned_;
     }
 
+    bool isFloat32Commutative() const { return true; }
+
     bool fallible();
     bool truncate();
 };
 
 class MMod : public MBinaryArithInstruction
 {
     bool unsigned_;
     bool canBeNegativeDividend_;
@@ -3839,27 +3932,31 @@ class MFromCharCode
 class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
 {
     js::Vector<MUse, 2, IonAllocPolicy> inputs_;
 
     uint32_t slot_;
     bool hasBackedgeType_;
     bool triedToSpecialize_;
     bool isIterator_;
+    bool canProduceFloat32_;
+    bool canConsumeFloat32_;
 
 #if DEBUG
     bool specialized_;
     uint32_t capacity_;
 #endif
 
     MPhi(uint32_t slot, MIRType resultType)
       : slot_(slot),
         hasBackedgeType_(false),
         triedToSpecialize_(false),
-        isIterator_(false)
+        isIterator_(false),
+        canProduceFloat32_(false),
+        canConsumeFloat32_(false)
 #if DEBUG
         , specialized_(false)
         , capacity_(0)
 #endif
     {
         setResultType(resultType);
     }
 
@@ -3947,16 +4044,32 @@ class MPhi MOZ_FINAL : public MDefinitio
         // those two cases).
         MDefinition *first = getOperand(0);
         for (size_t i = 1, e = numOperands(); i < e; i++) {
             if (getOperand(i) != first && getOperand(i) != this)
                 return NULL;
         }
         return first;
     }
+
+    bool canProduceFloat32() const {
+        return canProduceFloat32_;
+    }
+
+    void setCanProduceFloat32(bool can) {
+        canProduceFloat32_ = can;
+    }
+
+    bool canConsumeFloat32() const {
+        return canConsumeFloat32_;
+    }
+
+    void setCanConsumeFloat32(bool can) {
+        canConsumeFloat32_ = can;
+    }
 };
 
 // The goal of a Beta node is to split a def at a conditionally taken
 // branch, so that uses dominated by it have a different name.
 class MBeta : public MUnaryInstruction
 {
   private:
     const Range *comparison_;
@@ -4951,17 +5064,17 @@ class MStoreElementCommon
         racy_ = true;
     }
 };
 
 // Store a value to a dense array slots vector.
 class MStoreElement
   : public MAryInstruction<3>,
     public MStoreElementCommon,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >
 {
     bool needsHoleCheck_;
 
     MStoreElement(MDefinition *elements, MDefinition *index, MDefinition *value, bool needsHoleCheck) {
         setOperand(0, elements);
         setOperand(1, index);
         setOperand(2, value);
         needsHoleCheck_ = needsHoleCheck;
@@ -5001,17 +5114,17 @@ class MStoreElement
 
 // Like MStoreElement, but supports indexes >= initialized length. The downside
 // is that we cannot hoist the elements vector and bounds check, since this
 // instruction may update the (initialized) length and reallocate the elements
 // vector.
 class MStoreElementHole
   : public MAryInstruction<4>,
     public MStoreElementCommon,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >
 {
     MStoreElementHole(MDefinition *object, MDefinition *elements,
                       MDefinition *index, MDefinition *value) {
         setOperand(0, object);
         setOperand(1, elements);
         setOperand(2, index);
         setOperand(3, value);
         JS_ASSERT(elements->type() == MIRType_Elements);
@@ -5201,16 +5314,18 @@ class MLoadTypedArrayElement
     MDefinition *index() const {
         return getOperand(1);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
 
     void computeRange();
+
+    bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Load a value from a typed array. Out-of-bounds accesses are handled using
 // a VM call.
 class MLoadTypedArrayElementHole
   : public MBinaryInstruction,
     public SingleObjectPolicy
 {
@@ -5249,28 +5364,31 @@ class MLoadTypedArrayElementHole
         return getOperand(0);
     }
     MDefinition *index() const {
         return getOperand(1);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Load(AliasSet::TypedArrayElement);
     }
+    bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Load a value fallibly or infallibly from a statically known typed array.
 class MLoadTypedArrayElementStatic
   : public MUnaryInstruction,
     public IntPolicy<0>
 {
     MLoadTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr)
       : MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true)
     {
         int type = typedArray_->type();
-        if (type == ScalarTypeRepresentation::TYPE_FLOAT32 || type == ScalarTypeRepresentation::TYPE_FLOAT64)
+        if (type == ScalarTypeRepresentation::TYPE_FLOAT32)
+            setResultType(MIRType_Float32);
+        else if (type == ScalarTypeRepresentation::TYPE_FLOAT64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
     }
 
     CompilerRoot<TypedArrayObject*> typedArray_;
     bool fallible_;
 
@@ -5299,16 +5417,17 @@ class MLoadTypedArrayElementStatic
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
 
     void computeRange();
     bool truncate();
+    bool canProduceFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 class MStoreTypedArrayElement
   : public MTernaryInstruction,
     public StoreTypedArrayPolicy
 {
     int arrayType_;
 
@@ -5362,16 +5481,18 @@ class MStoreTypedArrayElement
     }
     bool racy() const {
         return racy_;
     }
     void setRacy() {
         racy_ = true;
     }
     bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 class MStoreTypedArrayElementHole
   : public MAryInstruction<4>,
     public StoreTypedArrayHolePolicy
 {
     int arrayType_;
 
@@ -5425,16 +5546,18 @@ class MStoreTypedArrayElementHole
     }
     MDefinition *value() const {
         return getOperand(3);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Store a value infallibly to a statically known typed array.
 class MStoreTypedArrayElementStatic :
     public MBinaryInstruction
   , public StoreTypedArrayElementStaticPolicy
 {
     MStoreTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr, MDefinition *v)
@@ -5466,16 +5589,18 @@ class MStoreTypedArrayElementStatic :
     size_t length() const;
 
     MDefinition *ptr() const { return getOperand(0); }
     MDefinition *value() const { return getOperand(1); }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
     bool isOperandTruncated(size_t index) const;
+
+    bool canConsumeFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
 };
 
 // Compute an "effective address", i.e., a compound computation of the form:
 //   base + index * scale + displacement
 class MEffectiveAddress : public MBinaryInstruction
 {
     MEffectiveAddress(MDefinition *base, MDefinition *index, Scale scale, int32_t displacement)
       : MBinaryInstruction(base, index), scale_(scale), displacement_(displacement)
@@ -5585,17 +5710,17 @@ class MLoadFixedSlot
         return AliasSet::Load(AliasSet::FixedSlot);
     }
 
     bool mightAlias(MDefinition *store);
 };
 
 class MStoreFixedSlot
   : public MBinaryInstruction,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
 {
     bool needsBarrier_;
     size_t slot_;
 
     MStoreFixedSlot(MDefinition *obj, MDefinition *rval, size_t slot, bool barrier)
       : MBinaryInstruction(obj, rval),
         needsBarrier_(barrier),
         slot_(slot)
@@ -6406,17 +6531,17 @@ class MForkJoinSlice
     bool possiblyCalls() const {
         return true;
     }
 };
 
 // Store to vp[slot] (slots that are not inline in an object).
 class MStoreSlot
   : public MBinaryInstruction,
-    public SingleObjectPolicy
+    public MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >
 {
     uint32_t slot_;
     MIRType slotType_;
     bool needsBarrier_;
 
     MStoreSlot(MDefinition *slots, uint32_t slot, MDefinition *value, bool barrier)
         : MBinaryInstruction(slots, value),
           slot_(slot),
@@ -6706,17 +6831,17 @@ class MCallSetProperty
     }
     bool possiblyCalls() const {
         return true;
     }
 };
 
 class MSetPropertyCache
   : public MSetPropertyInstruction,
-    public SingleObjectPolicy
+    public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
 {
     bool needsTypeBarrier_;
 
     MSetPropertyCache(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict,
                       bool typeBarrier)
       : MSetPropertyInstruction(obj, value, name, strict),
         needsTypeBarrier_(typeBarrier)
     {
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -71,16 +71,17 @@ namespace jit {
     _(Return)                                                               \
     _(Throw)                                                                \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
+    _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
     _(NewSlots)                                                             \
     _(NewParallelArray)                                                     \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
     _(NewDeclEnvObject)                                                     \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -158,16 +158,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(CharCodeAt)
     UNSAFE_OP(FromCharCode)
     SAFE_OP(Return)
     CUSTOM_OP(Throw)
     SAFE_OP(Box)     // Boxing just creates a JSVal, doesn't alloc.
     SAFE_OP(Unbox)
     SAFE_OP(GuardObject)
     SAFE_OP(ToDouble)
+    SAFE_OP(ToFloat32)
     SAFE_OP(ToInt32)
     SAFE_OP(TruncateToInt32)
     SAFE_OP(MaybeToDoubleElement)
     CUSTOM_OP(ToString)
     SAFE_OP(NewSlots)
     CUSTOM_OP(NewArray)
     CUSTOM_OP(NewObject)
     CUSTOM_OP(NewCallObject)
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1101,16 +1101,22 @@ MMod::computeRange()
 
 void
 MToDouble::computeRange()
 {
     setRange(new Range(getOperand(0)));
 }
 
 void
+MToFloat32::computeRange()
+{
+    setRange(new Range(getOperand(0)));
+}
+
+void
 MTruncateToInt32::computeRange()
 {
     Range *output = new Range(getOperand(0));
     output->wrapAroundToInt32();
     setRange(output);
 }
 
 void
@@ -1825,16 +1831,30 @@ MToDouble::truncate()
     setResultType(MIRType_Int32);
     if (range())
         range()->wrapAroundToInt32();
 
     return true;
 }
 
 bool
+MToFloat32::truncate()
+{
+    JS_ASSERT(type() == MIRType_Float32);
+
+    // We use the return type to flag that this MToFloat32 sould be replaced by a
+    // MTruncateToInt32 when modifying the graph.
+    setResultType(MIRType_Int32);
+    if (range())
+        range()->wrapAroundToInt32();
+
+    return true;
+}
+
+bool
 MLoadTypedArrayElementStatic::truncate()
 {
     setInfallible();
     return false;
 }
 
 bool
 MDefinition::isOperandTruncated(size_t index) const
@@ -1876,16 +1896,24 @@ bool
 MToDouble::isOperandTruncated(size_t index) const
 {
     // The return type is used to flag that we are replacing this Double by a
     // Truncate of its operand if needed.
     return type() == MIRType_Int32;
 }
 
 bool
+MToFloat32::isOperandTruncated(size_t index) const
+{
+    // The return type is used to flag that we are replacing this Float32 by a
+    // Truncate of its operand if needed.
+    return type() == MIRType_Int32;
+}
+
+bool
 MStoreTypedArrayElement::isOperandTruncated(size_t index) const
 {
     return index == 2 && !isFloatArray();
 }
 
 bool
 MStoreTypedArrayElementHole::isOperandTruncated(size_t index) const
 {
--- a/js/src/jit/SnapshotReader.h
+++ b/js/src/jit/SnapshotReader.h
@@ -52,16 +52,18 @@ class SnapshotReader
 
     template <typename T> inline T readVariableLength();
 
   public:
     enum SlotMode
     {
         CONSTANT,           // An index into the constant pool.
         DOUBLE_REG,         // Type is double, payload is in a register.
+        FLOAT32_REG,        // Type is float32, payload is in a register.
+        FLOAT32_STACK,      // Type is float32, payload is on the stack.
         TYPED_REG,          // Type is constant, payload is in a register.
         TYPED_STACK,        // Type is constant, payload is on the stack.
         UNTYPED,            // Type is not known.
         JS_UNDEFINED,       // UndefinedValue()
         JS_NULL,            // NullValue()
         JS_INT32            // Int32Value(n)
     };
 
@@ -130,16 +132,28 @@ class SnapshotReader
             known_type_.type = type;
             known_type_.payload = loc;
         }
         Slot(const FloatRegister &reg)
           : mode_(DOUBLE_REG)
         {
             fpu_ = reg.code();
         }
+        Slot(SlotMode mode, const FloatRegister &reg)
+          : mode_(mode)
+        {
+            JS_ASSERT(mode == FLOAT32_REG);
+            fpu_ = reg.code();
+        }
+        Slot(SlotMode mode, const Location &loc)
+          : mode_(mode)
+        {
+            JS_ASSERT(mode == FLOAT32_STACK);
+            known_type_.payload = loc;
+        }
         Slot(SlotMode mode)
           : mode_(mode)
         { }
         Slot(SlotMode mode, uint32_t index)
           : mode_(mode)
         {
             JS_ASSERT(mode == CONSTANT || mode == JS_INT32);
             value_ = index;
@@ -161,21 +175,21 @@ class SnapshotReader
             JS_ASSERT(mode() == TYPED_REG || mode() == TYPED_STACK);
             return known_type_.type;
         }
         Register reg() const {
             JS_ASSERT(mode() == TYPED_REG && knownType() != JSVAL_TYPE_DOUBLE);
             return known_type_.payload.reg();
         }
         FloatRegister floatReg() const {
-            JS_ASSERT(mode() == DOUBLE_REG);
+            JS_ASSERT(mode() == DOUBLE_REG || mode() == FLOAT32_REG);
             return FloatRegister::FromCode(fpu_);
         }
         int32_t stackSlot() const {
-            JS_ASSERT(mode() == TYPED_STACK);
+            JS_ASSERT(mode() == TYPED_STACK || mode() == FLOAT32_STACK);
             return known_type_.payload.stackSlot();
         }
 #if defined(JS_NUNBOX32)
         Location payload() const {
             JS_ASSERT(mode() == UNTYPED);
             return unknown_type_.payload;
         }
         Location type() const {
--- a/js/src/jit/SnapshotWriter.h
+++ b/js/src/jit/SnapshotWriter.h
@@ -41,16 +41,18 @@ class SnapshotWriter
     void endFrame();
 
     void addSlot(const FloatRegister &reg);
     void addSlot(JSValueType type, const Register &reg);
     void addSlot(JSValueType type, int32_t stackIndex);
     void addUndefinedSlot();
     void addNullSlot();
     void addInt32Slot(int32_t value);
+    void addFloat32Slot(const FloatRegister &reg);
+    void addFloat32Slot(int32_t stackIndex);
     void addConstantPoolSlot(uint32_t index);
 #if defined(JS_NUNBOX32)
     void addSlot(const Register &type, const Register &payload);
     void addSlot(const Register &type, int32_t payloadStackIndex);
     void addSlot(int32_t typeStackIndex, const Register &payload);
     void addSlot(int32_t typeStackIndex, int32_t payloadStackIndex);
 #elif defined(JS_PUNBOX64)
     void addSlot(const Register &value);
--- a/js/src/jit/Snapshots.cpp
+++ b/js/src/jit/Snapshots.cpp
@@ -51,23 +51,26 @@ using namespace js::jit;
 //         JSVAL_TYPE_OBJECT:
 //         JSVAL_TYPE_BOOLEAN:
 //         JSVAL_TYPE_STRING:
 //              If "reg" is InvalidReg1, this byte is followed by a [vws]
 //              stack offset. Otherwise, "reg" encodes a GPR register.
 //
 //         JSVAL_TYPE_NULL:
 //              Reg value:
-//                 0-29: Constant integer; Int32Value(n)
+//                 0-27: Constant integer; Int32Value(n)
+//                   28: Variable float32; Float register code
+//                   29: Variable float32; Stack index
 //                   30: NullValue()
 //                   31: Constant integer; Int32Value([vws])
 //
 //         JSVAL_TYPE_UNDEFINED:
 //              Reg value:
-//                 0-29: Constant value, index n into ionScript->constants()
+//                 0-27: Constant value, index n into ionScript->constants()
+//                28-29: unused
 //                   30: UndefinedValue()
 //                   31: Constant value, index [vwu] into
 //                       ionScript->constants()
 //
 //         JSVAL_TYPE_MAGIC: (reg value is 30)
 //              The value is a lazy argument object. Followed by extra fields
 //              indicating the location of the payload.
 //              [vwu] reg2 (0-29)
@@ -167,20 +170,22 @@ SnapshotReader::spewBailingFrom() const
 static const uint32_t NUNBOX32_STACK_STACK = 0;
 static const uint32_t NUNBOX32_STACK_REG   = 1;
 static const uint32_t NUNBOX32_REG_STACK   = 2;
 static const uint32_t NUNBOX32_REG_REG     = 3;
 #endif
 
 static const uint32_t MAX_TYPE_FIELD_VALUE = 7;
 
-static const uint32_t MAX_REG_FIELD_VALUE  = 31;
-static const uint32_t ESC_REG_FIELD_INDEX  = 31;
-static const uint32_t ESC_REG_FIELD_CONST  = 30;
-static const uint32_t MIN_REG_FIELD_ESC    = 30;
+static const uint32_t MAX_REG_FIELD_VALUE         = 31;
+static const uint32_t ESC_REG_FIELD_INDEX         = 31;
+static const uint32_t ESC_REG_FIELD_CONST         = 30;
+static const uint32_t ESC_REG_FIELD_FLOAT32_STACK = 29;
+static const uint32_t ESC_REG_FIELD_FLOAT32_REG   = 28;
+static const uint32_t MIN_REG_FIELD_ESC           = 28;
 
 SnapshotReader::Slot
 SnapshotReader::readSlot()
 {
     JS_ASSERT(slotsRead_ < slotCount_);
     IonSpew(IonSpew_Snapshots, "Reading slot %u", slotsRead_);
     slotsRead_++;
 
@@ -205,16 +210,20 @@ SnapshotReader::readSlot()
         JS_ASSERT(code == ESC_REG_FIELD_INDEX);
         return Slot(TYPED_STACK, type, Location::From(reader_.readSigned()));
 
       case JSVAL_TYPE_NULL:
         if (code == ESC_REG_FIELD_CONST)
             return Slot(JS_NULL);
         if (code == ESC_REG_FIELD_INDEX)
             return Slot(JS_INT32, reader_.readSigned());
+        if (code == ESC_REG_FIELD_FLOAT32_REG)
+            return Slot(FLOAT32_REG, FloatRegister::FromCode(reader_.readUnsigned()));
+        if (code == ESC_REG_FIELD_FLOAT32_STACK)
+            return Slot(FLOAT32_STACK, Location::From(reader_.readSigned()));
         return Slot(JS_INT32, code);
 
       case JSVAL_TYPE_UNDEFINED:
         if (code == ESC_REG_FIELD_CONST)
             return Slot(JS_UNDEFINED);
         if (code == ESC_REG_FIELD_INDEX)
             return Slot(CONSTANT, reader_.readUnsigned());
         return Slot(CONSTANT, code);
@@ -348,16 +357,17 @@ SnapshotWriter::writeSlotHeader(JSValueT
 
     slotsWritten_++;
     JS_ASSERT(slotsWritten_ <= nslots_);
 }
 
 void
 SnapshotWriter::addSlot(const FloatRegister &reg)
 {
+    JS_ASSERT(reg.code() < MIN_REG_FIELD_ESC);
     IonSpew(IonSpew_Snapshots, "    slot %u: double (reg %s)", slotsWritten_, reg.name());
 
     writeSlotHeader(JSVAL_TYPE_DOUBLE, reg.code());
 }
 
 static const char *
 ValTypeToString(JSValueType type)
 {
@@ -502,16 +512,33 @@ SnapshotWriter::addInt32Slot(int32_t val
         writeSlotHeader(JSVAL_TYPE_NULL, value);
     } else {
         writeSlotHeader(JSVAL_TYPE_NULL, ESC_REG_FIELD_INDEX);
         writer_.writeSigned(value);
     }
 }
 
 void
+SnapshotWriter::addFloat32Slot(const FloatRegister &reg)
+{
+    JS_ASSERT(reg.code() < MIN_REG_FIELD_ESC);
+    IonSpew(IonSpew_Snapshots, "    slot %u: float32 (reg %s)", slotsWritten_, reg.name());
+    writeSlotHeader(JSVAL_TYPE_NULL, ESC_REG_FIELD_FLOAT32_REG);
+    writer_.writeUnsigned(reg.code());
+}
+
+void
+SnapshotWriter::addFloat32Slot(int32_t stackIndex)
+{
+    IonSpew(IonSpew_Snapshots, "    slot %u: float32 (stack %d)", slotsWritten_, stackIndex);
+    writeSlotHeader(JSVAL_TYPE_NULL, ESC_REG_FIELD_FLOAT32_STACK);
+    writer_.writeSigned(stackIndex);
+}
+
+void
 SnapshotWriter::addConstantPoolSlot(uint32_t index)
 {
     IonSpew(IonSpew_Snapshots, "    slot %u: constant pool index %u", slotsWritten_, index);
 
     if (index < MIN_REG_FIELD_ESC) {
         writeSlotHeader(JSVAL_TYPE_UNDEFINED, index);
     } else {
         writeSlotHeader(JSVAL_TYPE_UNDEFINED, ESC_REG_FIELD_INDEX);
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -1,28 +1,42 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/TypePolicy.h"
 
+#include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 
 using namespace js;
 using namespace js::jit;
 
 MDefinition *
 BoxInputsPolicy::boxAt(MInstruction *at, MDefinition *operand)
 {
     if (operand->isUnbox())
         return operand->toUnbox()->input();
-    MBox *box = MBox::New(operand);
+    return alwaysBoxAt(at, operand);
+}
+
+MDefinition *
+BoxInputsPolicy::alwaysBoxAt(MInstruction *at, MDefinition *operand)
+{
+    MDefinition *boxedOperand = operand;
+    // Replace Float32 by double
+    if (operand->type() == MIRType_Float32) {
+        MInstruction *replace = MToDouble::New(operand);
+        operand->block()->insertBefore(at, replace);
+        boxedOperand = replace;
+    }
+    MBox *box = MBox::New(boxedOperand);
     at->block()->insertBefore(at, box);
     return box;
 }
 
 bool
 BoxInputsPolicy::adjustInputs(MInstruction *ins)
 {
     for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
@@ -35,17 +49,17 @@ BoxInputsPolicy::adjustInputs(MInstructi
 }
 
 bool
 ArithPolicy::adjustInputs(MInstruction *ins)
 {
     if (specialization_ == MIRType_None)
         return BoxInputsPolicy::adjustInputs(ins);
 
-    JS_ASSERT(ins->type() == MIRType_Double || ins->type() == MIRType_Int32);
+    JS_ASSERT(ins->type() == MIRType_Double || ins->type() == MIRType_Int32 || ins->type() == MIRType_Float32);
 
     for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
         MDefinition *in = ins->getOperand(i);
         if (in->type() == ins->type())
             continue;
 
         MInstruction *replace;
 
@@ -54,16 +68,18 @@ ArithPolicy::adjustInputs(MInstruction *
         if (in->type() == MIRType_Object || in->type() == MIRType_String ||
             (in->type() == MIRType_Undefined && specialization_ == MIRType_Int32))
         {
             in = boxAt(ins, in);
         }
 
         if (ins->type() == MIRType_Double)
             replace = MToDouble::New(in);
+        else if (ins->type() == MIRType_Float32)
+            replace = MToFloat32::New(in);
         else
             replace = MToInt32::New(in);
 
         ins->block()->insertBefore(ins, replace);
         ins->replaceOperand(i, replace);
     }
 
     return true;
@@ -94,16 +110,26 @@ BinaryStringPolicy::adjustInputs(MInstru
 }
 
 bool
 ComparePolicy::adjustInputs(MInstruction *def)
 {
     JS_ASSERT(def->isCompare());
     MCompare *compare = def->toCompare();
 
+    // Convert Float32 operands to doubles
+    for (size_t i = 0; i < 2; i++) {
+        MDefinition *in = def->getOperand(i);
+        if (in->type() == MIRType_Float32) {
+            MInstruction *replace = MToDouble::New(in);
+            def->block()->insertBefore(def, replace);
+            def->replaceOperand(i, replace);
+        }
+    }
+
     // Box inputs to get value
     if (compare->compareType() == MCompare::Compare_Unknown ||
         compare->compareType() == MCompare::Compare_Value)
     {
         return BoxInputsPolicy::adjustInputs(def);
     }
 
     // Compare_Boolean specialization is done for "Anything === Bool"
@@ -308,16 +334,23 @@ BitwisePolicy::adjustInputs(MInstruction
         MDefinition *in = ins->getOperand(i);
         if (in->type() == MIRType_Int32)
             continue;
 
         // See BinaryArithPolicy::adjustInputs for an explanation of the following
         if (in->type() == MIRType_Object || in->type() == MIRType_String)
             in = boxAt(ins, in);
 
+        if (in->type() == MIRType_Float32) {
+            MToDouble *replace = MToDouble::New(in);
+            ins->block()->insertBefore(ins, replace);
+            ins->replaceOperand(i, replace);
+            in = replace;
+        }
+
         MInstruction *replace = MTruncateToInt32::New(in);
         ins->block()->insertBefore(ins, replace);
         ins->replaceOperand(i, replace);
     }
 
     return true;
 }
 
@@ -407,16 +440,71 @@ DoublePolicy<Op>::staticAdjustInputs(MIn
     return true;
 }
 
 template bool DoublePolicy<0>::staticAdjustInputs(MInstruction *def);
 template bool DoublePolicy<1>::staticAdjustInputs(MInstruction *def);
 
 template <unsigned Op>
 bool
+Float32Policy<Op>::staticAdjustInputs(MInstruction *def)
+{
+    MDefinition *in = def->getOperand(Op);
+    if (in->type() == MIRType_Float32)
+        return true;
+
+    // Force a bailout. Objects may be effectful; strings are currently unhandled.
+    if (in->type() == MIRType_Object || in->type() == MIRType_String) {
+        MToDouble *toDouble = MToDouble::New(in);
+        def->block()->insertBefore(def, toDouble);
+
+        MBox *box = MBox::New(toDouble);
+        def->block()->insertBefore(def, box);
+
+        MUnbox *unbox = MUnbox::New(box, MIRType_Double, MUnbox::Fallible);
+        def->block()->insertBefore(def, unbox);
+
+        MToFloat32 *toFloat32 = MToFloat32::New(unbox);
+        def->block()->insertBefore(def, toFloat32);
+
+        def->replaceOperand(Op, unbox);
+
+        return true;
+    }
+
+    MToFloat32 *replace = MToFloat32::New(in);
+    def->block()->insertBefore(def, replace);
+    def->replaceOperand(Op, replace);
+    return true;
+}
+
+template bool Float32Policy<0>::staticAdjustInputs(MInstruction *def);
+template bool Float32Policy<1>::staticAdjustInputs(MInstruction *def);
+template bool Float32Policy<2>::staticAdjustInputs(MInstruction *def);
+
+template <unsigned Op>
+bool
+NoFloatPolicy<Op>::staticAdjustInputs(MInstruction *def)
+{
+    MDefinition *in = def->getOperand(Op);
+    if (in->type() == MIRType_Float32) {
+        MToDouble *replace = MToDouble::New(in);
+        def->block()->insertBefore(def, replace);
+        def->replaceOperand(Op, replace);
+    }
+    return true;
+}
+
+template bool NoFloatPolicy<0>::staticAdjustInputs(MInstruction *def);
+template bool NoFloatPolicy<1>::staticAdjustInputs(MInstruction *def);
+template bool NoFloatPolicy<2>::staticAdjustInputs(MInstruction *def);
+template bool NoFloatPolicy<3>::staticAdjustInputs(MInstruction *def);
+
+template <unsigned Op>
+bool
 BoxPolicy<Op>::staticAdjustInputs(MInstruction *ins)
 {
     MDefinition *in = ins->getOperand(Op);
     if (in->type() == MIRType_Value)
         return true;
 
     ins->replaceOperand(Op, boxAt(ins, in));
     return true;
@@ -463,16 +551,28 @@ template bool ObjectPolicy<1>::staticAdj
 template bool ObjectPolicy<2>::staticAdjustInputs(MInstruction *ins);
 template bool ObjectPolicy<3>::staticAdjustInputs(MInstruction *ins);
 
 bool
 CallPolicy::adjustInputs(MInstruction *ins)
 {
     MCall *call = ins->toCall();
 
+    // External calls shouldn't have Float32 parameters
+    for (uint32_t i = 0, numArgs = call->numActualArgs(); i < numArgs; i++) {
+        MDefinition *arg = call->getArg(i+1); // arg 0 is |this|
+        if (arg->type() == MIRType_Float32) {
+            JS_ASSERT(arg->isPassArg()); // can't be a type barrier, as Float32 doesn't rely on type inference
+            MPassArg *passArg = arg->toPassArg();
+            MInstruction *replace = MToDouble::New(passArg->getArgument());
+            passArg->replaceOperand(0, replace);
+            call->block()->insertBefore(passArg, replace);
+        }
+    }
+
     MDefinition *func = call->getFunction();
     if (func->type() == MIRType_Object)
         return true;
 
     // If the function is impossible to call,
     // bail out by causing a subsequent unbox to fail.
     if (func->type() != MIRType_Value)
         func = boxAt(call, func);
@@ -516,16 +616,17 @@ StoreTypedArrayPolicy::adjustValueInput(
                                         MDefinition *value, int valueOperand)
 {
     MDefinition *curValue = value;
     // First, ensure the value is int32, boolean, double or Value.
     // The conversion is based on TypedArrayObjectTemplate::setElementTail.
     switch (value->type()) {
       case MIRType_Int32:
       case MIRType_Double:
+      case MIRType_Float32:
       case MIRType_Boolean:
       case MIRType_Value:
         break;
       case MIRType_Null:
         value->setFoldedUnchecked();
         value = MConstant::New(Int32Value(0));
         ins->block()->insertBefore(ins, value->toInstruction());
         break;
@@ -545,16 +646,17 @@ StoreTypedArrayPolicy::adjustValueInput(
     if (value != curValue) {
         ins->replaceOperand(valueOperand, value);
         curValue = value;
     }
 
     JS_ASSERT(value->type() == MIRType_Int32 ||
               value->type() == MIRType_Boolean ||
               value->type() == MIRType_Double ||
+              value->type() == MIRType_Float32 ||
               value->type() == MIRType_Value);
 
     switch (arrayType) {
       case ScalarTypeRepresentation::TYPE_INT8:
       case ScalarTypeRepresentation::TYPE_UINT8:
       case ScalarTypeRepresentation::TYPE_INT16:
       case ScalarTypeRepresentation::TYPE_UINT16:
       case ScalarTypeRepresentation::TYPE_INT32:
@@ -564,16 +666,25 @@ StoreTypedArrayPolicy::adjustValueInput(
             ins->block()->insertBefore(ins, value->toInstruction());
         }
         break;
       case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED:
         // IonBuilder should have inserted ClampToUint8.
         JS_ASSERT(value->type() == MIRType_Int32);
         break;
       case ScalarTypeRepresentation::TYPE_FLOAT32:
+        if (LIRGenerator::allowFloat32Optimizations()) {
+            if (value->type() != MIRType_Float32) {
+                value = MToFloat32::New(value);
+                ins->block()->insertBefore(ins, value->toInstruction());
+            }
+            break;
+        }
+        // Fallthrough: if the LIRGenerator cannot directly store Float32, it will expect the
+        // stored value to be a double.
       case ScalarTypeRepresentation::TYPE_FLOAT64:
         if (value->type() != MIRType_Double) {
             value = MToDouble::New(value);
             ins->block()->insertBefore(ins, value->toInstruction());
         }
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Invalid array type");
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -30,16 +30,17 @@ class TypePolicy
 };
 
 class BoxInputsPolicy : public TypePolicy
 {
   protected:
     static MDefinition *boxAt(MInstruction *at, MDefinition *operand);
 
   public:
+    static MDefinition *alwaysBoxAt(MInstruction *at, MDefinition *operand);
     virtual bool adjustInputs(MInstruction *def);
 };
 
 class ArithPolicy : public BoxInputsPolicy
 {
   protected:
     // Specifies three levels of specialization:
     //  - < Value. This input is expected and required.
@@ -139,16 +140,55 @@ class DoublePolicy : public BoxInputsPol
 {
   public:
     static bool staticAdjustInputs(MInstruction *def);
     bool adjustInputs(MInstruction *def) {
         return staticAdjustInputs(def);
     }
 };
 
+// Expect a float32 for operand Op. If the input is a Value, it is unboxed.
+template <unsigned Op>
+class Float32Policy : public BoxInputsPolicy
+{
+  public:
+    static bool staticAdjustInputs(MInstruction *def);
+    bool adjustInputs(MInstruction *def) {
+        return staticAdjustInputs(def);
+    }
+};
+
+// Expect a float32 OR a double for operand Op, but will prioritize Float32
+// if the result type is set as such. If the input is a Value, it is unboxed.
+template <unsigned Op>
+class RuntimePolicy : public TypePolicy
+{
+    MIRType policyType_;
+
+  public:
+    bool adjustInputs(MInstruction *def) {
+        if (policyType_ == MIRType_Double)
+            return DoublePolicy<Op>::staticAdjustInputs(def);
+        return Float32Policy<Op>::staticAdjustInputs(def);
+    }
+    void setPolicyType(MIRType type) {
+        policyType_ = type;
+    }
+};
+
+template <unsigned Op>
+class NoFloatPolicy
+{
+  public:
+    static bool staticAdjustInputs(MInstruction *def);
+    bool adjustInputs(MInstruction *def) {
+        return staticAdjustInputs(def);
+    }
+};
+
 // Box objects or strings as an input to a ToDouble instruction.
 class ToDoublePolicy : public BoxInputsPolicy
 {
   public:
     static bool staticAdjustInputs(MInstruction *def);
     bool adjustInputs(MInstruction *def) {
         return staticAdjustInputs(def);
     }
@@ -246,17 +286,17 @@ class ClampPolicy : public BoxInputsPoli
 {
   public:
     bool adjustInputs(MInstruction *ins);
 };
 
 static inline bool
 CoercesToDouble(MIRType type)
 {
-    if (type == MIRType_Undefined || type == MIRType_Double)
+    if (type == MIRType_Undefined || IsFloatingPointType(type))
         return true;
     return false;
 }
 
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -62,16 +62,23 @@ class LIRGeneratorARM : public LIRGenera
     bool visitAsmJSUMod(MAsmJSUMod *ins);
 
     LTableSwitch *newLTableSwitch(const LAllocation &in, const LDefinition &inputCopy,
                                   MTableSwitch *ins);
     LTableSwitchV *newLTableSwitchV(MTableSwitch *ins);
     LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
     LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
 
+    bool lowerConstantFloat32(float d, MInstruction *ins) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+    bool lowerTruncateFToInt32(MTruncateToInt32 *ins) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+
   public:
     bool visitConstant(MConstant *ins);
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool lowerPhi(MPhi *phi);
     bool visitGuardShape(MGuardShape *ins);
     bool visitGuardObjectType(MGuardObjectType *ins);
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -49,16 +49,29 @@ class MacroAssemblerARM : public Assembl
     void convertInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertInt32ToDouble(const Address &src, FloatRegister dest);
     void convertUInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest);
     void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail);
     void convertDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail,
                               bool negativeZeroCheck = true);
 
+    void convertFloatToDouble(const FloatRegister &src, const FloatRegister &dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+    void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+    void convertInt32ToFloat32(const Register &src, const FloatRegister &dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+    void convertInt32ToFloat32(const Address &src, FloatRegister dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+
     void addDouble(FloatRegister src, FloatRegister dest);
     void subDouble(FloatRegister src, FloatRegister dest);
     void mulDouble(FloatRegister src, FloatRegister dest);
     void divDouble(FloatRegister src, FloatRegister dest);
 
     void negateDouble(FloatRegister reg);
     void inc64(AbsoluteAddress dest);
 
@@ -743,16 +756,26 @@ class MacroAssemblerARMCompat : public M
     void loadStaticDouble(const double *dp, const FloatRegister &dest);
     void loadConstantDouble(double dp, const FloatRegister &dest);
     // treat the value as a boolean, and set condition codes accordingly
     Condition testInt32Truthy(bool truthy, const ValueOperand &operand);
     Condition testBooleanTruthy(bool truthy, const ValueOperand &operand);
     Condition testDoubleTruthy(bool truthy, const FloatRegister &reg);
     Condition testStringTruthy(bool truthy, const ValueOperand &value);
 
+    void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+    void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+    void loadStaticFloat32(const float *dp, const FloatRegister &dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+
     template<typename T>
     void branchTestInt32(Condition cond, const T & t, Label *label) {
         Condition c = testInt32(cond, t);
         ma_b(label, c);
     }
     template<typename T>
     void branchTestBoolean(Condition cond, const T & t, Label *label) {
         Condition c = testBoolean(cond, t);
@@ -1182,16 +1205,23 @@ class MacroAssemblerARMCompat : public M
 
     void loadDouble(const Address &addr, const FloatRegister &dest);
     void loadDouble(const BaseIndex &src, const FloatRegister &dest);
 
     // Load a float value into a register, then expand it to a double.
     void loadFloatAsDouble(const Address &addr, const FloatRegister &dest);
     void loadFloatAsDouble(const BaseIndex &src, const FloatRegister &dest);
 
+    void loadFloat(const Address &addr, const FloatRegister &dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+    void loadFloat(const BaseIndex &src, const FloatRegister &dest) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+
     void store8(const Register &src, const Address &address);
     void store8(const Imm32 &imm, const Address &address);
     void store8(const Register &src, const BaseIndex &address);
     void store8(const Imm32 &imm, const BaseIndex &address);
 
     void store16(const Register &src, const Address &address);
     void store16(const Imm32 &imm, const Address &address);
     void store16(const Register &src, const BaseIndex &address);
@@ -1264,16 +1294,21 @@ class MacroAssemblerARMCompat : public M
     void breakpoint();
     // conditional breakpoint
     void breakpoint(Condition cc);
 
     void compareDouble(FloatRegister lhs, FloatRegister rhs);
     void branchDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs,
                       Label *label);
 
+    void branchFloat(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs,
+                      Label *label) {
+        MOZ_ASSUME_UNREACHABLE("NYI");
+    }
+
     void checkStackAlignment();
 
     void rshiftPtr(Imm32 imm, Register dest) {
         ma_lsr(imm, dest, dest);
     }
     void lshiftPtr(Imm32 imm, Register dest) {
         ma_lsl(imm, dest, dest);
     }
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -222,16 +222,20 @@ class AssemblerX86Shared
         // instruction stream.
         masm.jumpTablePointer(label->prev());
         label->setPrev(masm.size());
     }
     void writeDoubleConstant(double d, Label *label) {
         label->bind(masm.size());
         masm.doubleConstant(d);
     }
+    void writeFloatConstant(float f, Label *label) {
+        label->bind(masm.size());
+        masm.floatConstant(f);
+    }
     void movl(const Imm32 &imm32, const Register &dest) {
         masm.movl_i32r(imm32.value, dest.code());
     }
     void movl(const Register &src, const Register &dest) {
         masm.movl_rr(src.code(), dest.code());
     }
     void movl(const Operand &src, const Register &dest) {
         switch (src.kind()) {
@@ -327,29 +331,35 @@ class AssemblerX86Shared
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
     void movss(const Operand &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
+          case Operand::FPREG:
+            masm.movss_rr(src.fpu(), dest.code());
+            break;
           case Operand::REG_DISP:
             masm.movss_mr(src.disp(), src.base(), dest.code());
             break;
           case Operand::SCALE:
             masm.movss_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
     void movss(const FloatRegister &src, const Operand &dest) {
         JS_ASSERT(HasSSE2());
         switch (dest.kind()) {
+          case Operand::FPREG:
+            masm.movss_rr(src.code(), dest.fpu());
+            break;
           case Operand::REG_DISP:
             masm.movss_rm(src.code(), dest.disp(), dest.base());
             break;
           case Operand::SCALE:
             masm.movss_rm(src.code(), dest.disp(), dest.base(), dest.index(), dest.scale());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
@@ -1136,48 +1146,80 @@ class AssemblerX86Shared
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
     void cvttsd2si(const FloatRegister &src, const Register &dest) {
         JS_ASSERT(HasSSE2());
         masm.cvttsd2si_rr(src.code(), dest.code());
     }
+    void cvttss2si(const FloatRegister &src, const Register &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.cvttss2si_rr(src.code(), dest.code());
+    }
+    void cvtsi2ss(const Operand &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        switch (src.kind()) {
+          case Operand::REG:
+            masm.cvtsi2ss_rr(src.reg(), dest.code());
+            break;
+          case Operand::REG_DISP:
+            masm.cvtsi2ss_mr(src.disp(), src.base(), dest.code());
+            break;
+          case Operand::SCALE:
+            masm.cvtsi2ss_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
+        }
+    }
+    void cvtsi2ss(const Register &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.cvtsi2ss_rr(src.code(), dest.code());
+    }
     void cvtsi2sd(const Register &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.cvtsi2sd_rr(src.code(), dest.code());
     }
     void movmskpd(const FloatRegister &src, const Register &dest) {
         JS_ASSERT(HasSSE2());
         masm.movmskpd_rr(src.code(), dest.code());
     }
     void ptest(const FloatRegister &lhs, const FloatRegister &rhs) {
         JS_ASSERT(HasSSE41());
         masm.ptest_rr(rhs.code(), lhs.code());
     }
     void ucomisd(const FloatRegister &lhs, const FloatRegister &rhs) {
         JS_ASSERT(HasSSE2());
         masm.ucomisd_rr(rhs.code(), lhs.code());
     }
+    void ucomiss(const FloatRegister &lhs, const FloatRegister &rhs) {
+        JS_ASSERT(HasSSE2());
+        masm.ucomiss_rr(rhs.code(), lhs.code());
+    }
     void pcmpeqw(const FloatRegister &lhs, const FloatRegister &rhs) {
         JS_ASSERT(HasSSE2());
         masm.pcmpeqw_rr(rhs.code(), lhs.code());
     }    
     void movd(const Register &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.movd_rr(src.code(), dest.code());
     }
     void movd(const FloatRegister &src, const Register &dest) {
         JS_ASSERT(HasSSE2());
         masm.movd_rr(src.code(), dest.code());
     }
     void addsd(const FloatRegister &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.addsd_rr(src.code(), dest.code());
     }
+    void addss(const FloatRegister &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.addss_rr(src.code(), dest.code());
+    }
     void addsd(const Operand &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::FPREG:
             masm.addsd_rr(src.fpu(), dest.code());
             break;
           case Operand::REG_DISP:
             masm.addsd_mr(src.disp(), src.base(), dest.code());
@@ -1186,33 +1228,68 @@ class AssemblerX86Shared
           case Operand::ADDRESS:
             masm.addsd_mr(src.address(), dest.code());
             break;
 #endif
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
+    void addss(const Operand &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        switch (src.kind()) {
+          case Operand::FPREG:
+            masm.addss_rr(src.fpu(), dest.code());
+            break;
+          case Operand::REG_DISP:
+            masm.addss_mr(src.disp(), src.base(), dest.code());
+            break;
+#ifdef JS_CPU_X86
+          case Operand::ADDRESS:
+            masm.addss_mr(src.address(), dest.code());
+            break;
+#endif
+          default:
+            MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
+        }
+    }
     void subsd(const FloatRegister &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.subsd_rr(src.code(), dest.code());
     }
+    void subss(const FloatRegister &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.subss_rr(src.code(), dest.code());
+    }
     void subsd(const Operand &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::FPREG:
             masm.subsd_rr(src.fpu(), dest.code());
             break;
           case Operand::REG_DISP:
             masm.subsd_mr(src.disp(), src.base(), dest.code());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
+    void subss(const Operand &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        switch (src.kind()) {
+          case Operand::FPREG:
+            masm.subss_rr(src.fpu(), dest.code());
+            break;
+          case Operand::REG_DISP:
+            masm.subss_mr(src.disp(), src.base(), dest.code());
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
+        }
+    }
     void mulsd(const FloatRegister &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.mulsd_rr(src.code(), dest.code());
     }
     void mulsd(const Operand &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::FPREG:
@@ -1220,37 +1297,75 @@ class AssemblerX86Shared
             break;
           case Operand::REG_DISP:
             masm.mulsd_mr(src.disp(), src.base(), dest.code());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
+    void mulss(const Operand &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        switch (src.kind()) {
+          case Operand::FPREG:
+            masm.mulss_rr(src.fpu(), dest.code());
+            break;
+          case Operand::REG_DISP:
+            masm.mulss_mr(src.disp(), src.base(), dest.code());
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
+        }
+    }
+    void mulss(const FloatRegister &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.mulss_rr(src.code(), dest.code());
+    }
     void divsd(const FloatRegister &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.divsd_rr(src.code(), dest.code());
     }
+    void divss(const FloatRegister &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.divss_rr(src.code(), dest.code());
+    }
     void divsd(const Operand &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         switch (src.kind()) {
           case Operand::FPREG:
             masm.divsd_rr(src.fpu(), dest.code());
             break;
           case Operand::REG_DISP:
             masm.divsd_mr(src.disp(), src.base(), dest.code());
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
+    void divss(const Operand &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        switch (src.kind()) {
+          case Operand::FPREG:
+            masm.divss_rr(src.fpu(), dest.code());
+            break;
+          case Operand::REG_DISP:
+            masm.divss_mr(src.disp(), src.base(), dest.code());
+            break;
+          default:
+            MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
+        }
+    }
     void xorpd(const FloatRegister &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.xorpd_rr(src.code(), dest.code());
     }
+    void xorps(const FloatRegister &src, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.xorps_rr(src.code(), dest.code());
+    }
     void orpd(const FloatRegister &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.orpd_rr(src.code(), dest.code());
     }
     void andpd(const FloatRegister &src, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.andpd_rr(src.code(), dest.code());
     }
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -157,25 +157,33 @@ CodeGeneratorShared::encodeSlots(LSnapsh
           case MIRType_Null:
             snapshots_.addNullSlot();
             break;
           case MIRType_Int32:
           case MIRType_String:
           case MIRType_Object:
           case MIRType_Boolean:
           case MIRType_Double:
+          case MIRType_Float32:
           {
             LAllocation *payload = snapshot->payloadOfSlot(i);
-            JSValueType type = ValueTypeFromMIRType(mir->type());
+            JSValueType valueType = ValueTypeFromMIRType(type);
             if (payload->isMemory()) {
-                snapshots_.addSlot(type, ToStackIndex(payload));
+                if (type == MIRType_Float32)
+                    snapshots_.addFloat32Slot(ToStackIndex(payload));
+                else
+                    snapshots_.addSlot(valueType, ToStackIndex(payload));
             } else if (payload->isGeneralReg()) {
-                snapshots_.addSlot(type, ToRegister(payload));
+                snapshots_.addSlot(valueType, ToRegister(payload));
             } else if (payload->isFloatReg()) {
-                snapshots_.addSlot(ToFloatRegister(payload));
+                FloatRegister reg = ToFloatRegister(payload);
+                if (type == MIRType_Float32)
+                    snapshots_.addFloat32Slot(reg);
+                else
+                    snapshots_.addSlot(reg);
             } else {
                 MConstant *constant = mir->toConstant();
                 const Value &v = constant->value();
 
                 // Don't bother with the constant pool for smallish integers.
                 if (v.isInt32() && v.toInt32() >= -32 && v.toInt32() <= 32) {
                     snapshots_.addInt32Slot(v.toInt32());
                 } else {
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -85,16 +85,24 @@ bool
 CodeGeneratorX86Shared::visitDouble(LDouble *ins)
 {
     const LDefinition *out = ins->getDef(0);
     masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out));
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitFloat32(LFloat32 *ins)
+{
+    const LDefinition *out = ins->getDef(0);
+    masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitTestIAndBranch(LTestIAndBranch *test)
 {
     const LAllocation *opd = test->input();
 
     // Test the operand
     masm.testl(ToRegister(opd), ToRegister(opd));
     emitBranch(Assembler::NonZero, test->ifTrue(), test->ifFalse());
     return true;
@@ -1272,16 +1280,44 @@ CodeGeneratorX86Shared::visitMathD(LMath
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected opcode");
     }
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitMathF(LMathF *math)
+{
+    FloatRegister lhs = ToFloatRegister(math->lhs());
+    Operand rhs = ToOperand(math->rhs());
+
+    JS_ASSERT(ToFloatRegister(math->output()) == lhs);
+
+    switch (math->jsop()) {
+      case JSOP_ADD:
+        masm.addss(rhs, lhs);
+        break;
+      case JSOP_SUB:
+        masm.subss(rhs, lhs);
+        break;
+      case JSOP_MUL:
+        masm.mulss(rhs, lhs);
+        break;
+      case JSOP_DIV:
+        masm.divss(rhs, lhs);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("unexpected opcode");
+        return false;
+    }
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitFloor(LFloor *lir)
 {
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister scratch = ScratchFloatReg;
     Register output = ToRegister(lir->output());
 
     if (AssemblerX86Shared::HasSSE41()) {
         // Bail on negative-zero.
@@ -1522,11 +1558,21 @@ CodeGeneratorX86Shared::visitNegD(LNegD 
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
 
     masm.negateDouble(input);
     return true;
 }
 
+bool
+CodeGeneratorX86Shared::visitNegF(LNegF *ins)
+{
+    FloatRegister input = ToFloatRegister(ins->input());
+    JS_ASSERT(input == ToFloatRegister(ins->output()));
+
+    masm.negateFloat(input);
+    return true;
+}
+
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -74,16 +74,17 @@ class CodeGeneratorX86Shared : public Co
     bool emitTableSwitchDispatch(MTableSwitch *mir, const Register &index, const Register &base);
 
   public:
     CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     // Instruction visitors.
     virtual bool visitDouble(LDouble *ins);
+    virtual bool visitFloat32(LFloat32 *ins);
     virtual bool visitMinMaxD(LMinMaxD *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitPowHalfD(LPowHalfD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitMulI(LMulI *ins);
     virtual bool visitDivI(LDivI *ins);
@@ -100,27 +101,29 @@ class CodeGeneratorX86Shared : public Co
     virtual bool visitCompare(LCompare *comp);
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitCompareD(LCompareD *comp);
     virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp);
     virtual bool visitBitAndAndBranch(LBitAndAndBranch *baab);
     virtual bool visitNotI(LNotI *comp);
     virtual bool visitNotD(LNotD *comp);
     virtual bool visitMathD(LMathD *math);
+    virtual bool visitMathF(LMathF *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitGuardShape(LGuardShape *guard);
     virtual bool visitGuardObjectType(LGuardObjectType *guard);
     virtual bool visitGuardClass(LGuardClass *guard);
     virtual bool visitEffectiveAddress(LEffectiveAddress *ins);
     virtual bool visitUDivOrMod(LUDivOrMod *ins);
     virtual bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);
 
     bool visitNegI(LNegI *lir);
     bool visitNegD(LNegD *lir);
+    bool visitNegF(LNegF *lir);
 
     // Out of line visitors.
     bool visitOutOfLineBailout(OutOfLineBailout *ool);
     bool visitOutOfLineUndoALUOperation(OutOfLineUndoALUOperation *ool);
     bool visitMulNegativeZeroCheck(MulNegativeZeroCheck *ool);
     bool visitModOverflowCheck(ModOverflowCheck *ool);
     bool visitReturnZero(ReturnZero *ool);
     bool visitOutOfLineTableSwitch(OutOfLineTableSwitch *ool);
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -135,16 +135,17 @@ LIRGeneratorShared::defineReturn(LInstru
                                                LGeneralReg(JSReturnReg_Data)));
 
         if (getVirtualRegister() >= MAX_VIRTUAL_REGISTERS)
             return false;
 #elif defined(JS_PUNBOX64)
         lir->setDef(0, LDefinition(vreg, LDefinition::BOX, LGeneralReg(JSReturnReg)));
 #endif
         break;
+      case MIRType_Float32:
       case MIRType_Double:
         lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnFloatReg)));
         break;
       default:
         LDefinition::Type type = LDefinition::TypeFrom(mir->type());
         JS_ASSERT(type != LDefinition::DOUBLE);
         lir->setDef(0, LDefinition(vreg, type, LGeneralReg(ReturnReg)));
         break;
@@ -269,17 +270,17 @@ LIRGeneratorShared::useRegisterOrNonNega
     if (mir->isConstant() && mir->toConstant()->value().toInt32() >= 0)
         return LAllocation(mir->toConstant()->vp());
     return useRegisterAtStart(mir);
 }
 
 LAllocation
 LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition *mir)
 {
-    if (mir->isConstant() && mir->type() != MIRType_Double)
+    if (mir->isConstant() && mir->type() != MIRType_Double && mir->type() != MIRType_Float32)
         return LAllocation(mir->toConstant()->vp());
     return useRegister(mir);
 }
 
 #if defined(JS_CPU_ARM)
 LAllocation
 LIRGeneratorShared::useAnyOrConstant(MDefinition *mir)
 {
@@ -408,17 +409,17 @@ LIRGeneratorShared::add(T *ins, MInstruc
 // abstracted because MBox is a special value-returning instruction that
 // redefines its input payload if its input is not constant. Therefore, it is
 // illegal to request a box's payload by adding VREG_DATA_OFFSET to its raw id.
 static inline uint32_t
 VirtualRegisterOfPayload(MDefinition *mir)
 {
     if (mir->isBox()) {
         MDefinition *inner = mir->toBox()->getOperand(0);
-        if (!inner->isConstant() && inner->type() != MIRType_Double)
+        if (!inner->isConstant() && inner->type() != MIRType_Double && inner->type() != MIRType_Float32)
             return inner->virtualRegister();
     }
     if (mir->isTypeBarrier())
         return VirtualRegisterOfPayload(mir->getOperand(0));
     return mir->virtualRegister() + VREG_DATA_OFFSET;
 }
 
 // Note: always call ensureDefined before calling useType/usePayload,
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -176,14 +176,19 @@ class LIRGeneratorShared : public MInstr
     static bool allowTypedElementHoleCheck() {
         return false;
     }
 
     // Whether to generate typed array accesses on statically known objects.
     static bool allowStaticTypedArrayAccesses() {
         return false;
     }
+
+     // Whether we can emit Float32 specific optimizations.
+    static bool allowFloat32Optimizations() {
+       return false;
+    }
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_shared_h */
--- a/js/src/jit/shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/shared/Lowering-x86-shared.cpp
@@ -260,21 +260,30 @@ LIRGeneratorX86Shared::lowerUrshD(MUrsh 
 
 bool
 LIRGeneratorX86Shared::lowerConstantDouble(double d, MInstruction *mir)
 {
     return define(new LDouble(d), mir);
 }
 
 bool
+LIRGeneratorX86Shared::lowerConstantFloat32(float f, MInstruction *mir)
+{
+    return define(new LFloat32(f), mir);
+}
+
+bool
 LIRGeneratorX86Shared::visitConstant(MConstant *ins)
 {
     if (ins->type() == MIRType_Double)
         return lowerConstantDouble(ins->value().toDouble(), ins);
 
+    if (ins->type() == MIRType_Float32)
+        return lowerConstantFloat32(ins->value().toDouble(), ins);
+
     // Emit non-double constants at their uses.
     if (ins->canEmitAtUses())
         return emitAtUses(ins);
 
     return LIRGeneratorShared::visitConstant(ins);
 }
 
 bool
--- a/js/src/jit/shared/Lowering-x86-shared.h
+++ b/js/src/jit/shared/Lowering-x86-shared.h
@@ -41,15 +41,16 @@ class LIRGeneratorX86Shared : public LIR
     bool visitAsmJSUMod(MAsmJSUMod *ins);
     bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs);
     bool lowerDivI(MDiv *div);
     bool lowerModI(MMod *mod);
     bool lowerUDiv(MInstruction *div);
     bool lowerUMod(MInstruction *mod);
     bool lowerUrshD(MUrsh *mir);
     bool lowerConstantDouble(double d, MInstruction *ins);
+    bool lowerConstantFloat32(float d, MInstruction *ins);
     bool lowerTruncateDToInt32(MTruncateToInt32 *ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_Lowering_x86_shared_h */
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -57,16 +57,44 @@ class MacroAssemblerX86Shared : public A
             j(Parity, label);
             return;
         }
 
         JS_ASSERT(!(cond & DoubleConditionBitSpecial));
         j(ConditionFromDoubleCondition(cond), label);
     }
 
+    void compareFloat(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs) {
+        if (cond & DoubleConditionBitInvert)
+            ucomiss(rhs, lhs);
+        else
+            ucomiss(lhs, rhs);
+    }
+    void branchFloat(DoubleCondition cond, const FloatRegister &lhs,
+                      const FloatRegister &rhs, Label *label)
+    {
+        compareFloat(cond, lhs, rhs);
+
+        if (cond == DoubleEqual) {
+            Label unordered;
+            j(Parity, &unordered);
+            j(Equal, label);
+            bind(&unordered);
+            return;
+        }
+        if (cond == DoubleNotEqualOrUnordered) {
+            j(NotEqual, label);
+            j(Parity, label);
+            return;
+        }
+
+        JS_ASSERT(!(cond & DoubleConditionBitSpecial));
+        j(ConditionFromDoubleCondition(cond), label);
+    }
+
     void move32(const Imm32 &imm, const Register &dest) {
         if (imm.value == 0)
             xorl(dest, dest);
         else
             movl(imm, dest);
     }
     void move32(const Imm32 &imm, const Operand &dest) {
         movl(imm, dest);
@@ -206,16 +234,22 @@ class MacroAssemblerX86Shared : public A
     }
 
     void convertInt32ToDouble(const Register &src, const FloatRegister &dest) {
         cvtsi2sd(src, dest);
     }
     void convertInt32ToDouble(const Address &src, FloatRegister dest) {
         cvtsi2sd(Operand(src), dest);
     }
+    void convertInt32ToFloat32(const Register &src, const FloatRegister &dest) {
+        cvtsi2ss(src, dest);
+    }
+    void convertInt32ToFloat32(const Address &src, FloatRegister dest) {
+        cvtsi2ss(Operand(src), dest);
+    }
     Condition testDoubleTruthy(bool truthy, const FloatRegister &reg) {
         xorpd(ScratchFloatReg, ScratchFloatReg);
         ucomisd(ScratchFloatReg, reg);
         return truthy ? NonZero : Zero;
     }
     void load8ZeroExtend(const Address &src, const Register &dest) {
         movzbl(Operand(src), dest);
     }
@@ -288,28 +322,38 @@ class MacroAssemblerX86Shared : public A
     void negateDouble(FloatRegister reg) {
         // From MacroAssemblerX86Shared::maybeInlineDouble
         pcmpeqw(ScratchFloatReg, ScratchFloatReg);
         psllq(Imm32(63), ScratchFloatReg);
 
         // XOR the float in a float register with -0.0.
         xorpd(ScratchFloatReg, reg); // s ^ 0x80000000000000
     }
+    void negateFloat(FloatRegister reg) {
+        pcmpeqw(ScratchFloatReg, ScratchFloatReg);
+        psllq(Imm32(31), ScratchFloatReg);
+
+        // XOR the float in a float register with -0.0.
+        xorps(ScratchFloatReg, reg); // s ^ 0x80000000
+    }
     void addDouble(FloatRegister src, FloatRegister dest) {
         addsd(src, dest);
     }
     void subDouble(FloatRegister src, FloatRegister dest) {
         subsd(src, dest);
     }
     void mulDouble(FloatRegister src, FloatRegister dest) {
         mulsd(src, dest);
     }
     void divDouble(FloatRegister src, FloatRegister dest) {
         divsd(src, dest);
     }
+    void convertFloatToDouble(const FloatRegister &src, const FloatRegister &dest) {
+        cvtss2sd(src, dest);
+    }
     void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest) {
         cvtsd2ss(src, dest);
     }
     void loadFloatAsDouble(const Register &src, FloatRegister dest) {
         movd(src, dest);
         cvtss2sd(dest, dest);
     }
     void loadFloatAsDouble(const Address &src, FloatRegister dest) {
@@ -319,16 +363,28 @@ class MacroAssemblerX86Shared : public A
     void loadFloatAsDouble(const BaseIndex &src, FloatRegister dest) {
         movss(Operand(src), dest);
         cvtss2sd(dest, dest);
     }
     void loadFloatAsDouble(const Operand &src, FloatRegister dest) {
         movss(src, dest);
         cvtss2sd(dest, dest);
     }
+    void loadFloat(const Register &src, FloatRegister dest) {
+        movss(Operand(src), dest);
+    }
+    void loadFloat(const Address &src, FloatRegister dest) {
+        movss(Operand(src), dest);
+    }
+    void loadFloat(const BaseIndex &src, FloatRegister dest) {
+        movss(Operand(src), dest);
+    }
+    void loadFloat(const Operand &src, FloatRegister dest) {
+        movss(src, dest);
+    }
     void storeFloat(FloatRegister src, const Address &dest) {
         movss(src, Operand(dest));
     }
     void storeFloat(FloatRegister src, const BaseIndex &dest) {
         movss(src, Operand(dest));
     }
 
     // Checks whether a double is representable as a 32-bit integer. If so, the
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -99,38 +99,42 @@ LIRGeneratorX64::lowerUntypedPhiInput(MP
 }
 
 bool
 LIRGeneratorX64::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
 
-    if (ins->isFloatArray())
-        JS_ASSERT(ins->value()->type() == MIRType_Double);
-    else
+    if (ins->isFloatArray()) {
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
+    } else {
         JS_ASSERT(ins->value()->type() == MIRType_Int32);
+    }
 
     LUse elements = useRegister(ins->elements());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
     return add(new LStoreTypedArrayElement(elements, index, value), ins);
 }
 
 bool
 LIRGeneratorX64::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
     JS_ASSERT(ins->length()->type() == MIRType_Int32);
 
-    if (ins->isFloatArray())
-        JS_ASSERT(ins->value()->type() == MIRType_Double);
-    else
+    if (ins->isFloatArray()) {
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
+    } else {
         JS_ASSERT(ins->value()->type() == MIRType_Int32);
+    }
 
     LUse elements = useRegister(ins->elements());
     LAllocation length = useAnyOrConstant(ins->length());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value = useRegisterOrNonDoubleConstant(ins->value());
     return add(new LStoreTypedArrayElementHole(elements, length, index, value), ins);
 }
 
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -39,16 +39,20 @@ class LIRGeneratorX64 : public LIRGenera
     bool visitReturn(MReturn *ret);
     bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
     bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
     bool visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins);
     bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
     bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
     bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
+
+    static bool allowFloat32Optimizations() {
+        return true;
+    }
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_Lowering_x64_h */
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -957,20 +957,39 @@ class MacroAssemblerX64 : public MacroAs
     // These two functions use the low 32-bits of the full value register.
     void boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
         cvtsi2sd(operand.valueReg(), dest);
     }
     void int32ValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
         cvtsi2sd(operand.valueReg(), dest);
     }
 
+    void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2ss(operand.valueReg(), dest);
+    }
+    void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2ss(operand.valueReg(), dest);
+    }
+
     void loadConstantDouble(double d, const FloatRegister &dest);
+    void loadConstantFloat32(float f, const FloatRegister &dest) {
+        union FloatPun {
+            uint32_t u;
+            float f;
+        } pun;
+        pun.f = f;
+        mov(ImmWord(pun.u), ScratchReg);
+        movq(ScratchReg, dest);
+    }
     void loadStaticDouble(const double *dp, const FloatRegister &dest) {
         loadConstantDouble(*dp, dest);
     }
+    void loadStaticFloat32(const float *fp, const FloatRegister &dest) {
+        loadConstantFloat32(*fp, dest);
+    }
 
     void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail) {
         const uint64_t IndefiniteIntegerValue = 0x8000000000000000;
         JS_ASSERT(dest != ScratchReg);
         cvttsd2sq(src, dest);
         movq(ImmWord(IndefiniteIntegerValue), ScratchReg);
         cmpq(dest, ScratchReg);
         j(Assembler::Equal, fail);
--- a/js/src/jit/x86/Assembler-x86.cpp
+++ b/js/src/jit/x86/Assembler-x86.cpp
@@ -20,16 +20,17 @@ ABIArg
 ABIArgGenerator::next(MIRType type)
 {
     current_ = ABIArg(stackOffset_);
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
         stackOffset_ += sizeof(uint32_t);
         break;
+      case MIRType_Float32: // Float32 moves are actually double moves
       case MIRType_Double:
         stackOffset_ += sizeof(uint64_t);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
     return current_;
 }
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -246,16 +246,17 @@ class Assembler : public AssemblerX86Sha
             writeRelocation(src);
     }
 
   public:
     using AssemblerX86Shared::movl;
     using AssemblerX86Shared::j;
     using AssemblerX86Shared::jmp;
     using AssemblerX86Shared::movsd;
+    using AssemblerX86Shared::movss;
     using AssemblerX86Shared::retarget;
     using AssemblerX86Shared::cmpl;
     using AssemblerX86Shared::call;
     using AssemblerX86Shared::push;
     using AssemblerX86Shared::pop;
 
     static void TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader);
 
@@ -439,16 +440,21 @@ class Assembler : public AssemblerX86Sha
         label->reset();
     }
 
     void movsd(const double *dp, const FloatRegister &dest) {
         JS_ASSERT(HasSSE2());
         masm.movsd_mr((const void *)dp, dest.code());
     }
 
+    void movss(const float *dp, const FloatRegister &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movss_mr((const void *)dp, dest.code());
+    }
+
     // Move a 32-bit immediate into a register where the immediate can be
     // patched.
     CodeOffsetLabel movlWithPatch(Imm32 imm, Register dest) {
         masm.movl_i32r(imm.value, dest.code());
         return masm.currentOffset();
     }
 
     // Load from *addr where addr can be patched.
@@ -456,27 +462,37 @@ class Assembler : public AssemblerX86Sha
         masm.movl_mr(addr, dest.code());
         return masm.currentOffset();
     }
     CodeOffsetLabel movsdWithPatch(void *addr, FloatRegister dest) {
         JS_ASSERT(HasSSE2());
         masm.movsd_mr(addr, dest.code());
         return masm.currentOffset();
     }
+    CodeOffsetLabel movssWithPatch(void *addr, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movss_mr(addr, dest.code());
+        return masm.currentOffset();
+    }
 
     // Store to *addr where addr can be patched
     CodeOffsetLabel movlWithPatch(Register src, void *addr) {
         masm.movl_rm(src.code(), addr);
         return masm.currentOffset();
     }
     CodeOffsetLabel movsdWithPatch(FloatRegister dest, void *addr) {
         JS_ASSERT(HasSSE2());
         masm.movsd_rm(dest.code(), addr);
         return masm.currentOffset();
     }
+    CodeOffsetLabel movssWithPatch(FloatRegister dest, void *addr) {
+        JS_ASSERT(HasSSE2());
+        masm.movss_rm(dest.code(), addr);
+        return masm.currentOffset();
+    }
 
     // Load from *(base + disp32) where disp32 can be patched.
     CodeOffsetLabel movsblWithPatch(Address src, Register dest) {
         masm.movsbl_mr_disp32(src.offset, src.base.code(), dest.code());
         return masm.currentOffset();
     }
     CodeOffsetLabel movzblWithPatch(Address src, Register dest) {
         masm.movzbl_mr_disp32(src.offset, src.base.code(), dest.code());
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -459,20 +459,20 @@ CodeGeneratorX86::visitLoadTypedArrayEle
     masm.cmpl(ptr, Imm32(mir->length()));
     if (ool)
         masm.j(Assembler::AboveOrEqual, ool->entry());
     else if (!bailoutIf(Assembler::AboveOrEqual, ins->snapshot()))
         return false;
 
     Address srcAddr(ptr, (int32_t) mir->base());
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
+        JS_ASSERT(mir->type() == MIRType_Float32);
         FloatRegister dest = ToFloatRegister(out);
         masm.movssWithPatch(srcAddr, dest);
-        masm.cvtss2sd(dest, dest);
-        masm.canonicalizeDouble(dest);
+        masm.canonicalizeFloat(dest);
         if (ool)
             masm.bind(ool->rejoin());
         return true;
     }
     loadNonFloat32ViewTypeElement(vt, srcAddr, out);
     if (vt == ArrayBufferView::TYPE_FLOAT64)
         masm.canonicalizeDouble(ToFloatRegister(out));
     if (ool)
@@ -557,18 +557,18 @@ CodeGeneratorX86::storeNonFloat32ViewTyp
 }
 
 template<typename T>
 bool
 CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
                                        const T &dstAddr)
 {
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
+        uint32_t before = masm.size();
         masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
-        uint32_t before = masm.size();
         masm.movssWithPatch(ScratchFloatReg, dstAddr);
         uint32_t after = masm.size();
         return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
     }
     uint32_t before = masm.size();
     storeNonFloat32ViewTypeElement(vt, value, dstAddr);
     uint32_t after = masm.size();
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
@@ -584,18 +584,18 @@ CodeGeneratorX86::visitStoreTypedArrayEl
     const LAllocation *value = ins->value();
 
     masm.cmpl(ptr, Imm32(mir->length()));
     Label rejoin;
     masm.j(Assembler::AboveOrEqual, &rejoin);
 
     Address dstAddr(ptr, (int32_t) mir->base());
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
-        masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
-        masm.movssWithPatch(ScratchFloatReg, dstAddr);
+        JS_ASSERT(mir->value()->type() == MIRType_Float32);
+        masm.movssWithPatch(ToFloatRegister(value), dstAddr);
         masm.bind(&rejoin);
         return true;
     }
     storeNonFloat32ViewTypeElement(vt, value, dstAddr);
     masm.bind(&rejoin);
     return true;
 }
 
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -161,20 +161,22 @@ LIRGeneratorX86::lowerUntypedPhiInput(MP
 }
 
 bool
 LIRGeneratorX86::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
 
-    if (ins->isFloatArray())
-        JS_ASSERT(ins->value()->type() == MIRType_Double);
-    else
+    if (ins->isFloatArray()) {
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
+    } else {
         JS_ASSERT(ins->value()->type() == MIRType_Int32);
+    }
 
     LUse elements = useRegister(ins->elements());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value;
 
     // For byte arrays, the value has to be in a byte register on x86.
     if (ins->isByteArray())
         value = useFixed(ins->value(), eax);
@@ -185,20 +187,22 @@ LIRGeneratorX86::visitStoreTypedArrayEle
 
 bool
 LIRGeneratorX86::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins)
 {
     JS_ASSERT(ins->elements()->type() == MIRType_Elements);
     JS_ASSERT(ins->index()->type() == MIRType_Int32);
     JS_ASSERT(ins->length()->type() == MIRType_Int32);
 
-    if (ins->isFloatArray())
-        JS_ASSERT(ins->value()->type() == MIRType_Double);
-    else
+    if (ins->isFloatArray()) {
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
+        JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
+    } else {
         JS_ASSERT(ins->value()->type() == MIRType_Int32);
+    }
 
     LUse elements = useRegister(ins->elements());
     LAllocation length = useAnyOrConstant(ins->length());
     LAllocation index = useRegisterOrConstant(ins->index());
     LAllocation value;
 
     // For byte arrays, the value has to be in a byte register on x86.
     if (ins->isByteArray())
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -51,16 +51,20 @@ class LIRGeneratorX86 : public LIRGenera
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;
     }
+
+    static bool allowFloat32Optimizations() {
+        return true;
+    }
 };
 
 typedef LIRGeneratorX86 LIRGeneratorSpecific;
 
 } // namespace js
 } // namespace jit
 
 #endif /* jit_x86_Lowering_x86_h */
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -43,38 +43,79 @@ MacroAssemblerX86::loadConstantDouble(do
     Double &dbl = doubles_[doubleIndex];
     JS_ASSERT(!dbl.uses.bound());
 
     masm.movsd_mr(reinterpret_cast<const void *>(dbl.uses.prev()), dest.code());
     dbl.uses.setPrev(masm.size());
 }
 
 void
+MacroAssemblerX86::loadConstantFloat32(float f, const FloatRegister &dest)
+{
+    // Contrarily to loadConstantDouble, this one doesn't have any maybeInlineFloat,
+    // but that might be interesting to do it in the future.
+    if (!floatMap_.initialized()) {
+        enoughMemory_ &= floatMap_.init();
+        if (!enoughMemory_)
+            return;
+    }
+    size_t floatIndex;
+    FloatMap::AddPtr p = floatMap_.lookupForAdd(f);
+    if (p) {
+        floatIndex = p->value;
+    } else {
+        floatIndex = floats_.length();
+        enoughMemory_ &= floats_.append(Float(f));
+        enoughMemory_ &= floatMap_.add(p, f, floatIndex);
+        if (!enoughMemory_)
+            return;
+    }
+    Float &flt = floats_[floatIndex];
+    JS_ASSERT(!flt.uses.bound());
+
+    masm.movss_mr(reinterpret_cast<const void *>(flt.uses.prev()), dest.code());
+    flt.uses.setPrev(masm.size());
+}
+
+void
 MacroAssemblerX86::loadStaticDouble(const double *dp, const FloatRegister &dest) {
     if (maybeInlineDouble(*dp, dest))
         return;
 
     // x86 can just load from any old immediate address.
     movsd(dp, dest);
 }
 
 void
+MacroAssemblerX86::loadStaticFloat32(const float *fp, const FloatRegister &dest) {
+    // x86 can just load from any old immediate address.
+    movss(fp, dest);
+}
+
+void
 MacroAssemblerX86::finish()
 {
-    if (doubles_.empty())
+    if (doubles_.empty() && floats_.empty())
         return;
 
     masm.align(sizeof(double));
     for (size_t i = 0; i < doubles_.length(); i++) {
         CodeLabel cl(doubles_[i].uses);
         writeDoubleConstant(doubles_[i].value, cl.src());
         enoughMemory_ &= addCodeLabel(cl);
         if (!enoughMemory_)
             return;
     }
+    for (size_t i = 0; i < floats_.length(); i++) {
+        CodeLabel cl(floats_[i].uses);
+        writeFloatConstant(floats_[i].value, cl.src());
+        enoughMemory_ &= addCodeLabel(cl);
+        if (!enoughMemory_)
+            return;
+    }
 }
 
 void
 MacroAssemblerX86::setupABICall(uint32_t args)
 {
     JS_ASSERT(!inCall_);
     inCall_ = true;
 
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -28,19 +28,27 @@ class MacroAssemblerX86 : public MacroAs
     bool enoughMemory_;
 
     struct Double {
         double value;
         AbsoluteLabel uses;
         Double(double value) : value(value) {}
     };
     Vector<Double, 0, SystemAllocPolicy> doubles_;
+    struct Float {
+        float value;
+        AbsoluteLabel uses;
+        Float(float value) : value(value) {}
+    };
+    Vector<Float, 0, SystemAllocPolicy> floats_;
 
     typedef HashMap<double, size_t, DefaultHasher<double>, SystemAllocPolicy> DoubleMap;
     DoubleMap doubleMap_;
+    typedef HashMap<float, size_t, DefaultHasher<float>, SystemAllocPolicy> FloatMap;
+    FloatMap floatMap_;
 
   protected:
     MoveResolver moveResolver_;
 
   private:
     Operand payloadOf(const Address &address) {
         return Operand(address.base, address.offset);
     }
@@ -811,29 +819,43 @@ class MacroAssemblerX86 : public MacroAs
     }
     Register extractTag(const ValueOperand &value, Register scratch) {
         return value.typeReg();
     }
 
     void boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
         cvtsi2sd(operand.payloadReg(), dest);
     }
+    void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2ss(operand.payloadReg(), dest);
+    }
     void int32ValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
         cvtsi2sd(operand.payloadReg(), dest);
     }
+    void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
+        cvtsi2ss(operand.payloadReg(), dest);
+    }
 
     void loadConstantDouble(double d, const FloatRegister &dest);
+    void loadConstantFloat32(float f, const FloatRegister &dest);
     void loadStaticDouble(const double *dp, const FloatRegister &dest);
+    void loadStaticFloat32(const float *dp, const FloatRegister &dest);
 
     void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail) {
         const uint32_t IndefiniteIntegerValue = 0x80000000;
         cvttsd2si(src, dest);
         cmpl(dest, Imm32(IndefiniteIntegerValue));
         j(Assembler::Equal, fail);
     }
+    void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) {
+        const uint32_t IndefiniteIntegerValue = 0x80000000;
+        cvttss2si(src, dest);
+        cmpl(dest, Imm32(IndefiniteIntegerValue));
+        j(Assembler::Equal, fail);
+    }
 
     Condition testInt32Truthy(bool truthy, const ValueOperand &operand) {
         testl(operand.payloadReg(), operand.payloadReg());
         return truthy ? NonZero : Zero;
     }
     void branchTestBooleanTruthy(bool truthy, const ValueOperand &operand, Label *label) {
         testl(operand.payloadReg(), operand.payloadReg());
         j(truthy ? NonZero : Zero, label);
@@ -904,16 +926,30 @@ class MacroAssemblerX86 : public MacroAs
         cvtsi2sd(src, dest);
 
         // dest is now a double with the int range.
         // correct the double value by adding 0x80000000.
         static const double NegativeOne = 2147483648.0;
         addsd(Operand(&NegativeOne), dest);
     }
 
+    // Note: this function clobbers the source register.
+    void convertUInt32ToFloat32(const Register &src, const FloatRegister &dest) {
+        // src is [0, 2^32-1]
+        subl(Imm32(0x80000000), src);
+
+        // Do it the GCC way
+        cvtsi2ss(src, dest);
+
+        // dest is now a double with the int range.
+        // correct the double value by adding 0x80000000.
+        static const float NegativeOne = 2147483648.f;
+        addss(Operand(&NegativeOne), dest);
+    }
+
     void inc64(AbsoluteAddress dest) {
         addl(Imm32(1), Operand(dest));
         Label noOverflow;
         j(NonZero, &noOverflow);
         addl(Imm32(1), Operand(dest.offset(4)));
         bind(&noOverflow);
     }