Bug 1176230 - Try to fold ternary's with double-argument to NaNToZero. r=nbp
authorJohannes Schulte <j_schulte@outlook.com>
Fri, 24 Jun 2016 19:02:23 +0200
changeset 318906 01d621c2dbe3a92c8ea778fdd0b809120e03c6cd
parent 318905 f99da59c4383d121b57b4e97628ee5dd52bb4a3f
child 318907 4af41c7922f2cf7bfd28d60a02fc9823dcbb87aa
push id30854
push userryanvm@gmail.com
push dateFri, 21 Oct 2016 21:08:02 +0000
treeherdermozilla-central@806054dd12bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1176230
milestone52.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 1176230 - Try to fold ternary's with double-argument to NaNToZero. r=nbp
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/RangeAnalysis.cpp
js/src/jit/RangeAnalysis.h
js/src/jit/Recover.cpp
js/src/jit/Recover.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11985,10 +11985,53 @@ CodeGenerator::visitRotate(LRotate* ins)
         Register creg = ToRegister(count);
         if (mir->isLeftRotate())
             masm.rotateLeft(creg, input, dest);
         else
             masm.rotateRight(creg, input, dest);
     }
 }
 
+class OutOfLineNaNToZero : public OutOfLineCodeBase<CodeGenerator>
+{
+    LNaNToZero* lir_;
+
+  public:
+    explicit OutOfLineNaNToZero(LNaNToZero* lir)
+      : lir_(lir)
+    {}
+
+    void accept(CodeGenerator* codegen) {
+        codegen->visitOutOfLineNaNToZero(this);
+    }
+    LNaNToZero* lir() const {
+        return lir_;
+    }
+};
+
+void
+CodeGenerator::visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool)
+{
+    FloatRegister output = ToFloatRegister(ool->lir()->output());
+    masm.loadConstantDouble(0.0, output);
+    masm.jump(ool->rejoin());
+}
+
+void
+CodeGenerator::visitNaNToZero(LNaNToZero* lir)
+{
+    FloatRegister input = ToFloatRegister(lir->input());
+
+    OutOfLineNaNToZero* ool = new(alloc()) OutOfLineNaNToZero(lir);
+    addOutOfLineCode(ool, lir->mir());
+
+    if (lir->mir()->operandIsNeverNegativeZero()){
+        masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry());
+    } else {
+        FloatRegister scratch = ToFloatRegister(lir->tempDouble());
+        masm.loadConstantDouble(0.0, scratch);
+        masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, ool->entry());
+    }
+    masm.bind(ool->rejoin());
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -47,16 +47,17 @@ class OutOfLineCallPostWriteElementBarri
 class OutOfLineIsCallable;
 class OutOfLineIsConstructor;
 class OutOfLineRegExpMatcher;
 class OutOfLineRegExpSearcher;
 class OutOfLineRegExpTester;
 class OutOfLineRegExpPrototypeOptimizable;
 class OutOfLineRegExpInstanceOptimizable;
 class OutOfLineLambdaArrow;
+class OutOfLineNaNToZero;
 
 class CodeGenerator final : public CodeGeneratorSpecific
 {
     void generateArgumentsChecks(bool bailout = true);
     MOZ_MUST_USE bool generateBody();
 
     ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n, MIRType type);
 
@@ -378,16 +379,18 @@ class CodeGenerator final : public CodeG
     void visitGlobalNameConflictsCheck(LGlobalNameConflictsCheck* ins);
     void visitDebugger(LDebugger* ins);
     void visitNewTarget(LNewTarget* ins);
     void visitArrowNewTarget(LArrowNewTarget* ins);
     void visitCheckReturn(LCheckReturn* ins);
     void visitCheckIsObj(LCheckIsObj* ins);
     void visitCheckObjCoercible(LCheckObjCoercible* ins);
     void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins);
+    void visitNaNToZero(LNaNToZero* ins);
+    void visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool);
 
     void visitCheckOverRecursed(LCheckOverRecursed* lir);
     void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
 
     void visitUnboxFloatingPoint(LUnboxFloatingPoint* lir);
     void visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint* ool);
     void visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool);
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4836,16 +4836,29 @@ LIRGenerator::visitBlock(MBasicBlock* bl
 
     // Now emit the last instruction, which is some form of branch.
     if (!visitInstruction(block->lastIns()))
         return false;
 
     return true;
 }
 
+void
+LIRGenerator::visitNaNToZero(MNaNToZero *ins)
+{
+    MDefinition* input = ins->input();
+
+    if (ins->operandIsNeverNaN() && ins->operandIsNeverNegativeZero()) {
+        redefine(ins, input);
+        return;
+    }
+    LNaNToZero* lir = new(alloc()) LNaNToZero(useRegisterAtStart(input), tempDouble());
+    defineReuseInput(lir, ins, 0);
+}
+
 bool
 LIRGenerator::generate()
 {
     // Create all blocks and prep all phis beforehand.
     for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) {
         if (gen->shouldCancel("Lowering (preparation loop)"))
             return false;
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -317,16 +317,17 @@ class LIRGenerator : public LIRGenerator
     void visitArrayState(MArrayState* ins);
     void visitUnknownValue(MUnknownValue* ins);
     void visitLexicalCheck(MLexicalCheck* ins);
     void visitThrowRuntimeLexicalError(MThrowRuntimeLexicalError* ins);
     void visitGlobalNameConflictsCheck(MGlobalNameConflictsCheck* ins);
     void visitDebugger(MDebugger* ins);
     void visitNewTarget(MNewTarget* ins);
     void visitArrowNewTarget(MArrowNewTarget* ins);
+    void visitNaNToZero(MNaNToZero *ins);
     void visitAtomicIsLockFree(MAtomicIsLockFree* ins);
     void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins);
     void visitCheckReturn(MCheckReturn* ins);
     void visitCheckIsObj(MCheckIsObj* ins);
     void visitCheckObjCoercible(MCheckObjCoercible* ins);
     void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
 };
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2321,17 +2321,17 @@ void
 MPhi::removeAllOperands()
 {
     for (MUse& p : inputs_)
         p.producer()->removeUse(&p);
     inputs_.clear();
 }
 
 MDefinition*
-MPhi::foldsTernary()
+MPhi::foldsTernary(TempAllocator& alloc)
 {
     /* Look if this MPhi is a ternary construct.
      * This is a very loose term as it actually only checks for
      *
      *      MTest X
      *       /  \
      *    ...    ...
      *       \  /
@@ -2411,16 +2411,24 @@ MPhi::foldsTernary()
     // - fold testArg ? 0 : testArg to 0
     if (testArg->type() == MIRType::Int32 && c->numberToDouble() == 0) {
         // When folding to the constant we need to hoist it.
         if (trueDef == c && !c->block()->dominates(block()))
             c->block()->moveBefore(pred->lastIns(), c);
         return trueDef;
     }
 
+    // If testArg is an double type we can:
+    // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
+    if (testArg->type() == MIRType::Double && c->numberToDouble() == 0 && c != trueDef) {
+        MNaNToZero* replace = MNaNToZero::New(alloc, testArg);
+        test->block()->insertBefore(test, replace);
+        return replace;
+    }
+
     // If testArg is a string type we can:
     // - fold testArg ? testArg : "" to testArg
     // - fold testArg ? "" : testArg to ""
     if (testArg->type() == MIRType::String &&
         c->toString() == GetJitContext()->runtime->emptyString())
     {
         // When folding to the constant we need to hoist it.
         if (trueDef == c && !c->block()->dominates(block()))
@@ -2490,17 +2498,17 @@ MPhi::foldsFilterTypeSet()
 }
 
 MDefinition*
 MPhi::foldsTo(TempAllocator& alloc)
 {
     if (MDefinition* def = operandIfRedundant())
         return def;
 
-    if (MDefinition* def = foldsTernary())
+    if (MDefinition* def = foldsTernary(alloc))
         return def;
 
     if (MDefinition* def = foldsFilterTypeSet())
         return def;
 
     return this;
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7651,17 +7651,17 @@ class MPhi final
         MOZ_ALWAYS_TRUE(addInputSlow(ins));
     }
 
     // Update the type of this phi after adding |ins| as an input. Set
     // |*ptypeChange| to true if the type changed.
     bool checkForTypeChange(TempAllocator& alloc, MDefinition* ins, bool* ptypeChange);
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
-    MDefinition* foldsTernary();
+    MDefinition* foldsTernary(TempAllocator& alloc);
     MDefinition* foldsFilterTypeSet();
 
     bool congruentTo(const MDefinition* ins) const override;
 
     bool isIterator() const {
         return isIterator_;
     }
     void setIterator() {
@@ -7725,16 +7725,57 @@ class MBeta
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     void computeRange(TempAllocator& alloc) override;
 };
 
+// If input evaluates to false (i.e. it's NaN, 0 or -0), 0 is returned, else the input is returned
+class MNaNToZero
+  : public MUnaryInstruction,
+    public DoublePolicy<0>::Data
+{
+    bool operandIsNeverNaN_;
+    bool operandIsNeverNegativeZero_;
+    explicit MNaNToZero(MDefinition* input)
+      : MUnaryInstruction(input), operandIsNeverNaN_(false), operandIsNeverNegativeZero_(false)
+    {
+        setResultType(MIRType::Double);
+        setMovable();
+    }
+  public:
+    INSTRUCTION_HEADER(NaNToZero)
+    TRIVIAL_NEW_WRAPPERS
+
+    bool operandIsNeverNaN() const {
+        return operandIsNeverNaN_;
+    }
+
+    bool operandIsNeverNegativeZero() const {
+        return operandIsNeverNegativeZero_;
+    }
+
+    void collectRangeInfoPreTrunc() override;
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    void computeRange(TempAllocator& alloc) override;
+
+    bool writeRecoverData(CompactBufferWriter& writer) const override;
+    bool canRecoverOnBailout() const override {
+        return true;
+    }
+
+    ALLOW_CLONE(MNaNToZero)
+};
+
 // MIR representation of a Value on the OSR BaselineFrame.
 // The Value is indexed off of OsrFrameReg.
 class MOsrValue
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   private:
     ptrdiff_t frameOffset_;
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -41,16 +41,17 @@ namespace jit {
     _(Goto)                                                                 \
     _(Test)                                                                 \
     _(GotoWithFake)                                                         \
     _(ObjectGroupDispatch)                                                  \
     _(FunctionDispatch)                                                     \
     _(Compare)                                                              \
     _(Phi)                                                                  \
     _(Beta)                                                                 \
+    _(NaNToZero)                                                            \
     _(OsrValue)                                                             \
     _(OsrEnvironmentChain)                                                  \
     _(OsrReturnValue)                                                       \
     _(OsrArgumentsObject)                                                   \
     _(ReturnFromCtor)                                                       \
     _(BinarySharedStub)                                                     \
     _(UnarySharedStub)                                                      \
     _(NullarySharedStub)                                                    \
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1212,16 +1212,26 @@ Range::sign(TempAllocator& alloc, const 
 
     return new(alloc) Range(Max(Min(op->lower_, 1), -1),
                             Max(Min(op->upper_, 1), -1),
                             Range::ExcludesFractionalParts,
                             NegativeZeroFlag(op->canBeNegativeZero()),
                             0);
 }
 
+Range*
+Range::NaNToZero(TempAllocator& alloc, const Range *op)
+{
+    Range* copy = new(alloc) Range(*op);
+    if (copy->canBeNaN())
+        copy->max_exponent_ = Range::IncludesInfinity;
+    copy->refineToExcludeNegativeZero();
+    return copy;
+}
+
 bool
 Range::negativeZeroMul(const Range* lhs, const Range* rhs)
 {
     // The result can only be negative zero if both sides are finite and they
     // have differing signs.
     return (lhs->canHaveSignBitSet() && rhs->canBeFiniteNonNegative()) ||
            (rhs->canHaveSignBitSet() && lhs->canBeFiniteNonNegative());
 }
@@ -1869,16 +1879,23 @@ MRandom::computeRange(TempAllocator& all
     Range* r = Range::NewDoubleRange(alloc, 0.0, 1.0);
 
     // Random never returns negative zero.
     r->refineToExcludeNegativeZero();
 
     setRange(r);
 }
 
+void
+MNaNToZero::computeRange(TempAllocator& alloc)
+{
+    Range other(input());
+    setRange(Range::NaNToZero(alloc, &other));
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Range Analysis
 ///////////////////////////////////////////////////////////////////////////////
 
 bool
 RangeAnalysis::analyzeLoop(MBasicBlock* header)
 {
     MOZ_ASSERT(header->hasUniqueBackedge());
@@ -3461,16 +3478,27 @@ MBinaryBitwiseInstruction::collectRangeI
 
     if (rhs()->isConstant() && rhs()->type() == MIRType::Int32 &&
         DoesMaskMatchRange(rhs()->toConstant()->toInt32(), lhsRange))
     {
         maskMatchesLeftRange = true;
     }
 }
 
+void
+MNaNToZero::collectRangeInfoPreTrunc()
+{
+    Range inputRange(input());
+
+    if (!inputRange.canBeNaN())
+        operandIsNeverNaN_ = true;
+    if (!inputRange.canBeNegativeZero())
+        operandIsNeverNegativeZero_ = true;
+}
+
 bool
 RangeAnalysis::prepareForUCE(bool* shouldRemoveDeadCode)
 {
     *shouldRemoveDeadCode = false;
 
     for (ReversePostorderIterator iter(graph_.rpoBegin()); iter != graph_.rpoEnd(); iter++) {
         MBasicBlock* block = *iter;
 
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -477,16 +477,17 @@ class Range : public TempObject {
     static Range* rsh(TempAllocator& alloc, const Range* lhs, const Range* rhs);
     static Range* ursh(TempAllocator& alloc, const Range* lhs, const Range* rhs);
     static Range* abs(TempAllocator& alloc, const Range* op);
     static Range* min(TempAllocator& alloc, const Range* lhs, const Range* rhs);
     static Range* max(TempAllocator& alloc, const Range* lhs, const Range* rhs);
     static Range* floor(TempAllocator& alloc, const Range* op);
     static Range* ceil(TempAllocator& alloc, const Range* op);
     static Range* sign(TempAllocator& alloc, const Range* op);
+    static Range* NaNToZero(TempAllocator& alloc, const Range* op);
 
     static MOZ_MUST_USE bool negativeZeroMul(const Range* lhs, const Range* rhs);
 
     bool isUnknownInt32() const {
         return isInt32() && lower() == INT32_MIN && upper() == INT32_MAX;
     }
 
     bool isUnknown() const {
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1047,16 +1047,41 @@ RStringSplit::recover(JSContext* cx, Sna
         return false;
 
     result.setObject(*res);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
+MNaNToZero::writeRecoverData(CompactBufferWriter& writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_NaNToZero));
+    return true;
+}
+
+RNaNToZero::RNaNToZero(CompactBufferReader& reader)
+{ }
+
+
+bool
+RNaNToZero::recover(JSContext* cx, SnapshotIterator& iter) const
+{
+    RootedValue v(cx, iter.read());
+    RootedValue result(cx);
+
+    MOZ_ASSERT(v.isDouble());
+    result.setDouble((mozilla::IsNaN(v.toDouble()) || mozilla::IsNegativeZero(v.toDouble())) ? 0.0 : v.toDouble());
+
+    iter.storeInstructionResult(result);
+    return true;
+}
+
+bool
 MRegExpMatcher::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpMatcher));
     return true;
 }
 
 RRegExpMatcher::RRegExpMatcher(CompactBufferReader& reader)
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -85,16 +85,17 @@ namespace jit {
     _(MinMax)                                   \
     _(Abs)                                      \
     _(Sqrt)                                     \
     _(Atan2)                                    \
     _(Hypot)                                    \
     _(MathFunction)                             \
     _(Random)                                   \
     _(StringSplit)                              \
+    _(NaNToZero)                                \
     _(RegExpMatcher)                            \
     _(RegExpSearcher)                           \
     _(RegExpTester)                             \
     _(StringReplace)                            \
     _(TypeOf)                                   \
     _(ToDouble)                                 \
     _(ToFloat32)                                \
     _(TruncateToInt32)                          \
@@ -473,16 +474,24 @@ class RRandom final : public RInstructio
 class RStringSplit final : public RInstruction
 {
   public:
     RINSTRUCTION_HEADER_NUM_OP_(StringSplit, 3)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
+class RNaNToZero final : public RInstruction
+{
+  public:
+    RINSTRUCTION_HEADER_NUM_OP_(NaNToZero, 1);
+
+    bool recover(JSContext* cx, SnapshotIterator& iter) const;
+};
+
 class RRegExpMatcher final : public RInstruction
 {
   public:
     RINSTRUCTION_HEADER_NUM_OP_(RegExpMatcher, 3)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -4561,16 +4561,40 @@ class LPowHalfD : public LInstructionHel
 // register allocation as it doesn't need to sniff the snapshot out of the
 // LIRGraph.
 class LStart : public LInstructionHelper<0, 0, 0>
 {
   public:
     LIR_HEADER(Start)
 };
 
+class LNaNToZero : public LInstructionHelper<1, 1, 1>
+{
+  public:
+    LIR_HEADER(NaNToZero)
+
+    explicit LNaNToZero(const LAllocation& input, const LDefinition& tempDouble) {
+        setOperand(0, input);
+        setTemp(0, tempDouble);
+    }
+
+    const MNaNToZero* mir() {
+        return mir_->toNaNToZero();
+    }
+    const LAllocation* input() {
+        return getOperand(0);
+    }
+    const LDefinition* output() {
+        return getDef(0);
+    }
+    const LDefinition* tempDouble() {
+        return getTemp(0);
+    }
+};
+
 // Passed the BaselineFrame address in the OsrFrameReg by SideCannon().
 // Forwards this object to the LOsrValues for Value materialization.
 class LOsrEntry : public LInstructionHelper<1, 0, 1>
 {
   protected:
     Label label_;
     uint32_t frameDepth_;
 
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -217,16 +217,17 @@
     _(IntToString)                  \
     _(DoubleToString)               \
     _(ValueToString)                \
     _(ValueToObjectOrNull)          \
     _(Int32x4ToFloat32x4)           \
     _(Float32x4ToInt32x4)           \
     _(Float32x4ToUint32x4)          \
     _(Start)                        \
+    _(NaNToZero)                    \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrEnvironmentChain)          \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
     _(RegExpMatcher)                \
     _(RegExpSearcher)               \