Bug 764477 - IonMonkey: Inline Math.max and Math.min. r=jandem, mrosenberg
authorTom Schuster <evilpies@gmail.com>
Fri, 10 Aug 2012 14:17:26 +0200
changeset 103821 5be6b8747c745c16007eb2bd75ae4d5199f23010
parent 103820 b2fd7303d8485093972e57dd1705b531a01bae3d
child 103822 a129da8fe7ebe099cb31b568c603663a9764bfb5
push id1214
push userevilpies@gmail.com
push dateFri, 10 Aug 2012 12:18:09 +0000
reviewersjandem, mrosenberg
bugs764477
milestone17.0a1
Bug 764477 - IonMonkey: Inline Math.max and Math.min. r=jandem, mrosenberg
js/src/assembler/assembler/X86Assembler.h
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MCallOptimize.cpp
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.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/mathMinMax.js
--- a/js/src/assembler/assembler/X86Assembler.h
+++ b/js/src/assembler/assembler/X86Assembler.h
@@ -279,16 +279,17 @@ private:
         OP2_ADDSD_VsdWsd    = 0x58,
         OP2_MULSD_VsdWsd    = 0x59,
         OP2_CVTSS2SD_VsdEd  = 0x5A,
         OP2_CVTSD2SS_VsdEd  = 0x5A,
         OP2_SUBSD_VsdWsd    = 0x5C,
         OP2_DIVSD_VsdWsd    = 0x5E,
         OP2_SQRTSD_VsdWsd   = 0x51,
         OP2_ANDPD_VpdWpd    = 0x54,
+        OP2_ORPD_VpdWpd     = 0x56,
         OP2_XORPD_VpdWpd    = 0x57,
         OP2_MOVD_VdEd       = 0x6E,
         OP2_PSRLDQ_Vd       = 0x73,
         OP2_PCMPEQW         = 0x75,
         OP2_MOVD_EdVd       = 0x7E,
         OP2_JCC_rel32       = 0x80,
         OP_SETCC            = 0x90,
         OP2_IMUL_GvEv       = 0xAF,
@@ -2444,16 +2445,25 @@ public:
     {
         js::JaegerSpew(js::JSpew_Insns,
                        IPFX "xorpd      %s, %s\n", MAYBE_PAD,
                        nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
     }
 
+    void orpd_rr(XMMRegisterID src, XMMRegisterID dst)
+    {
+        js::JaegerSpew(js::JSpew_Insns,
+                       IPFX "orpd       %s, %s\n", MAYBE_PAD,
+                       nameFPReg(src), nameFPReg(dst));
+        m_formatter.prefix(PRE_SSE_66);
+        m_formatter.twoByteOp(OP2_ORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
+    }
+
     void andpd_rr(XMMRegisterID src, XMMRegisterID dst)
     {
         js::JaegerSpew(js::JSpew_Insns,
                        IPFX "andpd      %s, %s\n", MAYBE_PAD,
                        nameFPReg(src), nameFPReg(dst));
         m_formatter.prefix(PRE_SSE_66);
         m_formatter.twoByteOp(OP2_ANDPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
     }
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1600,16 +1600,45 @@ CodeGenerator::visitStringLength(LString
     Register output = ToRegister(lir->output());
 
     masm.loadPtr(lengthAndFlags, output);
     masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), output);
     return true;
 }
 
 bool
+CodeGenerator::visitMinMaxI(LMinMaxI *ins)
+{
+    Register first = ToRegister(ins->first());
+    Register output = ToRegister(ins->output());
+
+    JS_ASSERT(first == output);
+
+    if (ins->second()->isConstant())
+        masm.cmp32(first, Imm32(ToInt32(ins->second())));
+    else
+        masm.cmp32(first, ToRegister(ins->second()));
+
+    Label done;
+    if (ins->mir()->isMax())
+        masm.j(Assembler::GreaterThan, &done);
+    else
+        masm.j(Assembler::LessThan, &done);
+
+    if (ins->second()->isConstant())
+        masm.move32(Imm32(ToInt32(ins->second())), output);
+    else
+        masm.mov(ToRegister(ins->second()), output);
+
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
 CodeGenerator::visitAbsI(LAbsI *ins)
 {
     Register input = ToRegister(ins->input());
     Label positive;
 
     JS_ASSERT(input == ToRegister(ins->output()));
     masm.test32(input, input);
     masm.j(Assembler::GreaterThanOrEqual, &positive);
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -104,16 +104,17 @@ class CodeGenerator : public CodeGenerat
     bool visitLoadFixedSlotT(LLoadFixedSlotT *ins);
     bool visitStoreFixedSlotV(LStoreFixedSlotV *ins);
     bool visitStoreFixedSlotT(LStoreFixedSlotT *ins);
     bool visitAbsI(LAbsI *lir);
     bool visitPowI(LPowI *lir);
     bool visitPowD(LPowD *lir);
     bool visitMathFunctionD(LMathFunctionD *ins);
     bool visitModD(LModD *ins);
+    bool visitMinMaxI(LMinMaxI *lir);
     bool visitBinaryV(LBinaryV *lir);
     bool visitCompareS(LCompareS *lir);
     bool visitCompareV(LCompareV *lir);
     bool visitIsNullOrUndefined(LIsNullOrUndefined *lir);
     bool visitIsNullOrUndefinedAndBranch(LIsNullOrUndefinedAndBranch *lir);
     bool visitConcat(LConcat *lir);
     bool visitCharCodeAt(LCharCodeAt *lir);
     bool visitFromCharCode(LFromCharCode *lir);
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -376,16 +376,17 @@ class IonBuilder : public MIRGenerator
     InliningStatus inlineArrayPopShift(MArrayPopShift::Mode mode, uint32 argc, bool constructing);
     InliningStatus inlineArrayPush(uint32 argc, bool constructing);
 
     // Math natives.
     InliningStatus inlineMathAbs(uint32 argc, bool constructing);
     InliningStatus inlineMathFloor(uint32 argc, bool constructing);
     InliningStatus inlineMathRound(uint32 argc, bool constructing);
     InliningStatus inlineMathSqrt(uint32 argc, bool constructing);
+    InliningStatus inlineMathMinMax(bool max, uint32 argc, bool constructing);
     InliningStatus inlineMathPow(uint32 argc, bool constructing);
     InliningStatus inlineMathFunction(MMathFunction::Function function, uint32 argc,
                                       bool constructing);
 
     // String natives.
     InliningStatus inlineStrCharCodeAt(uint32 argc, bool constructing);
     InliningStatus inlineStrFromCharCode(uint32 argc, bool constructing);
     InliningStatus inlineStrCharAt(uint32 argc, bool constructing);
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -1241,16 +1241,64 @@ class LBinaryMath : public LInstructionH
     const LAllocation *lhs() {
         return this->getOperand(0);
     }
     const LAllocation *rhs() {
         return this->getOperand(1);
     }
 };
 
+class LMinMaxI : public LInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(MinMaxI);
+    LMinMaxI(const LAllocation &first, const LAllocation &second)
+    {
+        setOperand(0, first);
+        setOperand(1, second);
+    }
+
+    const LAllocation *first() {
+        return this->getOperand(0);
+    }
+    const LAllocation *second() {
+        return this->getOperand(1);
+    }
+    const LDefinition *output() {
+        return this->getDef(0);
+    }
+    MMinMax *mir() const {
+        return mir_->toMinMax();
+    }
+};
+
+class LMinMaxD : public LInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(MinMaxD);
+    LMinMaxD(const LAllocation &first, const LAllocation &second) 
+    {
+        setOperand(0, first);
+        setOperand(1, second);
+    }
+
+    const LAllocation *first() {
+        return this->getOperand(0);
+    }
+    const LAllocation *second() {
+        return this->getOperand(1);
+    }
+    const LDefinition *output() {
+        return this->getDef(0);
+    }
+    MMinMax *mir() const {
+        return mir_->toMinMax();
+    }
+};
+
 // Absolute value of an integer.
 class LAbsI : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(AbsI);
     LAbsI(const LAllocation &num) {
         setOperand(0, num);
     }
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -54,16 +54,18 @@
     _(CompareS)                     \
     _(CompareV)                     \
     _(CompareAndBranch)             \
     _(CompareDAndBranch)            \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
     _(IsNullOrUndefined)            \
     _(IsNullOrUndefinedAndBranch)   \
+    _(MinMaxI)                      \
+    _(MinMaxD)                      \
     _(AbsI)                         \
     _(AbsD)                         \
     _(SqrtD)                        \
     _(PowI)                         \
     _(PowD)                         \
     _(MathFunctionD)                \
     _(NotI)                         \
     _(NotD)                         \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -678,16 +678,32 @@ LIRGenerator::visitRound(MRound *ins)
     JS_ASSERT(ins->num()->type() == MIRType_Double);
     LRound *lir = new LRound(useRegister(ins->num()), tempFloat());
     if (!assignSnapshot(lir))
         return false;
     return define(lir, ins);
 }
 
 bool
+LIRGenerator::visitMinMax(MMinMax *ins)
+{
+    MDefinition *first = ins->getOperand(0);
+    MDefinition *second = ins->getOperand(1);
+
+    if (ins->specialization() == MIRType_Int32) {
+        ReorderCommutative(&first, &second);
+        LMinMaxI *lir = new LMinMaxI(useRegisterAtStart(first), useRegisterOrConstant(second));
+        return defineReuseInput(lir, ins, 0);
+    } else {
+        LMinMaxD *lir = new LMinMaxD(useRegisterAtStart(first), useRegister(second));
+        return defineReuseInput(lir, ins, 0);
+    }
+}
+
+bool
 LIRGenerator::visitAbs(MAbs *ins)
 {
     MDefinition *num = ins->num();
 
     if (num->type() == MIRType_Int32) {
         LAbsI *lir = new LAbsI(useRegisterAtStart(num));
         // needed to handle abs(INT32_MIN)
         if (!ins->range()->isFinite() && !assignSnapshot(lir))
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -99,16 +99,17 @@ class LIRGenerator : public LIRGenerator
     bool visitBitAnd(MBitAnd *ins);
     bool visitBitOr(MBitOr *ins);
     bool visitBitXor(MBitXor *ins);
     bool visitLsh(MLsh *ins);
     bool visitRsh(MRsh *ins);
     bool visitUrsh(MUrsh *ins);
     bool visitFloor(MFloor *ins);
     bool visitRound(MRound *ins);
+    bool visitMinMax(MMinMax *ins);
     bool visitAbs(MAbs *ins);
     bool visitSqrt(MSqrt *ins);
     bool visitPow(MPow *ins);
     bool visitMathFunction(MMathFunction *ins);
     bool visitAdd(MAdd *ins);
     bool visitSub(MSub *ins);
     bool visitMul(MMul *ins);
     bool visitDiv(MDiv *ins);
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -32,16 +32,20 @@ IonBuilder::inlineNativeCall(JSNative na
     if (native == js_math_abs)
         return inlineMathAbs(argc, constructing);
     if (native == js_math_floor)
         return inlineMathFloor(argc, constructing);
     if (native == js_math_round)
         return inlineMathRound(argc, constructing);
     if (native == js_math_sqrt)
         return inlineMathSqrt(argc, constructing);
+    if (native == js_math_max)
+        return inlineMathMinMax(true /* max */, argc, constructing);
+    if (native == js_math_min)
+        return inlineMathMinMax(false /* max */, argc, constructing);
     if (native == js_math_pow)
         return inlineMathPow(argc, constructing);
     if (native == js::math_sin)
         return inlineMathFunction(MMathFunction::Sin, argc, constructing);
     if (native == js::math_cos)
         return inlineMathFunction(MMathFunction::Cos, argc, constructing);
     if (native == js::math_tan)
         return inlineMathFunction(MMathFunction::Tan, argc, constructing);
@@ -538,16 +542,50 @@ IonBuilder::inlineMathPow(uint32 argc, b
 
     MPow *ins = MPow::New(argv[1], argv[2], arg2Type);
     current->add(ins);
     current->push(ins);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineMathMinMax(bool max, uint32 argc, bool constructing)
+{
+    if (argc != 2 || constructing)
+        return InliningStatus_NotInlined;
+
+    MIRType returnType = getInlineReturnType();
+    if (returnType != MIRType_Double && returnType != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    MIRType arg1Type = getInlineArgType(argc, 1);
+    if (arg1Type != MIRType_Double && arg1Type != MIRType_Int32)
+        return InliningStatus_NotInlined;
+    MIRType arg2Type = getInlineArgType(argc, 2);
+    if (arg2Type != MIRType_Double && arg2Type != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    if (returnType == MIRType_Int32 &&
+        (arg1Type == MIRType_Double || arg2Type == MIRType_Double))
+    {
+        // We would need to inform TI, if we happen to return a double.
+        return InliningStatus_NotInlined;
+    }
+
+    MDefinitionVector argv;
+    if (!discardCall(argc, argv, current))
+        return InliningStatus_Error;
+
+    MMinMax *ins = MMinMax::New(argv[1], argv[2], returnType, max);
+    current->add(ins);
+    current->push(ins);
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineStrCharCodeAt(uint32 argc, bool constructing)
 {
     if (argc != 1 || constructing)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
     if (getInlineArgType(argc, 0) != MIRType_String)
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -2118,16 +2118,61 @@ class MBinaryArithInstruction
     }
     AliasSet getAliasSet() const {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
     }
 };
 
+class MMinMax
+  : public MBinaryInstruction,
+    public ArithPolicy
+{
+    bool isMax_;
+
+    MMinMax(MDefinition *left, MDefinition *right, MIRType type, bool isMax)
+      : MBinaryInstruction(left, right),
+        isMax_(isMax)
+    {
+        JS_ASSERT(type == MIRType_Double || type == MIRType_Int32);
+        setResultType(type);
+        setMovable();
+        specialization_ = type;
+    }
+
+  public:
+    INSTRUCTION_HEADER(MinMax);
+    static MMinMax *New(MDefinition *left, MDefinition *right, MIRType type, bool isMax) {
+        return new MMinMax(left, right, type, isMax);
+    }
+
+    bool isMax() const {
+        return isMax_;
+    }
+    MIRType specialization() const {
+        return specialization_;
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+    bool congruentTo(MDefinition *const &ins) const {
+        if (!ins->isMinMax())
+            return false;
+        if (isMax() != ins->toMinMax()->isMax())
+            return false;
+        return congruentIfOperandsEqual(ins);
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 class MAbs
   : public MUnaryInstruction,
     public ArithPolicy
 {
     MAbs(MDefinition *num, MIRType type)
       : MUnaryInstruction(num)
     {
         JS_ASSERT(type == MIRType_Double || type == MIRType_Int32);
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -38,16 +38,17 @@ namespace ion {
     _(TypeOf)                                                               \
     _(ToId)                                                                 \
     _(BitAnd)                                                               \
     _(BitOr)                                                                \
     _(BitXor)                                                               \
     _(Lsh)                                                                  \
     _(Rsh)                                                                  \
     _(Ursh)                                                                 \
+    _(MinMax)                                                               \
     _(Abs)                                                                  \
     _(Sqrt)                                                                 \
     _(Pow)                                                                  \
     _(PowHalf)                                                              \
     _(MathFunction)                                                         \
     _(Add)                                                                  \
     _(Sub)                                                                  \
     _(Mul)                                                                  \
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -271,16 +271,61 @@ CodeGeneratorARM::visitOutOfLineBailout(
     masm.ma_mov(Imm32(ool->snapshot()->snapshotOffset()), ScratchRegister);
     masm.ma_push(ScratchRegister);
     masm.ma_push(ScratchRegister);
     masm.ma_b(deoptLabel_);
     return true;
 }
 
 bool
+CodeGeneratorARM::visitMinMaxD(LMinMaxD *ins)
+{
+    FloatRegister first = ToFloatRegister(ins->first());
+    FloatRegister second = ToFloatRegister(ins->second());
+    FloatRegister output = ToFloatRegister(ins->output());
+
+    JS_ASSERT(first == output);
+
+    Assembler::Condition cond = ins->mir()->isMax()
+        ? Assembler::VFP_LessThanOrEqual
+        : Assembler::VFP_GreaterThanOrEqual;
+    Label nan, equal, returnSecond, done;
+
+    masm.compareDouble(first, second);
+    masm.ma_b(&nan, Assembler::VFP_Unordered); // first or second is NaN, result is NaN.
+    masm.ma_b(&equal, Assembler::VFP_Equal); // make sure we handle -0 and 0 right.
+    masm.ma_b(&returnSecond, cond);
+    masm.ma_b(&done);
+
+    // Check for zero.
+    masm.bind(&equal);
+    masm.compareDouble(first, InvalidFloatReg);
+    masm.ma_b(&done, Assembler::VFP_NotEqualOrUnordered); // first wasn't 0 or -0, so just return it.
+    // So now both operands are either -0 or 0.
+    if (ins->mir()->isMax()) {
+        masm.ma_vadd(second, first, first); // -0 + -0 = -0 and -0 + 0 = 0.
+    } else {
+        masm.ma_vneg(first, first);
+        masm.ma_vsub(first, second, first);
+        masm.ma_vneg(first, first);
+    }
+    masm.ma_b(&done);
+
+    masm.bind(&nan);
+    masm.loadStaticDouble(&js_NaN, output);
+    masm.ma_b(&done);
+
+    masm.bind(&returnSecond);
+    masm.ma_vmov(second, output);
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
 CodeGeneratorARM::visitAbsD(LAbsD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     masm.as_vabs(input, input);
     return true;
 }
 
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -61,16 +61,17 @@ class CodeGeneratorARM : public CodeGene
     void emitSet(Assembler::Condition cond, const Register &dest);
 
     // Emits a branch that directs control flow to the true block if |cond| is
     // true, and the false block if |cond| is false.
     void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse);
 
   public:
     // Instruction visitors.
+    virtual bool visitMinMaxD(LMinMaxD *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitBitNotI(LBitNotI *ins);
     virtual bool visitBitOpI(LBitOpI *ins);
 
     virtual bool visitMulI(LMulI *ins);
--- a/js/src/ion/shared/Assembler-x86-shared.h
+++ b/js/src/ion/shared/Assembler-x86-shared.h
@@ -1050,16 +1050,19 @@ class AssemblerX86Shared
         masm.mulsd_rr(src.code(), dest.code());
     }
     void divsd(const FloatRegister &src, const FloatRegister &dest) {
         masm.divsd_rr(src.code(), dest.code());
     }
     void xorpd(const FloatRegister &src, const FloatRegister &dest) {
         masm.xorpd_rr(src.code(), dest.code());
     }
+    void orpd(const FloatRegister &src, const FloatRegister &dest) {
+        masm.orpd_rr(src.code(), dest.code());
+    }
     void andpd(const FloatRegister &src, const FloatRegister &dest) {
         masm.andpd_rr(src.code(), dest.code());
     }
     void sqrtsd(const FloatRegister &src, const FloatRegister &dest) {
         masm.sqrtsd_rr(src.code(), dest.code());
     }
     void roundsd(const FloatRegister &src, const FloatRegister &dest,
                  JSC::X86Assembler::RoundingMode mode)
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -397,16 +397,60 @@ CodeGeneratorX86Shared::visitOutOfLineBa
     if (!deoptLabel_)
         deoptLabel_ = new HeapLabel();
 
     masm.push(Imm32(ool->snapshot()->snapshotOffset()));
     masm.jmp(deoptLabel_);
     return true;
 }
 
+
+bool
+CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins)
+{
+    FloatRegister first = ToFloatRegister(ins->first());
+    FloatRegister second = ToFloatRegister(ins->second());
+    FloatRegister output = ToFloatRegister(ins->output());
+
+    JS_ASSERT(first == output);
+
+    Assembler::Condition cond = ins->mir()->isMax()
+                               ? Assembler::Above
+                               : Assembler::Below;
+    Label nan, equal, returnSecond, done;
+
+    masm.ucomisd(second, first);
+    masm.j(Assembler::Parity, &nan); // first or second is NaN, result is NaN.
+    masm.j(Assembler::Equal, &equal); // make sure we handle -0 and 0 right.
+    masm.j(cond, &returnSecond);
+    masm.jmp(&done);
+
+    // Check for zero.
+    masm.bind(&equal);
+    masm.xorpd(ScratchFloatReg, ScratchFloatReg);
+    masm.ucomisd(first, ScratchFloatReg);
+    masm.j(Assembler::NotEqual, &done); // first wasn't 0 or -0, so just return it.
+    // So now both operands are either -0 or 0.
+    if (ins->mir()->isMax())
+        masm.addsd(second, first); // -0 + -0 = -0 and -0 + 0 = 0.
+    else
+        masm.orpd(second, first); // This just ors the sign bit.
+    masm.jmp(&done);
+
+    masm.bind(&nan);
+    masm.loadStaticDouble(&js_NaN, output);
+    masm.jmp(&done);
+
+    masm.bind(&returnSecond);
+    masm.movsd(second, output);
+
+    masm.bind(&done);
+    return true;
+}
+
 bool
 CodeGeneratorX86Shared::visitAbsD(LAbsD *ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     JS_ASSERT(input == ToFloatRegister(ins->output()));
     masm.xorpd(ScratchFloatReg, ScratchFloatReg);
     masm.subsd(input, ScratchFloatReg); // negate the sign bit.
     masm.andpd(ScratchFloatReg, input); // s & ~s
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -80,16 +80,17 @@ class CodeGeneratorX86Shared : public Co
                     NaNCond ifNaN = NaN_Unexpected);
     void emitBranch(Assembler::DoubleCondition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse);
 
   public:
     CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph &graph);
 
   public:
     // Instruction visitors.
+    virtual bool visitMinMaxD(LMinMaxD *ins);
     virtual bool visitAbsD(LAbsD *ins);
     virtual bool visitSqrtD(LSqrtD *ins);
     virtual bool visitPowHalfD(LPowHalfD *ins);
     virtual bool visitAddI(LAddI *ins);
     virtual bool visitSubI(LSubI *ins);
     virtual bool visitMulI(LMulI *ins);
     virtual bool visitDivI(LDivI *ins);
     virtual bool visitModI(LModI *ins);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/mathMinMax.js
@@ -0,0 +1,42 @@
+var nan = Number.NaN;
+var negative_zero = -0;
+
+function max(a, b) {
+    return Math.max(a, b);
+}
+function min(a, b) {
+    return Math.min(a, b);
+}
+
+function main() {
+    for (var i = 0; i < 100; i++) {
+        assertEq(max(negative_zero, 0), 0);
+        assertEq(max(0, negative_zero), 0);
+        assertEq(min(0, negative_zero), negative_zero);
+        assertEq(min(negative_zero, 0), negative_zero);
+
+        assertEq(min(negative_zero, negative_zero), negative_zero);
+        assertEq(max(negative_zero, negative_zero), negative_zero);
+
+        assertEq(max(nan, 0), nan);
+        assertEq(min(nan, 0), nan);
+
+        assertEq(max(0, nan), nan);
+        assertEq(max(nan, 0), nan);
+
+        assertEq(max(3, 5), 5);
+        assertEq(max(5, 3), 5);
+
+        assertEq(min(3, 5), 3);
+        assertEq(min(5, 3), 3);
+
+        assertEq(max(Infinity, -Infinity), Infinity);
+        assertEq(min(Infinity, -Infinity), -Infinity);
+        assertEq(max(Infinity, nan), nan);
+
+        assertEq(max(negative_zero, -5), negative_zero);
+        assertEq(min(negative_zero, -5), -5);
+    }
+}
+
+main();
\ No newline at end of file