[JAEGER] Check for negative zero after MUL (bug 584770, r=dvander).
authorJan de Mooij <jandemooij@gmail.com>
Mon, 16 Aug 2010 11:16:47 -0400
changeset 53432 fe75f7100230edabbb935b24010b228b4b1f16f4
parent 53431 73eb2d14f7ac542212fa83c1740b1f32f22b30e3
child 53433 412127642a276ead5a0d9db7b6dc6286279b0170
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs584770
milestone2.0b4pre
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
[JAEGER] Check for negative zero after MUL (bug 584770, r=dvander).
js/src/methodjit/FastArithmetic.cpp
js/src/trace-test/tests/jaeger/mulNegZero.js
--- a/js/src/methodjit/FastArithmetic.cpp
+++ b/js/src/methodjit/FastArithmetic.cpp
@@ -570,17 +570,17 @@ mjit::Compiler::jsop_binary_full(FrameEn
             reg = regs.lhsData.reg();
         if (op == JSOP_SUB) {
             masm.neg32(regs.result);
             op = JSOP_ADD;
         }
     }
 
     /* Okay - good to emit the integer fast-path. */
-    MaybeJump overflow;
+    MaybeJump overflow, negZeroDone;
     switch (op) {
       case JSOP_ADD:
         if (reg.isSet())
             overflow = masm.branchAdd32(Assembler::Overflow, reg.reg(), regs.result);
         else
             overflow = masm.branchAdd32(Assembler::Overflow, Imm32(value), regs.result);
         break;
 
@@ -588,19 +588,67 @@ mjit::Compiler::jsop_binary_full(FrameEn
         if (reg.isSet())
             overflow = masm.branchSub32(Assembler::Overflow, reg.reg(), regs.result);
         else
             overflow = masm.branchSub32(Assembler::Overflow, Imm32(value), regs.result);
         break;
 
 #if !defined(JS_CPU_ARM)
       case JSOP_MUL:
+      {
         JS_ASSERT(reg.isSet());
+        
+        MaybeJump storeNegZero;
+        bool maybeNegZero = true;
+        bool hasConstant = (lhs->isConstant() || rhs->isConstant());
+        
+        if (hasConstant) {
+            value = (lhs->isConstant() ? lhs : rhs)->getValue().toInt32();
+            RegisterID nonConstReg = lhs->isConstant() ? regs.rhsData.reg() : regs.lhsData.reg();
+
+            if (value > 0)
+                maybeNegZero = false;
+            else if (value < 0)
+                storeNegZero = masm.branchTest32(Assembler::Zero, nonConstReg);
+            else
+                storeNegZero = masm.branch32(Assembler::LessThan, nonConstReg, Imm32(0));
+        }
         overflow = masm.branchMul32(Assembler::Overflow, reg.reg(), regs.result);
+
+        if (maybeNegZero) {
+            if (!hasConstant) {
+                Jump isZero = masm.branchTest32(Assembler::Zero, regs.result);
+                stubcc.linkExitDirect(isZero, stubcc.masm.label());
+                
+                /* Restore original value. */
+                if (regs.resultHasRhs) {
+                    if (regs.rhsNeedsRemat)
+                        stubcc.masm.loadPayload(frame.addressOf(rhs), regs.result);
+                    else
+                        stubcc.masm.move(regs.rhsData.reg(), regs.result);
+                } else {
+                    if (regs.lhsNeedsRemat)
+                        stubcc.masm.loadPayload(frame.addressOf(lhs), regs.result);
+                    else
+                        stubcc.masm.move(regs.lhsData.reg(), regs.result);
+                }
+                storeNegZero = stubcc.masm.branchOr32(Assembler::Signed, reg.reg(), regs.result);
+                stubcc.masm.xor32(regs.result, regs.result);
+                stubcc.crossJump(stubcc.masm.jump(), masm.label());
+                storeNegZero.getJump().linkTo(stubcc.masm.label(), &stubcc.masm);
+            } else {
+                JS_ASSERT(storeNegZero.isSet());
+                stubcc.linkExitDirect(storeNegZero.get(), stubcc.masm.label());
+            }
+            stubcc.masm.storeValue(DoubleValue(-0.0), frame.addressOf(lhs));
+            stubcc.masm.loadPayload(frame.addressOf(lhs), regs.result);
+            negZeroDone = stubcc.masm.jump();
+        }
         break;
+      }
 #endif
 
       default:
         JS_NOT_REACHED("unrecognized op");
     }
     op = origOp;
     
     JS_ASSERT(overflow.isSet());
@@ -659,16 +707,18 @@ mjit::Compiler::jsop_binary_full(FrameEn
 
     /* Finish up stack operations. */
     frame.popn(2);
     frame.pushNumber(regs.result, true);
 
     /* Merge back OOL double paths. */
     if (doublePathDone.isSet())
         stubcc.linkRejoin(doublePathDone.get());
+    if (negZeroDone.isSet())
+        stubcc.linkRejoin(negZeroDone.get());
     stubcc.linkRejoin(overflowDone.get());
 
     stubcc.rejoin(Changes(1));
 }
 
 static const uint64 DoubleNegMask = 0x8000000000000000ULL;
 
 void
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/jaeger/mulNegZero.js
@@ -0,0 +1,31 @@
+
+function mul(x, y)    { return x * y;  };
+function mulConst0(x) { return x * 0;  };
+function mulConst1(x) { return -5 * x; };
+function mulConst2(x) { return x * -5; };
+
+function f() {
+    assertEq(mulConst0(7), 0);
+    assertEq(mulConst0(-5), -0);
+    assertEq(mulConst0(0), 0);
+    assertEq(mulConst0(-0), -0);
+    
+    assertEq(mulConst1(7), -35);
+    assertEq(mulConst1(-8), 40);
+    assertEq(mulConst1(0), -0);
+    assertEq(mulConst1(-0), 0);
+    
+    assertEq(mulConst2(7), -35);
+    assertEq(mulConst2(-8), 40);
+    assertEq(mulConst2(0), -0);
+    assertEq(mulConst2(-0), 0);
+    
+    assertEq(mul(55, 2), 110);
+    assertEq(mul(0, -10), -0);
+    assertEq(mul(-5, 0), -0);
+    assertEq(mul(-0, 0), -0);
+    assertEq(mul(0, -0), -0);
+    assertEq(mul(0, 0), 0);
+    assertEq(mul(-0, -0), 0);
+}
+f();