[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 {
value = OpParameter<int64_t>(node);
else
return false;
return CanBeImmediate(value, mode);
}
bool CanBeImmediate(int64_t value, ImmediateMode mode) {
unsigned ignored;
switch (mode) {
case kLogical32Imm:
......@@ -55,7 +59,6 @@ class Arm64OperandGenerator FINAL : public OperandGenerator {
return Assembler::IsImmLogical(static_cast<uint64_t>(value), 64,
&ignored, &ignored, &ignored);
case kArithmeticImm:
// TODO(dcarney): -values can be handled by instruction swapping
return Assembler::IsImmAddSub(value);
case kShift32Imm:
return 0 <= value && value < 32;
......@@ -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) {
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
......@@ -442,7 +461,7 @@ void InstructionSelector::VisitInt32Add(Node* node) {
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
return;
}
VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm);
VisitAddSub<Int32BinopMatcher>(this, node, kArm64Add32, kArm64Sub32);
}
......@@ -465,7 +484,7 @@ void InstructionSelector::VisitInt64Add(Node* node) {
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
return;
}
VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm);
VisitAddSub<Int64BinopMatcher>(this, node, kArm64Add, kArm64Sub);
}
......@@ -486,7 +505,7 @@ void InstructionSelector::VisitInt32Sub(Node* node) {
Emit(kArm64Neg32, g.DefineAsRegister(node),
g.UseRegister(m.right().node()));
} else {
VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32, kArithmeticImm);
VisitAddSub<Int32BinopMatcher>(this, node, kArm64Sub32, kArm64Add32);
}
}
......@@ -507,7 +526,7 @@ void InstructionSelector::VisitInt64Sub(Node* node) {
if (m.left().Is(0)) {
Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node()));
} else {
VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm);
VisitAddSub<Int64BinopMatcher>(this, node, kArm64Sub, kArm64Add);
}
}
......
......@@ -84,11 +84,26 @@ static const uint32_t kLogicalImmediates[] = {
// ARM64 arithmetic instructions.
static const MachInst2 kAddSubInstructions[] = {
{&RawMachineAssembler::Int32Add, "Int32Add", kArm64Add32, kMachInt32},
{&RawMachineAssembler::Int64Add, "Int64Add", kArm64Add, kMachInt64},
{&RawMachineAssembler::Int32Sub, "Int32Sub", kArm64Sub32, kMachInt32},
{&RawMachineAssembler::Int64Sub, "Int64Sub", kArm64Sub, kMachInt64}};
struct AddSub {
MachInst2 mi;
ArchOpcode negate_arch_opcode;
};
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.
......@@ -288,32 +303,32 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorLogicalTest,
// -----------------------------------------------------------------------------
// Add and Sub instructions.
typedef InstructionSelectorTestWithParam<MachInst2>
InstructionSelectorAddSubTest;
typedef InstructionSelectorTestWithParam<AddSub> InstructionSelectorAddSubTest;
TEST_P(InstructionSelectorAddSubTest, Parameter) {
const MachInst2 dpi = GetParam();
const MachineType type = dpi.machine_type;
const AddSub dpi = GetParam();
const MachineType type = dpi.mi.machine_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();
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(1U, s[0]->OutputCount());
}
TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) {
const MachInst2 dpi = GetParam();
const MachineType type = dpi.machine_type;
const AddSub dpi = GetParam();
const MachineType type = dpi.mi.machine_type;
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
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();
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());
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1)));
......@@ -322,20 +337,52 @@ TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) {
}
TEST_P(InstructionSelectorAddSubTest, ImmediateOnLeft) {
const MachInst2 dpi = GetParam();
const MachineType type = dpi.machine_type;
TEST_P(InstructionSelectorAddSubTest, NegImmediateOnRight) {
const AddSub dpi = GetParam();
const MachineType type = dpi.mi.machine_type;
TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
if (imm == 0) continue;
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();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(dpi.negate_arch_opcode, 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());
}
}
// 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) {
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorAddSubTest,
::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(dpi.arch_opcode, s[0]->arch_opcode());
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)));
......@@ -345,10 +392,6 @@ TEST_P(InstructionSelectorAddSubTest, ImmediateOnLeft) {
}
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorAddSubTest,
::testing::ValuesIn(kAddSubInstructions));
TEST_F(InstructionSelectorTest, SubZeroOnLeft) {
// Subtraction with zero on the left maps to Neg.
{
......@@ -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.
......
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