[turbofan] Improve code generation for inline comparisons with zero.
authorbmeurer@chromium.org <bmeurer@chromium.org>
Thu, 23 Oct 2014 10:22:06 +0000 (10:22 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org>
Thu, 23 Oct 2014 10:22:06 +0000 (10:22 +0000)
TEST=cctest,unittests
R=dcarney@chromium.org

Review URL: https://codereview.chromium.org/669133004

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24832 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/compiler/arm/instruction-selector-arm.cc
src/compiler/ia32/instruction-selector-ia32.cc
src/compiler/instruction-selector.h
src/compiler/machine-operator-reducer.cc
src/compiler/simplified-lowering.cc
src/compiler/typer.cc
src/compiler/x64/instruction-selector-x64.cc
test/cctest/compiler/test-run-machops.cc
test/cctest/compiler/test-simplified-lowering.cc
test/unittests/compiler/arm/instruction-selector-arm-unittest.cc

index 267a961..ec0c5b8 100644 (file)
@@ -900,8 +900,7 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node,
 
 // Shared routine for multiple word compare operations.
 void VisitWordCompare(InstructionSelector* selector, Node* node,
-                      InstructionCode opcode, FlagsContinuation* cont,
-                      bool commutative) {
+                      InstructionCode opcode, FlagsContinuation* cont) {
   ArmOperandGenerator g(selector);
   Int32BinopMatcher m(node);
   InstructionOperand* inputs[5];
@@ -915,7 +914,7 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
     input_count++;
   } else if (TryMatchImmediateOrShift(selector, &opcode, m.left().node(),
                                       &input_count, &inputs[1])) {
-    if (!commutative) cont->Commute();
+    if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
     inputs[0] = g.UseRegister(m.right().node());
     input_count++;
   } else {
@@ -944,81 +943,49 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
 
 void VisitWordCompare(InstructionSelector* selector, Node* node,
                       FlagsContinuation* cont) {
-  VisitWordCompare(selector, node, kArmCmp, cont, false);
+  VisitWordCompare(selector, node, kArmCmp, cont);
 }
 
 
-void VisitWordTest(InstructionSelector* selector, Node* node,
-                   FlagsContinuation* cont) {
-  ArmOperandGenerator g(selector);
-  InstructionCode opcode =
-      cont->Encode(kArmTst) | AddressingModeField::encode(kMode_Operand2_R);
-  if (cont->IsBranch()) {
-    selector->Emit(opcode, nullptr, g.UseRegister(node), g.UseRegister(node),
-                   g.Label(cont->true_block()),
-                   g.Label(cont->false_block()))->MarkAsControl();
-  } else {
-    selector->Emit(opcode, g.DefineAsRegister(cont->result()),
-                   g.UseRegister(node), g.UseRegister(node));
-  }
-}
-
-}  // namespace
-
-
-void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
-                                      BasicBlock* fbranch) {
-  ArmOperandGenerator g(this);
-  Node* user = branch;
-  Node* value = branch->InputAt(0);
-
-  FlagsContinuation cont(kNotEqual, tbranch, fbranch);
-
-  // If we can fall through to the true block, invert the branch.
-  if (IsNextInAssemblyOrder(tbranch)) {
-    cont.Negate();
-    cont.SwapBlocks();
-  }
-
-  // Try to combine with comparisons against 0 by simply inverting the branch.
-  while (CanCover(user, value) && value->opcode() == IrOpcode::kWord32Equal) {
-    Int32BinopMatcher m(value);
-    if (m.right().Is(0)) {
-      user = value;
-      value = m.left().node();
-      cont.Negate();
-    } else {
-      break;
-    }
-  }
-
-  // Try to combine the branch with a comparison.
-  if (CanCover(user, value)) {
+// Shared routine for word comparisons against zero.
+void VisitWordCompareZero(InstructionSelector* selector, Node* user,
+                          Node* value, FlagsContinuation* cont) {
+  while (selector->CanCover(user, value)) {
     switch (value->opcode()) {
-      case IrOpcode::kWord32Equal:
-        cont.OverwriteAndNegateIfEqual(kEqual);
-        return VisitWordCompare(this, value, &cont);
+      case IrOpcode::kWord32Equal: {
+        // Combine with comparisons against 0 by simply inverting the
+        // continuation.
+        Int32BinopMatcher m(value);
+        if (m.right().Is(0)) {
+          user = value;
+          value = m.left().node();
+          cont->Negate();
+          continue;
+        }
+        cont->OverwriteAndNegateIfEqual(kEqual);
+        return VisitWordCompare(selector, value, cont);
+      }
       case IrOpcode::kInt32LessThan:
-        cont.OverwriteAndNegateIfEqual(kSignedLessThan);
-        return VisitWordCompare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kSignedLessThan);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kInt32LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
-        return VisitWordCompare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kUint32LessThan:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThan);
-        return VisitWordCompare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kUint32LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
-        return VisitWordCompare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kFloat64Equal:
-        cont.OverwriteAndNegateIfEqual(kUnorderedEqual);
-        return VisitFloat64Compare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnorderedEqual);
+        return VisitFloat64Compare(selector, value, cont);
       case IrOpcode::kFloat64LessThan:
-        cont.OverwriteAndNegateIfEqual(kUnorderedLessThan);
-        return VisitFloat64Compare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnorderedLessThan);
+        return VisitFloat64Compare(selector, value, cont);
       case IrOpcode::kFloat64LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
-        return VisitFloat64Compare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
+        return VisitFloat64Compare(selector, value, cont);
       case IrOpcode::kProjection:
         // Check if this is the overflow output projection of an
         // <Operation>WithOverflow node.
@@ -1028,16 +995,16 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
           // <Operation> is either NULL, which means there's no use of the
           // actual value, or was already defined, which means it is scheduled
           // *AFTER* this branch).
-          Node* node = value->InputAt(0);
-          Node* result = node->FindProjection(0);
-          if (!result || IsDefined(result)) {
+          Node* const node = value->InputAt(0);
+          Node* const result = node->FindProjection(0);
+          if (!result || selector->IsDefined(result)) {
             switch (node->opcode()) {
               case IrOpcode::kInt32AddWithOverflow:
-                cont.OverwriteAndNegateIfEqual(kOverflow);
-                return VisitBinop(this, node, kArmAdd, kArmAdd, &cont);
+                cont->OverwriteAndNegateIfEqual(kOverflow);
+                return VisitBinop(selector, node, kArmAdd, kArmAdd, cont);
               case IrOpcode::kInt32SubWithOverflow:
-                cont.OverwriteAndNegateIfEqual(kOverflow);
-                return VisitBinop(this, node, kArmSub, kArmRsb, &cont);
+                cont->OverwriteAndNegateIfEqual(kOverflow);
+                return VisitBinop(selector, node, kArmSub, kArmRsb, cont);
               default:
                 break;
             }
@@ -1045,64 +1012,63 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
         }
         break;
       case IrOpcode::kInt32Add:
-        return VisitWordCompare(this, value, kArmCmn, &cont, true);
+        return VisitWordCompare(selector, value, kArmCmn, cont);
       case IrOpcode::kInt32Sub:
-        return VisitWordCompare(this, value, kArmCmp, &cont, false);
+        return VisitWordCompare(selector, value, kArmCmp, cont);
       case IrOpcode::kWord32And:
-        return VisitWordCompare(this, value, kArmTst, &cont, true);
+        return VisitWordCompare(selector, value, kArmTst, cont);
       case IrOpcode::kWord32Or:
-        return VisitBinop(this, value, kArmOrr, kArmOrr, &cont);
+        return VisitBinop(selector, value, kArmOrr, kArmOrr, cont);
       case IrOpcode::kWord32Xor:
-        return VisitWordCompare(this, value, kArmTeq, &cont, true);
+        return VisitWordCompare(selector, value, kArmTeq, cont);
       case IrOpcode::kWord32Sar:
-        return VisitShift(this, value, TryMatchASR, &cont);
+        return VisitShift(selector, value, TryMatchASR, cont);
       case IrOpcode::kWord32Shl:
-        return VisitShift(this, value, TryMatchLSL, &cont);
+        return VisitShift(selector, value, TryMatchLSL, cont);
       case IrOpcode::kWord32Shr:
-        return VisitShift(this, value, TryMatchLSR, &cont);
+        return VisitShift(selector, value, TryMatchLSR, cont);
       case IrOpcode::kWord32Ror:
-        return VisitShift(this, value, TryMatchROR, &cont);
+        return VisitShift(selector, value, TryMatchROR, cont);
       default:
         break;
     }
+    break;
+  }
+
+  // Continuation could not be combined with a compare, emit compare against 0.
+  ArmOperandGenerator g(selector);
+  InstructionCode const opcode =
+      cont->Encode(kArmTst) | AddressingModeField::encode(kMode_Operand2_R);
+  InstructionOperand* const value_operand = g.UseRegister(value);
+  if (cont->IsBranch()) {
+    selector->Emit(opcode, nullptr, value_operand, value_operand,
+                   g.Label(cont->true_block()),
+                   g.Label(cont->false_block()))->MarkAsControl();
+  } else {
+    selector->Emit(opcode, g.DefineAsRegister(cont->result()), value_operand,
+                   value_operand);
   }
+}
 
-  // Branch could not be combined with a compare, emit compare against 0.
-  return VisitWordTest(this, value, &cont);
+}  // namespace
+
+
+void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
+                                      BasicBlock* fbranch) {
+  FlagsContinuation cont(kNotEqual, tbranch, fbranch);
+  if (IsNextInAssemblyOrder(tbranch)) {  // We can fallthru to the true block.
+    cont.Negate();
+    cont.SwapBlocks();
+  }
+  VisitWordCompareZero(this, branch, branch->InputAt(0), &cont);
 }
 
 
 void InstructionSelector::VisitWord32Equal(Node* const node) {
-  Node* const user = node;
   FlagsContinuation cont(kEqual, node);
-  Int32BinopMatcher m(user);
+  Int32BinopMatcher m(node);
   if (m.right().Is(0)) {
-    Node* const value = m.left().node();
-    if (CanCover(user, value)) {
-      switch (value->opcode()) {
-        case IrOpcode::kInt32Add:
-          return VisitWordCompare(this, value, kArmCmn, &cont, true);
-        case IrOpcode::kInt32Sub:
-          return VisitWordCompare(this, value, kArmCmp, &cont, false);
-        case IrOpcode::kWord32And:
-          return VisitWordCompare(this, value, kArmTst, &cont, true);
-        case IrOpcode::kWord32Or:
-          return VisitBinop(this, value, kArmOrr, kArmOrr, &cont);
-        case IrOpcode::kWord32Xor:
-          return VisitWordCompare(this, value, kArmTeq, &cont, true);
-        case IrOpcode::kWord32Sar:
-          return VisitShift(this, value, TryMatchASR, &cont);
-        case IrOpcode::kWord32Shl:
-          return VisitShift(this, value, TryMatchLSL, &cont);
-        case IrOpcode::kWord32Shr:
-          return VisitShift(this, value, TryMatchLSR, &cont);
-        case IrOpcode::kWord32Ror:
-          return VisitShift(this, value, TryMatchROR, &cont);
-        default:
-          break;
-      }
-      return VisitWordTest(this, value, &cont);
-    }
+    return VisitWordCompareZero(this, m.node(), m.left().node(), &cont);
   }
   VisitWordCompare(this, node, &cont);
 }
index f641dcb..b56230f 100644 (file)
@@ -647,10 +647,12 @@ void InstructionSelector::VisitCall(Node* node) {
 }
 
 
+namespace {
+
 // Shared routine for multiple compare operations.
-static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
-                         InstructionOperand* left, InstructionOperand* right,
-                         FlagsContinuation* cont) {
+void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
+                  InstructionOperand* left, InstructionOperand* right,
+                  FlagsContinuation* cont) {
   IA32OperandGenerator g(selector);
   if (cont->IsBranch()) {
     selector->Emit(cont->Encode(opcode), NULL, left, right,
@@ -666,9 +668,9 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
 
 
 // Shared routine for multiple compare operations.
-static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
-                         Node* left, Node* right, FlagsContinuation* cont,
-                         bool commutative) {
+void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
+                  Node* left, Node* right, FlagsContinuation* cont,
+                  bool commutative) {
   IA32OperandGenerator g(selector);
   if (commutative && g.CanBeBetterLeftOperand(right)) {
     std::swap(left, right);
@@ -677,9 +679,17 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
 }
 
 
+// Shared routine for multiple float compare operations.
+void VisitFloat64Compare(InstructionSelector* selector, Node* node,
+                         FlagsContinuation* cont) {
+  VisitCompare(selector, kSSEFloat64Cmp, node->InputAt(0), node->InputAt(1),
+               cont, node->op()->HasProperty(Operator::kCommutative));
+}
+
+
 // Shared routine for multiple word compare operations.
-static void VisitWordCompare(InstructionSelector* selector, Node* node,
-                             InstructionCode opcode, FlagsContinuation* cont) {
+void VisitWordCompare(InstructionSelector* selector, Node* node,
+                      InstructionCode opcode, FlagsContinuation* cont) {
   IA32OperandGenerator g(selector);
   Node* const left = node->InputAt(0);
   Node* const right = node->InputAt(1);
@@ -697,67 +707,52 @@ static void VisitWordCompare(InstructionSelector* selector, Node* node,
 }
 
 
-// Shared routine for multiple float compare operations.
-static void VisitFloat64Compare(InstructionSelector* selector, Node* node,
-                                FlagsContinuation* cont) {
-  VisitCompare(selector, kSSEFloat64Cmp, node->InputAt(0), node->InputAt(1),
-               cont, node->op()->HasProperty(Operator::kCommutative));
+void VisitWordCompare(InstructionSelector* selector, Node* node,
+                      FlagsContinuation* cont) {
+  VisitWordCompare(selector, node, kIA32Cmp, cont);
 }
 
 
-void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
-                                      BasicBlock* fbranch) {
-  IA32OperandGenerator g(this);
-  Node* user = branch;
-  Node* value = branch->InputAt(0);
-
-  FlagsContinuation cont(kNotEqual, tbranch, fbranch);
-
-  // If we can fall through to the true block, invert the branch.
-  if (IsNextInAssemblyOrder(tbranch)) {
-    cont.Negate();
-    cont.SwapBlocks();
-  }
-
-  // Try to combine with comparisons against 0 by simply inverting the branch.
-  while (CanCover(user, value) && value->opcode() == IrOpcode::kWord32Equal) {
-    Int32BinopMatcher m(value);
-    if (m.right().Is(0)) {
-      user = value;
-      value = m.left().node();
-      cont.Negate();
-    } else {
-      break;
-    }
-  }
-
+// Shared routine for word comparison with zero.
+void VisitWordCompareZero(InstructionSelector* selector, Node* user,
+                          Node* value, FlagsContinuation* cont) {
   // Try to combine the branch with a comparison.
-  if (CanCover(user, value)) {
+  while (selector->CanCover(user, value)) {
     switch (value->opcode()) {
-      case IrOpcode::kWord32Equal:
-        cont.OverwriteAndNegateIfEqual(kEqual);
-        return VisitWordCompare(this, value, kIA32Cmp, &cont);
+      case IrOpcode::kWord32Equal: {
+        // Try to combine with comparisons against 0 by simply inverting the
+        // continuation.
+        Int32BinopMatcher m(value);
+        if (m.right().Is(0)) {
+          user = value;
+          value = m.left().node();
+          cont->Negate();
+          continue;
+        }
+        cont->OverwriteAndNegateIfEqual(kEqual);
+        return VisitWordCompare(selector, value, cont);
+      }
       case IrOpcode::kInt32LessThan:
-        cont.OverwriteAndNegateIfEqual(kSignedLessThan);
-        return VisitWordCompare(this, value, kIA32Cmp, &cont);
+        cont->OverwriteAndNegateIfEqual(kSignedLessThan);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kInt32LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
-        return VisitWordCompare(this, value, kIA32Cmp, &cont);
+        cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kUint32LessThan:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThan);
-        return VisitWordCompare(this, value, kIA32Cmp, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kUint32LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
-        return VisitWordCompare(this, value, kIA32Cmp, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kFloat64Equal:
-        cont.OverwriteAndNegateIfEqual(kUnorderedEqual);
-        return VisitFloat64Compare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnorderedEqual);
+        return VisitFloat64Compare(selector, value, cont);
       case IrOpcode::kFloat64LessThan:
-        cont.OverwriteAndNegateIfEqual(kUnorderedLessThan);
-        return VisitFloat64Compare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnorderedLessThan);
+        return VisitFloat64Compare(selector, value, cont);
       case IrOpcode::kFloat64LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
-        return VisitFloat64Compare(this, value, &cont);
+        cont->OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
+        return VisitFloat64Compare(selector, value, cont);
       case IrOpcode::kProjection:
         // Check if this is the overflow output projection of an
         // <Operation>WithOverflow node.
@@ -769,14 +764,14 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
           // *AFTER* this branch).
           Node* node = value->InputAt(0);
           Node* result = node->FindProjection(0);
-          if (result == NULL || IsDefined(result)) {
+          if (result == NULL || selector->IsDefined(result)) {
             switch (node->opcode()) {
               case IrOpcode::kInt32AddWithOverflow:
-                cont.OverwriteAndNegateIfEqual(kOverflow);
-                return VisitBinop(this, node, kIA32Add, &cont);
+                cont->OverwriteAndNegateIfEqual(kOverflow);
+                return VisitBinop(selector, node, kIA32Add, cont);
               case IrOpcode::kInt32SubWithOverflow:
-                cont.OverwriteAndNegateIfEqual(kOverflow);
-                return VisitBinop(this, node, kIA32Sub, &cont);
+                cont->OverwriteAndNegateIfEqual(kOverflow);
+                return VisitBinop(selector, node, kIA32Sub, cont);
               default:
                 break;
             }
@@ -784,61 +779,65 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
         }
         break;
       case IrOpcode::kInt32Sub:
-        return VisitWordCompare(this, value, kIA32Cmp, &cont);
+        return VisitWordCompare(selector, value, cont);
       case IrOpcode::kWord32And:
-        return VisitWordCompare(this, value, kIA32Test, &cont);
+        return VisitWordCompare(selector, value, kIA32Test, cont);
       default:
         break;
     }
+    break;
   }
 
-  // Branch could not be combined with a compare, emit compare against 0.
-  VisitCompare(this, kIA32Cmp, g.Use(value), g.TempImmediate(0), &cont);
+  // Continuation could not be combined with a compare, emit compare against 0.
+  IA32OperandGenerator g(selector);
+  VisitCompare(selector, kIA32Cmp, g.Use(value), g.TempImmediate(0), cont);
+}
+
+}  // namespace
+
+
+void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
+                                      BasicBlock* fbranch) {
+  FlagsContinuation cont(kNotEqual, tbranch, fbranch);
+  if (IsNextInAssemblyOrder(tbranch)) {  // We can fallthru to the true block.
+    cont.Negate();
+    cont.SwapBlocks();
+  }
+  VisitWordCompareZero(this, branch, branch->InputAt(0), &cont);
 }
 
 
 void InstructionSelector::VisitWord32Equal(Node* const node) {
-  Node* const user = node;
   FlagsContinuation cont(kEqual, node);
-  Int32BinopMatcher m(user);
+  Int32BinopMatcher m(node);
   if (m.right().Is(0)) {
-    Node* const value = m.left().node();
-    if (CanCover(user, value)) {
-      switch (value->opcode()) {
-        case IrOpcode::kInt32Sub:
-          return VisitWordCompare(this, value, kIA32Cmp, &cont);
-        case IrOpcode::kWord32And:
-          return VisitWordCompare(this, value, kIA32Test, &cont);
-        default:
-          break;
-      }
-    }
+    return VisitWordCompareZero(this, m.node(), m.left().node(), &cont);
   }
-  VisitWordCompare(this, node, kIA32Cmp, &cont);
+  VisitWordCompare(this, node, &cont);
 }
 
 
 void InstructionSelector::VisitInt32LessThan(Node* node) {
   FlagsContinuation cont(kSignedLessThan, node);
-  VisitWordCompare(this, node, kIA32Cmp, &cont);
+  VisitWordCompare(this, node, &cont);
 }
 
 
 void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) {
   FlagsContinuation cont(kSignedLessThanOrEqual, node);
-  VisitWordCompare(this, node, kIA32Cmp, &cont);
+  VisitWordCompare(this, node, &cont);
 }
 
 
 void InstructionSelector::VisitUint32LessThan(Node* node) {
   FlagsContinuation cont(kUnsignedLessThan, node);
-  VisitWordCompare(this, node, kIA32Cmp, &cont);
+  VisitWordCompare(this, node, &cont);
 }
 
 
 void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
   FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
-  VisitWordCompare(this, node, kIA32Cmp, &cont);
+  VisitWordCompare(this, node, &cont);
 }
 
 
index c4cf18b..cc06f10 100644 (file)
@@ -85,20 +85,10 @@ class InstructionSelector FINAL {
     return Features(CpuFeatures::SupportedFeatures());
   }
 
-  // Checks if {node} is currently live.
-  bool IsLive(Node* node) const { return !IsDefined(node) && IsUsed(node); }
-
- private:
-  friend class OperandGenerator;
-
   // ===========================================================================
   // ============ Architecture-independent graph covering methods. =============
   // ===========================================================================
 
-  // Checks if {block} will appear directly after {current_block_} when
-  // assembling code, in which case, a fall-through can be used.
-  bool IsNextInAssemblyOrder(const BasicBlock* block) const;
-
   // Used in pattern matching during code generation.
   // Check if {node} can be covered while generating code for the current
   // instruction. A node can be covered if the {user} of the node has the only
@@ -109,13 +99,23 @@ class InstructionSelector FINAL {
   // generated for it.
   bool IsDefined(Node* node) const;
 
-  // Inform the instruction selection that {node} was just defined.
-  void MarkAsDefined(Node* node);
-
   // Checks if {node} has any uses, and therefore code has to be generated for
   // it.
   bool IsUsed(Node* node) const;
 
+  // Checks if {node} is currently live.
+  bool IsLive(Node* node) const { return !IsDefined(node) && IsUsed(node); }
+
+ private:
+  friend class OperandGenerator;
+
+  // Checks if {block} will appear directly after {current_block_} when
+  // assembling code, in which case, a fall-through can be used.
+  bool IsNextInAssemblyOrder(const BasicBlock* block) const;
+
+  // Inform the instruction selection that {node} was just defined.
+  void MarkAsDefined(Node* node);
+
   // Inform the instruction selection that {node} has at least one use and we
   // will need to generate code for it.
   void MarkAsUsed(Node* node);
index d7ca67c..a8b5edd 100644 (file)
@@ -243,6 +243,21 @@ Reduction MachineOperatorReducer::Reduce(Node* node) {
       if (m.LeftEqualsRight()) return ReplaceBool(true);  // x == x => true
       break;
     }
+    case IrOpcode::kWord64Equal: {
+      Int64BinopMatcher m(node);
+      if (m.IsFoldable()) {  // K == K => K
+        return ReplaceBool(m.left().Value() == m.right().Value());
+      }
+      if (m.left().IsInt64Sub() && m.right().Is(0)) {  // x - y == 0 => x == y
+        Int64BinopMatcher msub(m.left().node());
+        node->ReplaceInput(0, msub.left().node());
+        node->ReplaceInput(1, msub.right().node());
+        return Changed(node);
+      }
+      // TODO(turbofan): fold HeapConstant, ExternalReference, pointer compares
+      if (m.LeftEqualsRight()) return ReplaceBool(true);  // x == x => true
+      break;
+    }
     case IrOpcode::kInt32Add: {
       Int32BinopMatcher m(node);
       if (m.right().Is(0)) return Replace(m.left().node());  // x + 0 => x
index 26b6bd3..20c8a56 100644 (file)
@@ -456,8 +456,8 @@ class RepresentationSelector {
         if (lower()) {
           MachineTypeUnion input = GetInfo(node->InputAt(0))->output;
           if (input & kRepBit) {
-            // BooleanNot(x: kRepBit) => WordEqual(x, #0)
-            node->set_op(lowering->machine()->WordEqual());
+            // BooleanNot(x: kRepBit) => Word32Equal(x, #0)
+            node->set_op(lowering->machine()->Word32Equal());
             node->AppendInput(jsgraph_->zone(), jsgraph_->Int32Constant(0));
           } else {
             // BooleanNot(x: kRepTagged) => WordEqual(x, #false)
index 70f80cb..ae7a464 100644 (file)
@@ -1044,7 +1044,7 @@ Bounds Typer::Visitor::TypeBooleanNot(Node* node) {
 
 
 Bounds Typer::Visitor::TypeBooleanToNumber(Node* node) {
-  return Bounds(Type::None(zone()), Type::Number(zone()));
+  return Bounds(Type::None(zone()), typer_->zero_or_one);
 }
 
 
index af5c871..230e000 100644 (file)
@@ -821,6 +821,14 @@ static void VisitWordCompare(InstructionSelector* selector, Node* node,
 }
 
 
+// Shared routine for comparison with zero.
+static void VisitCompareZero(InstructionSelector* selector, Node* node,
+                             InstructionCode opcode, FlagsContinuation* cont) {
+  X64OperandGenerator g(selector);
+  VisitCompare(selector, opcode, g.Use(node), g.TempImmediate(0), cont);
+}
+
+
 // Shared routine for multiple float64 compare operations.
 static void VisitFloat64Compare(InstructionSelector* selector, Node* node,
                                 FlagsContinuation* cont) {
@@ -946,16 +954,30 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
   }
 
   // Branch could not be combined with a compare, emit compare against 0.
-  VisitCompare(this, kX64Cmp32, g.Use(value), g.TempImmediate(0), &cont);
+  VisitCompareZero(this, value, kX64Cmp32, &cont);
 }
 
 
 void InstructionSelector::VisitWord32Equal(Node* const node) {
-  Node* const user = node;
+  Node* user = node;
   FlagsContinuation cont(kEqual, node);
   Int32BinopMatcher m(user);
   if (m.right().Is(0)) {
-    Node* const value = m.left().node();
+    Node* value = m.left().node();
+
+    // Try to combine with comparisons against 0 by simply inverting the branch.
+    while (CanCover(user, value) && value->opcode() == IrOpcode::kWord32Equal) {
+      Int32BinopMatcher m(value);
+      if (m.right().Is(0)) {
+        user = value;
+        value = m.left().node();
+        cont.Negate();
+      } else {
+        break;
+      }
+    }
+
+    // Try to combine the branch with a comparison.
     if (CanCover(user, value)) {
       switch (value->opcode()) {
         case IrOpcode::kInt32Sub:
@@ -966,6 +988,7 @@ void InstructionSelector::VisitWord32Equal(Node* const node) {
           break;
       }
     }
+    return VisitCompareZero(this, value, kX64Cmp32, &cont);
   }
   VisitWordCompare(this, node, kX64Cmp32, &cont);
 }
@@ -996,11 +1019,25 @@ void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
 
 
 void InstructionSelector::VisitWord64Equal(Node* const node) {
-  Node* const user = node;
+  Node* user = node;
   FlagsContinuation cont(kEqual, node);
   Int64BinopMatcher m(user);
   if (m.right().Is(0)) {
-    Node* const value = m.left().node();
+    Node* value = m.left().node();
+
+    // Try to combine with comparisons against 0 by simply inverting the branch.
+    while (CanCover(user, value) && value->opcode() == IrOpcode::kWord64Equal) {
+      Int64BinopMatcher m(value);
+      if (m.right().Is(0)) {
+        user = value;
+        value = m.left().node();
+        cont.Negate();
+      } else {
+        break;
+      }
+    }
+
+    // Try to combine the branch with a comparison.
     if (CanCover(user, value)) {
       switch (value->opcode()) {
         case IrOpcode::kInt64Sub:
@@ -1011,6 +1048,7 @@ void InstructionSelector::VisitWord64Equal(Node* const node) {
           break;
       }
     }
+    return VisitCompareZero(this, value, kX64Cmp, &cont);
   }
   VisitWordCompare(this, node, kX64Cmp, &cont);
 }
index ea3ccdd..6935771 100644 (file)
@@ -556,6 +556,142 @@ TEST(RunInt32AddP) {
 }
 
 
+TEST(RunInt32AddAndWord32EqualP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+    m.Return(m.Int32Add(m.Parameter(0),
+                        m.Word32Equal(m.Parameter(1), m.Parameter(2))));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
+          CHECK_EQ(expected, m.Call(*i, *j, *k));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+    m.Return(m.Int32Add(m.Word32Equal(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
+          CHECK_EQ(expected, m.Call(*i, *j, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32AddAndWord32EqualImm) {
+  {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+      m.Return(m.Int32Add(m.Int32Constant(*i),
+                          m.Word32Equal(m.Parameter(0), m.Parameter(1))));
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j == *k));
+          CHECK_EQ(expected, m.Call(*j, *k));
+        }
+      }
+    }
+  }
+  {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+      m.Return(m.Int32Add(m.Word32Equal(m.Int32Constant(*i), m.Parameter(0)),
+                          m.Parameter(1)));
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>((*i == *j) + bit_cast<uint32_t>(*k));
+          CHECK_EQ(expected, m.Call(*j, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32AddAndWord32NotEqualP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+    m.Return(m.Int32Add(m.Parameter(0),
+                        m.Word32NotEqual(m.Parameter(1), m.Parameter(2))));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
+          CHECK_EQ(expected, m.Call(*i, *j, *k));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+    m.Return(m.Int32Add(m.Word32NotEqual(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
+          CHECK_EQ(expected, m.Call(*i, *j, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32AddAndWord32NotEqualImm) {
+  {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+      m.Return(m.Int32Add(m.Int32Constant(*i),
+                          m.Word32NotEqual(m.Parameter(0), m.Parameter(1))));
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>(bit_cast<uint32_t>(*i) + (*j != *k));
+          CHECK_EQ(expected, m.Call(*j, *k));
+        }
+      }
+    }
+  }
+  {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+      m.Return(m.Int32Add(m.Word32NotEqual(m.Int32Constant(*i), m.Parameter(0)),
+                          m.Parameter(1)));
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t const expected =
+              bit_cast<int32_t>((*i != *j) + bit_cast<uint32_t>(*k));
+          CHECK_EQ(expected, m.Call(*j, *k));
+        }
+      }
+    }
+  }
+}
+
+
 TEST(RunInt32AddAndWord32SarP) {
   {
     RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
index 6d9a7ab..6553bf7 100644 (file)
@@ -791,7 +791,7 @@ TEST(LowerBooleanNot_bit_bit) {
   Node* use = t.Branch(inv);
   t.Lower();
   Node* cmp = use->InputAt(0);
-  CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+  CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
   CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
   Node* f = t.jsgraph.Int32Constant(0);
   CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
@@ -808,7 +808,7 @@ TEST(LowerBooleanNot_bit_tagged) {
   t.Lower();
   CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode());
   Node* cmp = use->InputAt(0)->InputAt(0);
-  CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+  CHECK_EQ(t.machine()->Word32Equal()->opcode(), cmp->opcode());
   CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
   Node* f = t.jsgraph.Int32Constant(0);
   CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
index 9d87900..08a23e9 100644 (file)
@@ -1372,6 +1372,113 @@ TEST_F(InstructionSelectorTest, TruncateFloat64ToFloat32WithParameter) {
 
 
 // -----------------------------------------------------------------------------
+// Comparisons.
+
+
+namespace {
+
+struct Comparison {
+  Constructor constructor;
+  const char* constructor_name;
+  FlagsCondition flags_condition;
+  FlagsCondition negated_flags_condition;
+};
+
+
+std::ostream& operator<<(std::ostream& os, const Comparison& cmp) {
+  return os << cmp.constructor_name;
+}
+
+
+const Comparison kComparisons[] = {
+    {&RawMachineAssembler::Word32Equal, "Word32Equal", kEqual, kNotEqual},
+    {&RawMachineAssembler::Int32LessThan, "Int32LessThan", kSignedLessThan,
+     kSignedGreaterThanOrEqual},
+    {&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual",
+     kSignedLessThanOrEqual, kSignedGreaterThan},
+    {&RawMachineAssembler::Uint32LessThan, "Uint32LessThan", kUnsignedLessThan,
+     kUnsignedGreaterThanOrEqual},
+    {&RawMachineAssembler::Uint32LessThanOrEqual, "Uint32LessThanOrEqual",
+     kUnsignedLessThanOrEqual, kUnsignedGreaterThan}};
+
+}  // namespace
+
+
+typedef InstructionSelectorTestWithParam<Comparison>
+    InstructionSelectorComparisonTest;
+
+
+TEST_P(InstructionSelectorComparisonTest, Parameters) {
+  const Comparison& cmp = GetParam();
+  StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
+  Node* const p0 = m.Parameter(0);
+  Node* const p1 = m.Parameter(1);
+  Node* const r = (m.*cmp.constructor)(p0, p1);
+  m.Return(r);
+  Stream const s = m.Build();
+  ASSERT_EQ(1U, s.size());
+  EXPECT_EQ(kArmCmp, s[0]->arch_opcode());
+  EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
+  ASSERT_EQ(2U, s[0]->InputCount());
+  EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+  EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
+  ASSERT_EQ(1U, s[0]->OutputCount());
+  EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->OutputAt(0)));
+  EXPECT_EQ(kFlags_set, s[0]->flags_mode());
+  EXPECT_EQ(cmp.flags_condition, s[0]->flags_condition());
+}
+
+
+TEST_P(InstructionSelectorComparisonTest, Word32EqualWithZero) {
+  {
+    const Comparison& cmp = GetParam();
+    StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
+    Node* const p0 = m.Parameter(0);
+    Node* const p1 = m.Parameter(1);
+    Node* const r =
+        m.Word32Equal((m.*cmp.constructor)(p0, p1), m.Int32Constant(0));
+    m.Return(r);
+    Stream const s = m.Build();
+    ASSERT_EQ(1U, s.size());
+    EXPECT_EQ(kArmCmp, s[0]->arch_opcode());
+    EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
+    ASSERT_EQ(2U, s[0]->InputCount());
+    EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+    EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
+    ASSERT_EQ(1U, s[0]->OutputCount());
+    EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->OutputAt(0)));
+    EXPECT_EQ(kFlags_set, s[0]->flags_mode());
+    EXPECT_EQ(cmp.negated_flags_condition, s[0]->flags_condition());
+  }
+  {
+    const Comparison& cmp = GetParam();
+    StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
+    Node* const p0 = m.Parameter(0);
+    Node* const p1 = m.Parameter(1);
+    Node* const r =
+        m.Word32Equal(m.Int32Constant(0), (m.*cmp.constructor)(p0, p1));
+    m.Return(r);
+    Stream const s = m.Build();
+    ASSERT_EQ(1U, s.size());
+    EXPECT_EQ(kArmCmp, s[0]->arch_opcode());
+    EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
+    ASSERT_EQ(2U, s[0]->InputCount());
+    EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
+    EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
+    ASSERT_EQ(1U, s[0]->OutputCount());
+    EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->OutputAt(0)));
+    EXPECT_EQ(kFlags_set, s[0]->flags_mode());
+    EXPECT_EQ(cmp.negated_flags_condition, s[0]->flags_condition());
+  }
+}
+
+
+INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
+                        InstructionSelectorComparisonTest,
+                        ::testing::ValuesIn(kComparisons));
+
+
+// -----------------------------------------------------------------------------
 // Miscellaneous.