Bug 986673: OdinMonkey: emit bound checks on x64 if we can't use signal handlers; r=luke
authorBenjamin Bouvier <benj@benj.me>
Fri, 18 Jul 2014 12:14:39 +0200
changeset 216785 68f0964d3ebef4c0c778b5049dc0e3eaf6cb9d4d
parent 216784 63c6334c966d5ac551b49a19bb4ceee79110a841
child 216786 41f10856b94d82da771c9257517bd77a7733b587
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs986673
milestone33.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 986673: OdinMonkey: emit bound checks on x64 if we can't use signal handlers; r=luke
js/src/assembler/assembler/X86Assembler.h
js/src/jit/AsmJS.cpp
js/src/jit/AsmJSLink.cpp
js/src/jit/AsmJSModule.cpp
js/src/jit/AsmJSModule.h
js/src/jit/MIR.h
js/src/jit/shared/Assembler-shared.h
js/src/jit/shared/Assembler-x86-shared.h
js/src/jit/shared/CodeGenerator-x86-shared.cpp
js/src/jit/shared/CodeGenerator-x86-shared.h
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/Lowering-x64.cpp
js/src/jit/x86/Assembler-x86.h
js/src/jit/x86/CodeGenerator-x86.cpp
js/src/jit/x86/CodeGenerator-x86.h
js/src/jscntxt.h
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ArrayBufferObject.h
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -3452,26 +3452,27 @@ public:
     static int32_t addressImmediate(const void *address) {
 #if WTF_CPU_X86_64
         // x64's 64-bit addresses don't all fit in the 32-bit immediate.
         ASSERT(isAddressImmediate(address));
 #endif
         return static_cast<int32_t>(reinterpret_cast<intptr_t>(address));
     }
 
+    static void setInt32(void* where, int32_t value)
+    {
+        reinterpret_cast<int32_t*>(where)[-1] = value;
+    }
+
 private:
 
     static int32_t getInt32(void* where)
     {
         return reinterpret_cast<int32_t*>(where)[-1];
     }
-    static void setInt32(void* where, int32_t value)
-    {
-        reinterpret_cast<int32_t*>(where)[-1] = value;
-    }
 
     class X86InstructionFormatter {
 
         static const int maxInstructionSize = 16;
 
     public:
 
         // Legacy prefix bytes:
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1140,17 +1140,25 @@ class MOZ_STACK_CLASS ModuleCompiler
         uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
         uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
 
         // "use strict" should be added to the source if we are in an implicit
         // strict context, see also comment above addUseStrict in
         // js::FunctionToString.
         bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict();
 
-        module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict);
+#ifdef JS_CODEGEN_X64
+        // Only x64 will effectively use signal handlers for out of bounds checks.
+        bool canUseSignalHandlers = cx_->canUseSignalHandlers();
+#else
+        bool canUseSignalHandlers = false;
+#endif // JS_CODEGEN_X64
+
+        module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict,
+                                         canUseSignalHandlers);
         if (!module_)
             return false;
 
         return true;
     }
 
     bool failOffset(uint32_t offset, const char *str) {
         JS_ASSERT(!errorString_);
@@ -1222,16 +1230,17 @@ class MOZ_STACK_CLASS ModuleCompiler
     AsmJSParser &parser() const { return parser_; }
     TokenStream &tokenStream() const { return parser_.tokenStream; }
     MacroAssembler &masm() { return masm_; }
     Label &stackOverflowLabel() { return stackOverflowLabel_; }
     Label &interruptLabel() { return interruptLabel_; }
     bool hasError() const { return errorString_ != nullptr; }
     const AsmJSModule &module() const { return *module_.get(); }
     uint32_t srcStart() const { return module_->srcStart(); }
+    bool usesSignalHandlers() const { return module_->usesSignalHandlers(); }
 
     ParseNode *moduleFunctionNode() const { return moduleFunctionNode_; }
     PropertyName *moduleFunctionName() const { return moduleFunctionName_; }
 
     const Global *lookupGlobal(PropertyName *name) const {
         if (GlobalMap::Ptr p = globals_.lookup(name))
             return p->value();
         return nullptr;
@@ -2120,28 +2129,28 @@ class FunctionCompiler
     }
 
     MDefinition *loadHeap(Scalar::Type vt, MDefinition *ptr, NeedsBoundsCheck chk)
     {
         if (inDeadCode())
             return nullptr;
         MAsmJSLoadHeap *load = MAsmJSLoadHeap::New(alloc(), vt, ptr);
         curBlock_->add(load);
-        if (chk == NO_BOUNDS_CHECK)
+        if (chk == NO_BOUNDS_CHECK || m().usesSignalHandlers())
             load->setSkipBoundsCheck(true);
         return load;
     }
 
     void storeHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v, NeedsBoundsCheck chk)
     {
         if (inDeadCode())
             return;
         MAsmJSStoreHeap *store = MAsmJSStoreHeap::New(alloc(), vt, ptr, v);
         curBlock_->add(store);
-        if (chk == NO_BOUNDS_CHECK)
+        if (chk == NO_BOUNDS_CHECK || m().usesSignalHandlers())
             store->setSkipBoundsCheck(true);
     }
 
     MDefinition *loadGlobalVar(const ModuleCompiler::Global &global)
     {
         if (inDeadCode())
             return nullptr;
 
@@ -6966,17 +6975,17 @@ Warn(AsmJSParser &parser, int errorNumbe
 }
 
 static bool
 EstablishPreconditions(ExclusiveContext *cx, AsmJSParser &parser)
 {
     if (!cx->jitSupportsFloatingPoint())
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
 
-    if (!cx->canUseSignalHandlers())
+    if (!cx->signalHandlersInstalled())
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Platform missing signal handler support");
 
     if (cx->gcSystemPageSize() != AsmJSPageSize)
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
 
     if (!parser.options().asmJSOption)
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
 
@@ -7038,16 +7047,16 @@ js::CompileAsmJS(ExclusiveContext *cx, A
 
 bool
 js::IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // See EstablishPreconditions.
     bool available = cx->jitSupportsFloatingPoint() &&
-                     cx->canUseSignalHandlers() &&
+                     cx->signalHandlersInstalled() &&
                      cx->gcSystemPageSize() == AsmJSPageSize &&
                      !cx->compartment()->debugMode() &&
                      cx->runtime()->options().asmJS();
 
     args.rval().set(BooleanValue(available));
     return true;
 }
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -250,17 +250,25 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
             JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the"
                         "largest constant heap access offset rounded up to the next valid "
                         "heap size).",
                         heapLength,
                         module.minHeapLength()));
         return LinkFail(cx, msg.get());
     }
 
-    if (!ArrayBufferObject::prepareForAsmJS(cx, heap))
+    // If we've generated the code with signal handlers in mind (for bounds
+    // checks on x64 and for operation callback requesting on all platforms),
+    // we need to be able to use signals at runtime. In particular, a module
+    // can have been created using signals and cached, and executed without
+    // signals activated.
+    if (module.usesSignalHandlers() && !cx->canUseSignalHandlers())
+        return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");
+
+    if (!ArrayBufferObject::prepareForAsmJS(cx, heap, module.usesSignalHandlers()))
         return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
 
     module.initHeap(heap, cx);
     return true;
 }
 
 static bool
 DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -71,17 +71,17 @@ DeallocateExecutableMemory(uint8_t *code
 #ifdef XP_WIN
     JS_ALWAYS_TRUE(VirtualFree(code, 0, MEM_RELEASE));
 #else
     JS_ALWAYS_TRUE(munmap(code, totalBytes) == 0 || errno == ENOMEM);
 #endif
 }
 
 AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
-                         bool strict)
+                         bool strict, bool canUseSignalHandlers)
   : srcStart_(srcStart),
     srcBodyStart_(srcBodyStart),
     scriptSource_(scriptSource),
     globalArgumentName_(nullptr),
     importArgumentName_(nullptr),
     bufferArgumentName_(nullptr),
     code_(nullptr),
     interruptExit_(nullptr),
@@ -89,16 +89,17 @@ AsmJSModule::AsmJSModule(ScriptSource *s
     loadedFromCache_(false),
     codeIsProtected_(false)
 {
     mozilla::PodZero(&pod);
     pod.funcPtrTableAndExitBytes_ = SIZE_MAX;
     pod.functionBytes_ = UINT32_MAX;
     pod.minHeapLength_ = AsmJSAllocationGranularity;
     pod.strict_ = strict;
+    pod.usesSignalHandlers_ = canUseSignalHandlers;
 
     scriptSource_->incref();
 }
 
 AsmJSModule::~AsmJSModule()
 {
     scriptSource_->decref();
 
@@ -634,16 +635,29 @@ AsmJSModule::initHeap(Handle<ArrayBuffer
         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));
         JS_ASSERT(disp <= INT32_MAX);
         JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
     }
+#elif defined(JS_CODEGEN_X64)
+    int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
+    if (usesSignalHandlers())
+        return;
+    // If we cannot use the signal handlers, we need to patch the heap length
+    // checks at the right places. All accesses that have been recorded are the
+    // only ones that need bound checks (see also
+    // CodeGeneratorX64::visitAsmJS{Load,Store}Heap)
+    for (size_t i = 0; i < heapAccesses_.length(); i++) {
+        const jit::AsmJSHeapAccess &access = heapAccesses_[i];
+        if (access.hasLengthCheck())
+            JSC::X86Assembler::setInt32(access.patchLengthAt(code_), heapLength);
+    }
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     uint32_t heapLength = heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         jit::Assembler::UpdateBoundsCheck(heapLength,
                                           (jit::Instruction*)(heapAccesses_[i].offset() + code_));
     }
 #endif
 }
@@ -1209,17 +1223,18 @@ class AutoUnprotectCodeForClone
     }
 };
 
 bool
 AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
 {
     AutoUnprotectCodeForClone cloneGuard(cx, *this);
 
-    *moduleOut = cx->new_<AsmJSModule>(scriptSource_, srcStart_, srcBodyStart_, pod.strict_);
+    *moduleOut = cx->new_<AsmJSModule>(scriptSource_, srcStart_, srcBodyStart_, pod.strict_,
+                                       pod.usesSignalHandlers_);
     if (!*moduleOut)
         return false;
 
     AsmJSModule &out = **moduleOut;
 
     // Mirror the order of serialize/deserialize in cloning:
 
     out.pod = pod;
@@ -1669,18 +1684,20 @@ js::LookupAsmJSModuleInCache(ExclusiveCo
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cx, cursor);
     if (!moduleChars.match(parser))
         return true;
 
     uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
     uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
     bool strict = parser.pc->sc->strict && !parser.pc->sc->hasExplicitUseStrict();
+    // usesSignalHandlers will be clobbered when deserializing
     ScopedJSDeletePtr<AsmJSModule> module(
-        cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict));
+        cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict,
+                              /* usesSignalHandlers = */ false));
     if (!module)
         return false;
     cursor = module->deserialize(cx, cursor);
 
     // No need to flush the instruction cache now, it will be flushed when dynamically linking.
     AutoFlushICache afc("LookupAsmJSModuleInCache", /* inhibit= */ true);
     // We already know the exact extent of areas that need to be patched, just make sure we
     // flush all of them at once.
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -489,16 +489,17 @@ class AsmJSModule
         size_t                            totalBytes_;    // function bodies, stubs, and global data
         uint32_t                          minHeapLength_;
         uint32_t                          numGlobalVars_;
         uint32_t                          numFFIs_;
         uint32_t                          srcLength_;
         uint32_t                          srcLengthWithRightBrace_;
         bool                              strict_;
         bool                              hasArrayView_;
+        bool                              usesSignalHandlers_;
     } pod;
 
     // These two fields need to be kept out pod as they depend on the position
     // of the module within the ScriptSource and thus aren't invariant with
     // respect to caching.
     const uint32_t                        srcStart_;
     const uint32_t                        srcBodyStart_;
 
@@ -529,17 +530,17 @@ class AsmJSModule
     bool                                  loadedFromCache_;
 
     // This field is accessed concurrently when requesting an interrupt.
     // Access must be synchronized via the runtime's interrupt lock.
     mutable bool                          codeIsProtected_;
 
   public:
     explicit AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
-                         bool strict);
+                         bool strict, bool canUseSignalHandlers);
     void trace(JSTracer *trc);
     ~AsmJSModule();
 
     // An AsmJSModule transitions monotonically through these states:
     bool isFinishedWithModulePrologue() const { return pod.funcPtrTableAndExitBytes_ != SIZE_MAX; }
     bool isFinishedWithFunctionBodies() const { return pod.functionBytes_ != UINT32_MAX; }
     bool isFinished() const { return !!code_; }
     bool isStaticallyLinked() const { return !!interruptExit_; }
@@ -550,16 +551,19 @@ class AsmJSModule
 
     ScriptSource *scriptSource() const {
         JS_ASSERT(scriptSource_);
         return scriptSource_;
     }
     bool strict() const {
         return pod.strict_;
     }
+    bool usesSignalHandlers() const {
+        return pod.usesSignalHandlers_;
+    }
     bool loadedFromCache() const {
         return loadedFromCache_;
     }
 
     // srcStart() refers to the offset in the ScriptSource to the beginning of
     // the asm.js module function. If the function has been created with the
     // Function constructor, this will be the first character in the function
     // source. Otherwise, it will be the opening parenthesis of the arguments
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10384,29 +10384,29 @@ class MAsmJSNeg : public MUnaryInstructi
 };
 
 class MAsmJSHeapAccess
 {
     Scalar::Type viewType_;
     bool skipBoundsCheck_;
 
   public:
-    MAsmJSHeapAccess(Scalar::Type vt, bool s)
-      : viewType_(vt), skipBoundsCheck_(s)
+    MAsmJSHeapAccess(Scalar::Type vt)
+      : viewType_(vt), skipBoundsCheck_(false)
     {}
 
     Scalar::Type viewType() const { return viewType_; }
     bool skipBoundsCheck() const { return skipBoundsCheck_; }
     void setSkipBoundsCheck(bool v) { skipBoundsCheck_ = v; }
 };
 
 class MAsmJSLoadHeap : public MUnaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSLoadHeap(Scalar::Type vt, MDefinition *ptr)
-      : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt, false)
+      : MUnaryInstruction(ptr), MAsmJSHeapAccess(vt)
     {
         setMovable();
         if (vt == Scalar::Float32)
             setResultType(MIRType_Float32);
         else if (vt == Scalar::Float64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
@@ -10426,17 +10426,17 @@ class MAsmJSLoadHeap : public MUnaryInst
         return AliasSet::Load(AliasSet::AsmJSHeap);
     }
     bool mightAlias(const MDefinition *def) const;
 };
 
 class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
 {
     MAsmJSStoreHeap(Scalar::Type vt, MDefinition *ptr, MDefinition *v)
-      : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt, false)
+      : MBinaryInstruction(ptr, v) , MAsmJSHeapAccess(vt)
     {}
 
   public:
     INSTRUCTION_HEADER(AsmJSStoreHeap);
 
     static MAsmJSStoreHeap *New(TempAllocator &alloc, Scalar::Type vt,
                                 MDefinition *ptr, MDefinition *v)
     {
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -630,65 +630,61 @@ static const uint32_t AsmJSFrameSize = s
 // Summarizes a heap access made by asm.js code that needs to be patched later
 // and/or looked up by the asm.js signal handlers. Different architectures need
 // to know different things (x64: offset and length, ARM: where to patch in
 // heap length, x86: where to patch in heap length and base) hence the massive
 // #ifdefery.
 class AsmJSHeapAccess
 {
     uint32_t offset_;
-#if defined(JS_CODEGEN_X86)
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     uint8_t cmpDelta_;  // the number of bytes from the cmp to the load/store instruction
-#endif
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     uint8_t opLength_;  // the length of the load/store instruction
     uint8_t isFloat32Load_;
     AnyRegister::Code loadedReg_ : 8;
 #endif
 
     JS_STATIC_ASSERT(AnyRegister::Total < UINT8_MAX);
 
   public:
     AsmJSHeapAccess() {}
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+    static const uint32_t NoLengthCheck = UINT32_MAX;
+
     // 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, Scalar::Type vt,
-                    AnyRegister loadedReg, uint32_t cmp = UINT32_MAX)
+                    AnyRegister loadedReg, uint32_t cmp = NoLengthCheck)
       : offset_(offset),
-# if defined(JS_CODEGEN_X86)
-        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
-# endif
+        cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
         opLength_(after - offset),
         isFloat32Load_(vt == Scalar::Float32),
         loadedReg_(loadedReg.code())
     {}
-    AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = UINT32_MAX)
+    AsmJSHeapAccess(uint32_t offset, uint8_t after, uint32_t cmp = NoLengthCheck)
       : offset_(offset),
-# if defined(JS_CODEGEN_X86)
-        cmpDelta_(cmp == UINT32_MAX ? 0 : offset - cmp),
-# endif
+        cmpDelta_(cmp == NoLengthCheck ? 0 : offset - cmp),
         opLength_(after - offset),
         isFloat32Load_(false),
         loadedReg_(UINT8_MAX)
     {}
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     explicit AsmJSHeapAccess(uint32_t offset)
       : offset_(offset)
     {}
 #endif
 
     uint32_t offset() const { return offset_; }
     void setOffset(uint32_t offset) { offset_ = offset; }
 #if defined(JS_CODEGEN_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_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+    bool hasLengthCheck() const { return cmpDelta_ > 0; }
+    void *patchLengthAt(uint8_t *code) const { return code + (offset_ - cmpDelta_); }
     unsigned opLength() const { return opLength_; }
     bool isLoad() const { return loadedReg_ != UINT8_MAX; }
     bool isFloat32Load() const { return isFloat32Load_; }
     AnyRegister loadedReg() const { return AnyRegister::FromCode(loadedReg_); }
 #endif
 };
 
 typedef Vector<AsmJSHeapAccess, 0, SystemAllocPolicy> AsmJSHeapAccessVector;
--- a/js/src/jit/shared/Assembler-x86-shared.h
+++ b/js/src/jit/shared/Assembler-x86-shared.h
@@ -856,16 +856,20 @@ class AssemblerX86Shared : public Assemb
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
     void cmpl(const Operand &op, ImmPtr imm) {
         cmpl(op, ImmWord(uintptr_t(imm.value)));
     }
+    CodeOffsetLabel cmplWithPatch(Register lhs, Imm32 rhs) {
+        masm.cmpl_ir_force32(rhs.value, lhs.code());
+        return CodeOffsetLabel(masm.currentOffset());
+    }
     void cmpw(Register lhs, Register rhs) {
         masm.cmpw_rr(lhs.code(), rhs.code());
     }
     void setCC(Condition cond, Register r) {
         masm.setCC_r(static_cast<JSC::X86Assembler::Condition>(cond), r.code());
     }
     void testb(Register lhs, Register rhs) {
         JS_ASSERT(GeneralRegisterSet(Registers::SingleByteRegs).has(lhs));
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -23,16 +23,18 @@ using namespace js;
 using namespace js::jit;
 
 using mozilla::Abs;
 using mozilla::FloatingPoint;
 using mozilla::FloorLog2;
 using mozilla::NegativeInfinity;
 using mozilla::SpecificNaN;
 
+using JS::GenericNaN;
+
 namespace js {
 namespace jit {
 
 CodeGeneratorX86Shared::CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm)
   : CodeGeneratorShared(gen, graph, masm)
 {
 }
 
@@ -339,16 +341,32 @@ CodeGeneratorX86Shared::visitAsmJSPassSt
             masm.storePtr(ToRegister(ins->arg()), dst);
         else
             masm.storeDouble(ToFloatRegister(ins->arg()), dst);
     }
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
+{
+    if (ool->dest().isFloat()) {
+        if (ool->isFloat32Load())
+            masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
+        else
+            masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
+    } else {
+        Register destReg = ool->dest().gpr();
+        masm.mov(ImmWord(0), destReg);
+    }
+    masm.jmp(ool->rejoin());
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::generateOutOfLineCode()
 {
     if (!CodeGeneratorShared::generateOutOfLineCode())
         return false;
 
     if (deoptLabel_.used()) {
         // All non-table-based bailouts will go here.
         masm.bind(&deoptLabel_);
--- a/js/src/jit/shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.h
@@ -9,16 +9,17 @@
 
 #include "jit/shared/CodeGenerator-shared.h"
 
 namespace js {
 namespace jit {
 
 class OutOfLineBailout;
 class OutOfLineUndoALUOperation;
+class OutOfLineLoadTypedArrayOutOfBounds;
 class MulNegativeZeroCheck;
 class ModOverflowCheck;
 class ReturnZero;
 class OutOfLineTableSwitch;
 
 class CodeGeneratorX86Shared : public CodeGeneratorShared
 {
     friend class MoveResolverX86;
@@ -26,16 +27,35 @@ class CodeGeneratorX86Shared : public Co
     CodeGeneratorX86Shared *thisFromCtor() {
         return this;
     }
 
     template <typename T>
     bool bailout(const T &t, LSnapshot *snapshot);
 
   protected:
+
+    // Load a NaN or zero into a register for an out of bounds AsmJS or static
+    // typed array load.
+    class OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86Shared>
+    {
+        AnyRegister dest_;
+        bool isFloat32Load_;
+      public:
+        OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
+          : dest_(dest), isFloat32Load_(isFloat32Load)
+        {}
+
+        AnyRegister dest() const { return dest_; }
+        bool isFloat32Load() const { return isFloat32Load_; }
+        bool accept(CodeGeneratorX86Shared *codegen) {
+            return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this);
+        }
+    };
+
     // Label for the common return path.
     NonAssertingLabel returnLabel_;
     NonAssertingLabel deoptLabel_;
 
     inline Operand ToOperand(const LAllocation &a) {
         if (a.isGeneralReg())
             return Operand(a.toGeneralReg()->reg());
         if (a.isFloatReg())
@@ -172,16 +192,18 @@ class CodeGeneratorX86Shared : public Co
     virtual bool visitRoundF(LRoundF *lir);
     virtual bool visitGuardShape(LGuardShape *guard);
     virtual bool visitGuardObjectType(LGuardObjectType *guard);
     virtual bool visitGuardClass(LGuardClass *guard);
     virtual bool visitEffectiveAddress(LEffectiveAddress *ins);
     virtual bool visitUDivOrMod(LUDivOrMod *ins);
     virtual bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);
 
+    bool visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool);
+
     bool visitForkJoinGetSlice(LForkJoinGetSlice *ins);
 
     bool visitNegI(LNegI *lir);
     bool visitNegD(LNegD *lir);
     bool visitNegF(LNegF *lir);
 
     // Out of line visitors.
     bool visitOutOfLineBailout(OutOfLineBailout *ool);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -241,67 +241,81 @@ CodeGeneratorX64::visitStoreTypedArrayEl
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins)
 {
     MAsmJSLoadHeap *mir = ins->mir();
     Scalar::Type vt = mir->viewType();
     const LAllocation *ptr = ins->ptr();
-
-    // No need to note the access if it will never fault.
-    bool skipNote = mir->skipBoundsCheck();
+    const LDefinition *out = ins->output();
     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);
     }
 
+    OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr;
+    uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
+    if (!mir->skipBoundsCheck()) {
+        bool isFloat32Load = vt == Scalar::Float32;
+        ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load);
+        if (!addOutOfLineCode(ool))
+            return false;
+
+        CodeOffsetLabel cmp = masm.cmplWithPatch(ToRegister(ptr), Imm32(0));
+        masm.j(Assembler::AboveOrEqual, ool->entry());
+        maybeCmpOffset = cmp.offset();
+    }
+
     uint32_t before = masm.size();
     switch (vt) {
-      case Scalar::Int8:    masm.movsbl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Uint8:   masm.movzbl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Int16:   masm.movswl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Uint16:  masm.movzwl(srcAddr, ToRegister(ins->output())); break;
+      case Scalar::Int8:    masm.movsbl(srcAddr, ToRegister(out)); break;
+      case Scalar::Uint8:   masm.movzbl(srcAddr, ToRegister(out)); break;
+      case Scalar::Int16:   masm.movswl(srcAddr, ToRegister(out)); break;
+      case Scalar::Uint16:  masm.movzwl(srcAddr, ToRegister(out)); break;
       case Scalar::Int32:
-      case Scalar::Uint32:  masm.movl(srcAddr, ToRegister(ins->output())); break;
-      case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(ins->output())); break;
-      case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(ins->output())); break;
+      case Scalar::Uint32:  masm.movl(srcAddr, ToRegister(out)); break;
+      case Scalar::Float32: masm.loadFloat32(srcAddr, ToFloatRegister(out)); break;
+      case Scalar::Float64: masm.loadDouble(srcAddr, ToFloatRegister(out)); break;
       default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
     }
     uint32_t after = masm.size();
-    if (!skipNote)
-        masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(ins->output())));
+    if (ool)
+        masm.bind(ool->rejoin());
+    masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), maybeCmpOffset));
     return true;
 }
 
 bool
 CodeGeneratorX64::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
 {
     MAsmJSStoreHeap *mir = ins->mir();
     Scalar::Type 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);
 
     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);
+        dstAddr = Operand(HeapReg, ToRegister(ptr), TimesOne);
+    }
+
+    Label rejoin;
+    uint32_t maybeCmpOffset = AsmJSHeapAccess::NoLengthCheck;
+    if (!mir->skipBoundsCheck()) {
+        CodeOffsetLabel cmp = masm.cmplWithPatch(ToRegister(ptr), Imm32(0));
+        masm.j(Assembler::AboveOrEqual, &rejoin);
+        maybeCmpOffset = cmp.offset();
     }
 
     uint32_t before = masm.size();
     if (ins->value()->isConstant()) {
         switch (vt) {
           case Scalar::Int8:
           case Scalar::Uint8:   masm.movb(Imm32(ToInt32(ins->value())), dstAddr); break;
           case Scalar::Int16:
@@ -319,18 +333,19 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
           case Scalar::Int32:
           case Scalar::Uint32:  masm.movl(ToRegister(ins->value()), dstAddr); break;
           case Scalar::Float32: masm.storeFloat32(ToFloatRegister(ins->value()), dstAddr); break;
           case Scalar::Float64: masm.storeDouble(ToFloatRegister(ins->value()), dstAddr); break;
           default: MOZ_ASSUME_UNREACHABLE("unexpected array type");
         }
     }
     uint32_t after = masm.size();
-    if (!skipNote)
-        masm.append(AsmJSHeapAccess(before, after));
+    if (rejoin.used())
+        masm.bind(&rejoin);
+    masm.append(AsmJSHeapAccess(before, after, maybeCmpOffset));
     return true;
 }
 
 bool
 CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins)
 {
     MAsmJSLoadGlobalVar *mir = ins->mir();
 
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -126,26 +126,33 @@ LIRGeneratorX64::visitAsmJSUnsignedToFlo
     return define(lir, ins);
 }
 
 bool
 LIRGeneratorX64::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     JS_ASSERT(ptr->type() == MIRType_Int32);
+    LAllocation ptrAlloc;
 
-    // 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(alloc()) LAsmJSLoadHeap(LAllocation(ptr->toConstant()->vp()));
-        return define(lir, ins);
+    bool useConstant = false;
+    if (ptr->isConstant()) {
+        int32_t ptrValue = ptr->toConstant()->value().toInt32();
+        if (ins->skipBoundsCheck() && ptrValue >= 0) {
+            // 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.
+            useConstant = true;
+        }
+        // In other cases, still keep the pointer in a register.
     }
-    return define(new(alloc()) LAsmJSLoadHeap(useRegisterAtStart(ptr)), ins);
+
+    ptrAlloc = (useConstant) ? LAllocation(ptr->toConstant()->vp()) : useRegisterAtStart(ptr);
+    LAsmJSLoadHeap *lir = new(alloc()) LAsmJSLoadHeap(ptrAlloc);
+    return define(lir, ins);
 }
 
 bool
 LIRGeneratorX64::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
 {
     MDefinition *ptr = ins->ptr();
     JS_ASSERT(ptr->type() == MIRType_Int32);
     LAsmJSStoreHeap *lir;
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -338,20 +338,16 @@ class Assembler : public AssemblerX86Sha
           default:
             MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
         }
     }
     void cmpl(AsmJSAbsoluteAddress lhs, Register rhs) {
         masm.cmpl_rm_force32(rhs.code(), (void*)-1);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(masm.currentOffset()), lhs.kind()));
     }
-    CodeOffsetLabel cmplWithPatch(Register lhs, Imm32 rhs) {
-        masm.cmpl_ir_force32(rhs.value, lhs.code());
-        return CodeOffsetLabel(masm.currentOffset());
-    }
 
     void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) {
         JmpSrc src = masm.jmp();
         addPendingJump(src, target, reloc);
     }
     void j(Condition cond, ImmPtr target,
            Relocation::Kind reloc = Relocation::HARDCODED) {
         JmpSrc src = masm.jCC(static_cast<JSC::X86Assembler::Condition>(cond));
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -260,32 +260,16 @@ CodeGeneratorX86::visitAsmJSUInt32ToFloa
     if (input != temp)
         masm.mov(input, temp);
 
     // Beware: convertUInt32ToFloat32 clobbers input.
     masm.convertUInt32ToFloat32(temp, output);
     return true;
 }
 
-// Load a NaN or zero into a register for an out of bounds AsmJS or static
-// typed array load.
-class jit::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase<CodeGeneratorX86>
-{
-    AnyRegister dest_;
-    bool isFloat32Load_;
-  public:
-    OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load)
-      : dest_(dest), isFloat32Load_(isFloat32Load)
-    {}
-
-    AnyRegister dest() const { return dest_; }
-    bool isFloat32Load() const { return isFloat32Load_; }
-    bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); }
-};
-
 template<typename T>
 void
 CodeGeneratorX86::loadViewTypeElement(Scalar::Type vt, const T &srcAddr,
                                       const LDefinition *out)
 {
     switch (vt) {
       case Scalar::Int8:    masm.movsblWithPatch(srcAddr, ToRegister(out)); break;
       case Scalar::Uint8Clamped:
@@ -381,32 +365,16 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
     uint32_t before = masm.size();
     loadViewTypeElement(vt, srcAddr, out);
     uint32_t after = masm.size();
     masm.bind(ool->rejoin());
     masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset()));
     return true;
 }
 
-bool
-CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool)
-{
-    if (ool->dest().isFloat()) {
-        if (ool->isFloat32Load())
-            masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu());
-        else
-            masm.loadConstantDouble(GenericNaN(), ool->dest().fpu());
-    } else {
-        Register destReg = ool->dest().gpr();
-        masm.mov(ImmWord(0), destReg);
-    }
-    masm.jmp(ool->rejoin());
-    return true;
-}
-
 template<typename T>
 void
 CodeGeneratorX86::storeViewTypeElement(Scalar::Type vt, const LAllocation *value,
                                        const T &dstAddr)
 {
     switch (vt) {
       case Scalar::Int8:
       case Scalar::Uint8Clamped:
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -8,17 +8,16 @@
 #define jit_x86_CodeGenerator_x86_h
 
 #include "jit/shared/CodeGenerator-x86-shared.h"
 #include "jit/x86/Assembler-x86.h"
 
 namespace js {
 namespace jit {
 
-class OutOfLineLoadTypedArrayOutOfBounds;
 class OutOfLineTruncate;
 class OutOfLineTruncateFloat32;
 
 class CodeGeneratorX86 : public CodeGeneratorX86Shared
 {
   private:
     CodeGeneratorX86 *thisFromCtor() {
         return this;
@@ -61,17 +60,16 @@ class CodeGeneratorX86 : public CodeGene
     bool visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins);
     bool visitAsmJSLoadHeap(LAsmJSLoadHeap *ins);
     bool visitAsmJSStoreHeap(LAsmJSStoreHeap *ins);
     bool visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
-    bool visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool);
     bool visitOutOfLineTruncate(OutOfLineTruncate *ool);
     bool visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool);
 
     void postAsmJSCall(LAsmJSCall *lir);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -272,16 +272,17 @@ struct ThreadSafeContext : ContextFriend
     AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; }
     const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName *emptyString() { return runtime_->emptyString; }
     FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
     void *runtimeAddressForJit() { return runtime_; }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
+    bool signalHandlersInstalled() const { return runtime_->signalHandlersInstalled(); }
     bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
 
     // Thread local data that may be accessed freely.
     DtoaState *dtoaState() {
         return perThreadData->dtoaState;
     }
 };
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -409,20 +409,48 @@ ArrayBufferObject::changeContents(JSCont
     }
 }
 
 #if defined(JS_CPU_X64)
 // Refer to comment above AsmJSMappedSize in AsmJS.h.
 JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
 #endif
 
+/* static */ bool
+ArrayBufferObject::prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+{
+    if (buffer->isAsmJSArrayBuffer())
+        return true;
+
+    if (buffer->isSharedArrayBuffer())
+        return true;
+
+    if (!ensureNonInline(cx, buffer))
+        return false;
+
+    buffer->setIsAsmJSArrayBuffer();
+    return true;
+}
+
+void
+ArrayBufferObject::releaseAsmJSArrayNoSignals(FreeOp *fop)
+{
+    JS_ASSERT(!isAsmJSMappedArrayBuffer());
+    fop->free_(dataPointer());
+}
+
 #if defined(JS_ION) && defined(JS_CPU_X64)
 /* static */ bool
-ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer,
+                                   bool usesSignalHandlers)
 {
+    // If we can't use signal handlers, just do it like on other platforms.
+    if (!usesSignalHandlers)
+        return prepareForAsmJSNoSignals(cx, buffer);
+
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
     // SharedArrayBuffers are already created with AsmJS support in mind.
     if (buffer->isSharedArrayBuffer())
         return true;
 
     // Get the entire reserved region (with all pages inaccessible).
@@ -462,23 +490,29 @@ ArrayBufferObject::prepareForAsmJS(JSCon
 
     // Swap the new elements into the ArrayBufferObject.
     buffer->changeContents(cx, data);
     JS_ASSERT(data == buffer->dataPointer());
 
     // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
     // to js_free the data in the normal way.
     buffer->setIsAsmJSArrayBuffer();
+    buffer->setIsAsmJSMappedArrayBuffer();
 
     return true;
 }
 
 void
 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
 {
+    if (!isAsmJSMappedArrayBuffer()) {
+        releaseAsmJSArrayNoSignals(fop);
+        return;
+    }
+
     void *data = dataPointer();
 
     JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0);
 # ifdef XP_WIN
     VirtualFree(data, 0, MEM_RELEASE);
 # else
     munmap(data, AsmJSMappedSize);
 #   if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
@@ -487,35 +521,30 @@ ArrayBufferObject::releaseAsmJSArray(Fre
     if (AsmJSMappedSize > 0) {
         VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(data, AsmJSMappedSize);
     }
 #   endif
 # endif
 }
 #else  /* defined(JS_ION) && defined(JS_CPU_X64) */
 bool
-ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
+ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer,
+                                   bool usesSignalHandlers)
 {
-    if (buffer->isAsmJSArrayBuffer())
-        return true;
-
-    if (buffer->isSharedArrayBuffer())
-        return true;
-
-    if (!ensureNonInline(cx, buffer))
-        return false;
-
-    buffer->setIsAsmJSArrayBuffer();
-    return true;
+    // Platforms other than x64 don't use signalling for bounds checking, so
+    // just use the variant with no signals.
+    JS_ASSERT(!usesSignalHandlers);
+    return prepareForAsmJSNoSignals(cx, buffer);
 }
 
 void
 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
 {
-    fop->free_(dataPointer());
+    // See comment above.
+    releaseAsmJSArrayNoSignals(fop);
 }
 #endif
 
 bool
 ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
 {
     JS_ASSERT(!buffer.isSharedArrayBuffer());
 #ifdef JS_ION
@@ -769,23 +798,22 @@ ArrayBufferObject::stealContents(JSConte
 ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
 {
     ArrayBufferObject &buffer = AsArrayBuffer(obj);
 
     if (!buffer.ownsData())
         return;
 
     if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) {
-#if defined (JS_CPU_X64)
         // On x64, ArrayBufferObject::prepareForAsmJS switches the
         // ArrayBufferObject to use mmap'd storage.
-        sizes->nonHeapElementsAsmJS += buffer.byteLength();
-#else
-        sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
-#endif
+        if (buffer.isAsmJSMappedArrayBuffer())
+            sizes->nonHeapElementsAsmJS += buffer.byteLength();
+        else
+            sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
     } else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) {
         sizes->nonHeapElementsMapped += buffer.byteLength();
     } else if (buffer.dataPointer()) {
         sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
     }
 }
 
 /* static */ void
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -147,21 +147,24 @@ class ArrayBufferObject : public JSObjec
      * Check if the arrayBuffer contains any data. This will return false for
      * ArrayBuffer.prototype and neutered ArrayBuffers.
      */
     bool hasData() const {
         return getClass() == &class_;
     }
 
     bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; }
+    bool isAsmJSMappedArrayBuffer() const { return flags() & ASMJS_MAPPED_BUFFER; }
     bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; }
     bool isMappedArrayBuffer() const { return flags() & MAPPED_BUFFER; }
     bool isNeutered() const { return flags() & NEUTERED_BUFFER; }
 
-    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer);
+    static bool prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer,
+                                bool usesSignalHandlers);
+    static bool prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObject*> buffer);
     static bool canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer);
 
     static void finalize(FreeOp *fop, JSObject *obj);
 
     static void *createMappedContents(int fd, size_t offset, size_t length);
 
     static size_t flagsOffset() {
         return getFixedSlotOffset(FLAGS_SLOT);
@@ -179,56 +182,59 @@ class ArrayBufferObject : public JSObjec
     void setByteLength(size_t length);
 
     ArrayBufferViewObject *viewList() const;
     void setViewList(ArrayBufferViewObject *viewsHead);
     void setViewListNoBarrier(ArrayBufferViewObject *viewsHead);
 
     enum ArrayBufferFlags {
         // In the gcLiveArrayBuffers list.
-        IN_LIVE_LIST       =  0x1,
+        IN_LIVE_LIST        =  0x1,
 
         // The dataPointer() is owned by this buffer and should be released
         // when no longer in use. Releasing the pointer may be done by either
         // freeing or unmapping it, and how to do this is determined by the
         // buffer's other flags.
-        OWNS_DATA          =  0x2,
+        OWNS_DATA           =  0x2,
 
-        ASMJS_BUFFER       =  0x4,
-        SHARED_BUFFER      =  0x8,
-        MAPPED_BUFFER      = 0x10,
-        NEUTERED_BUFFER    = 0x20
+        ASMJS_BUFFER        =  0x4,
+        ASMJS_MAPPED_BUFFER =  0x8,
+        SHARED_BUFFER       = 0x10,
+        MAPPED_BUFFER       = 0x20,
+        NEUTERED_BUFFER     = 0x40,
     };
 
     uint32_t flags() const;
     void setFlags(uint32_t flags);
 
     bool inLiveList() const { return flags() & IN_LIVE_LIST; }
     void setInLiveList(bool value) {
         setFlags(value ? (flags() | IN_LIVE_LIST) : (flags() & ~IN_LIVE_LIST));
     }
 
     bool ownsData() const { return flags() & OWNS_DATA; }
     void setOwnsData(OwnsState owns) {
         setFlags(owns ? (flags() | OWNS_DATA) : (flags() & ~OWNS_DATA));
     }
 
     void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); }
+    void setIsAsmJSMappedArrayBuffer() { setFlags(flags() | ASMJS_MAPPED_BUFFER); }
     void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); }
     void setIsMappedArrayBuffer() { setFlags(flags() | MAPPED_BUFFER); }
     void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); }
 
     void initialize(size_t byteLength, void *data, OwnsState ownsState) {
         setByteLength(byteLength);
         setFlags(0);
         setViewListNoBarrier(nullptr);
         setDataPointer(data, ownsState);
     }
 
     void releaseAsmJSArray(FreeOp *fop);
+    void releaseAsmJSArrayNoSignals(FreeOp *fop);
     void releaseMappedArray();
 };
 
 /*
  * ArrayBufferViewObject
  *
  * Common definitions shared by all ArrayBufferViews.
  */