Bug 1311801 - Fold MTest to a Goto, if possible. r=h4writer
authorJohannes Schulte <j_schulte@outlook.com>
Thu, 04 Feb 2016 16:53:41 +0100
changeset 318905 f99da59c4383d121b57b4e97628ee5dd52bb4a3f
parent 318904 2a5ebc082cb2f1aee13e6a88b5b13d3531769a01
child 318906 01d621c2dbe3a92c8ea778fdd0b809120e03c6cd
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)
reviewersh4writer
bugs1311801
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 1311801 - Fold MTest to a Goto, if possible. r=h4writer
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/ValueNumbering.cpp
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -436,47 +436,112 @@ MTest::cacheOperandMightEmulateUndefined
 {
     MOZ_ASSERT(operandMightEmulateUndefined());
 
     if (!getOperand(0)->maybeEmulatesUndefined(constraints))
         markNoOperandEmulatesUndefined();
 }
 
 MDefinition*
-MTest::foldsTo(TempAllocator& alloc)
+MTest::foldsDoubleNegation(TempAllocator& alloc)
 {
     MDefinition* op = getOperand(0);
 
     if (op->isNot()) {
         // If the operand of the Not is itself a Not, they cancel out.
         MDefinition* opop = op->getOperand(0);
         if (opop->isNot())
             return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse());
         return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue());
     }
-
+    return nullptr;
+}
+
+MDefinition*
+MTest::foldsConstant(TempAllocator& alloc)
+{
+    MDefinition* op = getOperand(0);
     if (MConstant* opConst = op->maybeConstantValue()) {
         bool b;
         if (opConst->valueToBoolean(&b))
             return MGoto::New(alloc, b ? ifTrue() : ifFalse());
     }
+    return nullptr;
+}
+
+MDefinition*
+MTest::foldsTypes(TempAllocator& alloc)
+{
+    MDefinition* op = getOperand(0);
 
     switch (op->type()) {
       case MIRType::Undefined:
       case MIRType::Null:
         return MGoto::New(alloc, ifFalse());
       case MIRType::Symbol:
         return MGoto::New(alloc, ifTrue());
       case MIRType::Object:
         if (!operandMightEmulateUndefined())
             return MGoto::New(alloc, ifTrue());
         break;
       default:
         break;
     }
+    return nullptr;
+}
+
+MDefinition*
+MTest::foldsNeedlessControlFlow(TempAllocator& alloc)
+{
+    for (MInstructionIterator iter(ifTrue()->begin()), end(ifTrue()->end()); iter != end; ) {
+        MInstruction* ins = *iter++;
+        if (ins->isNop() || ins->isGoto())
+            continue;
+        if (ins->hasUses())
+            return nullptr;
+        if (!DeadIfUnused(ins))
+            return nullptr;
+    }
+
+    for (MInstructionIterator iter(ifFalse()->begin()), end(ifFalse()->end()); iter != end; ) {
+        MInstruction* ins = *iter++;
+        if (ins->isNop() || ins->isGoto())
+            continue;
+        if (ins->hasUses())
+            return nullptr;
+        if (!DeadIfUnused(ins))
+            return nullptr;
+    }
+
+    if (ifTrue()->numSuccessors() != 1 || ifFalse()->numSuccessors() != 1)
+        return nullptr;
+    if (ifTrue()->getSuccessor(0) != ifFalse()->getSuccessor(0))
+        return nullptr;
+
+    if (ifTrue()->successorWithPhis())
+        return nullptr;
+
+    return MGoto::New(alloc, ifTrue());
+}
+
+MDefinition*
+MTest::foldsTo(TempAllocator& alloc)
+{
+
+    if (MDefinition* def = foldsDoubleNegation(alloc))
+        return def;
+
+    if (MDefinition* def = foldsConstant(alloc))
+        return def;
+
+    if (MDefinition* def = foldsTypes(alloc))
+        return def;
+
+    if (MDefinition* def = foldsNeedlessControlFlow(alloc))
+        return def;
 
     return this;
 }
 
 void
 MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
                               bool* filtersNull)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -3100,16 +3100,20 @@ class MTest
     }
 
     // We cache whether our operand might emulate undefined, but we don't want
     // to do that from New() or the constructor, since those can be called on
     // background threads.  So make callers explicitly call it if they want us
     // to check whether the operand might do this.  If this method is never
     // called, we'll assume our operand can emulate undefined.
     void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
+    MDefinition* foldsDoubleNegation(TempAllocator& alloc);
+    MDefinition* foldsConstant(TempAllocator& alloc);
+    MDefinition* foldsTypes(TempAllocator& alloc);
+    MDefinition* foldsNeedlessControlFlow(TempAllocator& alloc);
     MDefinition* foldsTo(TempAllocator& alloc) override;
     void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
                                 bool* filtersNull);
 
     void markNoOperandEmulatesUndefined() {
         operandMightEmulateUndefined_ = false;
     }
     bool operandMightEmulateUndefined() const {
--- a/js/src/jit/ValueNumbering.cpp
+++ b/js/src/jit/ValueNumbering.cpp
@@ -801,16 +801,22 @@ ValueNumberer::visitDefinition(MDefiniti
             if (!discardDefsRecursively(def))
                 return false;
 
             // If that ended up discarding |sim|, then we're done here.
             if (sim->isDiscarded())
                 return true;
         }
 
+        if (!rerun_ && def->isPhi() && !sim->isPhi()) {
+            rerun_ = true;
+            JitSpew(JitSpew_GVN, "      Replacing phi%u may have enabled cascading optimisations; "
+                                 "will re-run", def->id());
+        }
+
         // Otherwise, procede to optimize with |sim| in place of |def|.
         def = sim;
 
         // If the simplified instruction was already part of the graph, then we
         // probably already visited and optimized this instruction.
         if (!isNewInstruction)
             return true;
     }