Bug 1301400: Baseline Wasm Compiler: Part 4: Implement QuotientI64 and RemainderI64, r=lth
authorh4writer <hv1989@gmail.com>
Thu, 29 Sep 2016 22:33:22 +0200
changeset 315904 90989a3b070d3c61961274e842c6ef305b84992a
parent 315903 7c97d5898871280008baf27b37c98d2e22e52ba5
child 315905 7d6daefd4e7963157897ebfefc5d16d8304b752c
push id20634
push usercbook@mozilla.com
push dateFri, 30 Sep 2016 10:10:13 +0000
treeherderfx-team@afe79b010d13 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1301400
milestone52.0a1
Bug 1301400: Baseline Wasm Compiler: Part 4: Implement QuotientI64 and RemainderI64, r=lth
js/src/asmjs/WasmBaselineCompile.cpp
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -443,16 +443,17 @@ class BaseCompiler
     size_t                      lastReadCallSite_;
     TempAllocator&              alloc_;
     const ValTypeVector&        locals_;         // Types of parameters and locals
     int32_t                     localSize_;      // Size of local area in bytes (stable after beginFunction)
     int32_t                     varLow_;         // Low byte offset of local area for true locals (not parameters)
     int32_t                     varHigh_;        // High byte offset + 1 of local area for true locals
     int32_t                     maxFramePushed_; // Max value of masm.framePushed() observed
     bool                        deadCode_;       // Flag indicating we should decode & discard the opcode
+    ValTypeVector               SigI64I64_;
     ValTypeVector               SigDD_;
     ValTypeVector               SigD_;
     ValTypeVector               SigF_;
     ValTypeVector               SigI_;
     ValTypeVector               Sig_;
     Label                       returnLabel_;
     Label                       outOfLinePrologue_;
     Label                       bodyLabel_;
@@ -487,16 +488,19 @@ class BaseCompiler
     RegI32 specific_eax;
     RegI32 specific_ecx;
     RegI32 specific_edx;
 #endif
 
 #if defined(JS_CODEGEN_X86)
     AllocatableGeneralRegisterSet singleByteRegs_;
 #endif
+#if defined(JS_NUNBOX32)
+    RegI64 abiReturnRegI64;
+#endif
 
     // The join registers are used to carry values out of blocks.
     // JoinRegI32 and joinRegI64 must overlap: emitBrIf and
     // emitBrTable assume that.
 
     RegI32 joinRegI32;
     RegI64 joinRegI64;
     RegF32 joinRegF32;
@@ -2346,17 +2350,21 @@ class BaseCompiler
     }
 
     void checkDivideByZeroI64(RegI64 rhs, RegI64 srcDest, Label* done) {
         MOZ_ASSERT(!isCompilingAsmJS());
 #ifdef JS_CODEGEN_X64
         masm.testq(rhs.reg.reg, rhs.reg.reg);
         masm.j(Assembler::Zero, wasm::JumpTarget::IntegerDivideByZero);
 #else
-        MOZ_CRASH("BaseCompiler platform hook: checkDivideByZeroI64");
+        Label nonZero;
+        masm.branchTest32(Assembler::NonZero, rhs.reg.low, rhs.reg.low, &nonZero);
+        masm.branchTest32(Assembler::Zero, rhs.reg.high, rhs.reg.high,
+                          wasm::JumpTarget::IntegerDivideByZero);
+        masm.bind(&nonZero);
 #endif
     }
 
     void checkDivideSignedOverflowI32(RegI32 rhs, RegI32 srcDest, Label* done, bool zeroOnOverflow) {
         Label notMin;
         masm.branch32(Assembler::NotEqual, srcDest.reg, Imm32(INT32_MIN), &notMin);
         if (zeroOnOverflow) {
             masm.branch32(Assembler::NotEqual, rhs.reg, Imm32(-1), &notMin);
@@ -2368,36 +2376,25 @@ class BaseCompiler
         } else {
             masm.branch32(Assembler::Equal, rhs.reg, Imm32(-1), wasm::JumpTarget::IntegerOverflow);
         }
         masm.bind(&notMin);
     }
 
     void checkDivideSignedOverflowI64(RegI64 rhs, RegI64 srcDest, Label* done, bool zeroOnOverflow) {
         MOZ_ASSERT(!isCompilingAsmJS());
-#ifdef JS_CODEGEN_X64
-        Label notMin;
-        {
-            ScratchI32 scratch(*this);
-            masm.move64(Imm64(INT64_MIN), Register64(scratch));
-            masm.cmpq(scratch, srcDest.reg.reg);
-        }
-        masm.j(Assembler::NotEqual, &notMin);
-        masm.cmpq(Imm32(-1), rhs.reg.reg);
-        if (zeroOnOverflow) {
-            masm.j(Assembler::NotEqual, &notMin);
-            masm.xorq(srcDest.reg.reg, srcDest.reg.reg);
-            masm.jump(done);
-        } else {
-            masm.j(Assembler::Equal, wasm::JumpTarget::IntegerOverflow);
-        }
-        masm.bind(&notMin);
-#else
-        MOZ_CRASH("BaseCompiler platform hook: checkDivideSignedOverflowI64");
-#endif
+        Label notmin;
+        masm.branch64(Assembler::NotEqual, srcDest.reg, Imm64(INT64_MIN), &notmin);
+        masm.branch64(Assembler::NotEqual, rhs.reg, Imm64(-1), &notmin);
+        if (zeroOnOverflow)
+            masm.xor64(srcDest.reg, srcDest.reg);
+        else
+            masm.jump(wasm::JumpTarget::IntegerOverflow);
+        masm.jump(done);
+        masm.bind(&notmin);
     }
 
     void quotientI64(RegI64 rhs, RegI64 srcDest, IsUnsigned isUnsigned) {
         // This follows quotientI32, above.
         Label done;
 
         checkDivideByZeroI64(rhs, srcDest, &done);
 
@@ -3168,16 +3165,20 @@ class BaseCompiler
     MOZ_MUST_USE
     bool emitCallImport(uint32_t callOffset);
     MOZ_MUST_USE
     bool emitCallIndirect(uint32_t callOffset, bool oldStyle);
     MOZ_MUST_USE
     bool emitUnaryMathBuiltinCall(uint32_t callOffset, SymbolicAddress callee, ValType operandType);
     MOZ_MUST_USE
     bool emitBinaryMathBuiltinCall(uint32_t callOffset, SymbolicAddress callee, ValType operandType);
+#ifdef JS_NUNBOX32
+    void emitDivOrModI64BuiltinCall(SymbolicAddress callee, RegI64 rhs, RegI64 srcDest,
+                                    RegI32 temp);
+#endif
     MOZ_MUST_USE
     bool emitGetLocal();
     MOZ_MUST_USE
     bool emitSetLocal();
     bool emitTeeLocal();
     MOZ_MUST_USE
     MOZ_MUST_USE
     bool emitGetGlobal();
@@ -3495,51 +3496,81 @@ BaseCompiler::emitQuotientU32()
 
     freeI32(r1);
     pushI32(r0);
 }
 
 void
 BaseCompiler::emitQuotientI64()
 {
+#ifdef JS_PUNBOX64
     RegI64 r0, r1;
-#if defined(JS_CODEGEN_X64)
+# 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);
-#elif defined(JS_CODEGEN_X86)
-    MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64");
-#else
+# else
     pop2xI64(&r0, &r1);
-#endif
+# endif
     quotientI64(r1, r0, IsUnsigned(false));
     freeI64(r1);
     pushI64(r0);
+#else
+# if defined(JS_CODEGEN_X86)
+    RegI64 r0, r1;
+    RegI32 temp;
+    needI64(abiReturnRegI64);
+    temp = needI32();
+    r1 = popI64();
+    r0 = popI64ToSpecific(abiReturnRegI64);
+    emitDivOrModI64BuiltinCall(SymbolicAddress::DivI64, r1, r0, temp);
+    freeI32(temp);
+    freeI64(r1);
+    pushI64(r0);
+# else
+    MOZ_CRASH("BaseCompiler platform hook: emitQuotientI64");
+# endif
+#endif
 }
 
 void
 BaseCompiler::emitQuotientU64()
 {
+#ifdef JS_PUNBOX64
     RegI64 r0, r1;
-#if defined(JS_CODEGEN_X64)
+ #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);
-#elif defined(JS_CODEGEN_X86)
-    MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64");
-#else
+ #else
     pop2xI64(&r0, &r1);
-#endif
+ #endif
     quotientI64(r1, r0, IsUnsigned(true));
     freeI64(r1);
     pushI64(r0);
+#else
+ #if defined(JS_CODEGEN_X86)
+    RegI64 r0, r1;
+    RegI32 temp;
+    needI64(abiReturnRegI64);
+    temp = needI32();
+    r1 = popI64();
+    r0 = popI64ToSpecific(abiReturnRegI64);
+    emitDivOrModI64BuiltinCall(SymbolicAddress::UDivI64, r1, r0, temp);
+    freeI32(temp);
+    freeI64(r1);
+    pushI64(r0);
+ #else
+    MOZ_CRASH("BaseCompiler platform hook: emitQuotientU64");
+ #endif
+#endif
 }
 
 void
 BaseCompiler::emitRemainderI32()
 {
     // TODO / OPTIMIZE: Fast case if lhs >= 0 and rhs is power of two.
     RegI32 r0, r1;
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
@@ -3584,49 +3615,79 @@ BaseCompiler::emitRemainderU32()
 
     freeI32(r1);
     pushI32(r0);
 }
 
 void
 BaseCompiler::emitRemainderI64()
 {
+#ifdef JS_PUNBOX64
     RegI64 r0, r1;
-#if defined(JS_CODEGEN_X64)
+ #ifdef JS_CODEGEN_X64
     need2xI64(specific_rax, specific_rdx);
     r1 = popI64();
     r0 = popI64ToSpecific(specific_rax);
     freeI64(specific_rdx);
-#elif defined(JS_CODEGEN_X86)
-    MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64");
-#else
+ #else
     pop2xI64(&r0, &r1);
-#endif
+ #endif
     remainderI64(r1, r0, IsUnsigned(false));
     freeI64(r1);
     pushI64(r0);
+#else
+ #if defined(JS_CODEGEN_X86)
+    RegI64 r0, r1;
+    RegI32 temp;
+    needI64(abiReturnRegI64);
+    temp = needI32();
+    r1 = popI64();
+    r0 = popI64ToSpecific(abiReturnRegI64);
+    emitDivOrModI64BuiltinCall(SymbolicAddress::ModI64, r1, r0, temp);
+    freeI32(temp);
+    freeI64(r1);
+    pushI64(r0);
+ #else
+    MOZ_CRASH("BaseCompiler platform hook: emitRemainderI64");
+ #endif
+#endif
 }
 
 void
 BaseCompiler::emitRemainderU64()
 {
+#ifdef JS_PUNBOX64
     RegI64 r0, r1;
-#if defined(JS_CODEGEN_X64)
+ #ifdef JS_CODEGEN_X64
     need2xI64(specific_rax, specific_rdx);
     r1 = popI64();
     r0 = popI64ToSpecific(specific_rax);
     freeI64(specific_rdx);
-#elif defined(JS_CODEGEN_X86)
-    MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64");
-#else
+ #else
     pop2xI64(&r0, &r1);
-#endif
+ #endif
     remainderI64(r1, r0, IsUnsigned(true));
     freeI64(r1);
     pushI64(r0);
+#else
+ #if defined(JS_CODEGEN_X86)
+    RegI64 r0, r1;
+    RegI32 temp;
+    needI64(abiReturnRegI64);
+    temp = needI32();
+    r1 = popI64();
+    r0 = popI64ToSpecific(abiReturnRegI64);
+    emitDivOrModI64BuiltinCall(SymbolicAddress::UModI64, r1, r0, temp);
+    freeI32(temp);
+    freeI64(r1);
+    pushI64(r0);
+ #else
+    MOZ_CRASH("BaseCompiler platform hook: emitRemainderU64");
+ #endif
+#endif
 }
 
 void
 BaseCompiler::emitDivideF32()
 {
     RegF32 r0, r1;
     pop2xF32(&r0, &r1);
     masm.divFloat32(r1.reg, r0.reg);
@@ -5281,16 +5342,45 @@ BaseCompiler::emitBinaryMathBuiltinCall(
     popValueStackBy(numArgs);
     masm.freeStack(stackSpace);
 
     pushReturned(baselineCall, retType);
 
     return true;
 }
 
+#ifdef JS_NUNBOX32
+void
+BaseCompiler::emitDivOrModI64BuiltinCall(SymbolicAddress callee, RegI64 rhs, RegI64 srcDest,
+                                         RegI32 temp)
+{
+    Label done;
+
+    sync();
+
+    checkDivideByZeroI64(rhs, srcDest, &done);
+
+    if (callee == SymbolicAddress::DivI64)
+        checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(false));
+    else if (callee == SymbolicAddress::ModI64)
+        checkDivideSignedOverflowI64(rhs, srcDest, &done, ZeroOnOverflow(true));
+
+    masm.setupUnalignedABICall(temp.reg);
+    masm.passABIArg(srcDest.reg.high);
+    masm.passABIArg(srcDest.reg.low);
+    masm.passABIArg(rhs.reg.high);
+    masm.passABIArg(rhs.reg.low);
+    masm.callWithABI(callee);
+
+    MOZ_ASSERT(abiReturnRegI64.reg == srcDest.reg);
+
+    masm.bind(&done);
+}
+#endif
+
 bool
 BaseCompiler::emitGetLocal()
 {
     uint32_t slot;
     if (!iter_.readGetLocal(locals_, &slot))
         return false;
 
     if (deadCode_)
@@ -6747,16 +6837,17 @@ BaseCompiler::BaseCompiler(const ModuleG
 #endif
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
       specific_eax(RegI32(eax)),
       specific_ecx(RegI32(ecx)),
       specific_edx(RegI32(edx)),
 #endif
 #ifdef JS_CODEGEN_X86
       singleByteRegs_(GeneralRegisterSet(Registers::SingleByteRegs)),
+      abiReturnRegI64(RegI64(Register64(edx, eax))),
 #endif
       joinRegI32(RegI32(ReturnReg)),
       joinRegI64(RegI64(ReturnReg64)),
       joinRegF32(RegF32(ReturnFloat32Reg)),
       joinRegF64(RegF64(ReturnDoubleReg))
 {
     // jit/RegisterAllocator.h: RegisterAllocator::RegisterAllocator()
 
@@ -6792,16 +6883,18 @@ BaseCompiler::init()
     if (!SigDD_.append(ValType::F64) || !SigDD_.append(ValType::F64))
         return false;
     if (!SigD_.append(ValType::F64))
         return false;
     if (!SigF_.append(ValType::F32))
         return false;
     if (!SigI_.append(ValType::I32))
         return false;
+    if (!SigI64I64_.append(ValType::I64) || !SigI64I64_.append(ValType::I64))
+        return false;
 
     const ValTypeVector& args = func_.sig().args();
 
     // localInfo_ contains an entry for every local in locals_, followed by
     // entries for special locals. Currently the only special local is the TLS
     // pointer.
     tlsSlot_ = locals_.length();
     if (!localInfo_.resize(locals_.length() + 1))