Bug 1420158 - rabaldr register targeting, Cleanup: move code and rename. r=bbouvier draft
authorLars T Hansen <lhansen@mozilla.com>
Mon, 04 Dec 2017 13:01:48 +0100
changeset 710305 bd35fe8a835196bb115153042dc73074e788acfc
parent 710304 32f78f25f9d9ea369489b7d9a260d29fa8d49859
child 710306 799d2ef374e44e11fcc78b2bf70741d1e3bba6e5
push id92813
push userbmo:lhansen@mozilla.com
push dateSat, 09 Dec 2017 13:50:46 +0000
reviewersbbouvier
bugs1420158
milestone59.0a1
Bug 1420158 - rabaldr register targeting, Cleanup: move code and rename. r=bbouvier This creates a new section in the file that sits between the low-level code generators (above) and the high-level logic and value stack manipulation (below). The intent is that this section will contain platform-specific helpers for register targeting when we pop the value stack. As explained in a new comment in the code, there are two types of helpers: simple popXForY methods, of which we've had many; and RAII wrappers. I've moved the existing popXForY methods into this section in this patch; the next patch introduces a number of RAII wrappers. MozReview-Commit-ID: Fq6TqgsFOkL
js/src/wasm/WasmBaselineCompile.cpp
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -3424,40 +3424,16 @@ class BaseCompiler final : public BaseCo
     }
 
     void returnCleanup(bool popStack) {
         if (popStack)
             fr.popStackBeforeBranch(controlOutermost().stackHeight);
         masm.jump(&returnLabel_);
     }
 
-    void pop2xI32ForIntMulDiv(RegI32* r0, RegI32* r1) {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        // srcDest must be eax, and edx will be clobbered.
-        need2xI32(specific.eax, specific.edx);
-        *r1 = popI32();
-        *r0 = popI32ToSpecific(specific.eax);
-        freeI32(specific.edx);
-#else
-        pop2xI32(r0, r1);
-#endif
-    }
-
-    void pop2xI64ForIntDiv(RegI64* r0, RegI64* r1) {
-#ifdef JS_CODEGEN_X64
-        // srcDest must be rax, and rdx will be clobbered.
-        need2xI64(specific.rax, specific.rdx);
-        *r1 = popI64();
-        *r0 = popI64ToSpecific(specific.rax);
-        freeI64(specific.rdx);
-#else
-        pop2xI64(r0, r1);
-#endif
-    }
-
     void checkDivideByZeroI32(RegI32 rhs, RegI32 srcDest, Label* done) {
         masm.branchTest32(Assembler::Zero, rhs, rhs, trap(Trap::IntegerDivideByZero));
     }
 
     void checkDivideByZeroI64(RegI64 r) {
         ScratchI32 scratch(*this);
         masm.branchTest64(Assembler::Zero, r, r, scratch, trap(Trap::IntegerDivideByZero));
     }
@@ -3484,95 +3460,75 @@ class BaseCompiler final : public BaseCo
             masm.jump(done);
         } else {
             masm.jump(trap(Trap::IntegerOverflow));
         }
         masm.bind(&notmin);
     }
 
 #ifndef RABALDR_INT_DIV_I64_CALLOUT
-    void quotientI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned,
+    void quotientI64(RegI64 rhs, RegI64 srcDest, RegI64 reserved, IsUnsigned isUnsigned,
                      bool isConst, int64_t c)
     {
         Label done;
 
         if (!isConst || c == 0)
             checkDivideByZeroI64(rhs);
 
         if (!isUnsigned && (!isConst || c == -1))
             checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
 
 # if defined(JS_CODEGEN_X64)
         // The caller must set up the following situation.
         MOZ_ASSERT(srcDest.reg == rax);
-        MOZ_ASSERT(isAvailableI64(specific.rdx));
+        MOZ_ASSERT(reserved == specific.rdx);
         if (isUnsigned) {
             masm.xorq(rdx, rdx);
             masm.udivq(rhs.reg);
         } else {
             masm.cqo();
             masm.idivq(rhs.reg);
         }
 # else
         MOZ_CRASH("BaseCompiler platform hook: quotientI64");
 # endif
         masm.bind(&done);
     }
 
-    void remainderI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned,
+    void remainderI64(RegI64 rhs, RegI64 srcDest, RegI64 reserved, IsUnsigned isUnsigned,
                       bool isConst, int64_t c)
     {
         Label done;
 
         if (!isConst || c == 0)
             checkDivideByZeroI64(rhs);
 
         if (!isUnsigned && (!isConst || c == -1))
             checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
 
 # if defined(JS_CODEGEN_X64)
         // The caller must set up the following situation.
         MOZ_ASSERT(srcDest.reg == rax);
-        MOZ_ASSERT(isAvailableI64(specific.rdx));
+        MOZ_ASSERT(reserved == specific.rdx);
 
         if (isUnsigned) {
             masm.xorq(rdx, rdx);
             masm.udivq(rhs.reg);
         } else {
             masm.cqo();
             masm.idivq(rhs.reg);
         }
         masm.movq(rdx, rax);
 # else
         MOZ_CRASH("BaseCompiler platform hook: remainderI64");
 # endif
         masm.bind(&done);
     }
 #endif // RABALDR_INT_DIV_I64_CALLOUT
 
-    void pop2xI32ForShiftOrRotate(RegI32* r0, RegI32* r1) {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        *r1 = popI32(specific.ecx);
-        *r0 = popI32();
-#else
-        pop2xI32(r0, r1);
-#endif
-    }
-
-    void pop2xI64ForShiftOrRotate(RegI64* r0, RegI64* r1) {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-        needI32(specific.ecx);
-        *r1 = widenI32(specific.ecx);
-        *r1 = popI64ToSpecific(*r1);
-        *r0 = popI64();
-#else
-        pop2xI64(r0, r1);
-#endif
-    }
-
     RegI32 needRotate64Temp() {
 #if defined(JS_CODEGEN_X86)
         return needI32();
 #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM)
         return RegI32::Invalid();
 #else
         MOZ_CRASH("BaseCompiler platform hook: needRotate64Temp");
 #endif
@@ -3599,39 +3555,16 @@ class BaseCompiler final : public BaseCo
         return AssemblerX86Shared::HasPOPCNT() ? RegI32::Invalid() : needI32();
 #elif defined(JS_CODEGEN_ARM)
         return needI32();
 #else
         MOZ_CRASH("BaseCompiler platform hook: needPopcnt64Temp");
 #endif
     }
 
-    RegI64 popI32ForSignExtendI64() {
-#if defined(JS_CODEGEN_X86)
-        need2xI32(specific.edx, specific.eax);
-        RegI32 r0 = popI32ToSpecific(specific.eax);
-        RegI64 x0 = specific.edx_eax;
-        (void)r0;               // x0 is the widening of r0
-#else
-        RegI32 r0 = popI32();
-        RegI64 x0 = widenI32(r0);
-#endif
-        return x0;
-    }
-
-    RegI64 popI64ForSignExtendI64() {
-#if defined(JS_CODEGEN_X86)
-        need2xI32(specific.edx, specific.eax);
-        // Low on top, high underneath
-        return popI64ToSpecific(specific.edx_eax);
-#else
-        return popI64();
-#endif
-    }
-
     class OutOfLineTruncateF32OrF64ToI32 : public OutOfLineCode
     {
         AnyReg src;
         RegI32 dest;
         bool isUnsigned;
         BytecodeOffset off;
 
       public:
@@ -4205,61 +4138,16 @@ class BaseCompiler final : public BaseCo
 #else
 
 # define ATOMIC_PTR(name, access, tls, ptr)                       \
     MOZ_CRASH("BaseCompiler platform hook: address computation"); \
     Address srcAddr
 
 #endif
 
-    void xchg64(MemoryAccessDesc* access, ValType type, WantResult wantResult)
-    {
-#if defined(JS_CODEGEN_X86)
-        RegI64 rd = specific.edx_eax;
-        needI64(rd);
-        needI32(specific.ecx);
-        // Claim scratch after the need() calls because they may need it to
-        // sync.
-        ScratchEBX scratch(*this);
-        RegI64 rv = specific.ecx_ebx;
-#elif defined(JS_CODEGEN_ARM)
-        RegI64 rv = needI64Pair();
-        RegI64 rd = needI64Pair();
-#else
-        RegI64 rv, rd;
-        MOZ_CRASH("BaseCompiler porting interface: xchg64");
-#endif
-
-        popI64ToSpecific(rv);
-
-        AccessCheck check;
-        RegI32 rp = popMemoryAccess(access, &check);
-        RegI32 tls = maybeLoadTlsForAccess(check);
-        prepareMemoryAccess(access, &check, tls, rp);
-        ATOMIC_PTR(srcAddr, access, tls, rp);
-
-        masm.atomicExchange64(srcAddr, rv, rd);
-
-        if (wantResult)
-            pushI64(rd);
-        else
-            freeI64(rd);
-
-        maybeFreeI32(tls);
-        freeI32(rp);
-
-#if defined(JS_CODEGEN_X86)
-        freeI32(specific.ecx);
-#elif defined(JS_CODEGEN_ARM)
-        freeI64(rv);
-#else
-        MOZ_CRASH("BaseCompiler porting interface: xchg64");
-#endif
-    }
-
     RegI32 needAtomicRMWTemp(AtomicOp op, MemoryAccessDesc* access) {
 #if defined(JS_CODEGEN_X86)
         // Handled specially in atomicRMW
         if (access->byteSize() == 1)
             return RegI32::Invalid();
 #endif
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         if (op != AtomicFetchAddOp && op != AtomicFetchSubOp)
@@ -4422,23 +4310,137 @@ class BaseCompiler final : public BaseCo
             masm.atomicExchange32(srcAddr, rv, rd);
             break;
           default:
             MOZ_CRASH("Bad type for atomic operation");
         }
     }
 
     ////////////////////////////////////////////////////////////
-
-    // Generally speaking, ABOVE this point there should be no value
-    // stack manipulation (calls to popI32 etc).
-
+    //
+    // Generally speaking, ABOVE this point there should be no
+    // value stack manipulation (calls to popI32 etc).
+    //
+    ////////////////////////////////////////////////////////////
+
+    ////////////////////////////////////////////////////////////
+    //
+    // Platform-specific popping and register targeting.
+    //
+    // These fall into two groups, popping methods for simple needs, and RAII
+    // wrappers for more complex behavior.
+
+    // The simple popping methods pop values into targeted registers; the caller
+    // can free registers using standard functions.  These are always called
+    // popXForY where X says something about types and Y something about the
+    // operation being targeted.
+
+    void pop2xI32ForMulDivI32(RegI32* r0, RegI32* r1, RegI32* reserved) {
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+        // r0 must be eax, and edx will be clobbered.
+        need2xI32(specific.eax, specific.edx);
+        *r1 = popI32();
+        *r0 = popI32ToSpecific(specific.eax);
+        *reserved = specific.edx;
+#else
+        pop2xI32(r0, r1);
+#endif
+    }
+
+    void pop2xI64ForMulI64(RegI64* r0, RegI64* r1, RegI32* temp, RegI64* reserved) {
+#if defined(JS_CODEGEN_X64)
+        // r0 must be rax, and rdx will be clobbered.
+        need2xI64(specific.rax, specific.rdx);
+        *r1 = popI64();
+        *r0 = popI64ToSpecific(specific.rax);
+        *reserved = specific.rdx;
+#elif defined(JS_CODEGEN_X86)
+        // As for x64, though edx is part of r0.
+        need2xI32(specific.eax, specific.edx);
+        *r1 = popI64();
+        *r0 = popI64ToSpecific(specific.edx_eax);
+        *temp = needI32();
+#else
+        pop2xI64(r0, r1);
+        *temp = needI32();
+#endif
+    }
+
+    void pop2xI64ForDivI64(RegI64* r0, RegI64* r1, RegI64* reserved) {
+#ifdef JS_CODEGEN_X64
+        // r0 must be rax, and rdx will be clobbered.
+        need2xI64(specific.rax, specific.rdx);
+        *r1 = popI64();
+        *r0 = popI64ToSpecific(specific.rax);
+        *reserved = specific.rdx;
+#else
+        pop2xI64(r0, r1);
+#endif
+    }
+
+    void pop2xI32ForShiftOrRotate(RegI32* r0, RegI32* r1) {
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+        // r1 must be ecx for a variable shift.
+        *r1 = popI32(specific.ecx);
+        *r0 = popI32();
+#else
+        pop2xI32(r0, r1);
+#endif
+    }
+
+    void pop2xI64ForShiftOrRotate(RegI64* r0, RegI64* r1) {
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+        // r1 must be ecx for a variable shift.
+        needI32(specific.ecx);
+        *r1 = popI64ToSpecific(widenI32(specific.ecx));
+        *r0 = popI64();
+#else
+        pop2xI64(r0, r1);
+#endif
+    }
+
+    void popI32ForSignExtendI64(RegI64* r0) {
+#if defined(JS_CODEGEN_X86)
+        // r0 must be edx:eax for cdq
+        need2xI32(specific.edx, specific.eax);
+        *r0 = specific.edx_eax;
+        popI32ToSpecific(specific.eax);
+#else
+        *r0 = widenI32(popI32());
+#endif
+    }
+
+    void popI64ForSignExtendI64(RegI64* r0) {
+#if defined(JS_CODEGEN_X86)
+        // r0 must be edx:eax for cdq
+        need2xI32(specific.edx, specific.eax);
+        // Low on top, high underneath
+        *r0 = popI64ToSpecific(specific.edx_eax);
+#else
+        *r0 = popI64();
+#endif
+    }
+
+    // The RAII wrappers are used because we sometimes have to free partial
+    // registers, as when part of a register is the scratch register that has
+    // been temporarily used, or not free a register at all, as when the
+    // register is the same as the destination register (but only on some
+    // platforms, not on all).  These are called PopX{32,64}Regs where X is the
+    // operation being targeted.
+
+    // (To be implemented)
+
+    ////////////////////////////////////////////////////////////
+    //
     // Generally speaking, BELOW this point there should be no
-    // platform dependencies.  We make an exception for x86 register
-    // targeting, which is not too hard to keep clean.
+    // platform dependencies.  We make very occasional exceptions
+    // when it doesn't become messy and further abstraction is
+    // not desirable.
+    //
+    ////////////////////////////////////////////////////////////
 
     ////////////////////////////////////////////////////////////
     //
     // Sundry wrappers.
 
     void pop2xI32(RegI32* r0, RegI32* r1) {
         *r1 = popI32();
         *r0 = popI32();
@@ -4805,16 +4807,19 @@ class BaseCompiler final : public BaseCo
 
     MOZ_MUST_USE bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType);
     MOZ_MUST_USE bool emitAtomicLoad(ValType type, Scalar::Type viewType);
     MOZ_MUST_USE bool emitAtomicRMW(ValType type, Scalar::Type viewType, AtomicOp op);
     MOZ_MUST_USE bool emitAtomicStore(ValType type, Scalar::Type viewType);
     MOZ_MUST_USE bool emitWait(ValType type, uint32_t byteSize);
     MOZ_MUST_USE bool emitWake();
     MOZ_MUST_USE bool emitAtomicXchg(ValType type, Scalar::Type viewType);
+#ifndef JS_64BIT
+    void emitAtomicXchg64(MemoryAccessDesc* access, ValType type, WantResult wantResult);
+#endif
 };
 
 void
 BaseCompiler::emitAddI32()
 {
     int32_t c;
     if (popConstI32(&c)) {
         RegI32 r = popI32();
@@ -4918,44 +4923,32 @@ BaseCompiler::emitSubtractF64()
     masm.subDouble(r1, r0);
     freeF64(r1);
     pushF64(r0);
 }
 
 void
 BaseCompiler::emitMultiplyI32()
 {
-    RegI32 r0, r1;
-    pop2xI32ForIntMulDiv(&r0, &r1);
+    RegI32 r0, r1, reserved;
+    pop2xI32ForMulDivI32(&r0, &r1, &reserved);
     masm.mul32(r1, r0);
+    maybeFreeI32(reserved);
     freeI32(r1);
     pushI32(r0);
 }
 
 void
 BaseCompiler::emitMultiplyI64()
 {
-    RegI64 r0, r1;
+    RegI64 r0, r1, reserved;
     RegI32 temp;
-#if defined(JS_CODEGEN_X64)
-    // srcDest must be rax, and rdx will be clobbered.
-    need2xI64(specific.rax, specific.rdx);
-    r1 = popI64();
-    r0 = popI64ToSpecific(specific.rax);
-    freeI64(specific.rdx);
-#elif defined(JS_CODEGEN_X86)
-    need2xI32(specific.eax, specific.edx);
-    r1 = popI64();
-    r0 = popI64ToSpecific(specific.edx_eax);
-    temp = needI32();
-#else
-    pop2xI64(&r0, &r1);
-    temp = needI32();
-#endif
+    pop2xI64ForMulI64(&r0, &r1, &temp, &reserved);
     masm.mul64(r1, r0, temp);
+    maybeFreeI64(reserved);
     maybeFreeI32(temp);
     freeI64(r1);
     pushI64(r0);
 }
 
 void
 BaseCompiler::emitMultiplyF32()
 {
@@ -4989,27 +4982,28 @@ BaseCompiler::emitQuotientI32()
             masm.add32(Imm32(c-1), r);
             masm.bind(&positive);
 
             masm.rshift32Arithmetic(Imm32(power & 31), r);
             pushI32(r);
         }
     } else {
         bool isConst = peekConstI32(&c);
-        RegI32 r0, r1;
-        pop2xI32ForIntMulDiv(&r0, &r1);
+        RegI32 r0, r1, reserved;
+        pop2xI32ForMulDivI32(&r0, &r1, &reserved);
 
         Label done;
         if (!isConst || c == 0)
             checkDivideByZeroI32(r1, r0, &done);
         if (!isConst || c == -1)
             checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(false));
         masm.quotient32(r1, r0, IsUnsigned(false));
         masm.bind(&done);
 
+        maybeFreeI32(reserved);
         freeI32(r1);
         pushI32(r0);
     }
 }
 
 void
 BaseCompiler::emitQuotientU32()
 {
@@ -5018,25 +5012,26 @@ BaseCompiler::emitQuotientU32()
     if (popConstPositivePowerOfTwoI32(&c, &power, 0)) {
         if (power != 0) {
             RegI32 r = popI32();
             masm.rshift32(Imm32(power & 31), r);
             pushI32(r);
         }
     } else {
         bool isConst = peekConstI32(&c);
-        RegI32 r0, r1;
-        pop2xI32ForIntMulDiv(&r0, &r1);
+        RegI32 r0, r1, reserved;
+        pop2xI32ForMulDivI32(&r0, &r1, &reserved);
 
         Label done;
         if (!isConst || c == 0)
             checkDivideByZeroI32(r1, r0, &done);
         masm.quotient32(r1, r0, IsUnsigned(true));
         masm.bind(&done);
 
+        maybeFreeI32(reserved);
         freeI32(r1);
         pushI32(r0);
     }
 }
 
 void
 BaseCompiler::emitRemainderI32()
 {
@@ -5055,52 +5050,54 @@ BaseCompiler::emitRemainderI32()
         masm.rshift32Arithmetic(Imm32(power & 31), temp);
         masm.lshift32(Imm32(power & 31), temp);
         masm.sub32(temp, r);
         freeI32(temp);
 
         pushI32(r);
     } else {
         bool isConst = peekConstI32(&c);
-        RegI32 r0, r1;
-        pop2xI32ForIntMulDiv(&r0, &r1);
+        RegI32 r0, r1, reserved;
+        pop2xI32ForMulDivI32(&r0, &r1, &reserved);
 
         Label done;
         if (!isConst || c == 0)
             checkDivideByZeroI32(r1, r0, &done);
         if (!isConst || c == -1)
             checkDivideSignedOverflowI32(r1, r0, &done, ZeroOnOverflow(true));
         masm.remainder32(r1, r0, IsUnsigned(false));
         masm.bind(&done);
 
+        maybeFreeI32(reserved);
         freeI32(r1);
         pushI32(r0);
     }
 }
 
 void
 BaseCompiler::emitRemainderU32()
 {
     int32_t c;
     uint_fast8_t power;
     if (popConstPositivePowerOfTwoI32(&c, &power, 1)) {
         RegI32 r = popI32();
         masm.and32(Imm32(c-1), r);
         pushI32(r);
     } else {
         bool isConst = peekConstI32(&c);
-        RegI32 r0, r1;
-        pop2xI32ForIntMulDiv(&r0, &r1);
+        RegI32 r0, r1, reserved;
+        pop2xI32ForMulDivI32(&r0, &r1, &reserved);
 
         Label done;
         if (!isConst || c == 0)
             checkDivideByZeroI32(r1, r0, &done);
         masm.remainder32(r1, r0, IsUnsigned(true));
         masm.bind(&done);
 
+        maybeFreeI32(reserved);
         freeI32(r1);
         pushI32(r0);
     }
 }
 
 #ifndef RABALDR_INT_DIV_I64_CALLOUT
 void
 BaseCompiler::emitQuotientI64()
@@ -5117,19 +5114,20 @@ BaseCompiler::emitQuotientI64()
             masm.add64(Imm64(c-1), r);
             masm.bind(&positive);
 
             masm.rshift64Arithmetic(Imm32(power & 63), r);
             pushI64(r);
         }
     } else {
         bool isConst = peekConstI64(&c);
-        RegI64 r0, r1;
-        pop2xI64ForIntDiv(&r0, &r1);
-        quotientI64(r1, r0, IsUnsigned(false), isConst, c);
+        RegI64 r0, r1, reserved;
+        pop2xI64ForDivI64(&r0, &r1, &reserved);
+        quotientI64(r1, r0, reserved, IsUnsigned(false), isConst, c);
+        maybeFreeI64(reserved);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64");
 # endif
 }
 
@@ -5142,19 +5140,20 @@ BaseCompiler::emitQuotientU64()
     if (popConstPositivePowerOfTwoI64(&c, &power, 0)) {
         if (power != 0) {
             RegI64 r = popI64();
             masm.rshift64(Imm32(power & 63), r);
             pushI64(r);
         }
     } else {
         bool isConst = peekConstI64(&c);
-        RegI64 r0, r1;
-        pop2xI64ForIntDiv(&r0, &r1);
-        quotientI64(r1, r0, IsUnsigned(true), isConst, c);
+        RegI64 r0, r1, reserved;
+        pop2xI64ForDivI64(&r0, &r1, &reserved);
+        quotientI64(r1, r0, reserved, IsUnsigned(true), isConst, c);
+        maybeFreeI64(reserved);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64");
 # endif
 }
 
@@ -5177,19 +5176,20 @@ BaseCompiler::emitRemainderI64()
         masm.rshift64Arithmetic(Imm32(power & 63), temp);
         masm.lshift64(Imm32(power & 63), temp);
         masm.sub64(temp, r);
         freeI64(temp);
 
         pushI64(r);
     } else {
         bool isConst = peekConstI64(&c);
-        RegI64 r0, r1;
-        pop2xI64ForIntDiv(&r0, &r1);
-        remainderI64(r1, r0, IsUnsigned(false), isConst, c);
+        RegI64 r0, r1, reserved;
+        pop2xI64ForDivI64(&r0, &r1, &reserved);
+        remainderI64(r1, r0, reserved, IsUnsigned(false), isConst, c);
+        maybeFreeI64(reserved);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64");
 # endif
 }
 
@@ -5200,19 +5200,20 @@ BaseCompiler::emitRemainderU64()
     int64_t c;
     uint_fast8_t power;
     if (popConstPositivePowerOfTwoI64(&c, &power, 1)) {
         RegI64 r = popI64();
         masm.and64(Imm64(c-1), r);
         pushI64(r);
     } else {
         bool isConst = peekConstI64(&c);
-        RegI64 r0, r1;
-        pop2xI64ForIntDiv(&r0, &r1);
-        remainderI64(r1, r0, IsUnsigned(true), isConst, c);
+        RegI64 r0, r1, reserved;
+        pop2xI64ForDivI64(&r0, &r1, &reserved);
+        remainderI64(r1, r0, reserved, IsUnsigned(true), isConst, c);
+        maybeFreeI64(reserved);
         freeI64(r1);
         pushI64(r0);
     }
 # else
     MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64");
 # endif
 }
 #endif // RABALDR_INT_DIV_I64_CALLOUT
@@ -5831,41 +5832,45 @@ BaseCompiler::emitExtendI32_16()
     RegI32 r = popI32();
     masm.move16SignExtend(r, r);
     pushI32(r);
 }
 
 void
 BaseCompiler::emitExtendI64_8()
 {
-    RegI64 r = popI64ForSignExtendI64();
+    RegI64 r;
+    popI64ForSignExtendI64(&r);
     masm.move8To64SignExtend(lowPart(r), r);
     pushI64(r);
 }
 
 void
 BaseCompiler::emitExtendI64_16()
 {
-    RegI64 r = popI64ForSignExtendI64();
+    RegI64 r;
+    popI64ForSignExtendI64(&r);
     masm.move16To64SignExtend(lowPart(r), r);
     pushI64(r);
 }
 
 void
 BaseCompiler::emitExtendI64_32()
 {
-    RegI64 x0 = popI64ForSignExtendI64();
-    masm.move32To64SignExtend(lowPart(x0), x0);
-    pushI64(x0);
+    RegI64 r;
+    popI64ForSignExtendI64(&r);
+    masm.move32To64SignExtend(lowPart(r), r);
+    pushI64(r);
 }
 
 void
 BaseCompiler::emitExtendI32ToI64()
 {
-    RegI64 x0 = popI32ForSignExtendI64();
+    RegI64 x0;
+    popI32ForSignExtendI64(&x0);
     masm.move32To64SignExtend(lowPart(x0), x0);
     pushI64(x0);
 }
 
 void
 BaseCompiler::emitExtendU32ToI64()
 {
     RegI32 r0 = popI32();
@@ -8030,17 +8035,17 @@ BaseCompiler::emitAtomicStore(ValType ty
     if (Scalar::byteSize(viewType) <= sizeof(void*))
         return storeCommon(&access, type);
 
     MOZ_ASSERT(type == ValType::I64 && Scalar::byteSize(viewType) == 8);
 
 #ifdef JS_64BIT
     MOZ_CRASH("Should not happen");
 #else
-    xchg64(&access, type, WantResult(false));
+    emitAtomicXchg64(&access, type, WantResult(false));
 #endif
 
     return true;
 }
 
 bool
 BaseCompiler::emitAtomicXchg(ValType type, Scalar::Type viewType)
 {
@@ -8099,22 +8104,70 @@ BaseCompiler::emitAtomicXchg(ValType typ
     masm.atomicExchange64(srcAddr, rv, rd);
     pushI64(rd);
 
     maybeFreeI32(tls);
     freeI32(rp);
     if (rv != rd)
         freeI64(rv);
 #else
-    xchg64(&access, type, WantResult(true));
+    emitAtomicXchg64(&access, type, WantResult(true));
 #endif
 
     return true;
 }
 
+#ifndef JS_64BIT
+void
+BaseCompiler::emitAtomicXchg64(MemoryAccessDesc* access, ValType type, WantResult wantResult)
+{
+# if defined(JS_CODEGEN_X86)
+    RegI64 rd = specific.edx_eax;
+    needI64(rd);
+    needI32(specific.ecx);
+    // Claim scratch after the need() calls because they may need it to
+    // sync.
+    ScratchEBX scratch(*this);
+    RegI64 rv = specific.ecx_ebx;
+# elif defined(JS_CODEGEN_ARM)
+    RegI64 rv = needI64Pair();
+    RegI64 rd = needI64Pair();
+# else
+    RegI64 rv, rd;
+    MOZ_CRASH("BaseCompiler porting interface: xchg64");
+# endif
+
+    popI64ToSpecific(rv);
+
+    AccessCheck check;
+    RegI32 rp = popMemoryAccess(access, &check);
+    RegI32 tls = maybeLoadTlsForAccess(check);
+    prepareMemoryAccess(access, &check, tls, rp);
+    ATOMIC_PTR(srcAddr, access, tls, rp);
+
+    masm.atomicExchange64(srcAddr, rv, rd);
+
+    if (wantResult)
+        pushI64(rd);
+    else
+        freeI64(rd);
+
+    maybeFreeI32(tls);
+    freeI32(rp);
+
+# if defined(JS_CODEGEN_X86)
+    freeI32(specific.ecx);
+# elif defined(JS_CODEGEN_ARM)
+    freeI64(rv);
+# else
+    MOZ_CRASH("BaseCompiler porting interface: xchg64");
+# endif
+}
+#endif
+
 bool
 BaseCompiler::emitWait(ValType type, uint32_t byteSize)
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
     Nothing nothing;
     LinearMemoryAddress<Nothing> addr;
     if (!iter_.readWait(&addr, type, byteSize, &nothing, &nothing))