[turbofan] x64 lea multiplication matching
R=bmeurer@chromium.org BUG= Review URL: https://codereview.chromium.org/615483003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24317 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
574378d91b
commit
51a2f97d80
1
BUILD.gn
1
BUILD.gn
@ -536,6 +536,7 @@ source_set("v8_base") {
|
||||
"src/compiler/node-aux-data.h",
|
||||
"src/compiler/node-cache.cc",
|
||||
"src/compiler/node-cache.h",
|
||||
"src/compiler/node-matchers.cc",
|
||||
"src/compiler/node-matchers.h",
|
||||
"src/compiler/node-properties-inl.h",
|
||||
"src/compiler/node-properties.h",
|
||||
|
104
src/compiler/node-matchers.cc
Normal file
104
src/compiler/node-matchers.cc
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/compiler/generic-node-inl.h"
|
||||
#include "src/compiler/node-matchers.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
const int ScaleFactorMatcher::kMatchedFactors[] = {1, 2, 4, 8};
|
||||
|
||||
|
||||
ScaleFactorMatcher::ScaleFactorMatcher(Node* node)
|
||||
: NodeMatcher(node), left_(NULL), power_(0) {
|
||||
if (opcode() != IrOpcode::kInt32Mul) return;
|
||||
// TODO(dcarney): should test 64 bit ints as well.
|
||||
Int32BinopMatcher m(this->node());
|
||||
if (!m.right().HasValue()) return;
|
||||
int32_t value = m.right().Value();
|
||||
switch (value) {
|
||||
case 8:
|
||||
power_++; // Fall through.
|
||||
case 4:
|
||||
power_++; // Fall through.
|
||||
case 2:
|
||||
power_++; // Fall through.
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
left_ = m.left().node();
|
||||
}
|
||||
|
||||
|
||||
IndexAndDisplacementMatcher::IndexAndDisplacementMatcher(Node* node)
|
||||
: NodeMatcher(node), index_node_(node), displacement_(0), power_(0) {
|
||||
if (opcode() == IrOpcode::kInt32Add) {
|
||||
Int32BinopMatcher m(this->node());
|
||||
if (m.right().HasValue()) {
|
||||
displacement_ = m.right().Value();
|
||||
index_node_ = m.left().node();
|
||||
}
|
||||
}
|
||||
// Test scale factor.
|
||||
ScaleFactorMatcher scale_matcher(index_node_);
|
||||
if (scale_matcher.Matches()) {
|
||||
index_node_ = scale_matcher.Left();
|
||||
power_ = scale_matcher.Power();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int LeaMultiplyMatcher::kMatchedFactors[7] = {1, 2, 3, 4, 5, 8, 9};
|
||||
|
||||
|
||||
LeaMultiplyMatcher::LeaMultiplyMatcher(Node* node)
|
||||
: NodeMatcher(node), left_(NULL), power_(0), displacement_(0) {
|
||||
if (opcode() != IrOpcode::kInt32Mul && opcode() != IrOpcode::kInt64Mul) {
|
||||
return;
|
||||
}
|
||||
int64_t value;
|
||||
Node* left = NULL;
|
||||
{
|
||||
Int32BinopMatcher m(this->node());
|
||||
if (m.right().HasValue()) {
|
||||
value = m.right().Value();
|
||||
left = m.left().node();
|
||||
} else {
|
||||
Int64BinopMatcher m(this->node());
|
||||
if (m.right().HasValue()) {
|
||||
value = m.right().Value();
|
||||
left = m.left().node();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (value) {
|
||||
case 9:
|
||||
case 8:
|
||||
power_++; // Fall through.
|
||||
case 5:
|
||||
case 4:
|
||||
power_++; // Fall through.
|
||||
case 3:
|
||||
case 2:
|
||||
power_++; // Fall through.
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (!base::bits::IsPowerOfTwo64(value)) {
|
||||
displacement_ = 1;
|
||||
}
|
||||
left_ = left;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -145,42 +145,21 @@ typedef BinopMatcher<Float64Matcher, Float64Matcher> Float64BinopMatcher;
|
||||
// Matches nodes of form [x * N] for N in {1,2,4,8}
|
||||
class ScaleFactorMatcher : public NodeMatcher {
|
||||
public:
|
||||
explicit ScaleFactorMatcher(Node* node)
|
||||
: NodeMatcher(node), left_(NULL), power_(0) {
|
||||
Match();
|
||||
}
|
||||
static const int kMatchedFactors[4];
|
||||
|
||||
bool Matches() { return left_ != NULL; }
|
||||
int Power() {
|
||||
explicit ScaleFactorMatcher(Node* node);
|
||||
|
||||
bool Matches() const { return left_ != NULL; }
|
||||
int Power() const {
|
||||
DCHECK(Matches());
|
||||
return power_;
|
||||
}
|
||||
Node* Left() {
|
||||
Node* Left() const {
|
||||
DCHECK(Matches());
|
||||
return left_;
|
||||
}
|
||||
|
||||
private:
|
||||
void Match() {
|
||||
if (opcode() != IrOpcode::kInt32Mul) return;
|
||||
Int32BinopMatcher m(node());
|
||||
if (!m.right().HasValue()) return;
|
||||
int32_t value = m.right().Value();
|
||||
switch (value) {
|
||||
case 8:
|
||||
power_++; // Fall through.
|
||||
case 4:
|
||||
power_++; // Fall through.
|
||||
case 2:
|
||||
power_++; // Fall through.
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
left_ = m.left().node();
|
||||
}
|
||||
|
||||
Node* left_;
|
||||
int power_;
|
||||
};
|
||||
@ -196,39 +175,52 @@ class ScaleFactorMatcher : public NodeMatcher {
|
||||
// for N in {1,2,4,8} and K int32_t
|
||||
class IndexAndDisplacementMatcher : public NodeMatcher {
|
||||
public:
|
||||
explicit IndexAndDisplacementMatcher(Node* node)
|
||||
: NodeMatcher(node), index_node_(node), displacement_(0), power_(0) {
|
||||
Match();
|
||||
}
|
||||
explicit IndexAndDisplacementMatcher(Node* node);
|
||||
|
||||
Node* index_node() { return index_node_; }
|
||||
int displacement() { return displacement_; }
|
||||
int power() { return power_; }
|
||||
Node* index_node() const { return index_node_; }
|
||||
int displacement() const { return displacement_; }
|
||||
int power() const { return power_; }
|
||||
|
||||
private:
|
||||
void Match() {
|
||||
if (opcode() == IrOpcode::kInt32Add) {
|
||||
// Assume reduction has put constant on the right.
|
||||
Int32BinopMatcher m(node());
|
||||
if (m.right().HasValue()) {
|
||||
displacement_ = m.right().Value();
|
||||
index_node_ = m.left().node();
|
||||
}
|
||||
}
|
||||
// Test scale factor.
|
||||
ScaleFactorMatcher scale_matcher(index_node_);
|
||||
if (scale_matcher.Matches()) {
|
||||
index_node_ = scale_matcher.Left();
|
||||
power_ = scale_matcher.Power();
|
||||
}
|
||||
}
|
||||
|
||||
Node* index_node_;
|
||||
int displacement_;
|
||||
int power_;
|
||||
};
|
||||
|
||||
|
||||
// Fairly intel-specify node matcher used for matching multiplies that can be
|
||||
// transformed to lea instructions.
|
||||
// Matches nodes of form:
|
||||
// [x * N]
|
||||
// for N in {1,2,3,4,5,8,9}
|
||||
class LeaMultiplyMatcher : public NodeMatcher {
|
||||
public:
|
||||
static const int kMatchedFactors[7];
|
||||
|
||||
explicit LeaMultiplyMatcher(Node* node);
|
||||
|
||||
bool Matches() const { return left_ != NULL; }
|
||||
int Power() const {
|
||||
DCHECK(Matches());
|
||||
return power_;
|
||||
}
|
||||
Node* Left() const {
|
||||
DCHECK(Matches());
|
||||
return left_;
|
||||
}
|
||||
// Displacement will be either 0 or 1.
|
||||
int32_t Displacement() const {
|
||||
DCHECK(Matches());
|
||||
return displacement_;
|
||||
}
|
||||
|
||||
private:
|
||||
Node* left_;
|
||||
int power_;
|
||||
int displacement_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -519,6 +519,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
||||
__ movsd(operand, i.InputDoubleRegister(index));
|
||||
}
|
||||
break;
|
||||
case kX64Lea32:
|
||||
__ leal(i.OutputRegister(), i.MemoryOperand());
|
||||
break;
|
||||
case kX64Lea:
|
||||
__ leaq(i.OutputRegister(), i.MemoryOperand());
|
||||
break;
|
||||
case kX64Push:
|
||||
if (HasImmediateInput(instr, 0)) {
|
||||
__ pushq(i.InputImmediate(0));
|
||||
|
@ -68,6 +68,8 @@ namespace compiler {
|
||||
V(X64Movq) \
|
||||
V(X64Movsd) \
|
||||
V(X64Movss) \
|
||||
V(X64Lea32) \
|
||||
V(X64Lea) \
|
||||
V(X64Push) \
|
||||
V(X64StoreWriteBarrier)
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/compiler/instruction-selector-unittest.h"
|
||||
#include "src/compiler/node-matchers.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -184,7 +185,7 @@ class AddressingModeUnitTest : public InstructionSelectorTest {
|
||||
Node* non_zero;
|
||||
Node* base_reg; // opaque value to generate base as register
|
||||
Node* index_reg; // opaque value to generate index as register
|
||||
Node* scales[4];
|
||||
Node* scales[arraysize(ScaleFactorMatcher::kMatchedFactors)];
|
||||
StreamBuilder* m;
|
||||
|
||||
void Reset() {
|
||||
@ -195,11 +196,10 @@ class AddressingModeUnitTest : public InstructionSelectorTest {
|
||||
non_zero = m->Int32Constant(127);
|
||||
base_reg = m->Parameter(0);
|
||||
index_reg = m->Parameter(0);
|
||||
|
||||
scales[0] = m->Int32Constant(1);
|
||||
scales[1] = m->Int32Constant(2);
|
||||
scales[2] = m->Int32Constant(4);
|
||||
scales[3] = m->Int32Constant(8);
|
||||
for (size_t i = 0; i < arraysize(ScaleFactorMatcher::kMatchedFactors);
|
||||
++i) {
|
||||
scales[i] = m->Int32Constant(ScaleFactorMatcher::kMatchedFactors[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -289,6 +289,110 @@ TEST_F(AddressingModeUnitTest, AddressingMode_MNI) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Multiplication.
|
||||
|
||||
namespace {
|
||||
|
||||
struct MultParam {
|
||||
int value;
|
||||
bool lea_expected;
|
||||
AddressingMode addressing_mode;
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const MultParam& m) {
|
||||
OStringStream ost;
|
||||
ost << m.value << "." << m.lea_expected << "." << m.addressing_mode;
|
||||
return os << ost.c_str();
|
||||
}
|
||||
|
||||
|
||||
const MultParam kMultParams[] = {{-1, false, kMode_None},
|
||||
{0, false, kMode_None},
|
||||
{1, true, kMode_M1},
|
||||
{2, true, kMode_M2},
|
||||
{3, true, kMode_MR2},
|
||||
{4, true, kMode_M4},
|
||||
{5, true, kMode_MR4},
|
||||
{6, false, kMode_None},
|
||||
{7, false, kMode_None},
|
||||
{8, true, kMode_M8},
|
||||
{9, true, kMode_MR8},
|
||||
{10, false, kMode_None},
|
||||
{11, false, kMode_None}};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
typedef InstructionSelectorTestWithParam<MultParam> InstructionSelectorMultTest;
|
||||
|
||||
|
||||
static unsigned InputCountForLea(AddressingMode mode) {
|
||||
switch (mode) {
|
||||
case kMode_MR1:
|
||||
case kMode_MR2:
|
||||
case kMode_MR4:
|
||||
case kMode_MR8:
|
||||
return 2U;
|
||||
case kMode_M1:
|
||||
case kMode_M2:
|
||||
case kMode_M4:
|
||||
case kMode_M8:
|
||||
return 1U;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_P(InstructionSelectorMultTest, Mult32) {
|
||||
const MultParam m_param = GetParam();
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32);
|
||||
Node* param = m.Parameter(0);
|
||||
Node* mult = m.Int32Mul(param, m.Int32Constant(m_param.value));
|
||||
m.Return(mult);
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(m_param.addressing_mode, s[0]->addressing_mode());
|
||||
if (m_param.lea_expected) {
|
||||
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
|
||||
ASSERT_EQ(InputCountForLea(s[0]->addressing_mode()), s[0]->InputCount());
|
||||
} else {
|
||||
EXPECT_EQ(kX64Imul32, s[0]->arch_opcode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
}
|
||||
EXPECT_EQ(param->id(), s.ToVreg(s[0]->InputAt(0)));
|
||||
}
|
||||
|
||||
|
||||
TEST_P(InstructionSelectorMultTest, Mult64) {
|
||||
const MultParam m_param = GetParam();
|
||||
StreamBuilder m(this, kMachInt64, kMachInt64);
|
||||
Node* param = m.Parameter(0);
|
||||
Node* mult = m.Int64Mul(param, m.Int64Constant(m_param.value));
|
||||
m.Return(mult);
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(m_param.addressing_mode, s[0]->addressing_mode());
|
||||
if (m_param.lea_expected) {
|
||||
EXPECT_EQ(kX64Lea, s[0]->arch_opcode());
|
||||
ASSERT_EQ(InputCountForLea(s[0]->addressing_mode()), s[0]->InputCount());
|
||||
EXPECT_EQ(param->id(), s.ToVreg(s[0]->InputAt(0)));
|
||||
} else {
|
||||
EXPECT_EQ(kX64Imul, s[0]->arch_opcode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
// TODO(dcarney): why is this happening?
|
||||
EXPECT_EQ(param->id(), s.ToVreg(s[0]->InputAt(1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorMultTest,
|
||||
::testing::ValuesIn(kMultParams));
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -55,6 +55,15 @@ class X64OperandGenerator FINAL : public OperandGenerator {
|
||||
};
|
||||
|
||||
|
||||
// Get the AddressingMode of scale factor N from the AddressingMode of scale
|
||||
// factor 1.
|
||||
static AddressingMode AdjustAddressingMode(AddressingMode base_mode,
|
||||
int power) {
|
||||
DCHECK(0 <= power && power < 4);
|
||||
return static_cast<AddressingMode>(static_cast<int>(base_mode) + power);
|
||||
}
|
||||
|
||||
|
||||
class AddressingModeMatcher {
|
||||
public:
|
||||
AddressingModeMatcher(X64OperandGenerator* g, Node* base, Node* index)
|
||||
@ -99,15 +108,11 @@ class AddressingModeMatcher {
|
||||
}
|
||||
}
|
||||
// Adjust mode to actual scale factor.
|
||||
mode_ = GetMode(mode_, matcher.power());
|
||||
mode_ = AdjustAddressingMode(mode_, matcher.power());
|
||||
}
|
||||
DCHECK_NE(kMode_None, mode_);
|
||||
}
|
||||
|
||||
AddressingMode GetMode(AddressingMode one, int power) {
|
||||
return static_cast<AddressingMode>(static_cast<int>(one) + power);
|
||||
}
|
||||
|
||||
size_t SetInputs(InstructionOperand** inputs) {
|
||||
size_t input_count = 0;
|
||||
// Compute inputs_ and input_count.
|
||||
@ -464,18 +469,48 @@ void InstructionSelector::VisitInt64Sub(Node* node) {
|
||||
static void VisitMul(InstructionSelector* selector, Node* node,
|
||||
ArchOpcode opcode) {
|
||||
X64OperandGenerator g(selector);
|
||||
Int32BinopMatcher m(node);
|
||||
Node* left = m.left().node();
|
||||
Node* right = m.right().node();
|
||||
if (g.CanBeImmediate(right)) {
|
||||
selector->Emit(opcode, g.DefineAsRegister(node), g.Use(left),
|
||||
g.UseImmediate(right));
|
||||
} else {
|
||||
if (g.CanBeBetterLeftOperand(right)) {
|
||||
std::swap(left, right);
|
||||
LeaMultiplyMatcher lea(node);
|
||||
// Try to match lea.
|
||||
if (lea.Matches()) {
|
||||
switch (opcode) {
|
||||
case kX64Imul32:
|
||||
opcode = kX64Lea32;
|
||||
break;
|
||||
case kX64Imul:
|
||||
opcode = kX64Lea;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
AddressingMode mode;
|
||||
size_t input_count;
|
||||
InstructionOperand* left = g.UseRegister(lea.Left());
|
||||
InstructionOperand* inputs[] = {left, left};
|
||||
if (lea.Displacement() != 0) {
|
||||
input_count = 2;
|
||||
mode = kMode_MR1;
|
||||
} else {
|
||||
input_count = 1;
|
||||
mode = kMode_M1;
|
||||
}
|
||||
mode = AdjustAddressingMode(mode, lea.Power());
|
||||
InstructionOperand* outputs[] = {g.DefineAsRegister(node)};
|
||||
selector->Emit(opcode | AddressingModeField::encode(mode), 1, outputs,
|
||||
input_count, inputs);
|
||||
} else {
|
||||
Int32BinopMatcher m(node);
|
||||
Node* left = m.left().node();
|
||||
Node* right = m.right().node();
|
||||
if (g.CanBeImmediate(right)) {
|
||||
selector->Emit(opcode, g.DefineAsRegister(node), g.Use(left),
|
||||
g.UseImmediate(right));
|
||||
} else {
|
||||
if (g.CanBeBetterLeftOperand(right)) {
|
||||
std::swap(left, right);
|
||||
}
|
||||
selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left),
|
||||
g.Use(right));
|
||||
}
|
||||
selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left),
|
||||
g.Use(right));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,6 +447,7 @@
|
||||
'../../src/compiler/node-aux-data.h',
|
||||
'../../src/compiler/node-cache.cc',
|
||||
'../../src/compiler/node-cache.h',
|
||||
'../../src/compiler/node-matchers.cc',
|
||||
'../../src/compiler/node-matchers.h',
|
||||
'../../src/compiler/node-properties-inl.h',
|
||||
'../../src/compiler/node-properties.h',
|
||||
|
Loading…
Reference in New Issue
Block a user