Bug 1029830 - IonMonkey: Fold MTest(Not(Not(x))) and Not(Not(Not(x))) r=nbp
authorDan Gohman <sunfish@mozilla.com>
Wed, 17 Sep 2014 10:27:25 -0700
changeset 205743 ff831540e312febfe640805f1cdc207ad8530185
parent 205742 6e9a69bead52911e66eb586184c558259a344caa
child 205744 8f27a48a25d5a7acabf69867597f3dfe0f951cdd
push id49264
push userdgohman@mozilla.com
push dateWed, 17 Sep 2014 17:27:46 +0000
treeherdermozilla-inbound@ce0a75f9481b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1029830
milestone35.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 1029830 - IonMonkey: Fold MTest(Not(Not(x))) and Not(Not(Not(x))) r=nbp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jsapi-tests/testJitFoldsTo.cpp
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -310,18 +310,23 @@ MTest::cacheOperandMightEmulateUndefined
         markOperandCantEmulateUndefined();
 }
 
 MDefinition *
 MTest::foldsTo(TempAllocator &alloc)
 {
     MDefinition *op = getOperand(0);
 
-    if (op->isNot())
+    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 this;
 }
 
 void
 MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition **subject, bool *filtersUndefined,
                               bool *filtersNull)
 {
@@ -2983,16 +2988,26 @@ MNot::foldsTo(TempAllocator &alloc)
         bool result = input()->toConstant()->valueToBoolean();
         if (type() == MIRType_Int32)
             return MConstant::New(alloc, Int32Value(!result));
 
         // ToBoolean can't cause side effects, so this is safe.
         return MConstant::New(alloc, BooleanValue(!result));
     }
 
+    // If the operand of the Not is itself a Not, they cancel out. But we can't
+    // always convert Not(Not(x)) to x because that may loose the conversion to
+    // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
+    MDefinition *op = getOperand(0);
+    if (op->isNot()) {
+        MDefinition *opop = op->getOperand(0);
+        if (opop->isNot())
+            return opop;
+    }
+
     // NOT of an undefined or null value is always true
     if (input()->type() == MIRType_Undefined || input()->type() == MIRType_Null)
         return MConstant::New(alloc, BooleanValue(true));
 
     // NOT of an object that can't emulate undefined is always false.
     if (input()->type() == MIRType_Object && !operandMightEmulateUndefined())
         return MConstant::New(alloc, BooleanValue(false));
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7222,26 +7222,26 @@ class MSetTypedObjectOffset
 // Perform !-operation
 class MNot
   : public MUnaryInstruction,
     public TestPolicy
 {
     bool operandMightEmulateUndefined_;
     bool operandIsNeverNaN_;
 
-  public:
     explicit MNot(MDefinition *input)
       : MUnaryInstruction(input),
         operandMightEmulateUndefined_(true),
         operandIsNeverNaN_(false)
     {
         setResultType(MIRType_Boolean);
         setMovable();
     }
 
+  public:
     static MNot *New(TempAllocator &alloc, MDefinition *elements) {
         return new(alloc) MNot(elements);
     }
     static MNot *NewAsmJS(TempAllocator &alloc, MDefinition *elements) {
         MNot *ins = new(alloc) MNot(elements);
         ins->setResultType(MIRType_Int32);
         return ins;
     }
--- a/js/src/jsapi-tests/testJitFoldsTo.cpp
+++ b/js/src/jsapi-tests/testJitFoldsTo.cpp
@@ -65,8 +65,145 @@ BEGIN_TEST(testJitFoldsTo_NoDivReciproca
     // Test that the div didn't get folded.
     MDefinition *op = ret->getOperand(0);
     CHECK(op->isDiv());
     CHECK(op->getOperand(0) == p);
     CHECK(op->getOperand(1) == c);
     return true;
 }
 END_TEST(testJitFoldsTo_NoDivReciprocal)
+
+BEGIN_TEST(testJitNotNot)
+{
+    MinimalFunc func;
+    MBasicBlock *block = func.createEntryBlock();
+
+    // return Not(Not(p))
+    MParameter *p = func.createParameter();
+    block->add(p);
+    MNot *not0 = MNot::New(func.alloc, p);
+    block->add(not0);
+    MNot *not1 = MNot::New(func.alloc, not0);
+    block->add(not1);
+    MReturn *ret = MReturn::New(func.alloc, not1);
+    block->end(ret);
+
+    if (!func.runGVN())
+        return false;
+
+    // Test that the nots did not get folded.
+    MDefinition *op = ret->getOperand(0);
+    CHECK(op->isNot());
+    CHECK(op->getOperand(0)->isNot());
+    CHECK(op->getOperand(0)->getOperand(0) == p);
+    return true;
+}
+END_TEST(testJitNotNot)
+
+BEGIN_TEST(testJitNotNotNot)
+{
+    MinimalFunc func;
+    MBasicBlock *block = func.createEntryBlock();
+
+    // return Not(Not(Not(p)))
+    MParameter *p = func.createParameter();
+    block->add(p);
+    MNot *not0 = MNot::New(func.alloc, p);
+    block->add(not0);
+    MNot *not1 = MNot::New(func.alloc, not0);
+    block->add(not1);
+    MNot *not2 = MNot::New(func.alloc, not1);
+    block->add(not2);
+    MReturn *ret = MReturn::New(func.alloc, not2);
+    block->end(ret);
+
+    if (!func.runGVN())
+        return false;
+
+    // Test that the nots got folded.
+    MDefinition *op = ret->getOperand(0);
+    CHECK(op->isNot());
+    CHECK(op->getOperand(0) == p);
+    return true;
+}
+END_TEST(testJitNotNotNot)
+
+BEGIN_TEST(testJitNotTest)
+{
+    MinimalFunc func;
+    MBasicBlock *block = func.createEntryBlock();
+    MBasicBlock *then = func.createBlock(block);
+    MBasicBlock *else_ = func.createBlock(block);
+    MBasicBlock *exit = func.createBlock(block);
+
+    // MTest(Not(p))
+    MParameter *p = func.createParameter();
+    block->add(p);
+    MNot *not0 = MNot::New(func.alloc, p);
+    block->add(not0);
+    MTest *test = MTest::New(func.alloc, not0, then, else_);
+    block->end(test);
+
+    MNop *anchor = MNop::New(func.alloc);
+    anchor->setGuard();
+    then->add(anchor);
+    then->end(MGoto::New(func.alloc, exit));
+
+    else_->end(MGoto::New(func.alloc, exit));
+
+    MReturn *ret = MReturn::New(func.alloc, p);
+    exit->end(ret);
+
+    exit->addPredecessorWithoutPhis(then);
+
+    if (!func.runGVN())
+        return false;
+
+    // Test that the not got folded.
+    test = block->lastIns()->toTest();
+    CHECK(test->getOperand(0) == p);
+    CHECK(test->getSuccessor(0) == else_);
+    CHECK(test->getSuccessor(1) == then);
+    return true;
+}
+END_TEST(testJitNotTest)
+
+BEGIN_TEST(testJitNotNotTest)
+{
+    MinimalFunc func;
+    MBasicBlock *block = func.createEntryBlock();
+    MBasicBlock *then = func.createBlock(block);
+    MBasicBlock *else_ = func.createBlock(block);
+    MBasicBlock *exit = func.createBlock(block);
+
+    // MTest(Not(Not(p)))
+    MParameter *p = func.createParameter();
+    block->add(p);
+    MNot *not0 = MNot::New(func.alloc, p);
+    block->add(not0);
+    MNot *not1 = MNot::New(func.alloc, not0);
+    block->add(not1);
+    MTest *test = MTest::New(func.alloc, not1, then, else_);
+    block->end(test);
+
+    MNop *anchor = MNop::New(func.alloc);
+    anchor->setGuard();
+    then->add(anchor);
+    then->end(MGoto::New(func.alloc, exit));
+
+    else_->end(MGoto::New(func.alloc, exit));
+
+    MReturn *ret = MReturn::New(func.alloc, p);
+    exit->end(ret);
+
+    exit->addPredecessorWithoutPhis(then);
+
+    if (!func.runGVN())
+        return false;
+
+    // Test that the nots got folded.
+    test = block->lastIns()->toTest();
+    CHECK(test->getOperand(0) == p);
+    CHECK(test->getSuccessor(0) == then);
+    CHECK(test->getSuccessor(1) == else_);
+    return true;
+}
+END_TEST(testJitNotNotTest)