| 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:
|
|
|