Index: test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc |
diff --git a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc |
index 5fc210be6e03a5f87b762af3742105e4a9d7485d..8d2b36dddd704684ab5d7cfb510ad11b22d454c1 100644 |
--- a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc |
+++ b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc |
@@ -3242,6 +3242,323 @@ TEST_F(InstructionSelectorTest, CmnShiftByImmediateOnLeft) { |
} |
} |
+// ----------------------------------------------------------------------------- |
+// Flag-setting add and and instructions. |
+ |
+const IntegerCmp kBinopCmpZeroRightInstructions[] = { |
+ {{&RawMachineAssembler::Word32Equal, "Word32Equal", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kEqual, |
+ kEqual}, |
+ {{&RawMachineAssembler::Word32NotEqual, "Word32NotEqual", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kNotEqual, |
+ kNotEqual}, |
+ {{&RawMachineAssembler::Int32LessThan, "Int32LessThan", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kNegative, |
+ kNegative}, |
+ {{&RawMachineAssembler::Int32GreaterThanOrEqual, "Int32GreaterThanOrEqual", |
+ kArm64Cmp32, MachineType::Int32()}, |
+ kPositiveOrZero, |
+ kPositiveOrZero}, |
+ {{&RawMachineAssembler::Uint32LessThanOrEqual, "Uint32LessThanOrEqual", |
+ kArm64Cmp32, MachineType::Int32()}, |
+ kEqual, |
+ kEqual}, |
+ {{&RawMachineAssembler::Uint32GreaterThan, "Uint32GreaterThan", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kNotEqual, |
+ kNotEqual}}; |
+ |
+const IntegerCmp kBinopCmpZeroLeftInstructions[] = { |
+ {{&RawMachineAssembler::Word32Equal, "Word32Equal", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kEqual, |
+ kEqual}, |
+ {{&RawMachineAssembler::Word32NotEqual, "Word32NotEqual", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kNotEqual, |
+ kNotEqual}, |
+ {{&RawMachineAssembler::Int32GreaterThan, "Int32GreaterThan", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kNegative, |
+ kNegative}, |
+ {{&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual", |
+ kArm64Cmp32, MachineType::Int32()}, |
+ kPositiveOrZero, |
+ kPositiveOrZero}, |
+ {{&RawMachineAssembler::Uint32GreaterThanOrEqual, |
+ "Uint32GreaterThanOrEqual", kArm64Cmp32, MachineType::Int32()}, |
+ kEqual, |
+ kEqual}, |
+ {{&RawMachineAssembler::Uint32LessThan, "Uint32LessThan", kArm64Cmp32, |
+ MachineType::Int32()}, |
+ kNotEqual, |
+ kNotEqual}}; |
+ |
+struct FlagSettingInst { |
+ MachInst2 mi; |
+ ArchOpcode no_output_opcode; |
+}; |
+ |
+std::ostream& operator<<(std::ostream& os, const FlagSettingInst& inst) { |
+ return os << inst.mi.constructor_name; |
+} |
+ |
+const FlagSettingInst kFlagSettingInstructions[] = { |
+ {{&RawMachineAssembler::Int32Add, "Int32Add", kArm64Add32, |
+ MachineType::Int32()}, |
+ kArm64Cmn32}, |
+ {{&RawMachineAssembler::Word32And, "Word32And", kArm64And32, |
+ MachineType::Int32()}, |
+ kArm64Tst32}}; |
+ |
+typedef InstructionSelectorTestWithParam<FlagSettingInst> |
+ InstructionSelectorFlagSettingTest; |
+ |
+TEST_P(InstructionSelectorFlagSettingTest, CmpZeroRight) { |
+ const FlagSettingInst inst = GetParam(); |
+ // Add with single user : a cmp instruction. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroRightInstructions) { |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
+ MachineType::Int32()); |
+ RawMachineLabel a, b; |
+ Node* binop = (m.*inst.mi.constructor)(m.Parameter(0), m.Parameter(1)); |
+ Node* comp = (m.*cmp.mi.constructor)(binop, m.Int32Constant(0)); |
+ m.Branch(comp, &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()); |
+ ASSERT_EQ(4U, s[0]->InputCount()); // The labels are also inputs. |
+ EXPECT_EQ(inst.no_output_opcode, s[0]->arch_opcode()); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(0)), s.ToVreg(s[0]->InputAt(0))); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(1)), s.ToVreg(s[0]->InputAt(1))); |
+ EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ } |
+} |
+ |
+TEST_P(InstructionSelectorFlagSettingTest, CmpZeroLeft) { |
+ const FlagSettingInst inst = GetParam(); |
+ // Test a cmp with zero on the left-hand side. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroLeftInstructions) { |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
+ MachineType::Int32()); |
+ RawMachineLabel a, b; |
+ Node* binop = (m.*inst.mi.constructor)(m.Parameter(0), m.Parameter(1)); |
+ Node* comp = (m.*cmp.mi.constructor)(m.Int32Constant(0), binop); |
+ m.Branch(comp, &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()); |
+ ASSERT_EQ(4U, s[0]->InputCount()); // The labels are also inputs. |
+ EXPECT_EQ(inst.no_output_opcode, s[0]->arch_opcode()); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(0)), s.ToVreg(s[0]->InputAt(0))); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(1)), s.ToVreg(s[0]->InputAt(1))); |
+ EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ } |
+} |
+ |
+TEST_P(InstructionSelectorFlagSettingTest, CmpZeroOnlyUserInBasicBlock) { |
+ const FlagSettingInst inst = GetParam(); |
+ // Binop with additional users, but in a different basic block. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroRightInstructions) { |
+ // For kEqual and kNotEqual, we generate a cbz or cbnz. |
+ if (cmp.cond == kEqual || cmp.cond == kNotEqual) continue; |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
+ MachineType::Int32()); |
+ RawMachineLabel a, b; |
+ Node* binop = (m.*inst.mi.constructor)(m.Parameter(0), m.Parameter(1)); |
+ Node* comp = (m.*cmp.mi.constructor)(binop, m.Int32Constant(0)); |
+ m.Branch(comp, &a, &b); |
+ m.Bind(&a); |
+ m.Return(binop); |
+ m.Bind(&b); |
+ m.Return(m.Int32Constant(0)); |
+ Stream s = m.Build(); |
+ ASSERT_EQ(1U, s.size()); |
+ ASSERT_EQ(4U, s[0]->InputCount()); // The labels are also inputs. |
+ EXPECT_EQ(inst.mi.arch_opcode, s[0]->arch_opcode()); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(0)), s.ToVreg(s[0]->InputAt(0))); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(1)), s.ToVreg(s[0]->InputAt(1))); |
+ EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ } |
+} |
+ |
+TEST_P(InstructionSelectorFlagSettingTest, ShiftedOperand) { |
+ const FlagSettingInst inst = GetParam(); |
+ // Like the test above, but with a shifted input to the binary operator. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroRightInstructions) { |
+ // For kEqual and kNotEqual, we generate a cbz or cbnz. |
+ if (cmp.cond == kEqual || cmp.cond == kNotEqual) continue; |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
+ MachineType::Int32()); |
+ RawMachineLabel a, b; |
+ Node* imm = m.Int32Constant(5); |
+ Node* shift = m.Word32Shl(m.Parameter(1), imm); |
+ Node* binop = (m.*inst.mi.constructor)(m.Parameter(0), shift); |
+ Node* comp = (m.*cmp.mi.constructor)(binop, m.Int32Constant(0)); |
+ m.Branch(comp, &a, &b); |
+ m.Bind(&a); |
+ m.Return(binop); |
+ m.Bind(&b); |
+ m.Return(m.Int32Constant(0)); |
+ Stream s = m.Build(); |
+ ASSERT_EQ(1U, s.size()); |
+ ASSERT_EQ(5U, s[0]->InputCount()); // The labels are also inputs. |
+ EXPECT_EQ(inst.mi.arch_opcode, s[0]->arch_opcode()); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(0)), s.ToVreg(s[0]->InputAt(0))); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(1)), s.ToVreg(s[0]->InputAt(1))); |
+ EXPECT_EQ(5, s.ToInt32(s[0]->InputAt(2))); |
+ EXPECT_EQ(kMode_Operand2_R_LSL_I, s[0]->addressing_mode()); |
+ EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ } |
+} |
+ |
+TEST_P(InstructionSelectorFlagSettingTest, UsersInSameBasicBlock) { |
+ const FlagSettingInst inst = GetParam(); |
+ // Binop with additional users, in the same basic block. We need to make sure |
+ // we don't try to optimise this case. |
+ TRACED_FOREACH(IntegerCmp, cmp, kIntegerCmpInstructions) { |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
+ MachineType::Int32()); |
+ RawMachineLabel a, b; |
+ Node* binop = (m.*inst.mi.constructor)(m.Parameter(0), m.Parameter(1)); |
+ Node* mul = m.Int32Mul(m.Parameter(0), binop); |
+ Node* comp = (m.*cmp.mi.constructor)(binop, m.Int32Constant(0)); |
+ m.Branch(comp, &a, &b); |
+ m.Bind(&a); |
+ m.Return(mul); |
+ m.Bind(&b); |
+ m.Return(m.Int32Constant(0)); |
+ Stream s = m.Build(); |
+ ASSERT_EQ(3U, s.size()); |
+ EXPECT_EQ(inst.mi.arch_opcode, s[0]->arch_opcode()); |
+ EXPECT_NE(kFlags_branch, s[0]->flags_mode()); |
+ EXPECT_EQ(kArm64Mul32, s[1]->arch_opcode()); |
+ EXPECT_EQ(cmp.cond == kEqual ? kArm64CompareAndBranch32 : kArm64Cmp32, |
+ s[2]->arch_opcode()); |
+ EXPECT_EQ(kFlags_branch, s[2]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[2]->flags_condition()); |
+ } |
+} |
+ |
+TEST_P(InstructionSelectorFlagSettingTest, CommuteImmediate) { |
+ const FlagSettingInst inst = GetParam(); |
+ // Immediate on left hand side of the binary operator. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroRightInstructions) { |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
+ RawMachineLabel a, b; |
+ // 3 can be an immediate on both arithmetic and logical instructions. |
+ Node* imm = m.Int32Constant(3); |
+ Node* binop = (m.*inst.mi.constructor)(imm, m.Parameter(0)); |
+ Node* comp = (m.*cmp.mi.constructor)(binop, m.Int32Constant(0)); |
+ m.Branch(comp, &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()); |
+ ASSERT_EQ(4U, s[0]->InputCount()); // The labels are also inputs. |
+ EXPECT_EQ(inst.no_output_opcode, s[0]->arch_opcode()); |
+ EXPECT_EQ(s.ToVreg(m.Parameter(0)), s.ToVreg(s[0]->InputAt(0))); |
+ EXPECT_EQ(3, s.ToInt32(s[0]->InputAt(1))); |
+ EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ } |
+} |
+ |
+TEST_P(InstructionSelectorFlagSettingTest, CommuteShift) { |
+ const FlagSettingInst inst = GetParam(); |
+ // Left-hand side operand shifted by immediate. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroRightInstructions) { |
+ TRACED_FOREACH(Shift, shift, kShiftInstructions) { |
+ // Only test relevant shifted operands. |
+ if (shift.mi.machine_type != MachineType::Int32()) continue; |
+ |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
+ MachineType::Int32()); |
+ Node* imm = m.Int32Constant(5); |
+ Node* shifted_operand = (m.*shift.mi.constructor)(m.Parameter(0), imm); |
+ Node* binop = (m.*inst.mi.constructor)(shifted_operand, m.Parameter(1)); |
+ Node* comp = (m.*cmp.mi.constructor)(binop, m.Int32Constant(0)); |
+ m.Return(comp); |
+ Stream s = m.Build(); |
+ // Cmn does not support ROR shifts. |
+ if (inst.no_output_opcode == kArm64Cmn32 && |
+ shift.mi.arch_opcode == kArm64Ror32) { |
+ ASSERT_EQ(2U, s.size()); |
+ continue; |
+ } |
+ ASSERT_EQ(1U, s.size()); |
+ EXPECT_EQ(inst.no_output_opcode, s[0]->arch_opcode()); |
+ EXPECT_EQ(shift.mode, s[0]->addressing_mode()); |
+ EXPECT_EQ(3U, s[0]->InputCount()); |
+ EXPECT_EQ(5, s.ToInt64(s[0]->InputAt(2))); |
+ EXPECT_EQ(1U, s[0]->OutputCount()); |
+ EXPECT_EQ(kFlags_set, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ } |
+ } |
+} |
+ |
+INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, |
+ InstructionSelectorFlagSettingTest, |
+ ::testing::ValuesIn(kFlagSettingInstructions)); |
+ |
+TEST_F(InstructionSelectorTest, TstInvalidImmediate) { |
+ // Make sure we do not generate an invalid immediate for TST. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroRightInstructions) { |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
+ RawMachineLabel a, b; |
+ // 5 is not a valid constant for TST. |
+ Node* imm = m.Int32Constant(5); |
+ Node* binop = m.Word32And(imm, m.Parameter(0)); |
+ Node* comp = (m.*cmp.mi.constructor)(binop, m.Int32Constant(0)); |
+ m.Branch(comp, &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()); |
+ ASSERT_EQ(4U, s[0]->InputCount()); // The labels are also inputs. |
+ EXPECT_EQ(kArm64Tst32, s[0]->arch_opcode()); |
+ EXPECT_NE(InstructionOperand::IMMEDIATE, s[0]->InputAt(0)->kind()); |
+ EXPECT_NE(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind()); |
+ EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ } |
+} |
+ |
+TEST_F(InstructionSelectorTest, CommuteAddsExtend) { |
+ // Extended left-hand side operand. |
+ TRACED_FOREACH(IntegerCmp, cmp, kBinopCmpZeroRightInstructions) { |
+ StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
+ MachineType::Int32()); |
+ Node* extend = m.Word32Sar(m.Word32Shl(m.Parameter(0), m.Int32Constant(24)), |
+ m.Int32Constant(24)); |
+ Node* binop = m.Int32Add(extend, m.Parameter(1)); |
+ m.Return((m.*cmp.mi.constructor)(binop, m.Int32Constant(0))); |
+ Stream s = m.Build(); |
+ ASSERT_EQ(1U, s.size()); |
+ EXPECT_EQ(kArm64Cmn32, s[0]->arch_opcode()); |
+ EXPECT_EQ(kFlags_set, s[0]->flags_mode()); |
+ EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
+ EXPECT_EQ(kMode_Operand2_R_SXTB, s[0]->addressing_mode()); |
+ } |
+} |
// ----------------------------------------------------------------------------- |
// Miscellaneous |