[turbofan] ARM64 support for multiply-accumulate
Add instruction selectors for multiply-accumulate, multiply-subtract and negated multiply operations. BUG= R=bmeurer@chromium.org Review URL: https://codereview.chromium.org/605693003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24276 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
f6e97c90fb
commit
85f47bd885
@ -204,6 +204,28 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
||||
case kArm64Mul32:
|
||||
__ Mul(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
|
||||
break;
|
||||
case kArm64Madd:
|
||||
__ Madd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
|
||||
i.InputRegister(2));
|
||||
break;
|
||||
case kArm64Madd32:
|
||||
__ Madd(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1),
|
||||
i.InputRegister32(2));
|
||||
break;
|
||||
case kArm64Msub:
|
||||
__ Msub(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
|
||||
i.InputRegister(2));
|
||||
break;
|
||||
case kArm64Msub32:
|
||||
__ Msub(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1),
|
||||
i.InputRegister32(2));
|
||||
break;
|
||||
case kArm64Mneg:
|
||||
__ Mneg(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
|
||||
break;
|
||||
case kArm64Mneg32:
|
||||
__ Mneg(i.OutputRegister32(), i.InputRegister32(0), i.InputRegister32(1));
|
||||
break;
|
||||
case kArm64Idiv:
|
||||
__ Sdiv(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
|
||||
break;
|
||||
|
@ -36,6 +36,12 @@ namespace compiler {
|
||||
V(Arm64Sub32) \
|
||||
V(Arm64Mul) \
|
||||
V(Arm64Mul32) \
|
||||
V(Arm64Madd) \
|
||||
V(Arm64Madd32) \
|
||||
V(Arm64Msub) \
|
||||
V(Arm64Msub32) \
|
||||
V(Arm64Mneg) \
|
||||
V(Arm64Mneg32) \
|
||||
V(Arm64Idiv) \
|
||||
V(Arm64Idiv32) \
|
||||
V(Arm64Udiv) \
|
||||
|
@ -799,10 +799,120 @@ TEST_P(InstructionSelectorMulDivTest, Parameter) {
|
||||
EXPECT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorMulDivTest,
|
||||
::testing::ValuesIn(kMulDivInstructions));
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct MulDPInst {
|
||||
const char* mul_constructor_name;
|
||||
Node* (RawMachineAssembler::*mul_constructor)(Node*, Node*);
|
||||
Node* (RawMachineAssembler::*add_constructor)(Node*, Node*);
|
||||
Node* (RawMachineAssembler::*sub_constructor)(Node*, Node*);
|
||||
ArchOpcode add_arch_opcode;
|
||||
ArchOpcode sub_arch_opcode;
|
||||
ArchOpcode neg_arch_opcode;
|
||||
MachineType machine_type;
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MulDPInst& inst) {
|
||||
return os << inst.mul_constructor_name;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
static const MulDPInst kMulDPInstructions[] = {
|
||||
{"Int32Mul", &RawMachineAssembler::Int32Mul, &RawMachineAssembler::Int32Add,
|
||||
&RawMachineAssembler::Int32Sub, kArm64Madd32, kArm64Msub32, kArm64Mneg32,
|
||||
kMachInt32},
|
||||
{"Int64Mul", &RawMachineAssembler::Int64Mul, &RawMachineAssembler::Int64Add,
|
||||
&RawMachineAssembler::Int64Sub, kArm64Madd, kArm64Msub, kArm64Mneg,
|
||||
kMachInt64}};
|
||||
|
||||
|
||||
typedef InstructionSelectorTestWithParam<MulDPInst>
|
||||
InstructionSelectorIntDPWithIntMulTest;
|
||||
|
||||
|
||||
TEST_P(InstructionSelectorIntDPWithIntMulTest, AddWithMul) {
|
||||
const MulDPInst mdpi = GetParam();
|
||||
const MachineType type = mdpi.machine_type;
|
||||
{
|
||||
StreamBuilder m(this, type, type, type, type);
|
||||
Node* n = (m.*mdpi.mul_constructor)(m.Parameter(1), m.Parameter(2));
|
||||
m.Return((m.*mdpi.add_constructor)(m.Parameter(0), n));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(mdpi.add_arch_opcode, s[0]->arch_opcode());
|
||||
EXPECT_EQ(3U, s[0]->InputCount());
|
||||
EXPECT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
{
|
||||
StreamBuilder m(this, type, type, type, type);
|
||||
Node* n = (m.*mdpi.mul_constructor)(m.Parameter(0), m.Parameter(1));
|
||||
m.Return((m.*mdpi.add_constructor)(n, m.Parameter(2)));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(mdpi.add_arch_opcode, s[0]->arch_opcode());
|
||||
EXPECT_EQ(3U, s[0]->InputCount());
|
||||
EXPECT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_P(InstructionSelectorIntDPWithIntMulTest, SubWithMul) {
|
||||
const MulDPInst mdpi = GetParam();
|
||||
const MachineType type = mdpi.machine_type;
|
||||
{
|
||||
StreamBuilder m(this, type, type, type, type);
|
||||
Node* n = (m.*mdpi.mul_constructor)(m.Parameter(1), m.Parameter(2));
|
||||
m.Return((m.*mdpi.sub_constructor)(m.Parameter(0), n));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(mdpi.sub_arch_opcode, s[0]->arch_opcode());
|
||||
EXPECT_EQ(3U, s[0]->InputCount());
|
||||
EXPECT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_P(InstructionSelectorIntDPWithIntMulTest, NegativeMul) {
|
||||
const MulDPInst mdpi = GetParam();
|
||||
const MachineType type = mdpi.machine_type;
|
||||
{
|
||||
StreamBuilder m(this, type, type, type);
|
||||
Node* n =
|
||||
(m.*mdpi.sub_constructor)(BuildConstant(m, type, 0), m.Parameter(0));
|
||||
m.Return((m.*mdpi.mul_constructor)(n, m.Parameter(1)));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(mdpi.neg_arch_opcode, s[0]->arch_opcode());
|
||||
EXPECT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
{
|
||||
StreamBuilder m(this, type, type, type);
|
||||
Node* n =
|
||||
(m.*mdpi.sub_constructor)(BuildConstant(m, type, 0), m.Parameter(1));
|
||||
m.Return((m.*mdpi.mul_constructor)(m.Parameter(0), n));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(mdpi.neg_arch_opcode, s[0]->arch_opcode());
|
||||
EXPECT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
|
||||
InstructionSelectorIntDPWithIntMulTest,
|
||||
::testing::ValuesIn(kMulDPInstructions));
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Floating point instructions.
|
||||
|
||||
|
@ -424,11 +424,47 @@ void InstructionSelector::VisitWord64Ror(Node* node) {
|
||||
|
||||
|
||||
void InstructionSelector::VisitInt32Add(Node* node) {
|
||||
Arm64OperandGenerator g(this);
|
||||
Int32BinopMatcher m(node);
|
||||
// Select Madd(x, y, z) for Add(Mul(x, y), z).
|
||||
if (m.left().IsInt32Mul() && CanCover(node, m.left().node())) {
|
||||
Int32BinopMatcher mleft(m.left().node());
|
||||
Emit(kArm64Madd32, g.DefineAsRegister(node),
|
||||
g.UseRegister(mleft.left().node()),
|
||||
g.UseRegister(mleft.right().node()), g.UseRegister(m.right().node()));
|
||||
return;
|
||||
}
|
||||
// Select Madd(x, y, z) for Add(x, Mul(x, y)).
|
||||
if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
|
||||
Int32BinopMatcher mright(m.right().node());
|
||||
Emit(kArm64Madd32, g.DefineAsRegister(node),
|
||||
g.UseRegister(mright.left().node()),
|
||||
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
|
||||
return;
|
||||
}
|
||||
VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm);
|
||||
}
|
||||
|
||||
|
||||
void InstructionSelector::VisitInt64Add(Node* node) {
|
||||
Arm64OperandGenerator g(this);
|
||||
Int64BinopMatcher m(node);
|
||||
// Select Madd(x, y, z) for Add(Mul(x, y), z).
|
||||
if (m.left().IsInt64Mul() && CanCover(node, m.left().node())) {
|
||||
Int64BinopMatcher mleft(m.left().node());
|
||||
Emit(kArm64Madd, g.DefineAsRegister(node),
|
||||
g.UseRegister(mleft.left().node()),
|
||||
g.UseRegister(mleft.right().node()), g.UseRegister(m.right().node()));
|
||||
return;
|
||||
}
|
||||
// Select Madd(x, y, z) for Add(x, Mul(x, y)).
|
||||
if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
|
||||
Int64BinopMatcher mright(m.right().node());
|
||||
Emit(kArm64Madd, g.DefineAsRegister(node),
|
||||
g.UseRegister(mright.left().node()),
|
||||
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
|
||||
return;
|
||||
}
|
||||
VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm);
|
||||
}
|
||||
|
||||
@ -436,6 +472,16 @@ void InstructionSelector::VisitInt64Add(Node* node) {
|
||||
void InstructionSelector::VisitInt32Sub(Node* node) {
|
||||
Arm64OperandGenerator g(this);
|
||||
Int32BinopMatcher m(node);
|
||||
|
||||
// Select Msub(a, x, y) for Sub(a, Mul(x, y)).
|
||||
if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) {
|
||||
Int32BinopMatcher mright(m.right().node());
|
||||
Emit(kArm64Msub32, g.DefineAsRegister(node),
|
||||
g.UseRegister(mright.left().node()),
|
||||
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.left().Is(0)) {
|
||||
Emit(kArm64Neg32, g.DefineAsRegister(node),
|
||||
g.UseRegister(m.right().node()));
|
||||
@ -448,6 +494,16 @@ void InstructionSelector::VisitInt32Sub(Node* node) {
|
||||
void InstructionSelector::VisitInt64Sub(Node* node) {
|
||||
Arm64OperandGenerator g(this);
|
||||
Int64BinopMatcher m(node);
|
||||
|
||||
// Select Msub(a, x, y) for Sub(a, Mul(x, y)).
|
||||
if (m.right().IsInt64Mul() && CanCover(node, m.right().node())) {
|
||||
Int64BinopMatcher mright(m.right().node());
|
||||
Emit(kArm64Msub, g.DefineAsRegister(node),
|
||||
g.UseRegister(mright.left().node()),
|
||||
g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.left().Is(0)) {
|
||||
Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node()));
|
||||
} else {
|
||||
@ -457,11 +513,64 @@ void InstructionSelector::VisitInt64Sub(Node* node) {
|
||||
|
||||
|
||||
void InstructionSelector::VisitInt32Mul(Node* node) {
|
||||
Arm64OperandGenerator g(this);
|
||||
Int32BinopMatcher m(node);
|
||||
|
||||
if (m.left().IsInt32Sub() && CanCover(node, m.left().node())) {
|
||||
Int32BinopMatcher mleft(m.left().node());
|
||||
|
||||
// Select Mneg(x, y) for Mul(Sub(0, x), y).
|
||||
if (mleft.left().Is(0)) {
|
||||
Emit(kArm64Mneg32, g.DefineAsRegister(node),
|
||||
g.UseRegister(mleft.right().node()),
|
||||
g.UseRegister(m.right().node()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m.right().IsInt32Sub() && CanCover(node, m.right().node())) {
|
||||
Int32BinopMatcher mright(m.right().node());
|
||||
|
||||
// Select Mneg(x, y) for Mul(x, Sub(0, y)).
|
||||
if (mright.left().Is(0)) {
|
||||
Emit(kArm64Mneg32, g.DefineAsRegister(node),
|
||||
g.UseRegister(m.left().node()),
|
||||
g.UseRegister(mright.right().node()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VisitRRR(this, kArm64Mul32, node);
|
||||
}
|
||||
|
||||
|
||||
void InstructionSelector::VisitInt64Mul(Node* node) {
|
||||
Arm64OperandGenerator g(this);
|
||||
Int64BinopMatcher m(node);
|
||||
|
||||
if (m.left().IsInt64Sub() && CanCover(node, m.left().node())) {
|
||||
Int64BinopMatcher mleft(m.left().node());
|
||||
|
||||
// Select Mneg(x, y) for Mul(Sub(0, x), y).
|
||||
if (mleft.left().Is(0)) {
|
||||
Emit(kArm64Mneg, g.DefineAsRegister(node),
|
||||
g.UseRegister(mleft.right().node()),
|
||||
g.UseRegister(m.right().node()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m.right().IsInt64Sub() && CanCover(node, m.right().node())) {
|
||||
Int64BinopMatcher mright(m.right().node());
|
||||
|
||||
// Select Mneg(x, y) for Mul(x, Sub(0, y)).
|
||||
if (mright.left().Is(0)) {
|
||||
Emit(kArm64Mneg, g.DefineAsRegister(node), g.UseRegister(m.left().node()),
|
||||
g.UseRegister(mright.right().node()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VisitRRR(this, kArm64Mul, node);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user