Commit 0c72c719 authored by Seth Brenith's avatar Seth Brenith Committed by Commit Bot

Move branch inversion on ==0 into platform-agnostic reducer

This change is based on a discussion from
https://crrev.com/c/v8/v8/+/2053769/4/src/compiler/machine-operator-reducer.cc#1696
wherein Tobias suggested moving the folding away of ==0 operations out
of the platform-specific instruction selectors and into the
MachineOperatorReducer. I noticed that CommonOperatorReducer already
handles some very similar cases, so I have tried putting the ==0 folding
into CommonOperatorReducer instead. I'm happy to move it into
MachineOperatorReducer if that's better; I still don't have a very good
understanding of how roles are separated among reducers.

Change-Id: Ia0285bd9fafeef29d87cc88654bd6d355d467e8f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2076498
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66688}
parent f3b4167f
...@@ -1896,16 +1896,6 @@ void VisitWordCompare(InstructionSelector* selector, Node* node, ...@@ -1896,16 +1896,6 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
// Shared routine for word comparisons against zero. // Shared routine for word comparisons against zero.
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
// Try to combine with comparisons against 0 by simply inverting the branch.
while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
Int32BinopMatcher m(value);
if (!m.right().Is(0)) break;
user = value;
value = m.left().node();
cont->Negate();
}
if (CanCover(user, value)) { if (CanCover(user, value)) {
switch (value->opcode()) { switch (value->opcode()) {
case IrOpcode::kWord32Equal: case IrOpcode::kWord32Equal:
......
...@@ -2395,15 +2395,6 @@ void VisitAtomicBinop(InstructionSelector* selector, Node* node, ...@@ -2395,15 +2395,6 @@ void VisitAtomicBinop(InstructionSelector* selector, Node* node,
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
Arm64OperandGenerator g(this); Arm64OperandGenerator g(this);
// Try to combine with comparisons against 0 by simply inverting the branch.
while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
Int32BinopMatcher m(value);
if (!m.right().Is(0)) break;
user = value;
value = m.left().node();
cont->Negate();
}
// Try to match bit checks to create TBZ/TBNZ instructions. // Try to match bit checks to create TBZ/TBNZ instructions.
// Unlike the switch below, CanCover check is not needed here. // Unlike the switch below, CanCover check is not needed here.
......
...@@ -1538,16 +1538,6 @@ void VisitPairAtomicBinOp(InstructionSelector* selector, Node* node, ...@@ -1538,16 +1538,6 @@ void VisitPairAtomicBinOp(InstructionSelector* selector, Node* node,
// Shared routine for word comparison with zero. // Shared routine for word comparison with zero.
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
// Try to combine with comparisons against 0 by simply inverting the branch.
while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
Int32BinopMatcher m(value);
if (!m.right().Is(0)) break;
user = value;
value = m.left().node();
cont->Negate();
}
if (CanCover(user, value)) { if (CanCover(user, value)) {
switch (value->opcode()) { switch (value->opcode()) {
case IrOpcode::kWord32Equal: case IrOpcode::kWord32Equal:
......
...@@ -1615,16 +1615,6 @@ void InstructionSelector::VisitStackPointerGreaterThan( ...@@ -1615,16 +1615,6 @@ void InstructionSelector::VisitStackPointerGreaterThan(
// Shared routine for word comparisons against zero. // Shared routine for word comparisons against zero.
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
// Try to combine with comparisons against 0 by simply inverting the branch.
while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
Int32BinopMatcher m(value);
if (!m.right().Is(0)) break;
user = value;
value = m.left().node();
cont->Negate();
}
if (CanCover(user, value)) { if (CanCover(user, value)) {
switch (value->opcode()) { switch (value->opcode()) {
case IrOpcode::kWord32Equal: case IrOpcode::kWord32Equal:
......
...@@ -2169,6 +2169,16 @@ void InstructionSelector::VisitStackPointerGreaterThan( ...@@ -2169,6 +2169,16 @@ void InstructionSelector::VisitStackPointerGreaterThan(
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
// Try to combine with comparisons against 0 by simply inverting the branch. // Try to combine with comparisons against 0 by simply inverting the branch.
// This was already performed by CommonOperatorReducer, but it did not include
// the 64-bit comparison case because not all platforms handle 64-bit
// comparisons the same way.
//
// We actually must not include the 64bit case in CommonOperatorReducer, it
// would be unsound on platforms with pointer compression, since we got sloppy
// there about truncating 64 to 32 bit implicitly. That's an issue related to
// the general problem that it's unclear if the semantics of a Branch node is
// a 32 or 64 bit non-zero check. On most platforms, it seems to be a 32bit
// check while on Mips64 it's a 64bit check.
while (CanCover(user, value)) { while (CanCover(user, value)) {
if (value->opcode() == IrOpcode::kWord32Equal) { if (value->opcode() == IrOpcode::kWord32Equal) {
Int32BinopMatcher m(value); Int32BinopMatcher m(value);
......
...@@ -1508,16 +1508,6 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node, ...@@ -1508,16 +1508,6 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node,
// Shared routine for word comparisons against zero. // Shared routine for word comparisons against zero.
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
// Try to combine with comparisons against 0 by simply inverting the branch.
while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
Int32BinopMatcher m(value);
if (!m.right().Is(0)) break;
user = value;
value = m.left().node();
cont->Negate();
}
if (CanCover(user, value)) { if (CanCover(user, value)) {
switch (value->opcode()) { switch (value->opcode()) {
case IrOpcode::kWord32Equal: case IrOpcode::kWord32Equal:
......
...@@ -1834,16 +1834,6 @@ void VisitLoadAndTest(InstructionSelector* selector, InstructionCode opcode, ...@@ -1834,16 +1834,6 @@ void VisitLoadAndTest(InstructionSelector* selector, InstructionCode opcode,
// Shared routine for word comparisons against zero. // Shared routine for word comparisons against zero.
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
// Try to combine with comparisons against 0 by simply inverting the branch.
while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
Int32BinopMatcher m(value);
if (!m.right().Is(0)) break;
user = value;
value = m.left().node();
cont->Negate();
}
FlagsCondition fc = cont->condition(); FlagsCondition fc = cont->condition();
if (CanCover(user, value)) { if (CanCover(user, value)) {
switch (value->opcode()) { switch (value->opcode()) {
......
...@@ -2063,16 +2063,6 @@ void VisitAtomicExchange(InstructionSelector* selector, Node* node, ...@@ -2063,16 +2063,6 @@ void VisitAtomicExchange(InstructionSelector* selector, Node* node,
// Shared routine for word comparison against zero. // Shared routine for word comparison against zero.
void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
FlagsContinuation* cont) { FlagsContinuation* cont) {
// Try to combine with comparisons against 0 by simply inverting the branch.
while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
Int32BinopMatcher m(value);
if (!m.right().Is(0)) break;
user = value;
value = m.left().node();
cont->Negate();
}
if (CanCover(user, value)) { if (CanCover(user, value)) {
switch (value->opcode()) { switch (value->opcode()) {
case IrOpcode::kWord32Equal: case IrOpcode::kWord32Equal:
......
...@@ -60,6 +60,9 @@ Reduction CommonOperatorReducer::Reduce(Node* node) { ...@@ -60,6 +60,9 @@ Reduction CommonOperatorReducer::Reduce(Node* node) {
case IrOpcode::kDeoptimizeIf: case IrOpcode::kDeoptimizeIf:
case IrOpcode::kDeoptimizeUnless: case IrOpcode::kDeoptimizeUnless:
return ReduceDeoptimizeConditional(node); return ReduceDeoptimizeConditional(node);
case IrOpcode::kTrapIf:
case IrOpcode::kTrapUnless:
return ReduceTrapConditional(node);
case IrOpcode::kMerge: case IrOpcode::kMerge:
return ReduceMerge(node); return ReduceMerge(node);
case IrOpcode::kEffectPhi: case IrOpcode::kEffectPhi:
...@@ -80,38 +83,77 @@ Reduction CommonOperatorReducer::Reduce(Node* node) { ...@@ -80,38 +83,77 @@ Reduction CommonOperatorReducer::Reduce(Node* node) {
return NoChange(); return NoChange();
} }
Node* CommonOperatorReducer::GetInvertedConditionOrNull(Node* cond) {
switch (cond->opcode()) {
case IrOpcode::kBooleanNot:
return cond->InputAt(0);
case IrOpcode::kSelect:
// Check whether {cond} is a Select acting as a boolean not (i.e. true
// being returned in the false case and vice versa).
if (DecideCondition(broker(), cond->InputAt(1)) == Decision::kFalse &&
DecideCondition(broker(), cond->InputAt(2)) == Decision::kTrue) {
return cond->InputAt(0);
}
break;
case IrOpcode::kWord32Equal: {
// Check whether the comparison is against zero.
Uint32BinopMatcher m(cond);
if (m.right().Is(0)) {
return m.left().node();
}
break;
}
default:
break;
}
return nullptr;
}
base::Optional<CommonOperatorReducer::SimplifiedCondition>
CommonOperatorReducer::TryGetSimplifiedCondition(Node* cond) {
Node* new_cond = cond;
bool is_inverted = false;
while (true) {
if (Node* next = GetInvertedConditionOrNull(new_cond)) {
new_cond = next;
is_inverted = !is_inverted;
} else {
if (cond == new_cond) return {};
return CommonOperatorReducer::SimplifiedCondition{new_cond, is_inverted};
}
}
}
Reduction CommonOperatorReducer::ReduceBranch(Node* node) { Reduction CommonOperatorReducer::ReduceBranch(Node* node) {
DCHECK_EQ(IrOpcode::kBranch, node->opcode()); DCHECK_EQ(IrOpcode::kBranch, node->opcode());
Node* const cond = node->InputAt(0); Node* const cond = node->InputAt(0);
// Swap IfTrue/IfFalse on {branch} if {cond} is a BooleanNot and use the input // Swap IfTrue/IfFalse on {branch} if {cond} is a BooleanNot or similar, and
// to BooleanNot as new condition for {branch}. Note we assume that {cond} was // use the input to BooleanNot as new condition for {branch}. Note we assume
// already properly optimized before we get here (as guaranteed by the graph // that {cond} was already properly optimized before we get here (as
// reduction logic). The same applies if {cond} is a Select acting as boolean // guaranteed by the graph reduction logic).
// not (i.e. true being returned in the false case and vice versa). if (auto simplified = TryGetSimplifiedCondition(cond)) {
if (cond->opcode() == IrOpcode::kBooleanNot || if (simplified->is_inverted) {
(cond->opcode() == IrOpcode::kSelect && for (Node* const use : node->uses()) {
DecideCondition(broker(), cond->InputAt(1)) == Decision::kFalse && switch (use->opcode()) {
DecideCondition(broker(), cond->InputAt(2)) == Decision::kTrue)) { case IrOpcode::kIfTrue:
for (Node* const use : node->uses()) { NodeProperties::ChangeOp(use, common()->IfFalse());
switch (use->opcode()) { break;
case IrOpcode::kIfTrue: case IrOpcode::kIfFalse:
NodeProperties::ChangeOp(use, common()->IfFalse()); NodeProperties::ChangeOp(use, common()->IfTrue());
break; break;
case IrOpcode::kIfFalse: default:
NodeProperties::ChangeOp(use, common()->IfTrue()); UNREACHABLE();
break; }
default:
UNREACHABLE();
} }
// Negate the hint for {branch}.
NodeProperties::ChangeOp(
node, common()->Branch(NegateBranchHint(BranchHintOf(node->op()))));
} }
// Update the condition of {branch}. No need to mark the uses for revisit, // Update the condition of {branch}. No need to mark the uses for revisit,
// since we tell the graph reducer that the {branch} was changed and the // since we tell the graph reducer that the {branch} was changed and the
// graph reduction logic will ensure that the uses are revisited properly. // graph reduction logic will ensure that the uses are revisited properly.
node->ReplaceInput(0, cond->InputAt(0)); node->ReplaceInput(0, simplified->condition);
// Negate the hint for {branch}.
NodeProperties::ChangeOp(
node, common()->Branch(NegateBranchHint(BranchHintOf(node->op()))));
return Changed(node); return Changed(node);
} }
Decision const decision = DecideCondition(broker(), cond); Decision const decision = DecideCondition(broker(), cond);
...@@ -141,17 +183,19 @@ Reduction CommonOperatorReducer::ReduceDeoptimizeConditional(Node* node) { ...@@ -141,17 +183,19 @@ Reduction CommonOperatorReducer::ReduceDeoptimizeConditional(Node* node) {
Node* frame_state = NodeProperties::GetValueInput(node, 1); Node* frame_state = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
// Swap DeoptimizeIf/DeoptimizeUnless on {node} if {cond} is a BooleaNot // Swap DeoptimizeIf/DeoptimizeUnless on {node} if {cond} is a BooleanNot or
// and use the input to BooleanNot as new condition for {node}. Note we // similar, and use the input to BooleanNot as new condition for {node}. Note
// assume that {cond} was already properly optimized before we get here // we assume that {cond} was already properly optimized before we get here (as
// (as guaranteed by the graph reduction logic). // guaranteed by the graph reduction logic).
if (condition->opcode() == IrOpcode::kBooleanNot) { if (auto simplified = TryGetSimplifiedCondition(condition)) {
NodeProperties::ReplaceValueInput(node, condition->InputAt(0), 0); NodeProperties::ReplaceValueInput(node, simplified->condition, 0);
NodeProperties::ChangeOp( if (simplified->is_inverted) {
node, NodeProperties::ChangeOp(
condition_is_true node,
? common()->DeoptimizeIf(p.kind(), p.reason(), p.feedback()) condition_is_true
: common()->DeoptimizeUnless(p.kind(), p.reason(), p.feedback())); ? common()->DeoptimizeIf(p.kind(), p.reason(), p.feedback())
: common()->DeoptimizeUnless(p.kind(), p.reason(), p.feedback()));
}
return Changed(node); return Changed(node);
} }
Decision const decision = DecideCondition(broker(), condition); Decision const decision = DecideCondition(broker(), condition);
...@@ -169,6 +213,28 @@ Reduction CommonOperatorReducer::ReduceDeoptimizeConditional(Node* node) { ...@@ -169,6 +213,28 @@ Reduction CommonOperatorReducer::ReduceDeoptimizeConditional(Node* node) {
return Replace(dead()); return Replace(dead());
} }
Reduction CommonOperatorReducer::ReduceTrapConditional(Node* node) {
DCHECK(node->opcode() == IrOpcode::kTrapIf ||
node->opcode() == IrOpcode::kTrapUnless);
bool condition_is_true = node->opcode() == IrOpcode::kTrapUnless;
TrapId trap_id = TrapIdOf(node->op());
Node* condition = NodeProperties::GetValueInput(node, 0);
// Swap TrapIf/TrapUnless on {node} if {cond} is a BooleanNot or similar,
// and use the input to BooleanNot as new condition for {node}. Note we
// assume that {cond} was already properly optimized before we get here
// (as guaranteed by the graph reduction logic).
if (auto simplified = TryGetSimplifiedCondition(condition)) {
NodeProperties::ReplaceValueInput(node, simplified->condition, 0);
if (simplified->is_inverted) {
NodeProperties::ChangeOp(node, condition_is_true
? common()->TrapIf(trap_id)
: common()->TrapUnless(trap_id));
}
return Changed(node);
}
return NoChange();
}
Reduction CommonOperatorReducer::ReduceMerge(Node* node) { Reduction CommonOperatorReducer::ReduceMerge(Node* node) {
DCHECK_EQ(IrOpcode::kMerge, node->opcode()); DCHECK_EQ(IrOpcode::kMerge, node->opcode());
// //
......
...@@ -36,6 +36,7 @@ class V8_EXPORT_PRIVATE CommonOperatorReducer final ...@@ -36,6 +36,7 @@ class V8_EXPORT_PRIVATE CommonOperatorReducer final
private: private:
Reduction ReduceBranch(Node* node); Reduction ReduceBranch(Node* node);
Reduction ReduceDeoptimizeConditional(Node* node); Reduction ReduceDeoptimizeConditional(Node* node);
Reduction ReduceTrapConditional(Node* node);
Reduction ReduceMerge(Node* node); Reduction ReduceMerge(Node* node);
Reduction ReduceEffectPhi(Node* node); Reduction ReduceEffectPhi(Node* node);
Reduction ReducePhi(Node* node); Reduction ReducePhi(Node* node);
...@@ -47,6 +48,17 @@ class V8_EXPORT_PRIVATE CommonOperatorReducer final ...@@ -47,6 +48,17 @@ class V8_EXPORT_PRIVATE CommonOperatorReducer final
Reduction Change(Node* node, Operator const* op, Node* a); Reduction Change(Node* node, Operator const* op, Node* a);
Reduction Change(Node* node, Operator const* op, Node* a, Node* b); Reduction Change(Node* node, Operator const* op, Node* a, Node* b);
// If the condition can be more simply represented by an inverted value,
// returns the node for that inverted value. Otherwise returns null.
Node* GetInvertedConditionOrNull(Node* cond);
struct SimplifiedCondition {
Node* condition;
bool is_inverted;
};
// Removes as many inversions as possible from a condition.
base::Optional<SimplifiedCondition> TryGetSimplifiedCondition(Node* cond);
Graph* graph() const { return graph_; } Graph* graph() const { return graph_; }
JSHeapBroker* broker() const { return broker_; } JSHeapBroker* broker() const { return broker_; }
CommonOperatorBuilder* common() const { return common_; } CommonOperatorBuilder* common() const { return common_; }
......
...@@ -264,7 +264,7 @@ void LiftoffAssembler::PatchPrepareStackFrame(int offset, int frame_size) { ...@@ -264,7 +264,7 @@ void LiftoffAssembler::PatchPrepareStackFrame(int offset, int frame_size) {
patching_assembler.PadWithNops(); patching_assembler.PadWithNops();
// Now generate the OOL code. // Now generate the OOL code.
AllocateStackSpace(bytes); AllocateStackSpace(frame_size);
// Jump back to the start of the function (from {pc_offset()} to {offset + // Jump back to the start of the function (from {pc_offset()} to {offset +
// liftoff::kPatchInstructionsRequired * kInstrSize}). // liftoff::kPatchInstructionsRequired * kInstrSize}).
int func_start_offset = int func_start_offset =
......
...@@ -402,131 +402,6 @@ TEST_P(InstructionSelectorDPITest, BranchWithShiftByImmediate) { ...@@ -402,131 +402,6 @@ TEST_P(InstructionSelectorDPITest, BranchWithShiftByImmediate) {
} }
} }
TEST_P(InstructionSelectorDPITest, BranchIfZeroWithParameters) {
const DPI dpi = GetParam();
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
RawMachineLabel a, b;
m.Branch(m.Word32Equal((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1)),
m.Int32Constant(0)),
&a, &b);
m.Bind(&a);
m.Return(m.Int32Constant(1));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.test_arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kEqual, s[0]->flags_condition());
}
TEST_P(InstructionSelectorDPITest, BranchIfNotZeroWithParameters) {
const DPI dpi = GetParam();
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
RawMachineLabel a, b;
m.Branch(
m.Word32NotEqual((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1)),
m.Int32Constant(0)),
&a, &b);
m.Bind(&a);
m.Return(m.Int32Constant(1));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.test_arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kNotEqual, s[0]->flags_condition());
}
TEST_P(InstructionSelectorDPITest, BranchIfZeroWithImmediate) {
const DPI dpi = GetParam();
TRACED_FOREACH(int32_t, imm, kImmediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32());
RawMachineLabel a, b;
m.Branch(m.Word32Equal(
(m.*dpi.constructor)(m.Parameter(0), m.Int32Constant(imm)),
m.Int32Constant(0)),
&a, &b);
m.Bind(&a);
m.Return(m.Int32Constant(1));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.test_arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_I, s[0]->addressing_mode());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kEqual, s[0]->flags_condition());
}
TRACED_FOREACH(int32_t, imm, kImmediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32());
RawMachineLabel a, b;
m.Branch(m.Word32Equal(
(m.*dpi.constructor)(m.Int32Constant(imm), m.Parameter(0)),
m.Int32Constant(0)),
&a, &b);
m.Bind(&a);
m.Return(m.Int32Constant(1));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.test_arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_I, s[0]->addressing_mode());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kEqual, s[0]->flags_condition());
}
}
TEST_P(InstructionSelectorDPITest, BranchIfNotZeroWithImmediate) {
const DPI dpi = GetParam();
TRACED_FOREACH(int32_t, imm, kImmediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32());
RawMachineLabel a, b;
m.Branch(m.Word32NotEqual(
(m.*dpi.constructor)(m.Parameter(0), m.Int32Constant(imm)),
m.Int32Constant(0)),
&a, &b);
m.Bind(&a);
m.Return(m.Int32Constant(1));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.test_arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_I, s[0]->addressing_mode());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kNotEqual, s[0]->flags_condition());
}
TRACED_FOREACH(int32_t, imm, kImmediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32());
RawMachineLabel a, b;
m.Branch(m.Word32NotEqual(
(m.*dpi.constructor)(m.Int32Constant(imm), m.Parameter(0)),
m.Int32Constant(0)),
&a, &b);
m.Bind(&a);
m.Return(m.Int32Constant(1));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.test_arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_I, s[0]->addressing_mode());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kNotEqual, s[0]->flags_condition());
}
}
INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorDPITest, INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorDPITest,
::testing::ValuesIn(kDPIs)); ::testing::ValuesIn(kDPIs));
...@@ -980,50 +855,6 @@ TEST_P(InstructionSelectorODPITest, BranchWithImmediate) { ...@@ -980,50 +855,6 @@ TEST_P(InstructionSelectorODPITest, BranchWithImmediate) {
} }
} }
TEST_P(InstructionSelectorODPITest, BranchIfZeroWithParameters) {
const ODPI odpi = GetParam();
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
RawMachineLabel a, b;
Node* n = (m.*odpi.constructor)(m.Parameter(0), m.Parameter(1));
m.Branch(m.Word32Equal(m.Projection(1, n), m.Int32Constant(0)), &a, &b);
m.Bind(&a);
m.Return(m.Projection(0, n));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(odpi.arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
EXPECT_EQ(4U, s[0]->InputCount());
EXPECT_EQ(1U, s[0]->OutputCount());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kNotOverflow, s[0]->flags_condition());
}
TEST_P(InstructionSelectorODPITest, BranchIfNotZeroWithParameters) {
const ODPI odpi = GetParam();
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
RawMachineLabel a, b;
Node* n = (m.*odpi.constructor)(m.Parameter(0), m.Parameter(1));
m.Branch(m.Word32NotEqual(m.Projection(1, n), m.Int32Constant(0)), &a, &b);
m.Bind(&a);
m.Return(m.Projection(0, n));
m.Bind(&b);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(odpi.arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_Operand2_R, s[0]->addressing_mode());
EXPECT_EQ(4U, s[0]->InputCount());
EXPECT_EQ(1U, s[0]->OutputCount());
EXPECT_EQ(kFlags_branch, s[0]->flags_mode());
EXPECT_EQ(kOverflow, s[0]->flags_condition());
}
INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorODPITest, INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorODPITest,
::testing::ValuesIn(kODPIs)); ::testing::ValuesIn(kODPIs));
...@@ -1952,8 +1783,6 @@ TEST_F(InstructionSelectorTest, Float64Sqrt) { ...@@ -1952,8 +1783,6 @@ TEST_F(InstructionSelectorTest, Float64Sqrt) {
const Comparison kBinopCmpZeroRightInstructions[] = { const Comparison kBinopCmpZeroRightInstructions[] = {
{&RawMachineAssembler::Word32Equal, "Word32Equal", kEqual, kNotEqual, {&RawMachineAssembler::Word32Equal, "Word32Equal", kEqual, kNotEqual,
kEqual}, kEqual},
{&RawMachineAssembler::Word32NotEqual, "Word32NotEqual", kNotEqual, kEqual,
kNotEqual},
{&RawMachineAssembler::Int32LessThan, "Int32LessThan", kNegative, {&RawMachineAssembler::Int32LessThan, "Int32LessThan", kNegative,
kPositiveOrZero, kNegative}, kPositiveOrZero, kNegative},
{&RawMachineAssembler::Int32GreaterThanOrEqual, "Int32GreaterThanOrEqual", {&RawMachineAssembler::Int32GreaterThanOrEqual, "Int32GreaterThanOrEqual",
...@@ -1966,8 +1795,6 @@ const Comparison kBinopCmpZeroRightInstructions[] = { ...@@ -1966,8 +1795,6 @@ const Comparison kBinopCmpZeroRightInstructions[] = {
const Comparison kBinopCmpZeroLeftInstructions[] = { const Comparison kBinopCmpZeroLeftInstructions[] = {
{&RawMachineAssembler::Word32Equal, "Word32Equal", kEqual, kNotEqual, {&RawMachineAssembler::Word32Equal, "Word32Equal", kEqual, kNotEqual,
kEqual}, kEqual},
{&RawMachineAssembler::Word32NotEqual, "Word32NotEqual", kNotEqual, kEqual,
kNotEqual},
{&RawMachineAssembler::Int32GreaterThan, "Int32GreaterThan", kNegative, {&RawMachineAssembler::Int32GreaterThan, "Int32GreaterThan", kNegative,
kPositiveOrZero, kNegative}, kPositiveOrZero, kNegative},
{&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual", {&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual",
......
...@@ -182,6 +182,26 @@ TEST_F(CommonOperatorReducerTest, BranchWithSelect) { ...@@ -182,6 +182,26 @@ TEST_F(CommonOperatorReducerTest, BranchWithSelect) {
} }
} }
TEST_F(CommonOperatorReducerTest, BranchWithEqualsZero) {
Node* const value = Parameter(0);
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
Node* const control = graph()->start();
Node* const branch = graph()->NewNode(
common()->Branch(hint),
graph()->NewNode(machine()->Word32Equal(), Uint32Constant(0), value),
control);
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
Reduction const r = Reduce(branch);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(branch, r.replacement());
EXPECT_THAT(branch, IsBranch(value, control));
EXPECT_THAT(if_false, IsIfTrue(branch));
EXPECT_THAT(if_true, IsIfFalse(branch));
EXPECT_EQ(NegateBranchHint(hint), BranchHintOf(branch->op()));
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Merge // Merge
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment