| Index: src/compiler/arm64/instruction-selector-arm64.cc | 
| diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc | 
| index 25f4de0325d8e33faff1f738a98363e9e9dd5d84..7389c158e425aa9a8a932e18dc7a5f253197044c 100644 | 
| --- a/src/compiler/arm64/instruction-selector-arm64.cc | 
| +++ b/src/compiler/arm64/instruction-selector-arm64.cc | 
| @@ -1921,14 +1921,126 @@ void VisitWordCompare(InstructionSelector* selector, Node* node, | 
| } | 
| } | 
|  | 
| +// This function checks whether we can convert: | 
| +// ((a <op> b) cmp 0), b.<cond> | 
| +// to: | 
| +// (a <ops> b), b.<cond'> | 
| +// where <ops> is the flag setting version of <op>. | 
| +// We only generate conditions <cond'> that are a combination of the N | 
| +// and Z flags. This avoids the need to make this function dependent on | 
| +// the flag-setting operation. | 
| +bool CanUseFlagSettingBinop(FlagsCondition cond) { | 
| +  switch (cond) { | 
| +    case kEqual: | 
| +    case kNotEqual: | 
| +    case kSignedLessThan: | 
| +    case kSignedGreaterThanOrEqual: | 
| +    case kUnsignedLessThanOrEqual:  // x <= 0 -> x == 0 | 
| +    case kUnsignedGreaterThan:      // x > 0 -> x != 0 | 
| +      return true; | 
| +    default: | 
| +      return false; | 
| +  } | 
| +} | 
| + | 
| +// Map <cond> to <cond'> so that the following transformation is possible: | 
| +// ((a <op> b) cmp 0), b.<cond> | 
| +// to: | 
| +// (a <ops> b), b.<cond'> | 
| +// where <ops> is the flag setting version of <op>. | 
| +FlagsCondition MapForFlagSettingBinop(FlagsCondition cond) { | 
| +  DCHECK(CanUseFlagSettingBinop(cond)); | 
| +  switch (cond) { | 
| +    case kEqual: | 
| +    case kNotEqual: | 
| +      return cond; | 
| +    case kSignedLessThan: | 
| +      return kNegative; | 
| +    case kSignedGreaterThanOrEqual: | 
| +      return kPositiveOrZero; | 
| +    case kUnsignedLessThanOrEqual:  // x <= 0 -> x == 0 | 
| +      return kEqual; | 
| +    case kUnsignedGreaterThan:  // x > 0 -> x != 0 | 
| +      return kNotEqual; | 
| +    default: | 
| +      UNREACHABLE(); | 
| +      return cond; | 
| +  } | 
| +} | 
| + | 
| +// This function checks if we can perform the transformation: | 
| +// ((a <op> b) cmp 0), b.<cond> | 
| +// to: | 
| +// (a <ops> b), b.<cond'> | 
| +// where <ops> is the flag setting version of <op>, and if so, | 
| +// updates {node}, {opcode} and {cont} accordingly. | 
| +void MaybeReplaceCmpZeroWithFlagSettingBinop(InstructionSelector* selector, | 
| +                                             Node** node, Node* binop, | 
| +                                             ArchOpcode* opcode, | 
| +                                             FlagsCondition cond, | 
| +                                             FlagsContinuation* cont, | 
| +                                             ImmediateMode* immediate_mode) { | 
| +  ArchOpcode binop_opcode; | 
| +  ArchOpcode no_output_opcode; | 
| +  ImmediateMode binop_immediate_mode; | 
| +  switch (binop->opcode()) { | 
| +    case IrOpcode::kInt32Add: | 
| +      binop_opcode = kArm64Add32; | 
| +      no_output_opcode = kArm64Cmn32; | 
| +      binop_immediate_mode = kArithmeticImm; | 
| +      break; | 
| +    case IrOpcode::kWord32And: | 
| +      binop_opcode = kArm64And32; | 
| +      no_output_opcode = kArm64Tst32; | 
| +      binop_immediate_mode = kLogical32Imm; | 
| +      break; | 
| +    default: | 
| +      UNREACHABLE(); | 
| +      return; | 
| +  } | 
| +  if (selector->CanCover(*node, binop)) { | 
| +    // The comparison is the only user of the add or and, so we can generate | 
| +    // a cmn or tst instead. | 
| +    cont->Overwrite(MapForFlagSettingBinop(cond)); | 
| +    *opcode = no_output_opcode; | 
| +    *node = binop; | 
| +    *immediate_mode = binop_immediate_mode; | 
| +  } else if (selector->IsOnlyUserOfNodeInSameBlock(*node, binop)) { | 
| +    // We can also handle the case where the add and the compare are in the | 
| +    // same basic block, and the compare is the only use of add in this basic | 
| +    // block (the add has users in other basic blocks). | 
| +    cont->Overwrite(MapForFlagSettingBinop(cond)); | 
| +    *opcode = binop_opcode; | 
| +    *node = binop; | 
| +    *immediate_mode = binop_immediate_mode; | 
| +  } | 
| +} | 
|  | 
| void VisitWord32Compare(InstructionSelector* selector, Node* node, | 
| FlagsContinuation* cont) { | 
| Int32BinopMatcher m(node); | 
| ArchOpcode opcode = kArm64Cmp32; | 
| - | 
| -  // Select negated compare for comparisons with negated right input. | 
| -  if (m.right().IsInt32Sub()) { | 
| +  FlagsCondition cond = cont->condition(); | 
| +  ImmediateMode immediate_mode = kArithmeticImm; | 
| +  if (m.right().Is(0) && (m.left().IsInt32Add() || m.left().IsWord32And())) { | 
| +    // Emit flag setting add/and instructions for comparisons against zero. | 
| +    if (CanUseFlagSettingBinop(cond)) { | 
| +      Node* binop = m.left().node(); | 
| +      MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode, | 
| +                                              cond, cont, &immediate_mode); | 
| +    } | 
| +  } else if (m.left().Is(0) && | 
| +             (m.right().IsInt32Add() || m.right().IsWord32And())) { | 
| +    // Same as above, but we need to commute the condition before we | 
| +    // continue with the rest of the checks. | 
| +    cond = CommuteFlagsCondition(cond); | 
| +    if (CanUseFlagSettingBinop(cond)) { | 
| +      Node* binop = m.right().node(); | 
| +      MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode, | 
| +                                              cond, cont, &immediate_mode); | 
| +    } | 
| +  } else if (m.right().IsInt32Sub()) { | 
| +    // Select negated compare for comparisons with negated right input. | 
| Node* sub = m.right().node(); | 
| Int32BinopMatcher msub(sub); | 
| if (msub.left().Is(0)) { | 
| @@ -1946,7 +2058,7 @@ void VisitWord32Compare(InstructionSelector* selector, Node* node, | 
| opcode = kArm64Cmn32; | 
| } | 
| } | 
| -  VisitBinop<Int32BinopMatcher>(selector, node, opcode, kArithmeticImm, cont); | 
| +  VisitBinop<Int32BinopMatcher>(selector, node, opcode, immediate_mode, cont); | 
| } | 
|  | 
|  | 
| @@ -2245,20 +2357,24 @@ void InstructionSelector::VisitWord32Equal(Node* const node) { | 
| if (CanCover(user, value)) { | 
| switch (value->opcode()) { | 
| case IrOpcode::kInt32Add: | 
| -          return VisitWordCompare(this, value, kArm64Cmn32, &cont, true, | 
| -                                  kArithmeticImm); | 
| +        case IrOpcode::kWord32And: | 
| +          return VisitWord32Compare(this, node, &cont); | 
| case IrOpcode::kInt32Sub: | 
| return VisitWordCompare(this, value, kArm64Cmp32, &cont, false, | 
| kArithmeticImm); | 
| -        case IrOpcode::kWord32And: | 
| -          return VisitWordCompare(this, value, kArm64Tst32, &cont, true, | 
| -                                  kLogical32Imm); | 
| case IrOpcode::kWord32Equal: { | 
| // Word32Equal(Word32Equal(x, y), 0) => Word32Compare(x, y, ne). | 
| Int32BinopMatcher mequal(value); | 
| node->ReplaceInput(0, mequal.left().node()); | 
| node->ReplaceInput(1, mequal.right().node()); | 
| cont.Negate(); | 
| +          // {node} still does not cover its new operands, because {mequal} is | 
| +          // still using them. | 
| +          // Since we won't generate any more code for {mequal}, set its | 
| +          // operands to zero to make sure {node} can cover them. | 
| +          // This improves pattern matching in VisitWord32Compare. | 
| +          mequal.node()->ReplaceInput(0, m.right().node()); | 
| +          mequal.node()->ReplaceInput(1, m.right().node()); | 
| return VisitWord32Compare(this, node, &cont); | 
| } | 
| default: | 
|  |