diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index d2fdf46aa6..3c5aa4173f 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -339,6 +339,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArm64Cmp32: __ Cmp(i.InputRegister32(0), i.InputOperand32(1)); break; + case kArm64Cmn: + __ Cmn(i.InputRegister(0), i.InputOperand(1)); + break; + case kArm64Cmn32: + __ Cmn(i.InputRegister32(0), i.InputOperand32(1)); + break; case kArm64Tst: __ Tst(i.InputRegister(0), i.InputOperand(1)); break; diff --git a/src/compiler/arm64/instruction-codes-arm64.h b/src/compiler/arm64/instruction-codes-arm64.h index ebc8c86cb2..656b48083c 100644 --- a/src/compiler/arm64/instruction-codes-arm64.h +++ b/src/compiler/arm64/instruction-codes-arm64.h @@ -18,6 +18,8 @@ namespace compiler { V(Arm64And32) \ V(Arm64Cmp) \ V(Arm64Cmp32) \ + V(Arm64Cmn) \ + V(Arm64Cmn32) \ V(Arm64Tst) \ V(Arm64Tst32) \ V(Arm64Or) \ diff --git a/src/compiler/arm64/instruction-selector-arm64-unittest.cc b/src/compiler/arm64/instruction-selector-arm64-unittest.cc index cc9c4fdd4c..b2abe6e25a 100644 --- a/src/compiler/arm64/instruction-selector-arm64-unittest.cc +++ b/src/compiler/arm64/instruction-selector-arm64-unittest.cc @@ -12,6 +12,8 @@ namespace compiler { namespace { +typedef RawMachineAssembler::Label MLabel; + template struct MachInst { T constructor; @@ -88,6 +90,13 @@ static const int32_t kAddSubImmediates[] = { 15597568, 15892480, 16773120}; +// ARM64 flag setting data processing instructions. +static const MachInst2 kDPFlagSetInstructions[] = { + {&RawMachineAssembler::Word32And, "Word32And", kArm64Tst32, kMachInt32}, + {&RawMachineAssembler::Int32Add, "Int32Add", kArm64Cmn32, kMachInt32}, + {&RawMachineAssembler::Int32Sub, "Int32Sub", kArm64Cmp32, kMachInt32}}; + + // ARM64 arithmetic with overflow instructions. static const MachInst2 kOvfAddSubInstructions[] = { {&RawMachineAssembler::Int32AddWithOverflow, "Int32AddWithOverflow", @@ -343,6 +352,129 @@ TEST_F(InstructionSelectorTest, SubZeroOnLeft) { } +// ----------------------------------------------------------------------------- +// Data processing controlled branches. + + +typedef InstructionSelectorTestWithParam + InstructionSelectorDPFlagSetTest; + + +TEST_P(InstructionSelectorDPFlagSetTest, BranchWithParameters) { + const MachInst2 dpi = GetParam(); + const MachineType type = dpi.machine_type; + StreamBuilder m(this, type, type, type); + MLabel a, b; + m.Branch((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1)), &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.arch_opcode, s[0]->arch_opcode()); + EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); +} + + +INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, + InstructionSelectorDPFlagSetTest, + ::testing::ValuesIn(kDPFlagSetInstructions)); + + +TEST_F(InstructionSelectorTest, AndBranchWithImmediateOnRight) { + TRACED_FOREACH(int32_t, imm, kLogicalImmediates) { + StreamBuilder m(this, kMachInt32, kMachInt32); + MLabel a, b; + m.Branch(m.Word32And(m.Parameter(0), m.Int32Constant(imm)), &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(kArm64Tst32, s[0]->arch_opcode()); + EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); + } +} + + +TEST_F(InstructionSelectorTest, AddBranchWithImmediateOnRight) { + TRACED_FOREACH(int32_t, imm, kAddSubImmediates) { + StreamBuilder m(this, kMachInt32, kMachInt32); + MLabel a, b; + m.Branch(m.Int32Add(m.Parameter(0), m.Int32Constant(imm)), &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(kArm64Cmn32, s[0]->arch_opcode()); + EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); + } +} + + +TEST_F(InstructionSelectorTest, SubBranchWithImmediateOnRight) { + TRACED_FOREACH(int32_t, imm, kAddSubImmediates) { + StreamBuilder m(this, kMachInt32, kMachInt32); + MLabel a, b; + m.Branch(m.Int32Sub(m.Parameter(0), m.Int32Constant(imm)), &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(kArm64Cmp32, s[0]->arch_opcode()); + EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); + } +} + + +TEST_F(InstructionSelectorTest, AndBranchWithImmediateOnLeft) { + TRACED_FOREACH(int32_t, imm, kLogicalImmediates) { + StreamBuilder m(this, kMachInt32, kMachInt32); + MLabel a, b; + m.Branch(m.Word32And(m.Int32Constant(imm), m.Parameter(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(kArm64Tst32, s[0]->arch_opcode()); + ASSERT_LE(1U, s[0]->InputCount()); + EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); + } +} + + +TEST_F(InstructionSelectorTest, AddBranchWithImmediateOnLeft) { + TRACED_FOREACH(int32_t, imm, kAddSubImmediates) { + StreamBuilder m(this, kMachInt32, kMachInt32); + MLabel a, b; + m.Branch(m.Int32Add(m.Int32Constant(imm), m.Parameter(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(kArm64Cmn32, s[0]->arch_opcode()); + ASSERT_LE(1U, s[0]->InputCount()); + EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); + } +} + + // ----------------------------------------------------------------------------- // Add and subtract instructions with overflow. @@ -454,6 +586,50 @@ TEST_P(InstructionSelectorOvfAddSubTest, BothImmediateOnRight) { } +TEST_P(InstructionSelectorOvfAddSubTest, BranchWithParameters) { + const MachInst2 dpi = GetParam(); + const MachineType type = dpi.machine_type; + StreamBuilder m(this, type, type, type); + MLabel a, b; + Node* n = (m.*dpi.constructor)(m.Parameter(0), m.Parameter(1)); + m.Branch(m.Projection(1, n), &a, &b); + m.Bind(&a); + m.Return(m.Int32Constant(0)); + m.Bind(&b); + m.Return(m.Projection(0, n)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); + 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()); +} + + +TEST_P(InstructionSelectorOvfAddSubTest, BranchWithImmediateOnRight) { + const MachInst2 dpi = GetParam(); + const MachineType type = dpi.machine_type; + TRACED_FOREACH(int32_t, imm, kAddSubImmediates) { + StreamBuilder m(this, type, type); + MLabel a, b; + Node* n = (m.*dpi.constructor)(m.Parameter(0), m.Int32Constant(imm)); + m.Branch(m.Projection(1, n), &a, &b); + m.Bind(&a); + m.Return(m.Int32Constant(0)); + m.Bind(&b); + m.Return(m.Projection(0, n)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); + ASSERT_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_CASE_P(InstructionSelectorTest, InstructionSelectorOvfAddSubTest, ::testing::ValuesIn(kOvfAddSubInstructions)); @@ -512,6 +688,29 @@ TEST_F(InstructionSelectorTest, OvfBothAddImmediateOnLeft) { } +TEST_F(InstructionSelectorTest, OvfBranchWithImmediateOnLeft) { + TRACED_FOREACH(int32_t, imm, kAddSubImmediates) { + StreamBuilder m(this, kMachInt32, kMachInt32); + MLabel a, b; + Node* n = m.Int32AddWithOverflow(m.Int32Constant(imm), m.Parameter(0)); + m.Branch(m.Projection(1, n), &a, &b); + m.Bind(&a); + m.Return(m.Int32Constant(0)); + m.Bind(&b); + m.Return(m.Projection(0, n)); + Stream s = m.Build(); + + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Add32, s[0]->arch_opcode()); + ASSERT_EQ(4U, s[0]->InputCount()); + EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1))); + EXPECT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(kFlags_branch, s[0]->flags_mode()); + EXPECT_EQ(kOverflow, s[0]->flags_condition()); + } +} + + // ----------------------------------------------------------------------------- // Shift instructions. diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index e659c9b521..d5cfdbe8fd 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -525,6 +525,10 @@ static void VisitWordCompare(InstructionSelector* selector, Node* node, void InstructionSelector::VisitWord32Test(Node* node, FlagsContinuation* cont) { switch (node->opcode()) { + case IrOpcode::kInt32Add: + return VisitWordCompare(this, node, kArm64Cmn32, cont, true); + case IrOpcode::kInt32Sub: + return VisitWordCompare(this, node, kArm64Cmp32, cont, false); case IrOpcode::kWord32And: return VisitWordCompare(this, node, kArm64Tst32, cont, true); default: