Index: src/compiler/arm/instruction-selector-arm.cc |
diff --git a/src/compiler/arm/instruction-selector-arm.cc b/src/compiler/arm/instruction-selector-arm.cc |
index 8fc0ddb915c93e6f8680a33bf0c2eecc2a124c88..5bc310172f2316181ccc0ad7853a85f4747fb468 100644 |
--- a/src/compiler/arm/instruction-selector-arm.cc |
+++ b/src/compiler/arm/instruction-selector-arm.cc |
@@ -1592,6 +1592,101 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node, |
} |
} |
+// Check 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; |
+ } |
+} |
+ |
+// Check 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, |
+ InstructionCode* opcode, |
+ FlagsCondition cond, |
+ FlagsContinuation* cont) { |
+ InstructionCode binop_opcode; |
+ InstructionCode no_output_opcode; |
+ switch (binop->opcode()) { |
+ case IrOpcode::kInt32Add: |
+ binop_opcode = kArmAdd; |
+ no_output_opcode = kArmCmn; |
+ break; |
+ case IrOpcode::kWord32And: |
+ binop_opcode = kArmAnd; |
+ no_output_opcode = kArmTst; |
+ break; |
+ case IrOpcode::kWord32Or: |
+ binop_opcode = kArmOrr; |
+ no_output_opcode = kArmOrr; |
+ break; |
+ case IrOpcode::kWord32Xor: |
+ binop_opcode = kArmEor; |
+ no_output_opcode = kArmTeq; |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ return; |
+ } |
+ if (selector->CanCover(*node, binop)) { |
+ // The comparison is the only user of {node}. |
+ cont->Overwrite(MapForFlagSettingBinop(cond)); |
+ *opcode = no_output_opcode; |
+ *node = binop; |
+ } else if (selector->IsOnlyUserOfNodeInSameBlock(*node, binop)) { |
+ // We can also handle the case where the {node} and the comparison are in |
+ // the same basic block, and the comparison is the only user of {node} in |
+ // this basic block ({node} has users in other basic blocks). |
+ cont->Overwrite(MapForFlagSettingBinop(cond)); |
+ *opcode = binop_opcode; |
+ *node = binop; |
+ } |
+} |
// Shared routine for multiple word compare operations. |
void VisitWordCompare(InstructionSelector* selector, Node* node, |
@@ -1600,8 +1695,10 @@ void VisitWordCompare(InstructionSelector* selector, Node* node, |
Int32BinopMatcher m(node); |
InstructionOperand inputs[5]; |
size_t input_count = 0; |
- InstructionOperand outputs[1]; |
+ InstructionOperand outputs[2]; |
size_t output_count = 0; |
+ bool has_result = (opcode != kArmCmp) && (opcode != kArmCmn) && |
+ (opcode != kArmTst) && (opcode != kArmTeq); |
if (TryMatchImmediateOrShift(selector, &opcode, m.right().node(), |
&input_count, &inputs[1])) { |
@@ -1618,6 +1715,17 @@ void VisitWordCompare(InstructionSelector* selector, Node* node, |
inputs[input_count++] = g.UseRegister(m.right().node()); |
} |
+ if (has_result) { |
+ if (cont->IsDeoptimize()) { |
+ // If we can deoptimize as a result of the binop, we need to make sure |
+ // that the deopt inputs are not overwritten by the binop result. One way |
+ // to achieve that is to declare the output register as same-as-first. |
+ outputs[output_count++] = g.DefineSameAsFirst(node); |
+ } else { |
+ outputs[output_count++] = g.DefineAsRegister(node); |
+ } |
+ } |
+ |
if (cont->IsBranch()) { |
inputs[input_count++] = g.Label(cont->true_block()); |
inputs[input_count++] = g.Label(cont->false_block()); |
@@ -1641,7 +1749,32 @@ void VisitWordCompare(InstructionSelector* selector, Node* node, |
void VisitWordCompare(InstructionSelector* selector, Node* node, |
FlagsContinuation* cont) { |
- VisitWordCompare(selector, node, kArmCmp, cont); |
+ InstructionCode opcode = kArmCmp; |
+ Int32BinopMatcher m(node); |
+ |
+ FlagsCondition cond = cont->condition(); |
+ if (m.right().Is(0) && (m.left().IsInt32Add() || m.left().IsWord32Or() || |
+ m.left().IsWord32And() || m.left().IsWord32Xor())) { |
+ // Emit flag setting instructions for comparisons against zero. |
+ if (CanUseFlagSettingBinop(cond)) { |
+ Node* binop = m.left().node(); |
+ MaybeReplaceCmpZeroWithFlagSettingBinop(selector, &node, binop, &opcode, |
+ cond, cont); |
+ } |
+ } else if (m.left().Is(0) && |
+ (m.right().IsInt32Add() || m.right().IsWord32Or() || |
+ m.right().IsWord32And() || m.right().IsWord32Xor())) { |
+ // 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); |
+ } |
+ } |
+ |
+ VisitWordCompare(selector, node, opcode, cont); |
} |