Bug 679493: implement JSOP_MUL - part 1: basic, r=dvander
authorHannes Verschore <hverschore@mozilla.com>
Thu, 18 Aug 2011 20:04:00 -0700
changeset 105190 b7c3e89e50cc4a52e7ddd593334144d640d56ab9
parent 105189 42d5f6784ef002539b3c8b708690a1d3f25c290b
child 105191 0b8dece77de126b2db768a78267f30800e36bcf0
push id14706
push usereakhgari@mozilla.com
push dateTue, 11 Sep 2012 20:39:52 +0000
treeherdermozilla-inbound@d50bf1edaabe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs679493
milestone8.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 679493: implement JSOP_MUL - part 1: basic, r=dvander
js/src/ion/IonBuilder.cpp
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/shared/Assembler-x86-shared.h
js/src/ion/shared/CodeGenerator-x86-shared.cpp
js/src/ion/shared/CodeGenerator-x86-shared.h
js/src/jit-test/tests/ion/bug679493.js
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -362,16 +362,17 @@ IonBuilder::inspectOpcode(JSOp op)
         return jsop_bitnot(op);
 
       case JSOP_BITAND:
       case JSOP_BITOR:
       case JSOP_BITXOR:
         return jsop_bitop(op);
 
       case JSOP_ADD:
+      case JSOP_MUL:
       	return jsop_binary(op);
 
       case JSOP_LOCALINC:
       case JSOP_INCLOCAL:
       case JSOP_LOCALDEC:
       case JSOP_DECLOCAL:
         return jsop_localinc(op);
 
@@ -1537,16 +1538,20 @@ IonBuilder::jsop_binary(JSOp op)
     MDefinition *left = current->pop();
 
     MBinaryArithInstruction *ins;
     switch (op) {
       case JSOP_ADD:
         ins = MAdd::New(left, right);
         break;
 
+      case JSOP_MUL:
+        ins = MMul::New(left, right);
+        break;
+
       default:
         JS_NOT_REACHED("unexpected binary opcode");
         return false;
     }
 
     current->add(ins);
     ins->infer(oracle->binaryOp(script, pc));
 
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -359,16 +359,23 @@ class LReturn : public LInstructionHelpe
 
 // Adds two integers, returning an integer value.
 class LAddI : public LInstructionHelper<1, 2, 0>
 {
   public:
     LIR_HEADER(AddI);
 };
 
+// Adds two integers, returning an integer value.
+class LMulI : public LInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(MulI);
+};
+
 // Performs an add, sub, mul, or div on two double values.
 class LMathD : public LInstructionHelper<1, 2, 0>
 {
     JSOp jsop_;
 
   public:
     LIR_HEADER(MathD);
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -58,16 +58,17 @@
     _(TestIAndBranch)               \
     _(TestDAndBranch)               \
     _(TestVAndBranch)               \
     _(CompareI)                     \
     _(CompareD)                     \
     _(CompareIAndBranch)            \
     _(CompareDAndBranch)            \
     _(AddI)                         \
+    _(MulI)                         \
     _(MathD)                        \
     _(Int32ToDouble)                \
     _(ValueToDouble)                \
     _(ValueToInt32)
 
 #if defined(JS_CPU_X86)
 # include "x86/LOpcodes-x86.h"
 #elif defined(JS_CPU_X64)
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -278,16 +278,41 @@ LIRGenerator::visitAdd(MAdd *ins)
         return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
     }
 
     JS_NOT_REACHED("NYI");
     return false;
 }
 
 bool
+LIRGenerator::visitMul(MMul *ins)
+{
+    MDefinition *lhs = ins->getOperand(0);
+    MDefinition *rhs = ins->getOperand(1);
+
+    JS_ASSERT(lhs->type() == rhs->type());
+
+    if (ins->specialization() == MIRType_Int32) {
+        JS_ASSERT(lhs->type() == MIRType_Int32);
+        ReorderCommutative(&lhs, &rhs);
+        LMulI *lir = new LMulI;
+        if (!assignSnapshot(lir))
+            return false;
+        return lowerForALU(lir, ins, lhs, rhs);
+    }
+    if (ins->specialization() == MIRType_Double) {
+        JS_ASSERT(lhs->type() == MIRType_Double);
+        return lowerForFPU(new LMathD(JSOP_MUL), ins, lhs, rhs);
+    }
+
+    JS_NOT_REACHED("NYI");
+    return false;
+}
+
+bool
 LIRGenerator::visitStart(MStart *start)
 {
     // This is a no-op.
     return true;
 }
 
 bool
 LIRGenerator::visitToDouble(MToDouble *convert)
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -85,16 +85,17 @@ class LIRGenerator : public LIRGenerator
     bool visitGoto(MGoto *ins);
     bool visitTest(MTest *test);
     bool visitCompare(MCompare *comp);
     bool visitBitNot(MBitNot *ins);
     bool visitBitAnd(MBitAnd *ins);
     bool visitBitOr(MBitOr *ins);
     bool visitBitXor(MBitXor *ins);
     bool visitAdd(MAdd *ins);
+    bool visitMul(MMul *ins);
     bool visitStart(MStart *start);
     bool visitToDouble(MToDouble *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitCopy(MCopy *ins);
 };
 
 } // namespace js
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -91,16 +91,19 @@ EvaluateConstantOperands(MBinaryInstruct
             ret = Int32Value(lhs.toInt32() | rhs.toInt32());
             break;
         case MDefinition::Op_BitXor:
             ret = Int32Value(lhs.toInt32() ^ rhs.toInt32());
             break;
         case MDefinition::Op_Add:
             ret.setNumber(lhs.toNumber() + rhs.toNumber());
             break;
+        case MDefinition::Op_Mul:
+            ret.setNumber(lhs.toNumber() * rhs.toNumber());
+            break;
         default:
             JS_NOT_REACHED("NYI");
     }
 
     if (ins->type() != MIRTypeFromValue(ret))
         return NULL;
 
     return MConstant::New(ret);
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1118,16 +1118,35 @@ class MAdd : public MBinaryArithInstruct
         return new MAdd(left, right);
     }
 
     double getIdentity() {
         return 0;
     }
 };
 
+class MMul : public MBinaryArithInstruction
+{
+    MMul(MDefinition *left, MDefinition *right)
+      : MBinaryArithInstruction(left, right)
+    {
+        setResultType(MIRType_Value);
+    }
+
+  public:
+    INSTRUCTION_HEADER(Mul);
+    static MMul *New(MDefinition *left, MDefinition *right) {
+        return new MMul(left, right);
+    }
+
+    double getIdentity() {
+        return 1;
+    }
+};
+
 class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
 {
     js::Vector<MDefinition *, 2, IonAllocPolicy> inputs_;
     uint32 slot_;
     bool triedToSpecialize_;
 
     MPhi(uint32 slot)
       : slot_(slot),
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -54,16 +54,17 @@ namespace ion {
     _(Test)                                                                 \
     _(Compare)                                                              \
     _(Phi)                                                                  \
     _(BitNot)                                                               \
     _(BitAnd)                                                               \
     _(BitOr)                                                                \
     _(BitXor)                                                               \
     _(Add)                                                                  \
+    _(Mul)                                                                  \
     _(Return)                                                               \
     _(Copy)                                                                 \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(ToDouble)                                                             \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(Start)
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -472,16 +472,34 @@ class AssemblerX86Shared
             break;
           case Operand::REG_DISP:
             masm.andl_mr(src.disp(), src.base(), dest.code());
             break;
           default:
             JS_NOT_REACHED("unexpected operand kind");
         }
     }
+    void imull(Imm32 imm, const Register &dest) {
+        masm.imull_i32r(dest.code(), imm.value, dest.code());
+    }
+    void imull(const Register &src, const Register &dest) {
+        masm.imull_rr(src.code(), dest.code());
+    }
+    void imull(const Operand &src, const Register &dest) {
+        switch (src.kind()) {
+          case Operand::REG:
+            masm.imull_rr(src.reg(), dest.code());
+            break;
+          case Operand::REG_DISP:
+            masm.imull_mr(src.disp(), src.base(), dest.code());
+            break;
+          default:
+            JS_NOT_REACHED("unexpected operand kind");
+        }
+    }
     void notl(const Operand &src) {
         switch (src.kind()) {
           case Operand::REG:
             masm.notl_r(src.reg());
             break;
           case Operand::REG_DISP:
             masm.notl_m(src.disp(), src.base());
             break;
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -220,16 +220,55 @@ CodeGeneratorX86Shared::visitAddI(LAddI 
 
     if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
         return false;
 
     return true;
 }
 
 bool
+CodeGeneratorX86Shared::visitMulI(LMulI *ins)
+{
+    const LAllocation *lhs = ins->getOperand(0);
+    const LAllocation *rhs = ins->getOperand(1);
+
+    if (rhs->isConstant()) {
+        // Bailout on -0.0
+        int32 constant = ToInt32(rhs);
+        if (ins->snapshot() && constant <= 0) {
+            Assembler::Condition bailoutCond = (constant == 0) ? Assembler::LessThan : Assembler::Equal;
+            masm.cmpl(Imm32(0), ToRegister(lhs));
+            if (bailoutIf(bailoutCond, ins->snapshot()))
+                    return false;
+        }
+
+        masm.imull(Imm32(ToInt32(rhs)), ToRegister(lhs));
+
+        // Bailout on overflow
+        if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
+            return false;
+    } else {
+        masm.imull(ToOperand(rhs), ToRegister(lhs));
+
+        // Bailout on overflow
+        if (ins->snapshot() && !bailoutIf(Assembler::Overflow, ins->snapshot()))
+            return false;
+
+        // Bailout on 0 (could be -0.0)
+        if (ins->snapshot()) {
+            masm.cmpl(Imm32(0), ToRegister(lhs));
+            if (!bailoutIf(Assembler::Zero, ins->snapshot()))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+bool
 CodeGeneratorX86Shared::visitBitNot(LBitNot *ins)
 {
     const LAllocation *input = ins->getOperand(0);
     JS_ASSERT(!input->isConstant());
 
     masm.notl(ToOperand(input));
     return true;
 }
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -89,16 +89,17 @@ class CodeGeneratorX86Shared : public Co
 
   public:
     CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph &graph);
 
   public:
     // Instruction visitors.
     virtual bool visitGoto(LGoto *jump);
     virtual bool visitAddI(LAddI *ins);
+    virtual bool visitMulI(LMulI *ins);
     virtual bool visitBitNot(LBitNot *ins);
     virtual bool visitBitOp(LBitOp *ins);
     virtual bool visitMoveGroup(LMoveGroup *group);
     virtual bool visitInteger(LInteger *ins);
     virtual bool visitTestIAndBranch(LTestIAndBranch *test);
     virtual bool visitCompareI(LCompareI *comp);
     virtual bool visitCompareIAndBranch(LCompareIAndBranch *comp);
     virtual bool visitMathD(LMathD *math);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug679493.js
@@ -0,0 +1,30 @@
+// Normal checks:
+function mul(x, y) {
+    return x*y;
+}
+assertEq(mul(1, 2), 2);
+assertEq(mul(0, 2), 0);
+assertEq(mul(0, -1), -0);
+assertEq(mul(100000000, 20000), 2000000000000);
+assertEq(mul(0, -2), -0);
+assertEq(mul(0, 0), 0);
+
+// Constant * value checks:
+assertEq(function(x){return x*5}(4), 20);
+assertEq(function(x){return x*5}(0), 0);
+assertEq(function(x){return x*5}(-4), -20);
+assertEq(function(x){return x*0}(0), 0);
+assertEq(function(x){return x*0}(5), 0);
+assertEq(function(x){return x*0}(-5), -0);
+assertEq(function(x){return x*-5}(4), -20);
+assertEq(function(x){return x*-5}(0), -0);
+assertEq(function(x){return x*-5}(-4), 20);
+assertEq(function(x){return x*20000}(100000000), 2000000000000);
+
+// Constant folding
+assertEq(function(){var x=5; return x*4}(), 20);
+assertEq(function(){var x=5; return x*-4}(), -20);
+assertEq(function(){var x=0; return x*4}(), 0);
+assertEq(function(){var x=0; return x*0}(), 0);
+assertEq(function(){var x=0; return x*-4}(), -0);
+assertEq(function(){var x=20000; return x*100000000}(), 2000000000000);