Finish Win64 support
authorLuke Wagner <luke@mozilla.com>
Fri, 22 Feb 2013 14:43:23 -0800
changeset 122629 c1150064a2d0c835ea1e699fc919847d499cba19
parent 122628 0cd58b909c788d1ecd3a5df39fd81cca7939cba3
child 122761 f00a483944d46f958e17a1a6b91013c283290ce4
push id53
push userlwagner@mozilla.com
push dateFri, 22 Feb 2013 22:43:51 +0000
milestone22.0a1
Finish Win64 support
js/src/ion/AsmJS.cpp
js/src/ion/AsmJSSignalHandlers.cpp
js/src/ion/x64/Assembler-x64.cpp
js/src/ion/x64/Assembler-x64.h
js/src/jstypedarray.cpp
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -3262,38 +3262,50 @@ CheckFFICall(FunctionCompiler &f, ParseN
 
     if (!f.ffiCall(exitIndex, args, use.toMIRType(), def))
         return false;
 
     *type = use.toFFIReturnType();
     return true;
 }
 
+static inline void *
+UnaryMathFunCast(double (*pf)(double))
+{
+    return JS_FUNC_TO_DATA_PTR(void*, pf);
+}
+
+static inline void *
+BinaryMathFunCast(double (*pf)(double, double))
+{
+    return JS_FUNC_TO_DATA_PTR(void*, pf);
+}
+
 static bool
 CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin mathBuiltin,
                      MDefinition **def, Type *type)
 {
     unsigned arity;
     void *callee;
     switch (mathBuiltin) {
       case AsmJSMathBuiltin_imul:  return CheckMathIMul(f, callNode, def, type);
       case AsmJSMathBuiltin_abs:   return CheckMathAbs(f, callNode, def, type);
-      case AsmJSMathBuiltin_sin:   arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, sin);     break;
-      case AsmJSMathBuiltin_cos:   arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, cos);     break;
-      case AsmJSMathBuiltin_tan:   arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, tan);     break;
-      case AsmJSMathBuiltin_asin:  arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, asin);    break;
-      case AsmJSMathBuiltin_acos:  arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, acos);    break;
-      case AsmJSMathBuiltin_atan:  arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, atan);    break;
-      case AsmJSMathBuiltin_ceil:  arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, ceil);    break;
-      case AsmJSMathBuiltin_floor: arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, floor);   break;
-      case AsmJSMathBuiltin_exp:   arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, exp);     break;
-      case AsmJSMathBuiltin_log:   arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, log);     break;
-      case AsmJSMathBuiltin_sqrt:  arity = 1; callee = JS_FUNC_TO_DATA_PTR(void*, sqrt);    break;
-      case AsmJSMathBuiltin_pow:   arity = 2; callee = JS_FUNC_TO_DATA_PTR(void*, ecmaPow); break;
-      case AsmJSMathBuiltin_atan2: arity = 2; callee = JS_FUNC_TO_DATA_PTR(void*, atan2);   break;
+      case AsmJSMathBuiltin_sin:   arity = 1; callee = UnaryMathFunCast(sin);     break;
+      case AsmJSMathBuiltin_cos:   arity = 1; callee = UnaryMathFunCast(cos);     break;
+      case AsmJSMathBuiltin_tan:   arity = 1; callee = UnaryMathFunCast(tan);     break;
+      case AsmJSMathBuiltin_asin:  arity = 1; callee = UnaryMathFunCast(asin);    break;
+      case AsmJSMathBuiltin_acos:  arity = 1; callee = UnaryMathFunCast(acos);    break;
+      case AsmJSMathBuiltin_atan:  arity = 1; callee = UnaryMathFunCast(atan);    break;
+      case AsmJSMathBuiltin_ceil:  arity = 1; callee = UnaryMathFunCast(ceil);    break;
+      case AsmJSMathBuiltin_floor: arity = 1; callee = UnaryMathFunCast(floor);   break;
+      case AsmJSMathBuiltin_exp:   arity = 1; callee = UnaryMathFunCast(exp);     break;
+      case AsmJSMathBuiltin_log:   arity = 1; callee = UnaryMathFunCast(log);     break;
+      case AsmJSMathBuiltin_sqrt:  arity = 1; callee = UnaryMathFunCast(sqrt);    break;
+      case AsmJSMathBuiltin_pow:   arity = 2; callee = BinaryMathFunCast(ecmaPow); break;
+      case AsmJSMathBuiltin_atan2: arity = 2; callee = BinaryMathFunCast(atan2);   break;
     }
 
     FunctionCompiler::Args args(f);
 
     if (!CheckCallArgs(f, callNode, Use::ToNumber, &args))
         return false;
 
     if (args.length() != arity)
@@ -4655,27 +4667,28 @@ GenerateOperationCallbackExit(ModuleComp
     masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfResumePC()), IntArgReg1);
     masm.storePtr(IntArgReg1, Address(StackPointer, masm.framePushed() + sizeof(void*)));
 
     // argument 0: cx
     masm.loadPtr(Address(IntArgReg0, AsmJSActivation::offsetOfContext()), IntArgReg0);
 
     // We know that StackPointer is word-aligned, but not necessarily
     // stack-aligned (StackAlignment is greater than word size on x64).
-    masm.mov(StackPointer, SomeNonVolatileReg);
+    Register nonVolatileReg = *GeneralRegisterIterator(GeneralRegisterSet(Registers::NonVolatileMask));
+    masm.mov(StackPointer, nonVolatileReg);
     masm.andPtr(Imm32(~(StackAlignment - 1)), StackPointer);
     if (ShadowSpaceSize)
         masm.subPtr(Imm32(ShadowSpaceSize), StackPointer);
 
     JSBool (*pf)(JSContext*) = js_HandleExecutionInterrupt;
     masm.call(ImmWord(JS_FUNC_TO_DATA_PTR(void*, pf)));
     masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
 
     // Restore the saved StackPointer.
-    masm.mov(SomeNonVolatileReg, StackPointer);
+    masm.mov(nonVolatileReg, StackPointer);
 
     // Restore the machine state to before the interrupt.
     masm.PopRegsInMask(AllRegs);  // restore all GP/FP registers
     JS_ASSERT(masm.framePushed() == 0);
     masm.popFlags();              // after this, nothing that sets conditions
     masm.ret();                   // pop resumePC into PC
 }
 
--- a/js/src/ion/AsmJSSignalHandlers.cpp
+++ b/js/src/ion/AsmJSSignalHandlers.cpp
@@ -9,26 +9,24 @@
 
 #include "jstypedarrayinlines.h"
 
 #include "ion/AsmJS.h"
 #include "ion/AsmJSModule.h"
 #include "ion/AsmJSSignalHandlers.h"
 #include "assembler/assembler/MacroAssembler.h"
 
-#include <signal.h>
-#include <sys/mman.h>
 
 using namespace js;
 using namespace js::ion;
 
+// Prevent races trying to install the signal handlers.
 #ifdef JS_THREADSAFE
 # include "jslock.h"
 
-// Prevent races trying to install the signal handlers.
 class SignalMutex
 {
     PRLock *mutex_;
 
   public:
     SignalMutex() {
         mutex_ = PR_NewLock();
         if (!mutex_)
@@ -68,152 +66,38 @@ struct SignalMutex
         bool handlersInstalled() const { return sHandlersInstalled; }
         void setHandlersInstalled() { sHandlersInstalled = true; }
     };
 };
 
 bool SignalMutex::Lock::sHandlersInstalled = false;
 #endif
 
+// Platform-independent reusable functions:
+
+static AsmJSActivation *
+InnermostAsmJSActivation()
+{
+    PerThreadData *threadData = TlsPerThreadData.get();
+    if (!threadData)
+        return NULL;
+
+    return threadData->asmJSActivationStackFromOwnerThread();
+}
+
 template <class T>
 static void
 SetXMMRegToNaN(T *xmm_reg)
 {
     JS_STATIC_ASSERT(sizeof(T) == 2 * sizeof(double));
     double *dbls = reinterpret_cast<double*>(xmm_reg);
     dbls[0] = js_NaN;
     dbls[1] = 0;
 }
 
-#if defined(__linux__)
-
-static const int SignalCode = SIGSEGV;
-
-static uint8_t **
-ContextToPC(mcontext_t &mcontext)
-{
-# if defined(JS_CPU_X64)
-    JS_STATIC_ASSERT(sizeof(mcontext.gregs[REG_RIP]) == 8);
-    return reinterpret_cast<uint8_t**>(&mcontext.gregs[REG_RIP]);
-# else
-#  error "TODO"
-# endif
-}
-
-static void
-SetRegisterToCoercedUndefined(mcontext_t &mcontext, AnyRegister reg)
-{
-# if defined(JS_CPU_X64)
-    if (reg.isFloat()) {
-        switch (reg.fpu().code()) {
-          case JSC::X86Registers::xmm0:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[0]); break;
-          case JSC::X86Registers::xmm1:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[1]); break;
-          case JSC::X86Registers::xmm2:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[2]); break;
-          case JSC::X86Registers::xmm3:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[3]); break;
-          case JSC::X86Registers::xmm4:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[4]); break;
-          case JSC::X86Registers::xmm5:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[5]); break;
-          case JSC::X86Registers::xmm6:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[6]); break;
-          case JSC::X86Registers::xmm7:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[7]); break;
-          case JSC::X86Registers::xmm8:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[8]); break;
-          case JSC::X86Registers::xmm9:  SetXMMRegToNaN(&mcontext.fpregs->_xmm[9]); break;
-          case JSC::X86Registers::xmm10: SetXMMRegToNaN(&mcontext.fpregs->_xmm[10]); break;
-          case JSC::X86Registers::xmm11: SetXMMRegToNaN(&mcontext.fpregs->_xmm[11]); break;
-          case JSC::X86Registers::xmm12: SetXMMRegToNaN(&mcontext.fpregs->_xmm[12]); break;
-          case JSC::X86Registers::xmm13: SetXMMRegToNaN(&mcontext.fpregs->_xmm[13]); break;
-          case JSC::X86Registers::xmm14: SetXMMRegToNaN(&mcontext.fpregs->_xmm[14]); break;
-          case JSC::X86Registers::xmm15: SetXMMRegToNaN(&mcontext.fpregs->_xmm[15]); break;
-          default: MOZ_CRASH();
-        }
-    } else {
-        switch (reg.gpr().code()) {
-          case JSC::X86Registers::eax: mcontext.gregs[REG_RAX] = 0; break;
-          case JSC::X86Registers::ecx: mcontext.gregs[REG_RCX] = 0; break;
-          case JSC::X86Registers::edx: mcontext.gregs[REG_RDX] = 0; break;
-          case JSC::X86Registers::ebx: mcontext.gregs[REG_RBX] = 0; break;
-          case JSC::X86Registers::esp: mcontext.gregs[REG_RSP] = 0; break;
-          case JSC::X86Registers::ebp: mcontext.gregs[REG_RBP] = 0; break;
-          case JSC::X86Registers::esi: mcontext.gregs[REG_RSI] = 0; break;
-          case JSC::X86Registers::edi: mcontext.gregs[REG_RDI] = 0; break;
-          case JSC::X86Registers::r8:  mcontext.gregs[REG_R8]  = 0; break;
-          case JSC::X86Registers::r9:  mcontext.gregs[REG_R9]  = 0; break;
-          case JSC::X86Registers::r10: mcontext.gregs[REG_R10] = 0; break;
-          case JSC::X86Registers::r11: mcontext.gregs[REG_R11] = 0; break;
-          case JSC::X86Registers::r12: mcontext.gregs[REG_R12] = 0; break;
-          case JSC::X86Registers::r13: mcontext.gregs[REG_R13] = 0; break;
-          case JSC::X86Registers::r14: mcontext.gregs[REG_R14] = 0; break;
-          case JSC::X86Registers::r15: mcontext.gregs[REG_R15] = 0; break;
-          default: MOZ_CRASH();
-        }
-    }
-# else
-#  error "TODO"
-# endif
-}
-
-#elif defined(XP_MACOSX)
-
-static const int SignalCode = SIGBUS;
-
-static uint8_t **
-ContextToPC(mcontext_t mcontext)
-{
-    JS_STATIC_ASSERT(sizeof(mcontext->__ss.__rip) == 8);
-    return reinterpret_cast<uint8_t **>(&mcontext->__ss.__rip);
-}
-
-static void
-SetRegisterToCoercedUndefined(mcontext_t &mcontext, AnyRegister reg)
-{
-    if (reg.isFloat()) {
-        switch (reg.fpu().code()) {
-          case JSC::X86Registers::xmm0:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm0); break;
-          case JSC::X86Registers::xmm1:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm1); break;
-          case JSC::X86Registers::xmm2:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm2); break;
-          case JSC::X86Registers::xmm3:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm3); break;
-          case JSC::X86Registers::xmm4:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm4); break;
-          case JSC::X86Registers::xmm5:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm5); break;
-          case JSC::X86Registers::xmm6:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm6); break;
-          case JSC::X86Registers::xmm7:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm7); break;
-          case JSC::X86Registers::xmm8:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm8); break;
-          case JSC::X86Registers::xmm9:  SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm9); break;
-          case JSC::X86Registers::xmm10: SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm10); break;
-          case JSC::X86Registers::xmm11: SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm11); break;
-          case JSC::X86Registers::xmm12: SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm12); break;
-          case JSC::X86Registers::xmm13: SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm13); break;
-          case JSC::X86Registers::xmm14: SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm14); break;
-          case JSC::X86Registers::xmm15: SetXMMRegToNaN(&mcontext->__fs.__fpu_xmm15); break;
-          default: MOZ_CRASH();
-        }
-    } else {
-        switch (reg.gpr().code()) {
-          case JSC::X86Registers::eax: mcontext->__ss.__rax = 0; break;
-          case JSC::X86Registers::ecx: mcontext->__ss.__rcx = 0; break;
-          case JSC::X86Registers::edx: mcontext->__ss.__rdx = 0; break;
-          case JSC::X86Registers::ebx: mcontext->__ss.__rbx = 0; break;
-          case JSC::X86Registers::esp: mcontext->__ss.__rsp = 0; break;
-          case JSC::X86Registers::ebp: mcontext->__ss.__rbp = 0; break;
-          case JSC::X86Registers::esi: mcontext->__ss.__rsi = 0; break;
-          case JSC::X86Registers::edi: mcontext->__ss.__rdi = 0; break;
-          case JSC::X86Registers::r8:  mcontext->__ss.__r8  = 0; break;
-          case JSC::X86Registers::r9:  mcontext->__ss.__r9  = 0; break;
-          case JSC::X86Registers::r10: mcontext->__ss.__r10 = 0; break;
-          case JSC::X86Registers::r11: mcontext->__ss.__r11 = 0; break;
-          case JSC::X86Registers::r12: mcontext->__ss.__r12 = 0; break;
-          case JSC::X86Registers::r13: mcontext->__ss.__r13 = 0; break;
-          case JSC::X86Registers::r14: mcontext->__ss.__r14 = 0; break;
-          case JSC::X86Registers::r15: mcontext->__ss.__r15 = 0; break;
-          default: MOZ_CRASH();
-        }
-    }
-}
-
-#endif
-
-struct sigaction prevSigAction;
-
 static bool
 PCIsInModule(const AsmJSModule &module, void *pc)
 {
     uint8_t *code = module.functionCode();
     return pc >= code && pc < (code + module.functionBytes());
 }
 
 // Perform a binary search on the projected offsets of the known heap accesses
@@ -242,51 +126,314 @@ LookupHeapAccess(const AsmJSModule &modu
     if (targetOffset == module.heapAccess(low).offset())
         return &module.heapAccess(low);
     if (targetOffset == module.heapAccess(high).offset())
         return &module.heapAccess(high);
 
     return NULL;
 }
 
+#if defined(XP_WIN)
+# include "jswin.h"
+
+static uint8_t **
+ContextToPC(PCONTEXT context)
+{
+#  if defined(JS_CPU_X64)
+    JS_STATIC_ASSERT(sizeof(context->Rip) == 8);
+    return reinterpret_cast<uint8_t**>(&context->Rip);
+#  else
+#   error "TODO"
+#  endif
+}
+
+static void
+SetRegisterToCoercedUndefined(CONTEXT *context, AnyRegister reg)
+{
+#  if defined(JS_CPU_X64)
+    if (reg.isFloat()) {
+        switch (reg.fpu().code()) {
+          case JSC::X86Registers::xmm0:  SetXMMRegToNaN(&context->Xmm0); break;
+          case JSC::X86Registers::xmm1:  SetXMMRegToNaN(&context->Xmm1); break;
+          case JSC::X86Registers::xmm2:  SetXMMRegToNaN(&context->Xmm2); break;
+          case JSC::X86Registers::xmm3:  SetXMMRegToNaN(&context->Xmm3); break;
+          case JSC::X86Registers::xmm4:  SetXMMRegToNaN(&context->Xmm4); break;
+          case JSC::X86Registers::xmm5:  SetXMMRegToNaN(&context->Xmm5); break;
+          case JSC::X86Registers::xmm6:  SetXMMRegToNaN(&context->Xmm6); break;
+          case JSC::X86Registers::xmm7:  SetXMMRegToNaN(&context->Xmm7); break;
+          case JSC::X86Registers::xmm8:  SetXMMRegToNaN(&context->Xmm8); break;
+          case JSC::X86Registers::xmm9:  SetXMMRegToNaN(&context->Xmm9); break;
+          case JSC::X86Registers::xmm10: SetXMMRegToNaN(&context->Xmm10); break;
+          case JSC::X86Registers::xmm11: SetXMMRegToNaN(&context->Xmm11); break;
+          case JSC::X86Registers::xmm12: SetXMMRegToNaN(&context->Xmm12); break;
+          case JSC::X86Registers::xmm13: SetXMMRegToNaN(&context->Xmm13); break;
+          case JSC::X86Registers::xmm14: SetXMMRegToNaN(&context->Xmm14); break;
+          case JSC::X86Registers::xmm15: SetXMMRegToNaN(&context->Xmm15); break;
+          default: MOZ_CRASH();
+        }
+    } else {
+        switch (reg.gpr().code()) {
+          case JSC::X86Registers::eax: context->Rax = 0; break;
+          case JSC::X86Registers::ecx: context->Rcx = 0; break;
+          case JSC::X86Registers::edx: context->Rdx = 0; break;
+          case JSC::X86Registers::ebx: context->Rbx = 0; break;
+          case JSC::X86Registers::esp: context->Rsp = 0; break;
+          case JSC::X86Registers::ebp: context->Rbp = 0; break;
+          case JSC::X86Registers::esi: context->Rsi = 0; break;
+          case JSC::X86Registers::edi: context->Rdi = 0; break;
+          case JSC::X86Registers::r8:  context->R8  = 0; break;
+          case JSC::X86Registers::r9:  context->R9  = 0; break;
+          case JSC::X86Registers::r10: context->R10 = 0; break;
+          case JSC::X86Registers::r11: context->R11 = 0; break;
+          case JSC::X86Registers::r12: context->R12 = 0; break;
+          case JSC::X86Registers::r13: context->R13 = 0; break;
+          case JSC::X86Registers::r14: context->R14 = 0; break;
+          case JSC::X86Registers::r15: context->R15 = 0; break;
+          default: MOZ_CRASH();
+        }
+    }
+#  else
+#   error "TODO"
+#  endif
+}
+
+static bool
+HandleException(PEXCEPTION_POINTERS exception)
+{
+    EXCEPTION_RECORD *record = exception->ExceptionRecord;
+    CONTEXT *context = exception->ContextRecord;
+
+    if (record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
+        return false;
+
+    AsmJSActivation *activation = InnermostAsmJSActivation();
+    if (!activation)
+        return false;
+
+    uint8_t **ppc = ContextToPC(context);
+    uint8_t *pc = *ppc;
+	JS_ASSERT(pc == record->ExceptionAddress);
+
+    const AsmJSModule &module = activation->module();
+    if (!PCIsInModule(module, pc))
+        return false;
+
+	if (record->NumberParameters < 2)
+		return false;
+
+    void *faultingAddress = (void*)record->ExceptionInformation[1];
+
+    // If we faulted trying to execute code in 'module', this must be an
+    // operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect
+    // execution to a trampoline which will call js_HandleExecutionInterrupt.
+    // The trampoline will jump to activation->resumePC if execution isn't
+    // interrupted.
+    if (PCIsInModule(module, faultingAddress)) {
+        activation->setResumePC(pc);
+        *ppc = module.operationCallbackExit();
+        DWORD oldProtect;
+        if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_EXECUTE, &oldProtect))
+            MOZ_CRASH();
+        return true;
+    }
+
+#ifdef JS_CPU_X64
+    // This isn't necessary, but, since we can, include this extra layer of
+    // checking to make sure we aren't covering up a real bug.
+    if (faultingAddress < activation->heap() || faultingAddress >= activation->heap() + FourGiB)
+        return false;
+#endif
+
+    const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc);
+    if (!heapAccess)
+        return false;
+
+    // Also not necessary, but, since we can, do.
+    if (heapAccess->isLoad() != !record->ExceptionInformation[0])
+        return false;
+
+    // We now know that this is an out-of-bounds access made by an asm.js
+    // load/store that we should handle. If this is a load, assign the
+    // JS-defined result value to the destination register (ToInt32(undefined)
+    // or ToNumber(undefined), determined by the type of the destination
+    // register) and set the PC to the next op. Upon return from the handler,
+    // execution will resume at this next PC.
+    if (heapAccess->isLoad())
+        SetRegisterToCoercedUndefined(context, heapAccess->loadedReg());
+    *ppc += heapAccess->opLength();
+    return true;
+}
+
+static LONG WINAPI
+AsmJSExceptionHandler(LPEXCEPTION_POINTERS exception)
+{
+    if (HandleException(exception))
+        return EXCEPTION_CONTINUE_EXECUTION;
+
+    // No need to worry about calling other handlers, the OS does this for us.
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
+#else  // If not Windows, assume Unix
+# include <signal.h>
+# include <sys/mman.h>
+
+// Unfortunately, we still need OS-specific code to read/write to the thread
+// state via the mcontext_t.
+# if defined(__linux__)
+static const int SignalCode = SIGSEGV;
+
+static uint8_t **
+ContextToPC(mcontext_t &context)
+{
+#  if defined(JS_CPU_X64)
+    JS_STATIC_ASSERT(sizeof(context.gregs[REG_RIP]) == 8);
+    return reinterpret_cast<uint8_t**>(&context.gregs[REG_RIP]);
+#  else
+#   error "TODO"
+#  endif
+}
+
+static void
+SetRegisterToCoercedUndefined(mcontext_t &context, AnyRegister reg)
+{
+#  if defined(JS_CPU_X64)
+    if (reg.isFloat()) {
+        switch (reg.fpu().code()) {
+          case JSC::X86Registers::xmm0:  SetXMMRegToNaN(&context.fpregs->_xmm[0]); break;
+          case JSC::X86Registers::xmm1:  SetXMMRegToNaN(&context.fpregs->_xmm[1]); break;
+          case JSC::X86Registers::xmm2:  SetXMMRegToNaN(&context.fpregs->_xmm[2]); break;
+          case JSC::X86Registers::xmm3:  SetXMMRegToNaN(&context.fpregs->_xmm[3]); break;
+          case JSC::X86Registers::xmm4:  SetXMMRegToNaN(&context.fpregs->_xmm[4]); break;
+          case JSC::X86Registers::xmm5:  SetXMMRegToNaN(&context.fpregs->_xmm[5]); break;
+          case JSC::X86Registers::xmm6:  SetXMMRegToNaN(&context.fpregs->_xmm[6]); break;
+          case JSC::X86Registers::xmm7:  SetXMMRegToNaN(&context.fpregs->_xmm[7]); break;
+          case JSC::X86Registers::xmm8:  SetXMMRegToNaN(&context.fpregs->_xmm[8]); break;
+          case JSC::X86Registers::xmm9:  SetXMMRegToNaN(&context.fpregs->_xmm[9]); break;
+          case JSC::X86Registers::xmm10: SetXMMRegToNaN(&context.fpregs->_xmm[10]); break;
+          case JSC::X86Registers::xmm11: SetXMMRegToNaN(&context.fpregs->_xmm[11]); break;
+          case JSC::X86Registers::xmm12: SetXMMRegToNaN(&context.fpregs->_xmm[12]); break;
+          case JSC::X86Registers::xmm13: SetXMMRegToNaN(&context.fpregs->_xmm[13]); break;
+          case JSC::X86Registers::xmm14: SetXMMRegToNaN(&context.fpregs->_xmm[14]); break;
+          case JSC::X86Registers::xmm15: SetXMMRegToNaN(&context.fpregs->_xmm[15]); break;
+          default: MOZ_CRASH();
+        }
+    } else {
+        switch (reg.gpr().code()) {
+          case JSC::X86Registers::eax: context.gregs[REG_RAX] = 0; break;
+          case JSC::X86Registers::ecx: context.gregs[REG_RCX] = 0; break;
+          case JSC::X86Registers::edx: context.gregs[REG_RDX] = 0; break;
+          case JSC::X86Registers::ebx: context.gregs[REG_RBX] = 0; break;
+          case JSC::X86Registers::esp: context.gregs[REG_RSP] = 0; break;
+          case JSC::X86Registers::ebp: context.gregs[REG_RBP] = 0; break;
+          case JSC::X86Registers::esi: context.gregs[REG_RSI] = 0; break;
+          case JSC::X86Registers::edi: context.gregs[REG_RDI] = 0; break;
+          case JSC::X86Registers::r8:  context.gregs[REG_R8]  = 0; break;
+          case JSC::X86Registers::r9:  context.gregs[REG_R9]  = 0; break;
+          case JSC::X86Registers::r10: context.gregs[REG_R10] = 0; break;
+          case JSC::X86Registers::r11: context.gregs[REG_R11] = 0; break;
+          case JSC::X86Registers::r12: context.gregs[REG_R12] = 0; break;
+          case JSC::X86Registers::r13: context.gregs[REG_R13] = 0; break;
+          case JSC::X86Registers::r14: context.gregs[REG_R14] = 0; break;
+          case JSC::X86Registers::r15: context.gregs[REG_R15] = 0; break;
+          default: MOZ_CRASH();
+        }
+    }
+#  else
+#   error "TODO"
+#  endif
+}
+# elif defined(XP_MACOSX)
+static const int SignalCode = SIGBUS;
+
+static uint8_t **
+ContextToPC(mcontext_t context)
+{
+    JS_STATIC_ASSERT(sizeof(context->__ss.__rip) == 8);
+    return reinterpret_cast<uint8_t **>(&context->__ss.__rip);
+}
+
+static void
+SetRegisterToCoercedUndefined(mcontext_t &context, AnyRegister reg)
+{
+    if (reg.isFloat()) {
+        switch (reg.fpu().code()) {
+          case JSC::X86Registers::xmm0:  SetXMMRegToNaN(&context->__fs.__fpu_xmm0); break;
+          case JSC::X86Registers::xmm1:  SetXMMRegToNaN(&context->__fs.__fpu_xmm1); break;
+          case JSC::X86Registers::xmm2:  SetXMMRegToNaN(&context->__fs.__fpu_xmm2); break;
+          case JSC::X86Registers::xmm3:  SetXMMRegToNaN(&context->__fs.__fpu_xmm3); break;
+          case JSC::X86Registers::xmm4:  SetXMMRegToNaN(&context->__fs.__fpu_xmm4); break;
+          case JSC::X86Registers::xmm5:  SetXMMRegToNaN(&context->__fs.__fpu_xmm5); break;
+          case JSC::X86Registers::xmm6:  SetXMMRegToNaN(&context->__fs.__fpu_xmm6); break;
+          case JSC::X86Registers::xmm7:  SetXMMRegToNaN(&context->__fs.__fpu_xmm7); break;
+          case JSC::X86Registers::xmm8:  SetXMMRegToNaN(&context->__fs.__fpu_xmm8); break;
+          case JSC::X86Registers::xmm9:  SetXMMRegToNaN(&context->__fs.__fpu_xmm9); break;
+          case JSC::X86Registers::xmm10: SetXMMRegToNaN(&context->__fs.__fpu_xmm10); break;
+          case JSC::X86Registers::xmm11: SetXMMRegToNaN(&context->__fs.__fpu_xmm11); break;
+          case JSC::X86Registers::xmm12: SetXMMRegToNaN(&context->__fs.__fpu_xmm12); break;
+          case JSC::X86Registers::xmm13: SetXMMRegToNaN(&context->__fs.__fpu_xmm13); break;
+          case JSC::X86Registers::xmm14: SetXMMRegToNaN(&context->__fs.__fpu_xmm14); break;
+          case JSC::X86Registers::xmm15: SetXMMRegToNaN(&context->__fs.__fpu_xmm15); break;
+          default: MOZ_CRASH();
+        }
+    } else {
+        switch (reg.gpr().code()) {
+          case JSC::X86Registers::eax: context->__ss.__rax = 0; break;
+          case JSC::X86Registers::ecx: context->__ss.__rcx = 0; break;
+          case JSC::X86Registers::edx: context->__ss.__rdx = 0; break;
+          case JSC::X86Registers::ebx: context->__ss.__rbx = 0; break;
+          case JSC::X86Registers::esp: context->__ss.__rsp = 0; break;
+          case JSC::X86Registers::ebp: context->__ss.__rbp = 0; break;
+          case JSC::X86Registers::esi: context->__ss.__rsi = 0; break;
+          case JSC::X86Registers::edi: context->__ss.__rdi = 0; break;
+          case JSC::X86Registers::r8:  context->__ss.__r8  = 0; break;
+          case JSC::X86Registers::r9:  context->__ss.__r9  = 0; break;
+          case JSC::X86Registers::r10: context->__ss.__r10 = 0; break;
+          case JSC::X86Registers::r11: context->__ss.__r11 = 0; break;
+          case JSC::X86Registers::r12: context->__ss.__r12 = 0; break;
+          case JSC::X86Registers::r13: context->__ss.__r13 = 0; break;
+          case JSC::X86Registers::r14: context->__ss.__r14 = 0; break;
+          case JSC::X86Registers::r15: context->__ss.__r15 = 0; break;
+          default: MOZ_CRASH();
+        }
+    }
+}
+# endif  // end of OS-specific mcontext accessors
+
 // Be very cautious and default to not handling; we don't want to accidentally
 // silence real crashes from real bugs.
 static bool
-HandleSignal(int signum, siginfo_t *info, void *context)
+HandleSignal(int signum, siginfo_t *info, void *ctx)
 {
     if (signum != SignalCode)
         return false;
-    
-    PerThreadData *threadData = TlsPerThreadData.get();
-    if (!threadData)
-        return false;
 
-    AsmJSActivation *activation = threadData->asmJSActivationStackFromOwnerThread();
+    AsmJSActivation *activation = InnermostAsmJSActivation();
     if (!activation)
         return false;
 
-    mcontext_t &mcontext = reinterpret_cast<ucontext_t*>(context)->uc_mcontext;
-    uint8_t **ppc = ContextToPC(mcontext);
+    mcontext_t &context = reinterpret_cast<ucontext_t*>(ctx)->uc_mcontext;
+    uint8_t **ppc = ContextToPC(context);
     uint8_t *pc = *ppc;
 
     const AsmJSModule &module = activation->module();
     if (!PCIsInModule(module, pc))
         return false;
 
     void *faultingAddress = info->si_addr;
 
     // If we faulted trying to execute code in 'module', this must be an
     // operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect
     // execution to a trampoline which will call js_HandleExecutionInterrupt.
     // The trampoline will jump to activation->resumePC if execution isn't
     // interrupted.
     if (PCIsInModule(module, faultingAddress)) {
         activation->setResumePC(pc);
         *ppc = module.operationCallbackExit();
-        mprotect(module.functionCode(), module.functionBytes(), PROT_READ | PROT_WRITE | PROT_EXEC);
+        mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC);
         return true;
     }
 
 #ifdef JS_CPU_X64
     // This isn't necessary, but, since we can, include this extra layer of
     // checking to make sure we aren't covering up a real bug.
     if (faultingAddress < activation->heap() || faultingAddress >= activation->heap() + FourGiB)
         return false;
@@ -298,58 +445,66 @@ HandleSignal(int signum, siginfo_t *info
 
     // We now know that this is an out-of-bounds access made by an asm.js
     // load/store that we should handle. If this is a load, assign the
     // JS-defined result value to the destination register (ToInt32(undefined)
     // or ToNumber(undefined), determined by the type of the destination
     // register) and set the PC to the next op. Upon return from the handler,
     // execution will resume at this next PC.
     if (heapAccess->isLoad())
-        SetRegisterToCoercedUndefined(mcontext, heapAccess->loadedReg());
+        SetRegisterToCoercedUndefined(context, heapAccess->loadedReg());
     *ppc += heapAccess->opLength();
     return true;
 }
 
+static struct sigaction sPrevHandler;
+
 static void
 AsmJSMemoryFaultHandler(int signum, siginfo_t *info, void *context)
 {
     if (HandleSignal(signum, info, context))
         return;
 
     // This signal is not for any asm.js code we expect, so we need to forward
     // the signal to the next handler. If there is no next handler (SIG_IGN or
     // SIG_DFL), then it's time to crash. To do this, we set the signal back to
     // it's previous disposition and return. This will cause the faulting op to
     // be re-executed which will crash in the normal way. The advantage to
     // doing this is that we remove ourselves from the crash stack which
     // simplifies crash reports. Note: the order of these tests matter.
-    if (prevSigAction.sa_flags & SA_SIGINFO) {
-        prevSigAction.sa_sigaction(signum, info, context);
+    if (sPrevHandler.sa_flags & SA_SIGINFO) {
+        sPrevHandler.sa_sigaction(signum, info, context);
         exit(signum);  // backstop
-    } else if (prevSigAction.sa_handler == SIG_DFL || prevSigAction.sa_handler == SIG_IGN) {
-        sigaction(SignalCode, &prevSigAction, NULL);
+    } else if (sPrevHandler.sa_handler == SIG_DFL || sPrevHandler.sa_handler == SIG_IGN) {
+        sigaction(SignalCode, &sPrevHandler, NULL);
     } else {
-        prevSigAction.sa_handler(signum);
+        sPrevHandler.sa_handler(signum);
         exit(signum);  // backstop
     }
 }
+#endif
 
 bool
 js::EnsureAsmJSSignalHandlersInstalled()
 {
     SignalMutex::Lock lock;
     if (lock.handlersInstalled())
         return true;
 
+#if defined(XP_WIN)
+    if (!AddVectoredExceptionHandler(/* FirstHandler = */true, AsmJSExceptionHandler))
+        return false;
+#else
     struct sigaction sigAction;
     sigAction.sa_sigaction = &AsmJSMemoryFaultHandler;
     sigemptyset(&sigAction.sa_mask);
     sigAction.sa_flags = SA_SIGINFO;
-    if (sigaction(SignalCode, &sigAction, &prevSigAction))
+    if (sigaction(SignalCode, &sigAction, &sPrevHandler))
         return false;
+#endif
 
     lock.setHandlersInstalled();
     return true;
 }
 
 // To interrupt execution of a JSRuntime, any thread may call
 // JS_TriggerOperationCallback (JSRuntime::triggerOperationCallback from inside
 // the engine). Normally, this sets some state that is polled at regular
@@ -365,11 +520,18 @@ js::TriggerOperationCallbackForAsmJSCode
 {
     PerThreadData::AsmJSActivationStackLock lock(rt->mainThread);
 
     AsmJSActivation *activation = rt->mainThread.asmJSActivationStackFromAnyThread();
     if (!activation)
         return;
 
     const AsmJSModule &module = activation->module();
+
+#if defined(XP_WIN)
+    DWORD oldProtect;
+    if (!VirtualProtect(module.functionCode(), 4096, PAGE_NOACCESS, &oldProtect))
+        MOZ_CRASH();
+#else
     if (mprotect(module.functionCode(), module.functionBytes(), PROT_NONE))
         MOZ_CRASH();
+#endif
 }
--- a/js/src/ion/x64/Assembler-x64.cpp
+++ b/js/src/ion/x64/Assembler-x64.cpp
@@ -10,52 +10,75 @@
 #include "ion/LIR.h"
 
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 ABIArgGenerator::ABIArgGenerator()
-  : intRegIndex_(0),
+  : 
+#if defined(XP_WIN)
+    regIndex_(0),
+    stackOffset_(ShadowSpaceSize),
+#else
+    intRegIndex_(0),
     floatRegIndex_(0),
     stackOffset_(0),
+#endif
     current_()
 {}
 
 ABIArg
 ABIArgGenerator::next(MIRType type)
 {
-#if defined(_WIN64)
-# error "ABI is different on win64"
-#else
+#if defined(XP_WIN)
+    JS_STATIC_ASSERT(NumIntArgRegs == NumFloatArgRegs);
+    if (regIndex_ == NumIntArgRegs) {
+        current_ = ABIArg(stackOffset_);
+        stackOffset_ += sizeof(uint64_t);
+        return current_;
+    }
+    switch (type) {
+      case MIRType_Int32:
+      case MIRType_Pointer:
+        current_ = ABIArg(IntArgRegs[regIndex_++]);
+        break;
+      case MIRType_Double:
+        current_ = ABIArg(FloatArgRegs[regIndex_++]);
+        break;
+      default:
+        JS_NOT_REACHED("Unexpected argument type");
+    }
+    return current_;
+#elif defined(XP_MACOSX) || defined(__linux__)
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
-        current_ = ABIArg(IntArgRegs[intRegIndex_]);
-        intRegIndex_++;
+        current_ = ABIArg(IntArgRegs[intRegIndex_++]);
         break;
       case MIRType_Double:
         if (floatRegIndex_ == NumFloatArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
-        current_ = ABIArg(FloatArgRegs[floatRegIndex_]);
-        floatRegIndex_++;
+        current_ = ABIArg(FloatArgRegs[floatRegIndex_++]);
         break;
       default:
         JS_NOT_REACHED("Unexpected argument type");
     }
     return current_;
+#else
+# error "Missing ABI"
 #endif
 }
 
 void
 Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc)
 {
     if (!jumpRelocations_.length()) {
         // The jump relocation table starts with a fixed-width integer pointing
--- a/js/src/ion/x64/Assembler-x64.h
+++ b/js/src/ion/x64/Assembler-x64.h
@@ -109,18 +109,16 @@ static const Register IntArgReg0 = rdi;
 static const Register IntArgReg1 = rsi;
 static const Register IntArgReg2 = rdx;
 static const Register IntArgReg3 = rcx;
 static const Register IntArgReg4 = r8;
 static const Register IntArgReg5 = r9;
 static const uint32_t NumIntArgRegs = 6;
 static const Register IntArgRegs[NumIntArgRegs] = { rdi, rsi, rdx, rcx, r8, r9 };
 
-static const Register SomeNonVolatileReg = rbx;
-
 static const Register CallTempNonArgRegs[] = { rax, rbx };
 static const uint32_t NumCallTempNonArgRegs =
     mozilla::ArrayLength(CallTempNonArgRegs);
 
 static const FloatRegister FloatArgReg0 = xmm0;
 static const FloatRegister FloatArgReg1 = xmm1;
 static const FloatRegister FloatArgReg2 = xmm2;
 static const FloatRegister FloatArgReg3 = xmm3;
@@ -129,18 +127,22 @@ static const FloatRegister FloatArgReg5 
 static const FloatRegister FloatArgReg6 = xmm6;
 static const FloatRegister FloatArgReg7 = xmm7;
 static const uint32_t NumFloatArgRegs = 8;
 static const FloatRegister FloatArgRegs[NumFloatArgRegs] = { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 };
 #endif
 
 class ABIArgGenerator
 {
+#if defined(XP_WIN)
+    unsigned regIndex_;
+#else
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
+#endif
     uint32_t stackOffset_;
     ABIArg current_;
 
   public:
     ABIArgGenerator();
     ABIArg next(MIRType argType);
     ABIArg &current() { return current_; }
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -38,17 +38,16 @@
 #include "jstypedarrayinlines.h"
 
 #include "vm/GlobalObject-inl.h"
 
 /* Includes to get to low-level memory-mapping functionality. */
 #ifdef XP_WIN
 # include "jswin.h"
 #else
-# include <unistd.h>
 # include <sys/mman.h>
 #endif
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::DebugOnly;
@@ -354,63 +353,104 @@ JS_STATIC_ASSERT(sizeof(ObjectElements) 
 static const size_t AsmJSBufferReservedLength = FourGiB + 2 * PageSize;
 
 bool
 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
 {
     if (buffer->isAsmJSArrayBuffer())
         return true;
 
-    void *p = mmap(NULL, AsmJSBufferReservedLength, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+    // Get the entire reserved region (with all pages inaccessible).
+    void *p;
+#ifdef XP_WIN
+    p = VirtualAlloc(NULL, AsmJSBufferReservedLength, MEM_RESERVE, PAGE_NOACCESS);
+    if (!p)
+        return false;
+#else
+    p = mmap(NULL, AsmJSBufferReservedLength, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
     if (p == MAP_FAILED)
         return false;
-
+#endif
+
+    // Compute the valid region of the reserved memory, aligning the base address
+    // of the elements with 'padding' bytes to ensure that the end of the
+    // elements array lines up exactly with a page boundary.
     JS_ASSERT(uintptr_t(p) % PageSize == 0);
     uint8_t *begin = reinterpret_cast<uint8_t*>(p);
     uint32_t padding = ComputeByteAlignment(buffer->byteLength(), PageSize);
-    uint8_t *data = begin + PageSize + padding;
-    ObjectElements *newHeader = reinterpret_cast<ObjectElements*>(data - sizeof(ObjectElements));
-
     size_t validLength = PageSize + padding + buffer->byteLength();
     JS_ASSERT(validLength % PageSize == 0);
+
+    // Enable access to the valid region.
+#ifdef XP_WIN
+    if (!VirtualAlloc(begin, validLength, MEM_COMMIT, PAGE_READWRITE))
+        return false;
+#else
     if (mprotect(begin, validLength, PROT_READ | PROT_WRITE))
         return false;
-
+#endif
+
+    // Copy over the current contents of the typed array.
+    uint8_t *data = begin + PageSize + padding;
     memcpy(data, buffer->dataPointer(), buffer->byteLength());
 
+    // Swap the new elements into the ArrayBufferObject.
+    ObjectElements *newHeader = reinterpret_cast<ObjectElements*>(data - sizeof(ObjectElements));
     ObjectElements *oldHeader = buffer->hasDynamicElements() ? buffer->getElementsHeader() : NULL;
     buffer->changeContents(cx, newHeader);
     js_free(oldHeader);
 
+    // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
+    // to js_free the header in the normal way.
     newHeader->setIsAsmJSArrayBuffer();
     JS_ASSERT(data == buffer->dataPointer());
     return true;
 }
 
 void
 ArrayBufferObject::neuterAsmJSArrayBuffer(ArrayBufferObject &buffer)
 {
     JS_ASSERT(buffer.isAsmJSArrayBuffer());
+
+    // TODO: need to actually allow this to be tested from the shell. roughly,
+    // we want to mprotect [buffer.dataPointer(), buffer.byteLength()).
+    // However, this region is not page length.  Instead, shift the
+    // ObjectElements header down so that it lies right before the page boundary
+    // and then mprotect the (now-aligned) region:
+    //   [newDataPointer, oldDataPointer+byteLength)
+    MOZ_CRASH();
+
+#ifdef XP_WIN
+    // Wrong:
+    if (!VirtualAlloc(buffer.dataPointer(), buffer.byteLength(), MEM_RESERVE, PAGE_NOACCESS))
+        MOZ_CRASH();
+#else
+    // Wrong:
     if (mprotect(buffer.dataPointer(), buffer.byteLength(), PROT_NONE))
         MOZ_CRASH();
+#endif
 }
 
 void
 ArrayBufferObject::releaseAsmJSArrayBuffer(RawObject obj)
 {
     ArrayBufferObject &buffer = obj->asArrayBuffer();
     JS_ASSERT(buffer.isAsmJSArrayBuffer());
 
     // See prepareForAsmJS.
     uint8_t *data = buffer.dataPointer();
     uint32_t padding = ComputeByteAlignment(buffer.byteLength(), PageSize);
     uint8_t *begin = data - padding - PageSize;
     JS_ASSERT(uintptr_t(begin) % PageSize == 0);
 
+#ifdef XP_WIN
+    VirtualAlloc(begin, AsmJSBufferReservedLength, MEM_RESERVE, PAGE_NOACCESS);
+#else
     munmap(begin, AsmJSBufferReservedLength);
+#endif
 }
 #endif
 
 #ifdef JSGC_GENERATIONAL
 class WeakObjectSlotRef : public js::gc::BufferableRef
 {
     JSObject *owner;
     size_t slot;