Bug 865516 - Optimize access to the heap with a constant index. r=luke
authorDouglas Crosher <dtc-moz@scieneer.com>
Fri, 06 Sep 2013 07:44:06 +1000
changeset 145852 9f988f6ee6dff3f61bc17a42b050d742466d4241
parent 145851 9fa7a3f9e4d60d194e08d49cef512e79452f5600
child 145853 9c34952d655b3a95184f23c0e2dd61f947c0573d
push id33408
push userryanvm@gmail.com
push dateFri, 06 Sep 2013 13:37:57 +0000
treeherdermozilla-inbound@bb399c38e3b6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs865516
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 865516 - Optimize access to the heap with a constant index. r=luke
js/src/assembler/assembler/X86Assembler.h
js/src/jit-test/tests/asm.js/testHeapAccess.js
js/src/jit-test/tests/asm.js/testZOOB.js
js/src/jit/AsmJS.cpp
js/src/jit/AsmJSLink.cpp
js/src/jit/AsmJSModule.cpp
js/src/jit/AsmJSModule.h
js/src/jit/Ion.cpp
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MIRGenerator.h
js/src/jit/MIRGraph.cpp
js/src/jit/RangeAnalysis.cpp
js/src/jit/RangeAnalysis.h
js/src/jit/RegisterSets.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/Lowering-arm.cpp
js/src/jit/arm/Lowering-arm.h
js/src/jit/shared/Lowering-shared-inl.h
js/src/jit/shared/Lowering-shared.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x64/Lowering-x64.h
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/CodeGenerator-x86.h
js/src/jit/x86/Lowering-x86.cpp
js/src/jit/x86/Lowering-x86.h
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -1491,37 +1491,47 @@ public:
     void movw_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         spew("movw       %s, %s0x%x(%s)",
              nameIReg(2,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.prefix(PRE_OPERAND_SIZE);
         m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
     }
 
+    void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
+    {
+        spew("movw       %s, %d(%s,%s,%d)",
+             nameIReg(2, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
+    }
+
+#if !WTF_CPU_X86_64
+    void movw_rm(RegisterID src, const void* addr)
+    {
+        spew("movw       %s, %p",
+             nameIReg(2, src), addr);
+        m_formatter.prefix(PRE_OPERAND_SIZE);
+        m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
+    }
+#endif
+
     void movl_rm(RegisterID src, int offset, RegisterID base)
     {
         spew("movl       %s, %s0x%x(%s)",
              nameIReg(4,src), PRETTY_PRINT_OFFSET(offset), nameIReg(base));
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, offset);
     }
 
     void movl_rm_disp32(RegisterID src, int offset, RegisterID base)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp_disp32(OP_MOV_EvGv, src, base, offset);
     }
 
-    void movw_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
-    {
-        spew("movw       %s, %d(%s,%s,%d)",
-             nameIReg(2, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
-        m_formatter.prefix(PRE_OPERAND_SIZE);
-        m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
-    }
-
     void movl_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("movl       %s, %d(%s,%s,%d)",
              nameIReg(4, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.oneByteOp(OP_MOV_EvGv, src, base, index, scale, offset);
     }
 
     void movl_mEAX(const void* addr)
@@ -1559,16 +1569,28 @@ public:
 
     void movl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movl       %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_MOV_GvEv, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movl       %p, %s",
+             addr, nameIReg(4, dst));
+        if (dst == X86Registers::eax)
+            movl_mEAX(addr);
+        else
+            m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
+    }
+#endif
+
     void movl_i32r(int imm, RegisterID dst)
     {
         spew("movl       $0x%x, %s",
              imm, nameIReg(4, dst));
         m_formatter.oneByteOp(OP_MOV_EAXIv, dst);
         m_formatter.immediate32(imm);
     }
 
@@ -1775,26 +1797,16 @@ public:
         spew("movl       %s, %p",
              nameIReg(4, src), addr);
         if (src == X86Registers::eax)
             movl_EAXm(addr);
         else
             m_formatter.oneByteOp(OP_MOV_EvGv, src, addr);
     }
 
-    void movl_mr(const void* addr, RegisterID dst)
-    {
-        spew("movl       %p, %s",
-             addr, nameIReg(4, dst));
-        if (dst == X86Registers::eax)
-            movl_mEAX(addr);
-        else
-            m_formatter.oneByteOp(OP_MOV_GvEv, dst, addr);
-    }
-
     void movl_i32m(int imm, const void* addr)
     {
         FIXME_INSN_PRINTING;
         m_formatter.oneByteOp(OP_GROUP11_EvIz, GROUP11_MOV, addr);
         m_formatter.immediate32(imm);
     }
 #endif
 
@@ -1814,16 +1826,25 @@ public:
 
     void movb_rm(RegisterID src, int offset, RegisterID base, RegisterID index, int scale)
     {
         spew("movb       %s, %d(%s,%s,%d)",
              nameIReg(1, src), offset, nameIReg(base), nameIReg(index), 1<<scale);
         m_formatter.oneByteOp8(OP_MOV_EbGv, src, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movb_rm(RegisterID src, const void* addr)
+    {
+        spew("movb       %s, %p",
+             nameIReg(1, src), addr);
+        m_formatter.oneByteOp(OP_MOV_EbGv, src, addr);
+    }
+#endif
+
     void movzbl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movzbl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, offset);
     }
 
     void movzbl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1835,16 +1856,25 @@ public:
 
     void movzbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movzbl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movzbl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movzbl     %p, %s",
+             addr, nameIReg(dst));
+        m_formatter.twoByteOp(OP2_MOVZX_GvEb, dst, addr);
+    }
+#endif
+
     void movsbl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movsbl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, offset);
     }
 
     void movsbl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1856,16 +1886,25 @@ public:
 
     void movsbl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movsbl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movsbl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movsbl     %p, %s",
+             addr, nameIReg(4, dst));
+        m_formatter.twoByteOp(OP2_MOVSX_GvEb, dst, addr);
+    }
+#endif
+
     void movzwl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movzwl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, offset);
     }
 
     void movzwl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1877,16 +1916,25 @@ public:
 
     void movzwl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movzwl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movzwl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movzwl     %p, %s",
+             addr, nameIReg(4, dst));
+        m_formatter.twoByteOp(OP2_MOVZX_GvEw, dst, addr);
+    }
+#endif
+
     void movswl_mr(int offset, RegisterID base, RegisterID dst)
     {
         spew("movswl     %s0x%x(%s), %s",
              PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameIReg(4, dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, offset);
     }
 
     void movswl_mr_disp32(int offset, RegisterID base, RegisterID dst)
@@ -1898,16 +1946,25 @@ public:
 
     void movswl_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
     {
         spew("movswl     %d(%s,%s,%d), %s",
              offset, nameIReg(base), nameIReg(index), 1<<scale, nameIReg(dst));
         m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, base, index, scale, offset);
     }
 
+#if !WTF_CPU_X86_64
+    void movswl_mr(const void* addr, RegisterID dst)
+    {
+        spew("movswl     %p, %s",
+             addr, nameIReg(4, dst));
+        m_formatter.twoByteOp(OP2_MOVSX_GvEw, dst, addr);
+    }
+#endif
+
     void movzbl_rr(RegisterID src, RegisterID dst)
     {
         spew("movzbl     %s, %s",
              nameIReg(1,src), nameIReg(4,dst));
         m_formatter.twoByteOp8_movx(OP2_MOVZX_GvEb, dst, src);
     }
 
     void leal_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
@@ -2329,32 +2386,52 @@ 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);
     }
 
--- a/js/src/jit-test/tests/asm.js/testHeapAccess.js
+++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js
@@ -160,10 +160,288 @@ var code = asmCompile('glob', 'imp', 'b'
 asmLink(code, this, null, new ArrayBuffer(4096));
 
 asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i) { i=i|0; u32[12] = i } return f'), this, null, BUF_64KB)(11);
 assertEq(new Int32Array(BUF_64KB)[12], 11);
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[12]|0 } return f'), this, null, BUF_64KB)(), 11);
 new Float64Array(BUF_64KB)[0] = 3.5;
 assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +-f64[0] } return f'), this, null, BUF_64KB)(), -3.5);
 
+// Test constant loads.
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[1] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[0], 0);
+assertEq(new Uint8Array(buf)[1], 255);
+assertEq(new Uint8Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[4095] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[4094], 0);
+assertEq(new Uint8Array(buf)[4095], 255);
+assertEq(new Uint8Array(buf)[4096], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[4096] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[4095], 0);
+assertEq(new Uint8Array(buf)[4096], 255);
+assertEq(new Uint8Array(buf)[4097], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[8192] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[258048] = -1 } return f'), this, null, buf)();
+assertEq(new Uint8Array(buf)[258047], 0);
+assertEq(new Uint8Array(buf)[258048], 255);
+assertEq(new Uint8Array(buf)[258049], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[1] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[0], 0);
+assertEq(new Int8Array(buf)[1], -1);
+assertEq(new Int8Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[4095] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[4094], 0);
+assertEq(new Int8Array(buf)[4095], -1);
+assertEq(new Int8Array(buf)[4096], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[4096] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[4095], 0);
+assertEq(new Int8Array(buf)[4096], -1);
+assertEq(new Int8Array(buf)[4097], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[8192] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[258048] = -1 } return f'), this, null, buf)();
+assertEq(new Int8Array(buf)[258047], 0);
+assertEq(new Int8Array(buf)[258048], -1);
+assertEq(new Int8Array(buf)[258049], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[1] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[0], 0);
+assertEq(new Uint16Array(buf)[1], 65535);
+assertEq(new Uint16Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[2047] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[2046], 0);
+assertEq(new Uint16Array(buf)[2047], 65535);
+assertEq(new Uint16Array(buf)[2048], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[2048] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[2047], 0);
+assertEq(new Uint16Array(buf)[2048], 65535);
+assertEq(new Uint16Array(buf)[2049], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[4096] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[126976] = -1 } return f'), this, null, buf)();
+assertEq(new Uint16Array(buf)[126975], 0);
+assertEq(new Uint16Array(buf)[126976], 65535);
+assertEq(new Uint16Array(buf)[126977], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[1] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[0], 0);
+assertEq(new Int16Array(buf)[1], -1);
+assertEq(new Int16Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[2047] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[2046], 0);
+assertEq(new Int16Array(buf)[2047], -1);
+assertEq(new Int16Array(buf)[2048], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[2048] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[2047], 0);
+assertEq(new Int16Array(buf)[2048], -1);
+assertEq(new Int16Array(buf)[2049], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[4096] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[126976] = -1 } return f'), this, null, buf)();
+assertEq(new Int16Array(buf)[126975], 0);
+assertEq(new Int16Array(buf)[126976], -1);
+assertEq(new Int16Array(buf)[126977], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[0], 0);
+assertEq(new Uint32Array(buf)[1], 4294967295);
+assertEq(new Uint32Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1023] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[1022], 0);
+assertEq(new Uint32Array(buf)[1023], 4294967295);
+assertEq(new Uint32Array(buf)[1024], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[1024] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[1023], 0);
+assertEq(new Uint32Array(buf)[1024], 4294967295);
+assertEq(new Uint32Array(buf)[1025], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[2048] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[61440] = -1 } return f'), this, null, buf)();
+assertEq(new Uint32Array(buf)[61439], 0);
+assertEq(new Uint32Array(buf)[61440], 4294967295);
+assertEq(new Uint32Array(buf)[61441], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[0], 0);
+assertEq(new Int32Array(buf)[1], -1);
+assertEq(new Int32Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1023] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1022], 0);
+assertEq(new Int32Array(buf)[1023], -1);
+assertEq(new Int32Array(buf)[124], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[1024] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1023], 0);
+assertEq(new Int32Array(buf)[1024], -1);
+assertEq(new Int32Array(buf)[1025], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[2048] = -1 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[61440] = -1 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[61439], 0);
+assertEq(new Int32Array(buf)[61440], -1);
+assertEq(new Int32Array(buf)[61441], 0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1] = -1.0 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[0], 0);
+assertEq(new Float32Array(buf)[1], -1.0);
+assertEq(new Int32Array(buf)[2], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1023] = -1.0 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1022], 0);
+assertEq(new Float32Array(buf)[1023], -1.0);
+assertEq(new Int32Array(buf)[124], 0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[1024] = -1.0 } return f'), this, null, buf)();
+assertEq(new Int32Array(buf)[1023], 0);
+assertEq(new Float32Array(buf)[1024], -1.0);
+assertEq(new Int32Array(buf)[1025], 0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[2048] = -1.0 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[61440] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float32Array(buf)[61439], 0.0);
+assertEq(new Float32Array(buf)[61440], -1.0);
+assertEq(new Float32Array(buf)[61441], 0.0);
+
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[1] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[0], 0.0);
+assertEq(new Float64Array(buf)[1], -1.0);
+assertEq(new Float64Array(buf)[2], 0.0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[511] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[510], 0.0);
+assertEq(new Float64Array(buf)[511], -1.0);
+assertEq(new Float64Array(buf)[512], 0.0);
+var buf = new ArrayBuffer(8192);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[512] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[511], 0.0);
+assertEq(new Float64Array(buf)[512], -1.0);
+assertEq(new Float64Array(buf)[513], 0.0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[1024] = -1.0 } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[28672] = -1.0 } return f'), this, null, buf)();
+assertEq(new Float64Array(buf)[28671], 0.0);
+assertEq(new Float64Array(buf)[28672], -1.0);
+assertEq(new Float64Array(buf)[28673], 0.0);
+
+
+var buf = new ArrayBuffer(8192);
+new Uint8Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[1]|0; } return f'), this, null, buf)(),255);
+new Int8Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[1]|0; } return f'), this, null, buf)(),-1);
+var buf = new ArrayBuffer(262144);
+new Uint8Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[126976]|0; } return f'), this, null, buf)(),255);
+new Int8Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[126976]|0; } return f'), this, null, buf)(),-1);
+
+var buf = new ArrayBuffer(8192);
+new Uint16Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[1]|0; } return f'), this, null, buf)(),65535);
+new Int16Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[1]|0; } return f'), this, null, buf)(),-1);
+var buf = new ArrayBuffer(262144);
+new Uint16Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[126976]|0; } return f'), this, null, buf)(),65535);
+new Int16Array(buf)[126976] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[126976]|0; } return f'), this, null, buf)(),-1);
+
+var buf = new ArrayBuffer(8192);
+new Uint32Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[1]|0; } return f'), this, null, buf)(),-1);
+new Int32Array(buf)[1] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[1]|0; } return f'), this, null, buf)(),-1);
+var buf = new ArrayBuffer(262144);
+new Int32Array(buf)[61440] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[61440]|0; } return f'), this, null, buf)(),-1);
+
+var buf = new ArrayBuffer(8192);
+new Float32Array(buf)[1] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1]; } return f'), this, null, buf)(),-1.0);
+new Float32Array(buf)[1023] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1023]; } return f'), this, null, buf)(),-1.0);
+new Float32Array(buf)[1024] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[1024]; } return f'), this, null, buf)(),-1.0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[2048]; } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+new Float32Array(buf)[61440] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[61440]; } return f'), this, null, buf)(),-1.0);
+
+var buf = new ArrayBuffer(8192);
+new Float64Array(buf)[1] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[1]; } return f'), this, null, buf)(),-1.0);
+new Float64Array(buf)[511] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[511]; } return f'), this, null, buf)(),-1.0);
+new Float64Array(buf)[512] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[512]; } return f'), this, null, buf)(),-1.0);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[1024]; } return f'), this, null, buf);
+var buf = new ArrayBuffer(262144);
+new Float64Array(buf)[28672] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[28672]; } return f'), this, null, buf)(),-1.0);
+
+// Test bitwise-and optimizations.
+var buf = new ArrayBuffer(8192);
+new Uint8Array(buf)[8191] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[8191&8191]|0; } return f'), this, null, buf)(),255);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[(8191&8191)>>0]|0; } return f'), this, null, buf)(),255);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[8192&8191] = -1; u8[0] = 0; return u8[8192&8191]|0; } return f'), this, null, buf)(),0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u8[(8192&8191)>>0] = -1; u8[0] = 0; return u8[(8192&8191)>>0]|0; } return f'), this, null, buf)(),0);
+new Int8Array(buf)[8191] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[8191&8191]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i8[(8191&8191)>>0]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[8192&8191] = -1; i8[0] = 0; return i8[8192&8191]|0; } return f'), this, null, buf)(),0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i8[(8192&8191)>>0] = -1; i8[0] = 0; return i8[(8192&8191)>>0]|0; } return f'), this, null, buf)(),0);
+var buf = new ArrayBuffer(8192);
+new Uint16Array(buf)[4095] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[(8190&8191)>>1]|0; } return f'), this, null, buf)(),65535);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u16[(8191&8191)>>1]|0; } return f'), this, null, buf)(),65535);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u16[(8192&8191)>>1] = -1; u16[0] = 0; return u16[(8192&8191)>>1]|0; } return f'), this, null, buf)(),0);
+new Int16Array(buf)[4095] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[(8190&8191)>>1]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i16[(8191&8191)>>1]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i16[(8192&8191)>>1] = -1; i16[0] = 0; return i16[(8192&8191)>>1]|0; } return f'), this, null, buf)(),0);
+
+var buf = new ArrayBuffer(8192);
+new Uint32Array(buf)[2047] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(8188&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(8191&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { u32[(8192&8191)>>2] = -1; u32[0] = 0; return u32[(8192&8191)>>2]|0; } return f'), this, null, buf)(),0);
+new Int32Array(buf)[2047] = -1;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(8188&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return i32[(8191&8191)>>2]|0; } return f'), this, null, buf)(),-1);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { i32[(8192&8191)>>2] = -1; i32[0] = 0; return i32[(8192&8191)>>2]|0; } return f'), this, null, buf)(),0);
+
+var buf = new ArrayBuffer(8192);
+new Float32Array(buf)[2047] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(8188&8191)>>2]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f32[(8191&8191)>>2]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f32[(8192&8191)>>2] = -1.0; f32[0] = 0.0; return +f32[(8192&8191)>>2]; } return f'), this, null, buf)(),0.0);
+
+var buf = new ArrayBuffer(8192);
+new Float64Array(buf)[1023] = -1.0;
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8184&8191)>>3]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return +f64[(8191&8191)>>3]; } return f'), this, null, buf)(),-1.0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { f64[(8192&8191)>>3] = -1.0; f64[0] = 0.0; return +f64[(8192&8191)>>3]; } return f'), this, null, buf)(),0.0);
+
+
 // Bug 882012
 assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + "var id=foreign.id;var doubles=new stdlib.Float64Array(heap);function g(){doubles[0]=+id(2.0);return +doubles[0];}return g"), this, {id: function(x){return x;}}, BUF_64KB)(), 2.0);
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -1,21 +1,55 @@
 load(libdir + "asm.js");
 
 // constants
 var buf = new ArrayBuffer(4096);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x80000000]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x8fffffff]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0xffffffff]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-1]|0 } return f'), this, null, buf)(), 0);
-assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[-2]|0 } return f'), this, null, buf)(), 0);
+
+// An unshifted literal constant byte index in the range 0 to 2^31-1 inclusive should give a link failure.
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x7fffffff]|0 } return f'), this, null, buf);
+assertAsmLinkFail(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x1fffffff]|0 } return f'), this, null, buf);
+
+
+// An unshifted literal constant byte index outside the range 0 to 2^31-1 inclusive should cause an error compiling.
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x20000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x3fffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x40000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x7fffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x80000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x8fffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0xffffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x100000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x80000000]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x100000000]|0 } return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int16Array(b); function f() {return arr[-1]|0 } return f');
 assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[-2]|0 } return f');
 
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[10-12]|0 } return f');
+
+// An intish shifted literal constant index should not fail to compile or link.
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0x3fffffff>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x3fffffff>>2]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0xffffffff>>2]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[-1>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[-1>>2]|0 } return f'), this, null, buf)(), 0);
+// Unsigned (intish) folded constant index.
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff>>>0]|0 } return f'), this, null, buf)(), 0);
+assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {arr[0] = 1; return arr[(0xffffffff+1)>>>0]|0 } return f'), this, null, buf)(), 1);
+
+// A non-intish shifted literal constant index should cause an error compiling.
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b); function f() {return arr[0x100000000>>0]|0 } return f');
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int32Array(b); function f() {return arr[0x100000000>>2]|0 } return f');
+
+// Folded non-intish constant expressions should cause an error compiling.
+assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.Int8Array(b);  function f() {return arr[0xffffffff+1]|0 } return f');
+
+
+
 function testInt(ctor, shift, scale, disp) {
     var ab = new ArrayBuffer(4096);
     var arr = new ctor(ab);
     for (var i = 0; i < arr.length; i++)
         arr[i] = i;
     var f = asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + 'var arr=new glob.' + ctor.name + '(b); function f(i) {i=i|0; return arr[((i<<' + scale + ')+' + disp + ')>>' + shift + ']|0 } return f'), this, null, ab);
     for (var i of [0,1,2,3,4,1023,1024,1025,4095,4096,4097])
         assertEq(f(i), arr[((i<<scale)+disp)>>shift]|0);
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -904,16 +904,21 @@ TypedArrayStoreType(ArrayBufferView::Vie
       case ArrayBufferView::TYPE_FLOAT32:
       case ArrayBufferView::TYPE_FLOAT64:
         return ArrayStore_Doublish;
       default:;
     }
     MOZ_ASSUME_UNREACHABLE("Unexpected array type");
 }
 
+enum NeedsBoundsCheck {
+    NO_BOUNDS_CHECK,
+    NEEDS_BOUNDS_CHECK
+};
+
 /*****************************************************************************/
 
 namespace {
 
 typedef Vector<PropertyName*,1> LabelVector;
 typedef Vector<MBasicBlock*,8> BlockVector;
 
 // ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
@@ -1468,16 +1473,25 @@ class MOZ_STACK_CLASS ModuleCompiler
         if (!module_->addExit(ffiIndex, exitIndex))
             return false;
         return exits_.add(p, OldMove(exitDescriptor), *exitIndex);
     }
     bool addGlobalAccess(AsmJSGlobalAccess access) {
         return globalAccesses_.append(access);
     }
 
+    // Note a constraint on the minimum size of the heap.  The heap size is
+    // constrained when linking to be at least the maximum of all such constraints.
+    void requireHeapLengthToBeAtLeast(uint32_t len) {
+        module_->requireHeapLengthToBeAtLeast(len);
+    }
+    uint32_t minHeapLength() const {
+        return module_->minHeapLength();
+    }
+
     bool collectAccesses(MIRGenerator &gen) {
         if (!module_->addHeapAccesses(gen.heapAccesses()))
             return false;
         if (!globalAccesses_.appendAll(gen.globalAccesses()))
             return false;
         return true;
     }
 
@@ -1946,30 +1960,35 @@ class FunctionCompiler
 
     void assign(const Local &local, MDefinition *def)
     {
         if (!curBlock_)
             return;
         curBlock_->setSlot(info().localSlot(local.slot), def);
     }
 
-    MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
+    MDefinition *loadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, NeedsBoundsCheck chk)
     {
         if (!curBlock_)
             return NULL;
         MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(vt, ptr);
         curBlock_->add(load);
+        if (chk == NO_BOUNDS_CHECK)
+            load->setSkipBoundsCheck(true);
         return load;
     }
 
-    void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v)
+    void storeHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
     {
         if (!curBlock_)
             return;
-        curBlock_->add(MAsmJSStoreHeap::New(vt, ptr, v));
+        MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(vt, ptr, v);
+        curBlock_->add(store);
+        if (chk == NO_BOUNDS_CHECK)
+            store->setSkipBoundsCheck(true);
     }
 
     MDefinition *loadGlobalVar(const ModuleCompiler::Global &global)
     {
         if (!curBlock_)
             return NULL;
         MIRType type = global.varType().toMIRType();
         unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varIndex());
@@ -3083,96 +3102,160 @@ CheckVarRef(FunctionCompiler &f, ParseNo
         }
         return true;
     }
 
     return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
 }
 
 static bool
+FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
+                     NeedsBoundsCheck *needsBoundsCheck)
+{
+    ParseNode *indexNode = BinaryLeft(*indexExpr);
+    ParseNode *maskNode = BinaryRight(*indexExpr);
+
+    uint32_t mask2;
+    if (IsLiteralUint32(maskNode, &mask2)) {
+        // Flag the access to skip the bounds check if the mask ensures that an 'out of
+        // bounds' access can not occur based on the current heap length constraint.
+        if (mozilla::CountLeadingZeroes32(f.m().minHeapLength() - 1) <= mozilla::CountLeadingZeroes32(mask2))
+            *needsBoundsCheck = NO_BOUNDS_CHECK;
+        *mask &= mask2;
+        *indexExpr = indexNode;
+        return true;
+    }
+
+    return false;
+}
+
+static bool
 CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType *viewType,
-                 MDefinition **def)
+                 MDefinition **def, NeedsBoundsCheck *needsBoundsCheck)
 {
     ParseNode *viewName = ElemBase(elem);
     ParseNode *indexExpr = ElemIndex(elem);
+    *needsBoundsCheck = NEEDS_BOUNDS_CHECK;
 
     if (!viewName->isKind(PNK_NAME))
         return f.fail(viewName, "base of array access must be a typed array view name");
 
     const ModuleCompiler::Global *global = f.lookupGlobal(viewName->name());
     if (!global || global->which() != ModuleCompiler::Global::ArrayView)
         return f.fail(viewName, "base of array access must be a typed array view name");
 
     *viewType = global->viewType();
 
     uint32_t pointer;
     if (IsLiteralUint32(indexExpr, &pointer)) {
+        if (pointer > (uint32_t(INT32_MAX) >> TypedArrayShift(*viewType)))
+            return f.fail(indexExpr, "constant index out of range");
         pointer <<= TypedArrayShift(*viewType);
+        // It is adequate to note pointer+1 rather than rounding up to the next
+        // access-size boundary because access is always aligned and the constraint
+        // will be rounded up to a larger alignment later.
+        f.m().requireHeapLengthToBeAtLeast(uint32_t(pointer) + 1);
+        *needsBoundsCheck = NO_BOUNDS_CHECK;
         *def = f.constant(Int32Value(pointer));
         return true;
     }
 
+    // Mask off the low bits to account for the clearing effect of a right shift
+    // followed by the left shift implicit in the array access. E.g., H32[i>>2]
+    // loses the low two bits.
+    int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
+
     MDefinition *pointerDef;
     if (indexExpr->isKind(PNK_RSH)) {
         ParseNode *shiftNode = BinaryRight(indexExpr);
         ParseNode *pointerNode = BinaryLeft(indexExpr);
 
         uint32_t shift;
         if (!IsLiteralUint32(shiftNode, &shift))
             return f.failf(shiftNode, "shift amount must be constant");
 
         unsigned requiredShift = TypedArrayShift(*viewType);
         if (shift != requiredShift)
             return f.failf(shiftNode, "shift amount must be %u", requiredShift);
 
+        if (pointerNode->isKind(PNK_BITAND))
+            FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
+
+        // Fold a 'literal constant right shifted' now, and skip the bounds check if
+        // currently possible. This handles the optimization of many of these uses without
+        // the need for range analysis, and saves the generation of a MBitAnd op.
+        if (IsLiteralUint32(pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) {
+            // Cases: b[c>>n], and b[(c&m)>>n]
+            pointer &= mask;
+            if (pointer < f.m().minHeapLength())
+                *needsBoundsCheck = NO_BOUNDS_CHECK;
+            *def = f.constant(Int32Value(pointer));
+            return true;
+        }
+
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
             return false;
 
         if (!pointerType.isIntish())
             return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
     } else {
         if (TypedArrayShift(*viewType) != 0)
             return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
 
+        JS_ASSERT(mask == -1);
+        bool folded = false;
+
+        if (indexExpr->isKind(PNK_BITAND))
+            folded = FoldMaskedArrayIndex(f, &indexExpr, &mask, needsBoundsCheck);
+
         Type pointerType;
         if (!CheckExpr(f, indexExpr, &pointerDef, &pointerType))
             return false;
 
-        if (!pointerType.isInt())
-            return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
-    }
-
-    // Mask off the low bits to account for clearing effect of a right shift
-    // followed by the left shift implicit in the array access. E.g., H32[i>>2]
-    // loses the low two bits.
-    int32_t mask = ~((uint32_t(1) << TypedArrayShift(*viewType)) - 1);
-    *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
+        if (folded) {
+            if (!pointerType.isIntish())
+                return f.failf(indexExpr, "%s is not a subtype of intish", pointerType.toChars());
+        } else {
+            if (!pointerType.isInt())
+                return f.failf(indexExpr, "%s is not a subtype of int", pointerType.toChars());
+        }
+    }
+
+    // Don't generate the mask op if there is no need for it which could happen for
+    // a shift of zero.
+    if (mask == -1)
+        *def = pointerDef;
+    else
+        *def = f.bitwise<MBitAnd>(pointerDef, f.constant(Int32Value(mask)));
+
     return true;
 }
 
 static bool
 CheckArrayLoad(FunctionCompiler &f, ParseNode *elem, MDefinition **def, Type *type)
 {
     ArrayBufferView::ViewType viewType;
     MDefinition *pointerDef;
-    if (!CheckArrayAccess(f, elem, &viewType, &pointerDef))
+    NeedsBoundsCheck needsBoundsCheck;
+    if (!CheckArrayAccess(f, elem, &viewType, &pointerDef, &needsBoundsCheck))
         return false;
 
-    *def = f.loadHeap(viewType, pointerDef);
+    *def = f.loadHeap(viewType, pointerDef, needsBoundsCheck);
     *type = TypedArrayLoadType(viewType);
     return true;
 }
 
 static bool
 CheckStoreArray(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
 {
     ArrayBufferView::ViewType viewType;
     MDefinition *pointerDef;
-    if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef))
+    NeedsBoundsCheck needsBoundsCheck;
+    if (!CheckArrayAccess(f, lhs, &viewType, &pointerDef, &needsBoundsCheck))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
     switch (TypedArrayStoreType(viewType)) {
@@ -3181,17 +3264,17 @@ CheckStoreArray(FunctionCompiler &f, Par
             return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
         break;
       case ArrayStore_Doublish:
         if (!rhsType.isDoublish())
             return f.failf(lhs, "%s is not a subtype of doublish", rhsType.toChars());
         break;
     }
 
-    f.storeHeap(viewType, pointerDef, rhsDef);
+    f.storeHeap(viewType, pointerDef, rhsDef, needsBoundsCheck);
 
     *def = rhsDef;
     *type = rhsType;
     return true;
 }
 
 static bool
 CheckAssignName(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition **def, Type *type)
@@ -4708,17 +4791,24 @@ CheckFunction(ModuleCompiler &m, LifoAll
     if (func->defined())
         return m.failName(fn, "function '%s' already defined", FunctionName(fn));
 
     func->define(fn->pn_pos.begin);
     func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
 
     m.parser().release(mark);
 
+    // Copy the cumulative minimum heap size constraint to the MIR for use in analysis.  The length
+    // is also constrained to be a power of two, so firstly round up - a larger 'heap required
+    // length' can help range analysis to prove that bounds checks are not needed.
+    size_t len = mozilla::RoundUpPow2((size_t) m.minHeapLength());
+    m.requireHeapLengthToBeAtLeast(len);
+
     *mir = f.extractMIR();
+    (*mir)->noteMinAsmJSHeapLength(len);
     *funcOut = func;
     return true;
 }
 
 static bool
 GenerateCode(ModuleCompiler &m, ModuleCompiler::Func &func, MIRGenerator &mir, LIRGraph &lir)
 {
     int64_t before = PRMJ_Now();
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -213,16 +213,22 @@ DynamicallyLinkModule(JSContext *cx, Cal
         if (!IsTypedArrayBuffer(bufferVal))
             return LinkFail(cx, "bad ArrayBuffer argument");
 
         heap = &bufferVal.toObject().as<ArrayBufferObject>();
 
         if (!IsPowerOfTwo(heap->byteLength()) || heap->byteLength() < AsmJSAllocationGranularity)
             return LinkFail(cx, "ArrayBuffer byteLength must be a power of two greater than or equal to 4096");
 
+        // This check is sufficient without considering the size of the loaded datum because heap
+        // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
+        JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
+        if (heap->byteLength() < module.minHeapLength())
+            return LinkFail(cx, "ArrayBuffer byteLength less than the largest source code heap length constraint.");
+
         if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
 
         module.patchHeapAccesses(heap, cx);
     }
 
     AutoObjectVector ffis(cx);
     if (!ffis.resize(module.numFFIs()))
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -21,21 +21,25 @@
 
 using namespace js;
 
 void
 AsmJSModule::patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx)
 {
     JS_ASSERT(IsPowerOfTwo(heap->byteLength()));
 #if defined(JS_CPU_X86)
-    void *heapOffset = (void*)heap->dataPointer();
+    uint8_t *heapOffset = heap->dataPointer();
     void *heapLength = (void*)heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
-        JSC::X86Assembler::setPointer(heapAccesses_[i].patchLengthAt(code_), heapLength);
-        JSC::X86Assembler::setPointer(heapAccesses_[i].patchOffsetAt(code_), heapOffset);
+        const jit::AsmJSHeapAccess &access = heapAccesses_[i];
+        if (access.hasLengthCheck())
+            JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
+        void *addr = access.patchOffsetAt(code_);
+        uint32_t disp = reinterpret_cast<uint32_t>(JSC::X86Assembler::getPointer(addr));
+        JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
     }
 #elif defined(JS_CPU_ARM)
     uint32_t bits = mozilla::CeilingLog2(heap->byteLength());
     for (unsigned i = 0; i < heapAccesses_.length(); i++)
         jit::Assembler::updateBoundsCheck(bits, (jit::Instruction*)(heapAccesses_[i].offset() + code_));
     // We already know the exact extent of areas that need to be patched, just make sure we
     // flush all of them at once.
     jit::AutoFlushCache::updateTop(uintptr_t(code_), pod.codeBytes_);
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -313,16 +313,17 @@ class AsmJSModule
     PropertyName *                        globalArgumentName_;
     PropertyName *                        importArgumentName_;
     PropertyName *                        bufferArgumentName_;
 
     GlobalVector                          globals_;
     ExitVector                            exits_;
     ExportedFunctionVector                exports_;
     HeapAccessVector                      heapAccesses_;
+    uint32_t                              minHeapLength_;
 #if defined(MOZ_VTUNE) or defined(JS_ION_PERF)
     ProfiledFunctionVector                profiledFunctions_;
 #endif
 #if defined(JS_ION_PERF)
     ProfiledBlocksFunctionVector          perfProfiledBlocksFunctions_;
 #endif
 
     struct Pod {
@@ -344,16 +345,17 @@ class AsmJSModule
     AsmJSModuleSourceDesc                 sourceDesc_;
     FunctionCountsVector                  functionCounts_;
 
   public:
     explicit AsmJSModule()
       : globalArgumentName_(NULL),
         importArgumentName_(NULL),
         bufferArgumentName_(NULL),
+        minHeapLength_(AsmJSAllocationGranularity),
         code_(NULL),
         operationCallbackExit_(NULL),
         linked_(false)
     {
         mozilla::PodZero(&pod);
     }
 
     ~AsmJSModule();
@@ -625,16 +627,24 @@ class AsmJSModule
     jit::AsmJSHeapAccess &heapAccess(unsigned i) {
         return heapAccesses_[i];
     }
     const jit::AsmJSHeapAccess &heapAccess(unsigned i) const {
         return heapAccesses_[i];
     }
     void patchHeapAccesses(ArrayBufferObject *heap, JSContext *cx);
 
+    void requireHeapLengthToBeAtLeast(uint32_t len) {
+        if (len > minHeapLength_)
+            minHeapLength_ = len;
+    }
+    uint32_t minHeapLength() const {
+        return minHeapLength_;
+    }
+
     uint8_t *allocateCodeAndGlobalSegment(ExclusiveContext *cx, size_t bytesNeeded);
 
     uint8_t *functionCode() const {
         JS_ASSERT(code_);
         JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
         return code_;
     }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1301,17 +1301,17 @@ OptimizeMIR(MIRGenerator *mir)
             AssertExtendedGraphCoherency(graph);
 
             if (mir->shouldCancel("LICM"))
                 return false;
         }
     }
 
     if (js_IonOptions.rangeAnalysis) {
-        RangeAnalysis r(graph);
+        RangeAnalysis r(mir, graph);
         if (!r.addBetaNobes())
             return false;
         IonSpewPass("Beta");
         AssertExtendedGraphCoherency(graph);
 
         if (mir->shouldCancel("RA Beta"))
             return false;
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2818,23 +2818,16 @@ LIRGenerator::visitHaveSameClass(MHaveSa
 
     JS_ASSERT(lhs->type() == MIRType_Object);
     JS_ASSERT(rhs->type() == MIRType_Object);
 
     return define(new LHaveSameClass(useRegister(lhs), useRegister(rhs), temp()), ins);
 }
 
 bool
-LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
-{
-    LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(useRegisterAtStart(ins->ptr()));
-    return define(lir, ins);
-}
-
-bool
 LIRGenerator::visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins)
 {
     return define(new LAsmJSLoadGlobalVar, ins);
 }
 
 bool
 LIRGenerator::visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins)
 {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -232,17 +232,16 @@ class LIRGenerator : public LIRGenerator
     bool visitThrow(MThrow *ins);
     bool visitIn(MIn *ins);
     bool visitInArray(MInArray *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitCallInstanceOf(MCallInstanceOf *ins);
     bool visitFunctionBoundary(MFunctionBoundary *ins);
     bool visitIsCallable(MIsCallable *ins);
     bool visitHaveSameClass(MHaveSameClass *ins);
-    bool visitAsmJSLoadHeap(MAsmJSLoadHeap *ins);
     bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins);
     bool visitAsmJSParameter(MAsmJSParameter *ins);
     bool visitAsmJSReturn(MAsmJSReturn *ins);
     bool visitAsmJSVoidReturn(MAsmJSVoidReturn *ins);
     bool visitAsmJSPassStackArg(MAsmJSPassStackArg *ins);
     bool visitAsmJSCall(MAsmJSCall *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8172,56 +8172,65 @@ class MAsmJSUMod : public MBinaryInstruc
 
   public:
     INSTRUCTION_HEADER(AsmJSUMod);
     static MAsmJSUMod *New(MDefinition *left, MDefinition *right) {
         return new MAsmJSUMod(left, right);
     }
 };
 
-class MAsmJSLoadHeap : public MUnaryInstruction
+class MAsmJSHeapAccess
+{
+    ArrayBufferView::ViewType viewType_;
+    bool skipBoundsCheck_;
+
+  public:
+    MAsmJSHeapAccess(ArrayBufferView::ViewType vt, bool s)
+      : viewType_(vt), skipBoundsCheck_(s)
+    {}
+
+    ArrayBufferView::ViewType viewType() const { return viewType_; }
+    bool skipBoundsCheck() const { return skipBoundsCheck_; }
+    void setSkipBoundsCheck(bool v) { skipBoundsCheck_ = v; }
+};
+
+class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSLoadHeap(ArrayBufferView::ViewType vt, MDefinition *ptr)
-      : MUnaryInstruction(ptr), viewType_(vt)
+      : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
     {
         if (vt == ArrayBufferView::TYPE_FLOAT32 || vt == ArrayBufferView::TYPE_FLOAT64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
     }
 
-    ArrayBufferView::ViewType viewType_;
-
   public:
     INSTRUCTION_HEADER(AsmJSLoadHeap);
 
     static MAsmJSLoadHeap *New(ArrayBufferView::ViewType vt, MDefinition *ptr) {
         return new MAsmJSLoadHeap(vt, ptr);
     }
 
-    ArrayBufferView::ViewType viewType() const { return viewType_; }
     MDefinition *ptr() const { return getOperand(0); }
 };
 
-class MAsmJSStoreHeap : public MBinaryInstruction
+class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSStoreHeap(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v)
-      : MBinaryInstruction(ptr, v), viewType_(vt)
+      : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt, false)
     {}
 
-    ArrayBufferView::ViewType viewType_;
-
   public:
     INSTRUCTION_HEADER(AsmJSStoreHeap);
 
     static MAsmJSStoreHeap *New(ArrayBufferView::ViewType vt, MDefinition *ptr, MDefinition *v) {
         return new MAsmJSStoreHeap(vt, ptr, v);
     }
 
-    ArrayBufferView::ViewType viewType() const { return viewType_; }
     MDefinition *ptr() const { return getOperand(0); }
     MDefinition *value() const { return getOperand(1); }
 };
 
 class MAsmJSLoadGlobalVar : public MNullaryInstruction
 {
     MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset)
       : globalDataOffset_(globalDataOffset)
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -117,16 +117,22 @@ class MIRGenerator
         return performsAsmJSCall_;
     }
     bool noteHeapAccess(AsmJSHeapAccess heapAccess) {
         return asmJSHeapAccesses_.append(heapAccess);
     }
     const Vector<AsmJSHeapAccess, 0, IonAllocPolicy> &heapAccesses() const {
         return asmJSHeapAccesses_;
     }
+    void noteMinAsmJSHeapLength(uint32_t len) {
+        minAsmJSHeapLength_ = len;
+    }
+    uint32_t minAsmJSHeapLength() const {
+        return minAsmJSHeapLength_;
+    }
     bool noteGlobalAccess(unsigned offset, unsigned globalDataOffset) {
         return asmJSGlobalAccesses_.append(AsmJSGlobalAccess(offset, globalDataOffset));
     }
     const Vector<AsmJSGlobalAccess, 0, IonAllocPolicy> &globalAccesses() const {
         return asmJSGlobalAccesses_;
     }
 
   public:
@@ -140,16 +146,17 @@ class MIRGenerator
     MIRGraph *graph_;
     bool error_;
     size_t cancelBuild_;
 
     uint32_t maxAsmJSStackArgBytes_;
     bool performsAsmJSCall_;
     AsmJSHeapAccessVector asmJSHeapAccesses_;
     AsmJSGlobalAccessVector asmJSGlobalAccesses_;
+    uint32_t minAsmJSHeapLength_;
 
 #if defined(JS_ION_PERF)
     AsmJSPerfSpewer asmJSPerfSpewer_;
 
   public:
     AsmJSPerfSpewer &perfSpewer() { return asmJSPerfSpewer_; }
 #endif
 };
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -7,32 +7,34 @@
 #include "jit/MIRGraph.h"
 
 #include "jsanalyze.h"
 
 #include "jit/Ion.h"
 #include "jit/IonBuilder.h"
 #include "jit/IonSpewer.h"
 #include "jit/MIR.h"
+#include "jit/AsmJS.h"
 
 #include "jsinferinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 MIRGenerator::MIRGenerator(JSCompartment *compartment,
                            TempAllocator *temp, MIRGraph *graph, CompileInfo *info)
   : compartment(compartment),
     info_(info),
     temp_(temp),
     graph_(graph),
     error_(false),
     cancelBuild_(0),
     maxAsmJSStackArgBytes_(0),
-    performsAsmJSCall_(false)
+    performsAsmJSCall_(false),
+    minAsmJSHeapLength_(AsmJSAllocationGranularity)
 { }
 
 bool
 MIRGenerator::abortFmt(const char *message, va_list ap)
 {
     IonSpewVA(IonSpew_Abort, message, ap);
     error_ = true;
     return false;
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1614,16 +1614,36 @@ RangeAnalysis::analyze()
 
             def->computeRange();
             IonSpew(IonSpew_Range, "computing range on %d", def->id());
             SpewRange(def);
         }
 
         if (block->isLoopHeader())
             analyzeLoop(block);
+
+        if (mir->compilingAsmJS()) {
+            for (MInstructionIterator i = block->begin(); i != block->end(); i++) {
+                if (i->isAsmJSLoadHeap()) {
+                    MAsmJSLoadHeap *ins = i->toAsmJSLoadHeap();
+                    Range *range = ins->ptr()->range();
+                    if (range && !range->isLowerInfinite() && range->lower() >= 0 &&
+                        !range->isUpperInfinite() &&
+                        (uint32_t) range->upper() < mir->minAsmJSHeapLength())
+                        ins->setSkipBoundsCheck(true);
+                } else if (i->isAsmJSStoreHeap()) {
+                    MAsmJSStoreHeap *ins = i->toAsmJSStoreHeap();
+                    Range *range = ins->ptr()->range();
+                    if (range && !range->isLowerInfinite() && range->lower() >= 0 &&
+                        !range->isUpperInfinite() &&
+                        (uint32_t) range->upper() < mir->minAsmJSHeapLength())
+                        ins->setSkipBoundsCheck(true);
+                }
+            }
+        }
     }
 
     return true;
 }
 
 bool
 RangeAnalysis::addRangeAssertions()
 {
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -69,21 +69,22 @@ struct SymbolicBound : public TempObject
 class RangeAnalysis
 {
   protected:
     bool blockDominates(MBasicBlock *b, MBasicBlock *b2);
     void replaceDominatedUsesWith(MDefinition *orig, MDefinition *dom,
                                   MBasicBlock *block);
 
   protected:
+    MIRGenerator *mir;
     MIRGraph &graph_;
 
   public:
-    MOZ_CONSTEXPR RangeAnalysis(MIRGraph &graph) :
-        graph_(graph) {}
+    MOZ_CONSTEXPR RangeAnalysis(MIRGenerator *mir, MIRGraph &graph) :
+        mir(mir), graph_(graph) {}
     bool addBetaNobes();
     bool analyze();
     bool addRangeAssertions();
     bool removeBetaNobes();
     bool truncate();
 
   private:
     void analyzeLoop(MBasicBlock *header);
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -799,44 +799,47 @@ class AsmJSHeapAccess
     uint8_t isFloat32Load_;
     jit::AnyRegister::Code loadedReg_ : 8;
 #endif
 
     JS_STATIC_ASSERT(jit::AnyRegister::Total < UINT8_MAX);
 
   public:
 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
+    // If 'cmp' equals 'offset' or if it is not supplied then the
+    // cmpDelta_ is zero indicating that there is no length to patch.
     AsmJSHeapAccess(uint32_t offset, uint32_t after, ArrayBufferView::ViewType vt,
                     AnyRegister loadedReg, uint32_t cmp = UINT32_MAX)
       : offset_(offset),
 # if defined(JS_CPU_X86)
-        cmpDelta_(offset - cmp),
+        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
 # endif
         opLength_(after - offset),
         isFloat32Load_(vt == ArrayBufferView::TYPE_FLOAT32),
         loadedReg_(loadedReg.code())
     {}
     AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = UINT32_MAX)
       : offset_(offset),
 # if defined(JS_CPU_X86)
-        cmpDelta_(offset - cmp),
+        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
 # endif
         opLength_(after - offset),
         isFloat32Load_(false),
         loadedReg_(UINT8_MAX)
     {}
 #elif defined(JS_CPU_ARM)
     explicit AsmJSHeapAccess(uint32_t offset)
       : offset_(offset)
     {}
 #endif
 
     uint32_t offset() const { return offset_; }
     void setOffset(uint32_t offset) { offset_ = offset; }
 #if defined(JS_CPU_X86)
+    bool hasLengthCheck() const { return cmpDelta_ > 0; }
     void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
     void *patchOffsetAt(uint8_t *code) const { return code + (offset_ + opLength_); }
 #endif
 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
     unsigned opLength() const { return opLength_; }
     bool isLoad() const { return loadedReg_ != UINT8_MAX; }
     bool isFloat32Load() const { return isFloat32Load_; }
     jit::AnyRegister loadedReg() const { return jit::AnyRegister::FromCode(loadedReg_); }
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1774,42 +1774,78 @@ CodeGeneratorARM::visitStoreTypedArrayEl
 bool
 CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     const MAsmJSLoadHeap *mir = ins->mir();
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->viewType()) {
-      case ArrayBufferView::TYPE_INT8:    isSigned = true; size = 8; break;
-      case ArrayBufferView::TYPE_UINT8:   isSigned = false; size = 8; break;
-      case ArrayBufferView::TYPE_INT16:   isSigned = true; size = 16; break;
+      case ArrayBufferView::TYPE_INT8:    isSigned = true;  size =  8; break;
+      case ArrayBufferView::TYPE_UINT8:   isSigned = false; size =  8; break;
+      case ArrayBufferView::TYPE_INT16:   isSigned = true;  size = 16; break;
       case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  isSigned = true;  size = 32; break;
       case ArrayBufferView::TYPE_FLOAT64: isFloat = true;   size = 64; break;
-      case ArrayBufferView::TYPE_FLOAT32:
-        isFloat = true;
-        size = 32;
-        break;
+      case ArrayBufferView::TYPE_FLOAT32: isFloat = true;   size = 32; break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
-    Register index = ToRegister(ins->ptr());
-    BufferOffset bo = masm.ma_BoundsCheck(index);
+
+    const LAllocation *ptr = ins->ptr();
+
+    if (ptr->isConstant()) {
+        JS_ASSERT(mir->skipBoundsCheck());
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        JS_ASSERT(ptrImm >= 0);
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->output()));
+            if (size == 32) {
+                masm.ma_vldr(Operand(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
+                masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
+            } else {
+                masm.ma_vldr(Operand(HeapReg, ptrImm), vd, Assembler::Always);
+            }
+        }  else {
+            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
+                                  ToRegister(ins->output()), Offset, Assembler::Always);
+        }
+        return true;
+    }
+
+    Register ptrReg = ToRegister(ptr);
+
+    if (mir->skipBoundsCheck()) {
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->output()));
+            if (size == 32) {
+                masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
+                masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Always);
+            } else {
+                masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Always);
+            }
+        } else {
+            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
+                                  ToRegister(ins->output()), Offset, Assembler::Always);
+        }
+        return true;
+    }
+
+    BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
     if (isFloat) {
         VFPRegister vd(ToFloatRegister(ins->output()));
         if (size == 32) {
-            masm.ma_vldr(vd.singleOverlay(), HeapReg, index, 0, Assembler::Zero);
+            masm.ma_vldr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Zero);
             masm.as_vcvt(vd, vd.singleOverlay(), false, Assembler::Zero);
         } else {
-            masm.ma_vldr(vd, HeapReg, index, 0, Assembler::Zero);
+            masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Zero);
         }
         masm.ma_vmov(NANReg, ToFloatRegister(ins->output()), Assembler::NonZero);
-    }  else {
-        masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, index,
+    } else {
+        masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg,
                               ToRegister(ins->output()), Offset, Assembler::Zero);
         masm.ma_mov(Imm32(0), ToRegister(ins->output()), NoSetCond, Assembler::NonZero);
     }
     return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
 }
 
 bool
 CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
@@ -1820,35 +1856,66 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
     bool isFloat = false;
     switch (mir->viewType()) {
       case ArrayBufferView::TYPE_INT8:
       case ArrayBufferView::TYPE_UINT8:   isSigned = false; size = 8; break;
       case ArrayBufferView::TYPE_INT16:
       case ArrayBufferView::TYPE_UINT16:  isSigned = false; size = 16; break;
       case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  isSigned = true;  size = 32; break;
-      case ArrayBufferView::TYPE_FLOAT64: isFloat = true;   size = 64; break;
-      case ArrayBufferView::TYPE_FLOAT32:
-        isFloat = true;
-        size = 32;
-        break;
+      case ArrayBufferView::TYPE_FLOAT64: isFloat  = true;  size = 64; break;
+      case ArrayBufferView::TYPE_FLOAT32: isFloat = true;   size = 32; break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
-    Register index = ToRegister(ins->ptr());
+    const LAllocation *ptr = ins->ptr();
+    if (ptr->isConstant()) {
+        JS_ASSERT(mir->skipBoundsCheck());
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        JS_ASSERT(ptrImm >= 0);
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->value()));
+            if (size == 32) {
+                masm.as_vcvt(VFPRegister(ScratchFloatReg).singleOverlay(), vd, false, Assembler::Always);
+                masm.ma_vstr(VFPRegister(ScratchFloatReg).singleOverlay(), Operand(HeapReg, ptrImm), Assembler::Always);
+            } else {
+                masm.ma_vstr(vd, Operand(HeapReg, ptrImm), Assembler::Always);
+            }
+        } else {
+            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
+                                  ToRegister(ins->value()), Offset, Assembler::Always);
+        }
+        return true;
+    }
 
-    BufferOffset bo = masm.ma_BoundsCheck(index);
+    Register ptrReg = ToRegister(ptr);
+
+    if (mir->skipBoundsCheck()) {
+        Register ptrReg = ToRegister(ptr);
+        if (isFloat) {
+            VFPRegister vd(ToFloatRegister(ins->value()));
+            if (size == 32)
+                masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Always);
+            else
+                masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
+        } else {
+            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
+                                  ToRegister(ins->value()), Offset, Assembler::Always);
+        }
+        return true;
+    }
+
+    BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
     if (isFloat) {
         VFPRegister vd(ToFloatRegister(ins->value()));
-        if (size == 32) {
-            masm.storeFloat(vd, HeapReg, index, Assembler::Zero);
-        } else {
-            masm.ma_vstr(vd, HeapReg, index, 0, Assembler::Zero);
-        }
-    }  else {
-        masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, index,
+        if (size == 32)
+            masm.storeFloat(vd, HeapReg, ptrReg, Assembler::Zero);
+        else
+            masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Zero);
+    } else {
+        masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                               ToRegister(ins->value()), Offset, Assembler::Zero);
     }
     return gen->noteHeapAccess(AsmJSHeapAccess(bo.getOffset()));
 }
 
 bool
 CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg *ins)
 {
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -497,35 +497,48 @@ bool
 LIRGeneratorARM::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_Int32);
     LUInt32ToDouble *lir = new LUInt32ToDouble(useRegisterAtStart(ins->input()));
     return define(lir, ins);
 }
 
 bool
+LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
+{
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+    LAllocation ptrAlloc;
+
+    // For the ARM it is best to keep the 'ptr' in a register if a bounds check is needed.
+    if (ptr->isConstant() && ins->skipBoundsCheck()) {
+        int32_t ptrValue = ptr->toConstant()->value().toInt32();
+        // A bounds check is only skipped for a positive index.
+        JS_ASSERT(ptrValue >= 0);
+        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+    } else
+        ptrAlloc = useRegisterAtStart(ptr);
+
+    return define(new LAsmJSLoadHeap(ptrAlloc), ins);
+}
+
+bool
 LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
-    LAsmJSStoreHeap *lir;
-    switch (ins->viewType()) {
-      case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
-      case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
-      case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterAtStart(ins->value()));
-        break;
-      case ArrayBufferView::TYPE_FLOAT32:
-      case ArrayBufferView::TYPE_FLOAT64:
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterAtStart(ins->value()));
-        break;
-      default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
-    }
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+    LAllocation ptrAlloc;
 
-    return add(lir, ins);
+    if (ptr->isConstant() && ins->skipBoundsCheck()) {
+        JS_ASSERT(ptr->toConstant()->value().toInt32() >= 0);
+        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+    } else
+        ptrAlloc = useRegisterAtStart(ptr);
+
+    return add(new LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins);
 }
 
 bool
 LIRGeneratorARM::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
 {
     return define(new LAsmJSLoadFuncPtr(useRegister(ins->index()), temp()), ins);
 }
 
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -73,16 +73,17 @@ class LIRGeneratorARM : public LIRGenera
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool lowerPhi(MPhi *phi);
     bool visitGuardShape(MGuardShape *ins);
     bool visitGuardObjectType(MGuardObjectType *ins);
     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);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -259,16 +259,24 @@ LAllocation
 LIRGeneratorShared::useRegisterOrConstantAtStart(MDefinition *mir)
 {
     if (mir->isConstant())
         return LAllocation(mir->toConstant()->vp());
     return useRegisterAtStart(mir);
 }
 
 LAllocation
+LIRGeneratorShared::useRegisterOrNonNegativeConstantAtStart(MDefinition *mir)
+{
+    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)
         return LAllocation(mir->toConstant()->vp());
     return useRegister(mir);
 }
 
 #if defined(JS_CPU_ARM)
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -81,16 +81,17 @@ class LIRGeneratorShared : public MInstr
     // "Storable" is architecture dependend, and will include registers and constants on X86
     // and only registers on ARM.
     // this is a generic "things we can expect to write into memory in 1 instruction"
     inline LAllocation useStorable(MDefinition *mir);
     inline LAllocation useStorableAtStart(MDefinition *mir);
     inline LAllocation useKeepaliveOrConstant(MDefinition *mir);
     inline LAllocation useRegisterOrConstant(MDefinition *mir);
     inline LAllocation useRegisterOrConstantAtStart(MDefinition *mir);
+    inline LAllocation useRegisterOrNonNegativeConstantAtStart(MDefinition *mir);
     inline LAllocation useRegisterOrNonDoubleConstant(MDefinition *mir);
 
 #ifdef JS_NUNBOX32
     inline LUse useType(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayload(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayloadAtStart(MDefinition *mir, LUse::Policy policy);
     inline LUse usePayloadInRegisterAtStart(MDefinition *mir);
 
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -389,84 +389,109 @@ CodeGeneratorX64::visitStoreTypedArrayEl
     MOZ_ASSUME_UNREACHABLE("NYI");
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     MAsmJSLoadHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
+    const LAllocation *ptr = ins->ptr();
 
-    Operand srcAddr(HeapReg, ToRegister(ins->ptr()), TimesOne);
+    // No need to note the access if it will never fault.
+    bool skipNote = mir->skipBoundsCheck();
+    Operand srcAddr(HeapReg);
+
+    if (ptr->isConstant()) {
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        // Note only a positive index is accepted here because a negative offset would
+        // not wrap back into the protected area reserved for the heap.
+        JS_ASSERT(ptrImm >= 0);
+        srcAddr = Operand(HeapReg, ptrImm);
+    } else {
+        srcAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
+    }
 
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
         FloatRegister dest = ToFloatRegister(ins->output());
         uint32_t before = masm.size();
         masm.movss(srcAddr, dest);
         uint32_t after = masm.size();
         masm.cvtss2sd(dest, dest);
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
+        return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
     }
 
     uint32_t before = masm.size();
     switch (vt) {
       case ArrayBufferView::TYPE_INT8:    masm.movsbl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_UINT8:   masm.movzbl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_INT16:   masm.movswl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_UINT16:  masm.movzwl(srcAddr, ToRegister(ins->output())); break;
-      case ArrayBufferView::TYPE_INT32:   masm.movl(srcAddr, ToRegister(ins->output())); break;
+      case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  masm.movl(srcAddr, ToRegister(ins->output())); break;
       case ArrayBufferView::TYPE_FLOAT64: masm.movsd(srcAddr, ToFloatRegister(ins->output())); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
     uint32_t after = masm.size();
-    return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
+    return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
 }
 
 bool
 CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
     MAsmJSStoreHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
+    const LAllocation *ptr = ins->ptr();
+    // No need to note the access if it will never fault.
+    bool skipNote = mir->skipBoundsCheck();
+    Operand dstAddr(HeapReg);
 
-    Operand dstAddr(HeapReg, ToRegister(ins->ptr()), TimesOne);
+    if (ptr->isConstant()) {
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        // Note only a positive index is accepted here because a negative offset would
+        // not wrap back into the protected area reserved for the heap.
+        JS_ASSERT(ptrImm >= 0);
+        dstAddr = Operand(HeapReg, ptrImm);
+    } else {
+        dstAddr = Operand(HeapReg, ToRegister(ins->ptr()), TimesOne);
+    }
 
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
         masm.convertDoubleToFloat(ToFloatRegister(ins->value()), ScratchFloatReg);
         uint32_t before = masm.size();
         masm.movss(ScratchFloatReg, dstAddr);
         uint32_t after = masm.size();
-        return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
+        return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
     }
 
     uint32_t before = masm.size();
     if (ins->value()->isConstant()) {
         switch (vt) {
-          case ArrayBufferView::TYPE_INT8:    masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
+          case ArrayBufferView::TYPE_INT8:
           case ArrayBufferView::TYPE_UINT8:   masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
-          case ArrayBufferView::TYPE_INT16:   masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
+          case ArrayBufferView::TYPE_INT16:
           case ArrayBufferView::TYPE_UINT16:  masm.movw(Imm32(ToInt32(ins->value())), dstAddr); break;
-          case ArrayBufferView::TYPE_INT32:   masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
+          case ArrayBufferView::TYPE_INT32:
           case ArrayBufferView::TYPE_UINT32:  masm.movl(Imm32(ToInt32(ins->value())), dstAddr); break;
           default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
         }
     } else {
         switch (vt) {
-          case ArrayBufferView::TYPE_INT8:    masm.movb(ToRegister(ins->value()), dstAddr); break;
+          case ArrayBufferView::TYPE_INT8:
           case ArrayBufferView::TYPE_UINT8:   masm.movb(ToRegister(ins->value()), dstAddr); break;
-          case ArrayBufferView::TYPE_INT16:   masm.movw(ToRegister(ins->value()), dstAddr); break;
+          case ArrayBufferView::TYPE_INT16:
           case ArrayBufferView::TYPE_UINT16:  masm.movw(ToRegister(ins->value()), dstAddr); break;
-          case ArrayBufferView::TYPE_INT32:   masm.movl(ToRegister(ins->value()), dstAddr); break;
+          case ArrayBufferView::TYPE_INT32:
           case ArrayBufferView::TYPE_UINT32:  masm.movl(ToRegister(ins->value()), dstAddr); break;
           case ArrayBufferView::TYPE_FLOAT64: masm.movsd(ToFloatRegister(ins->value()), dstAddr); break;
           default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
         }
     }
     uint32_t after = masm.size();
-    return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
+    return skipNote || gen->noteHeapAccess(AsmJSHeapAccess(before, after));
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     MAsmJSLoadGlobalVar *mir = ins->mir();
 
     CodeOffsetLabel label;
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -138,30 +138,51 @@ bool
 LIRGeneratorX64::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_Int32);
     LUInt32ToDouble *lir = new LUInt32ToDouble(useRegisterAtStart(ins->input()));
     return define(lir, ins);
 }
 
 bool
+LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
+{
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+
+    // The X64 does not inline an explicit bounds check so has no need to keep the
+    // index in a register, however only a positive index is accepted because a
+    // negative offset encoded as an offset in the addressing mode would not wrap
+    // back into the protected area reserved for the heap.
+    if (ptr->isConstant() && ptr->toConstant()->value().toInt32() >= 0) {
+        LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(LAllocation(ptr->toConstant()->vp()));
+        return define(lir, ins);
+    }
+    return define(new LAsmJSLoadHeap(useRegisterAtStart(ptr)), ins);
+}
+
+bool
 LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
+    MDefinition *ptr = ins->ptr();
+    JS_ASSERT(ptr->type() == MIRType_Int32);
     LAsmJSStoreHeap *lir;
+
+    // Note only a positive constant index is accepted because a negative offset
+    // encoded as an offset in the addressing mode would not wrap back into the
+    // protected area reserved for the heap.
+    LAllocation ptrAlloc = useRegisterOrNonNegativeConstantAtStart(ptr);
     switch (ins->viewType()) {
       case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
       case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
       case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterOrConstantAtStart(ins->value()));
+        lir = new LAsmJSStoreHeap(ptrAlloc, useRegisterOrConstantAtStart(ins->value()));
         break;
-      case ArrayBufferView::TYPE_FLOAT32:
-      case ArrayBufferView::TYPE_FLOAT64:
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterAtStart(ins->value()));
+      case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64:
+        lir = new LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
         break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
 
     return add(lir, ins);
 }
 
 bool
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -35,16 +35,17 @@ class LIRGeneratorX64 : public LIRGenera
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     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);
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace jit
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -529,16 +529,73 @@ class Assembler : public AssemblerX86Sha
         return masm.currentOffset();
     }
 
     // Load from *(addr + index*scale) where addr can be patched.
     CodeOffsetLabel movlWithPatch(void *addr, Register index, Scale scale, Register dest) {
         masm.movl_mr(addr, index.code(), scale, dest.code());
         return masm.currentOffset();
     }
+
+    // Load from *address where address can be patched.
+    CodeOffsetLabel movsblWithPatch(const AbsoluteAddress &src, Register dest) {
+        masm.movsbl_mr(src.addr, dest.code());
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movzblWithPatch(const AbsoluteAddress &src, Register dest) {
+        masm.movzbl_mr(src.addr, dest.code());
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movswlWithPatch(const AbsoluteAddress &src, Register dest) {
+        masm.movswl_mr(src.addr, dest.code());
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movzwlWithPatch(const AbsoluteAddress &src, Register dest) {
+        masm.movzwl_mr(src.addr, dest.code());
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movlWithPatch(const AbsoluteAddress &src, Register dest) {
+        masm.movl_mr(src.addr, dest.code());
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movssWithPatch(const AbsoluteAddress &src, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movss_mr(src.addr, dest.code());
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movsdWithPatch(const AbsoluteAddress &src, FloatRegister dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movsd_mr(src.addr, dest.code());
+        return masm.currentOffset();
+    }
+
+    // Store to *address where address can be patched.
+    CodeOffsetLabel movbWithPatch(Register src, const AbsoluteAddress &dest) {
+        masm.movb_rm(src.code(), dest.addr);
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movwWithPatch(Register src, const AbsoluteAddress &dest) {
+        masm.movw_rm(src.code(), dest.addr);
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movlWithPatch(Register src, const AbsoluteAddress &dest) {
+        masm.movl_rm(src.code(), dest.addr);
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movssWithPatch(FloatRegister src, const AbsoluteAddress &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movss_rm(src.code(), dest.addr);
+        return masm.currentOffset();
+    }
+    CodeOffsetLabel movsdWithPatch(FloatRegister src, const AbsoluteAddress &dest) {
+        JS_ASSERT(HasSSE2());
+        masm.movsd_rm(src.code(), dest.addr);
+        return masm.currentOffset();
+    }
+
 };
 
 // Get a register in which we plan to put a quantity that will be used as an
 // integer argument.  This differs from GetIntArgReg in that if we have no more
 // actual argument registers to use we will fall back on using whatever
 // CallTempReg* don't overlap the argument registers, and only fail once those
 // run out too.
 static inline bool
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -398,33 +398,53 @@ class jit::OutOfLineLoadTypedArrayOutOfB
 {
     AnyRegister dest_;
   public:
     OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest) : dest_(dest) {}
     const AnyRegister &dest() const { return dest_; }
     bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); }
 };
 
+template<typename T>
 void
-CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const Address &srcAddr,
-                                      const LDefinition *out)
+CodeGeneratorX86::loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
+                                                const LDefinition *out)
 {
     switch (vt) {
       case ArrayBufferView::TYPE_INT8:    masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_UINT8_CLAMPED:
       case ArrayBufferView::TYPE_UINT8:   masm.movzblWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_INT16:   masm.movswlWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_UINT16:  masm.movzwlWithPatch(srcAddr, ToRegister(out)); break;
-      case ArrayBufferView::TYPE_INT32:   masm.movlWithPatch(srcAddr, ToRegister(out)); break;
+      case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(srcAddr, ToRegister(out)); break;
       case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
 }
 
+template<typename T>
+bool
+CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
+                                      const LDefinition *out)
+{
+    if (vt == ArrayBufferView::TYPE_FLOAT32) {
+        FloatRegister dest = ToFloatRegister(out);
+        uint32_t before = masm.size();
+        masm.movssWithPatch(srcAddr, dest);
+        uint32_t after = masm.size();
+        masm.cvtss2sd(dest, dest);
+        return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest)));
+    }
+    uint32_t before = masm.size();
+    loadNonFloat32ViewTypeElement(vt, srcAddr, out);
+    uint32_t after = masm.size();
+    return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out)));
+}
+
 bool
 CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins)
 {
     const MLoadTypedArrayElementStatic *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
 
     Register ptr = ToRegister(ins->ptr());
     const LDefinition *out = ins->output();
@@ -447,55 +467,64 @@ CodeGeneratorX86::visitLoadTypedArrayEle
         FloatRegister dest = ToFloatRegister(out);
         masm.movssWithPatch(srcAddr, dest);
         masm.cvtss2sd(dest, dest);
         masm.canonicalizeDouble(dest);
         if (ool)
             masm.bind(ool->rejoin());
         return true;
     }
-    loadViewTypeElement(vt, srcAddr, out);
+    loadNonFloat32ViewTypeElement(vt, srcAddr, out);
     if (vt == ArrayBufferView::TYPE_FLOAT64)
         masm.canonicalizeDouble(ToFloatRegister(out));
     if (ool)
         masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
-    // This is identical to LoadTypedArrayElementStatic, except that the
-    // array's base and length are not known ahead of time and can be patched
-    // later on, and the instruction is always infallible.
     const MAsmJSLoadHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
+    const LAllocation *ptr = ins->ptr();
+    const LDefinition *out = ins->output();
 
-    Register ptr = ToRegister(ins->ptr());
-    const LDefinition *out = ins->output();
+    if (ptr->isConstant()) {
+        JS_ASSERT(mir->skipBoundsCheck());
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        JS_ASSERT(ptrImm >= 0);
+        AbsoluteAddress srcAddr((void *) ptrImm);
+        return loadViewTypeElement(vt, srcAddr, out);
+    }
+
+    Register ptrReg = ToRegister(ptr);
+    Address srcAddr(ptrReg, 0);
+
+    if (mir->skipBoundsCheck())
+        return loadViewTypeElement(vt, srcAddr, out);
 
     OutOfLineLoadTypedArrayOutOfBounds *ool = new OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out));
     if (!addOutOfLineCode(ool))
         return false;
 
-    CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0));
+    CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
     masm.j(Assembler::AboveOrEqual, ool->entry());
 
-    Address srcAddr(ptr, 0);
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
         FloatRegister dest = ToFloatRegister(out);
         uint32_t before = masm.size();
         masm.movssWithPatch(srcAddr, dest);
         uint32_t after = masm.size();
         masm.cvtss2sd(dest, dest);
         masm.bind(ool->rejoin());
         return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, AnyRegister(dest), cmp.offset()));
     }
     uint32_t before = masm.size();
-    loadViewTypeElement(vt, srcAddr, out);
+    loadNonFloat32ViewTypeElement(vt, srcAddr, out);
     uint32_t after = masm.size();
     masm.bind(ool->rejoin());
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset()));
 }
 
 bool
 CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
 {
@@ -504,33 +533,52 @@ CodeGeneratorX86::visitOutOfLineLoadType
     } else {
         Register destReg = ool->dest().gpr();
         masm.xorl(destReg, destReg);
     }
     masm.jmp(ool->rejoin());
     return true;
 }
 
+template<typename T>
 void
-CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
-                                       const Address &dstAddr)
+CodeGeneratorX86::storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                                                 const T &dstAddr)
 {
     switch (vt) {
-      case ArrayBufferView::TYPE_INT8:    masm.movbWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_INT8:
       case ArrayBufferView::TYPE_UINT8_CLAMPED:
       case ArrayBufferView::TYPE_UINT8:   masm.movbWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_INT16:   masm.movwWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_INT16:
       case ArrayBufferView::TYPE_UINT16:  masm.movwWithPatch(ToRegister(value), dstAddr); break;
-      case ArrayBufferView::TYPE_INT32:   masm.movlWithPatch(ToRegister(value), dstAddr); break;
+      case ArrayBufferView::TYPE_INT32:
       case ArrayBufferView::TYPE_UINT32:  masm.movlWithPatch(ToRegister(value), dstAddr); break;
       case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
 }
 
+template<typename T>
+bool
+CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                                       const T &dstAddr)
+{
+    if (vt == ArrayBufferView::TYPE_FLOAT32) {
+        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));
+}
+
 bool
 CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins)
 {
     MStoreTypedArrayElementStatic *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
 
     Register ptr = ToRegister(ins->ptr());
     const LAllocation *value = ins->value();
@@ -541,48 +589,57 @@ CodeGeneratorX86::visitStoreTypedArrayEl
 
     Address dstAddr(ptr, (int32_t) mir->base());
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
         masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
         masm.movssWithPatch(ScratchFloatReg, dstAddr);
         masm.bind(&rejoin);
         return true;
     }
-    storeViewTypeElement(vt, value, dstAddr);
+    storeNonFloat32ViewTypeElement(vt, value, dstAddr);
     masm.bind(&rejoin);
     return true;
 }
 
 bool
 CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
-    // This is identical to StoreTypedArrayElementStatic, except that the
-    // array's base and length are not known ahead of time and can be patched
-    // later on.
     MAsmJSStoreHeap *mir = ins->mir();
     ArrayBufferView::ViewType vt = mir->viewType();
+    const LAllocation *value = ins->value();
+    const LAllocation *ptr = ins->ptr();
 
-    Register ptr = ToRegister(ins->ptr());
-    const LAllocation *value = ins->value();
+    if (ptr->isConstant()) {
+        JS_ASSERT(mir->skipBoundsCheck());
+        int32_t ptrImm = ptr->toConstant()->toInt32();
+        JS_ASSERT(ptrImm >= 0);
+        AbsoluteAddress dstAddr((void *) ptrImm);
+        return storeViewTypeElement(vt, value, dstAddr);
+    }
 
-    CodeOffsetLabel cmp = masm.cmplWithPatch(ptr, Imm32(0));
+    Register ptrReg = ToRegister(ptr);
+    Address dstAddr(ptrReg, 0);
+
+    if (mir->skipBoundsCheck())
+        return storeViewTypeElement(vt, value, dstAddr);
+
+    CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0));
     Label rejoin;
     masm.j(Assembler::AboveOrEqual, &rejoin);
 
-    Address dstAddr(ptr, 0);
     if (vt == ArrayBufferView::TYPE_FLOAT32) {
         masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
         uint32_t before = masm.size();
         masm.movssWithPatch(ScratchFloatReg, dstAddr);
         uint32_t after = masm.size();
         masm.bind(&rejoin);
         return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));
     }
     uint32_t before = masm.size();
-    storeViewTypeElement(vt, value, dstAddr);
+    storeNonFloat32ViewTypeElement(vt, value, dstAddr);
     uint32_t after = masm.size();
     masm.bind(&rejoin);
     return gen->noteHeapAccess(AsmJSHeapAccess(before, after, cmp.offset()));
 }
 
 bool
 CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -23,20 +23,28 @@ class CodeGeneratorX86 : public CodeGene
         return this;
     }
 
   protected:
     ValueOperand ToValue(LInstruction *ins, size_t pos);
     ValueOperand ToOutValue(LInstruction *ins);
     ValueOperand ToTempValue(LInstruction *ins, size_t pos);
 
-    void loadViewTypeElement(ArrayBufferView::ViewType vt, const Address &srcAddr,
+    template<typename T>
+    bool loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
                              const LDefinition *out);
-    void storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
-                              const Address &dstAddr);
+    template<typename T>
+    void loadNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr,
+                                       const LDefinition *out);
+    template<typename T>
+    bool storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                              const T &dstAddr);
+    template<typename T>
+    void storeNonFloat32ViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value,
+                                        const T &dstAddr);
     void storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType,
                            const Register &elements, const LAllocation *index);
 
   public:
     CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm);
 
   public:
     bool visitBox(LBox *box);
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -212,36 +212,77 @@ bool
 LIRGeneratorX86::visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble *ins)
 {
     JS_ASSERT(ins->input()->type() == MIRType_Int32);
     LUInt32ToDouble *lir = new LUInt32ToDouble(useRegisterAtStart(ins->input()), temp());
     return define(lir, ins);
 }
 
 bool
+LIRGeneratorX86::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
+{
+    MDefinition *ptr = ins->ptr();
+    LAllocation ptrAlloc;
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+
+    // For the x86 it is best to keep the 'ptr' in a register if a bounds check is needed.
+    if (ptr->isConstant() && ins->skipBoundsCheck()) {
+        int32_t ptrValue = ptr->toConstant()->value().toInt32();
+        // A bounds check is only skipped for a positive index.
+        JS_ASSERT(ptrValue >= 0);
+        ptrAlloc = LAllocation(ptr->toConstant()->vp());
+    } else {
+        ptrAlloc = useRegisterAtStart(ptr);
+    }
+    LAsmJSLoadHeap *lir = new LAsmJSLoadHeap(ptrAlloc);
+    return define(lir, ins);
+}
+
+bool
 LIRGeneratorX86::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
+    MDefinition *ptr = ins->ptr();
     LAsmJSStoreHeap *lir;
+    JS_ASSERT(ptr->type() == MIRType_Int32);
+
+    if (ptr->isConstant() && ins->skipBoundsCheck()) {
+        int32_t ptrValue = ptr->toConstant()->value().toInt32();
+        JS_ASSERT(ptrValue >= 0);
+        LAllocation ptrAlloc = LAllocation(ptr->toConstant()->vp());
+        switch (ins->viewType()) {
+          case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
+            // See comment below.
+            lir = new LAsmJSStoreHeap(ptrAlloc, useFixed(ins->value(), eax));
+            break;
+          case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
+          case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
+          case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64:
+            // See comment below.
+            lir = new LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value()));
+            break;
+          default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
+        }
+        return add(lir, ins);
+    }
+
     switch (ins->viewType()) {
       case ArrayBufferView::TYPE_INT8: case ArrayBufferView::TYPE_UINT8:
         // It's a trap! On x86, the 1-byte store can only use one of
         // {al,bl,cl,dl,ah,bh,ch,dh}. That means if the register allocator
         // gives us one of {edi,esi,ebp,esp}, we're out of luck. (The formatter
         // will assert on us.) Ideally, we'd just ask the register allocator to
         // give us one of {al,bl,cl,dl}. For now, just useFixed(al).
-        lir = new LAsmJSStoreHeap(useRegister(ins->ptr()),
-                                  useFixed(ins->value(), eax));
+        lir = new LAsmJSStoreHeap(useRegister(ins->ptr()), useFixed(ins->value(), eax));
         break;
       case ArrayBufferView::TYPE_INT16: case ArrayBufferView::TYPE_UINT16:
       case ArrayBufferView::TYPE_INT32: case ArrayBufferView::TYPE_UINT32:
       case ArrayBufferView::TYPE_FLOAT32: case ArrayBufferView::TYPE_FLOAT64:
-        // For now, don't allow constants. The immediate operand affects
-        // instruction layout which affects patching.
-        lir = new LAsmJSStoreHeap(useRegisterAtStart(ins->ptr()),
-                                  useRegisterAtStart(ins->value()));
+        // For now, don't allow constant values. The immediate operand
+        // affects instruction layout which affects patching.
+        lir = new LAsmJSStoreHeap(useRegisterAtStart(ptr), useRegisterAtStart(ins->value()));
         break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
 
     return add(lir, ins);
 }
 
 bool
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -38,16 +38,17 @@ class LIRGeneratorX86 : public LIRGenera
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     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);
     bool lowerPhi(MPhi *phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }