[turbofan] Negated immediates for ARM64 add/sub

Add ARM64 instruction selector support for negating the sense of an arithmetic
instruction when its immediate is negative.

BUG=
R=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/610323004

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24407 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f40d582c
...@@ -44,6 +44,10 @@ class Arm64OperandGenerator FINAL : public OperandGenerator { ...@@ -44,6 +44,10 @@ class Arm64OperandGenerator FINAL : public OperandGenerator {
value = OpParameter<int64_t>(node); value = OpParameter<int64_t>(node);
else else
return false; return false;
return CanBeImmediate(value, mode);
}
bool CanBeImmediate(int64_t value, ImmediateMode mode) {
unsigned ignored; unsigned ignored;
switch (mode) { switch (mode) {
case kLogical32Imm: case kLogical32Imm:
...@@ -55,7 +59,6 @@ class Arm64OperandGenerator FINAL : public OperandGenerator { ...@@ -55,7 +59,6 @@ class Arm64OperandGenerator FINAL : public OperandGenerator {
return Assembler::IsImmLogical(static_cast<uint64_t>(value), 64, return Assembler::IsImmLogical(static_cast<uint64_t>(value), 64,
&ignored, &ignored, &ignored); &ignored, &ignored, &ignored);
case kArithmeticImm: case kArithmeticImm:
// TODO(dcarney): -values can be handled by instruction swapping
return Assembler::IsImmAddSub(value); return Assembler::IsImmAddSub(value);
case kShift32Imm: case kShift32Imm:
return 0 <= value && value < 32; return 0 <= value && value < 32;
...@@ -155,6 +158,22 @@ static void VisitBinop(InstructionSelector* selector, Node* node, ...@@ -155,6 +158,22 @@ static void VisitBinop(InstructionSelector* selector, Node* node,
} }
template <typename Matcher>
static void VisitAddSub(InstructionSelector* selector, Node* node,
ArchOpcode opcode, ArchOpcode negate_opcode) {
Arm64OperandGenerator g(selector);
Matcher m(node);
if (m.right().HasValue() && (m.right().Value() < 0) &&
g.CanBeImmediate(-m.right().Value(), kArithmeticImm)) {
selector->Emit(negate_opcode, g.DefineAsRegister(node),
g.UseRegister(m.left().node()),
g.TempImmediate(-m.right().Value()));
} else {
VisitBinop<Matcher>(selector, node, opcode, kArithmeticImm);
}
}
void InstructionSelector::VisitLoad(Node* node) { void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node)); MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node)); MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
...@@ -442,7 +461,7 @@ void InstructionSelector::VisitInt32Add(Node* node) { ...@@ -442,7 +461,7 @@ void InstructionSelector::VisitInt32Add(Node* node) {
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node())); g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
return; return;
} }
VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm); VisitAddSub<Int32BinopMatcher>(this, node, kArm64Add32, kArm64Sub32);
} }
...@@ -465,7 +484,7 @@ void InstructionSelector::VisitInt64Add(Node* node) { ...@@ -465,7 +484,7 @@ void InstructionSelector::VisitInt64Add(Node* node) {
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node())); g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
return; return;
} }
VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm); VisitAddSub<Int64BinopMatcher>(this, node, kArm64Add, kArm64Sub);
} }
...@@ -486,7 +505,7 @@ void InstructionSelector::VisitInt32Sub(Node* node) { ...@@ -486,7 +505,7 @@ void InstructionSelector::VisitInt32Sub(Node* node) {
Emit(kArm64Neg32, g.DefineAsRegister(node), Emit(kArm64Neg32, g.DefineAsRegister(node),
g.UseRegister(m.right().node())); g.UseRegister(m.right().node()));
} else { } else {
VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32, kArithmeticImm); VisitAddSub<Int32BinopMatcher>(this, node, kArm64Sub32, kArm64Add32);
} }
} }
...@@ -507,7 +526,7 @@ void InstructionSelector::VisitInt64Sub(Node* node) { ...@@ -507,7 +526,7 @@ void InstructionSelector::VisitInt64Sub(Node* node) {
if (m.left().Is(0)) { if (m.left().Is(0)) {
Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node())); Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node()));
} else { } else {
VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm); VisitAddSub<Int64BinopMatcher>(this, node, kArm64Sub, kArm64Add);
} }
} }
......
...@@ -84,11 +84,26 @@ static const uint32_t kLogicalImmediates[] = { ...@@ -84,11 +84,26 @@ static const uint32_t kLogicalImmediates[] = {
// ARM64 arithmetic instructions. // ARM64 arithmetic instructions.
static const MachInst2 kAddSubInstructions[] = { struct AddSub {
{&RawMachineAssembler::Int32Add, "Int32Add", kArm64Add32, kMachInt32}, MachInst2 mi;
{&RawMachineAssembler::Int64Add, "Int64Add", kArm64Add, kMachInt64}, ArchOpcode negate_arch_opcode;
{&RawMachineAssembler::Int32Sub, "Int32Sub", kArm64Sub32, kMachInt32}, };
{&RawMachineAssembler::Int64Sub, "Int64Sub", kArm64Sub, kMachInt64}};
std::ostream& operator<<(std::ostream& os, const AddSub& op) {
return os << op.mi;
}
static const AddSub kAddSubInstructions[] = {
{{&RawMachineAssembler::Int32Add, "Int32Add", kArm64Add32, kMachInt32},
kArm64Sub32},
{{&RawMachineAssembler::Int64Add, "Int64Add", kArm64Add, kMachInt64},
kArm64Sub},
{{&RawMachineAssembler::Int32Sub, "Int32Sub", kArm64Sub32, kMachInt32},
kArm64Add32},
{{&RawMachineAssembler::Int64Sub, "Int64Sub", kArm64Sub, kMachInt64},
kArm64Add}};
// ARM64 Add/Sub immediates: 12-bit immediate optionally shifted by 12. // ARM64 Add/Sub immediates: 12-bit immediate optionally shifted by 12.
...@@ -288,32 +303,32 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorLogicalTest, ...@@ -288,32 +303,32 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorLogicalTest,
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Add and Sub instructions. // Add and Sub instructions.
typedef InstructionSelectorTestWithParam<MachInst2> typedef InstructionSelectorTestWithParam<AddSub> InstructionSelectorAddSubTest;
InstructionSelectorAddSubTest;
TEST_P(InstructionSelectorAddSubTest, Parameter) { TEST_P(InstructionSelectorAddSubTest, Parameter) {
const MachInst2 dpi = GetParam(); const AddSub dpi = GetParam();
const MachineType type = dpi.machine_type; const MachineType type = dpi.mi.machine_type;
StreamBuilder m(this, type, type, type); StreamBuilder m(this, type, type, type);
m.Return((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1))); m.Return((m.*dpi.mi.constructor)(m.Parameter(0), m.Parameter(1)));
Stream s = m.Build(); Stream s = m.Build();
ASSERT_EQ(1U, s.size()); ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); EXPECT_EQ(dpi.mi.arch_opcode, s[0]->arch_opcode());
EXPECT_EQ(2U, s[0]->InputCount()); EXPECT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(1U, s[0]->OutputCount()); EXPECT_EQ(1U, s[0]->OutputCount());
} }
TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) { TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) {
const MachInst2 dpi = GetParam(); const AddSub dpi = GetParam();
const MachineType type = dpi.machine_type; const MachineType type = dpi.mi.machine_type;
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) { TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
StreamBuilder m(this, type, type); StreamBuilder m(this, type, type);
m.Return((m.*dpi.constructor)(m.Parameter(0), BuildConstant(m, type, imm))); m.Return(
(m.*dpi.mi.constructor)(m.Parameter(0), BuildConstant(m, type, imm)));
Stream s = m.Build(); Stream s = m.Build();
ASSERT_EQ(1U, s.size()); ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); EXPECT_EQ(dpi.mi.arch_opcode, s[0]->arch_opcode());
ASSERT_EQ(2U, s[0]->InputCount()); ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1))); EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1)));
...@@ -322,26 +337,22 @@ TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) { ...@@ -322,26 +337,22 @@ TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) {
} }
TEST_P(InstructionSelectorAddSubTest, ImmediateOnLeft) { TEST_P(InstructionSelectorAddSubTest, NegImmediateOnRight) {
const MachInst2 dpi = GetParam(); const AddSub dpi = GetParam();
const MachineType type = dpi.machine_type; const MachineType type = dpi.mi.machine_type;
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) { TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
if (imm == 0) continue;
StreamBuilder m(this, type, type); StreamBuilder m(this, type, type);
m.Return((m.*dpi.constructor)(BuildConstant(m, type, imm), m.Parameter(0))); m.Return(
(m.*dpi.mi.constructor)(m.Parameter(0), BuildConstant(m, type, -imm)));
Stream s = m.Build(); Stream s = m.Build();
// Add can support an immediate on the left by commuting, but Sub can't
// commute. We test zero-on-left Sub later.
if (strstr(dpi.constructor_name, "Add") != NULL) {
ASSERT_EQ(1U, s.size()); ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); EXPECT_EQ(dpi.negate_arch_opcode, s[0]->arch_opcode());
ASSERT_EQ(2U, s[0]->InputCount()); ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); ASSERT_TRUE(s[0]->InputAt(1)->IsImmediate());
EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1))); EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1)));
EXPECT_EQ(1U, s[0]->OutputCount()); EXPECT_EQ(1U, s[0]->OutputCount());
} }
}
} }
...@@ -349,6 +360,38 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorAddSubTest, ...@@ -349,6 +360,38 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorAddSubTest,
::testing::ValuesIn(kAddSubInstructions)); ::testing::ValuesIn(kAddSubInstructions));
TEST_F(InstructionSelectorTest, AddImmediateOnLeft) {
{
// 32-bit add.
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
StreamBuilder m(this, kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Int32Constant(imm), m.Parameter(0)));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArm64Add32, s[0]->arch_opcode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1)));
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
{
// 64-bit add.
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
StreamBuilder m(this, kMachInt64, kMachInt64);
m.Return(m.Int64Add(m.Int64Constant(imm), m.Parameter(0)));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArm64Add, s[0]->arch_opcode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1)));
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
}
TEST_F(InstructionSelectorTest, SubZeroOnLeft) { TEST_F(InstructionSelectorTest, SubZeroOnLeft) {
// Subtraction with zero on the left maps to Neg. // Subtraction with zero on the left maps to Neg.
{ {
...@@ -376,6 +419,42 @@ TEST_F(InstructionSelectorTest, SubZeroOnLeft) { ...@@ -376,6 +419,42 @@ TEST_F(InstructionSelectorTest, SubZeroOnLeft) {
} }
TEST_F(InstructionSelectorTest, AddNegImmediateOnLeft) {
{
// 32-bit add.
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
if (imm == 0) continue;
StreamBuilder m(this, kMachInt32, kMachInt32);
m.Return(m.Int32Add(m.Int32Constant(-imm), m.Parameter(0)));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArm64Sub32, s[0]->arch_opcode());
ASSERT_EQ(2U, s[0]->InputCount());
ASSERT_TRUE(s[0]->InputAt(1)->IsImmediate());
EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1)));
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
{
// 64-bit add.
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
if (imm == 0) continue;
StreamBuilder m(this, kMachInt64, kMachInt64);
m.Return(m.Int64Add(m.Int64Constant(-imm), m.Parameter(0)));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kArm64Sub, s[0]->arch_opcode());
ASSERT_EQ(2U, s[0]->InputCount());
ASSERT_TRUE(s[0]->InputAt(1)->IsImmediate());
EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1)));
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Data processing controlled branches. // Data processing controlled branches.
......
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