[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:
dcarney@chromium.org 2014-09-30 09:46:30 +00:00
parent 574378d91b
commit 51a2f97d80
8 changed files with 318 additions and 73 deletions

View File

@ -536,6 +536,7 @@ source_set("v8_base") {
"src/compiler/node-aux-data.h", "src/compiler/node-aux-data.h",
"src/compiler/node-cache.cc", "src/compiler/node-cache.cc",
"src/compiler/node-cache.h", "src/compiler/node-cache.h",
"src/compiler/node-matchers.cc",
"src/compiler/node-matchers.h", "src/compiler/node-matchers.h",
"src/compiler/node-properties-inl.h", "src/compiler/node-properties-inl.h",
"src/compiler/node-properties.h", "src/compiler/node-properties.h",

View 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

View File

@ -145,42 +145,21 @@ typedef BinopMatcher<Float64Matcher, Float64Matcher> Float64BinopMatcher;
// Matches nodes of form [x * N] for N in {1,2,4,8} // Matches nodes of form [x * N] for N in {1,2,4,8}
class ScaleFactorMatcher : public NodeMatcher { class ScaleFactorMatcher : public NodeMatcher {
public: public:
explicit ScaleFactorMatcher(Node* node) static const int kMatchedFactors[4];
: NodeMatcher(node), left_(NULL), power_(0) {
Match();
}
bool Matches() { return left_ != NULL; } explicit ScaleFactorMatcher(Node* node);
int Power() {
bool Matches() const { return left_ != NULL; }
int Power() const {
DCHECK(Matches()); DCHECK(Matches());
return power_; return power_;
} }
Node* Left() { Node* Left() const {
DCHECK(Matches()); DCHECK(Matches());
return left_; return left_;
} }
private: 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_; Node* left_;
int power_; int power_;
}; };
@ -196,39 +175,52 @@ class ScaleFactorMatcher : public NodeMatcher {
// for N in {1,2,4,8} and K int32_t // for N in {1,2,4,8} and K int32_t
class IndexAndDisplacementMatcher : public NodeMatcher { class IndexAndDisplacementMatcher : public NodeMatcher {
public: public:
explicit IndexAndDisplacementMatcher(Node* node) explicit IndexAndDisplacementMatcher(Node* node);
: NodeMatcher(node), index_node_(node), displacement_(0), power_(0) {
Match();
}
Node* index_node() { return index_node_; } Node* index_node() const { return index_node_; }
int displacement() { return displacement_; } int displacement() const { return displacement_; }
int power() { return power_; } int power() const { return power_; }
private: 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_; Node* index_node_;
int displacement_; int displacement_;
int power_; 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 compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -519,6 +519,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ movsd(operand, i.InputDoubleRegister(index)); __ movsd(operand, i.InputDoubleRegister(index));
} }
break; break;
case kX64Lea32:
__ leal(i.OutputRegister(), i.MemoryOperand());
break;
case kX64Lea:
__ leaq(i.OutputRegister(), i.MemoryOperand());
break;
case kX64Push: case kX64Push:
if (HasImmediateInput(instr, 0)) { if (HasImmediateInput(instr, 0)) {
__ pushq(i.InputImmediate(0)); __ pushq(i.InputImmediate(0));

View File

@ -68,6 +68,8 @@ namespace compiler {
V(X64Movq) \ V(X64Movq) \
V(X64Movsd) \ V(X64Movsd) \
V(X64Movss) \ V(X64Movss) \
V(X64Lea32) \
V(X64Lea) \
V(X64Push) \ V(X64Push) \
V(X64StoreWriteBarrier) V(X64StoreWriteBarrier)

View File

@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/compiler/instruction-selector-unittest.h" #include "src/compiler/instruction-selector-unittest.h"
#include "src/compiler/node-matchers.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
@ -184,7 +185,7 @@ class AddressingModeUnitTest : public InstructionSelectorTest {
Node* non_zero; Node* non_zero;
Node* base_reg; // opaque value to generate base as register Node* base_reg; // opaque value to generate base as register
Node* index_reg; // opaque value to generate index as register Node* index_reg; // opaque value to generate index as register
Node* scales[4]; Node* scales[arraysize(ScaleFactorMatcher::kMatchedFactors)];
StreamBuilder* m; StreamBuilder* m;
void Reset() { void Reset() {
@ -195,11 +196,10 @@ class AddressingModeUnitTest : public InstructionSelectorTest {
non_zero = m->Int32Constant(127); non_zero = m->Int32Constant(127);
base_reg = m->Parameter(0); base_reg = m->Parameter(0);
index_reg = m->Parameter(0); index_reg = m->Parameter(0);
for (size_t i = 0; i < arraysize(ScaleFactorMatcher::kMatchedFactors);
scales[0] = m->Int32Constant(1); ++i) {
scales[1] = m->Int32Constant(2); scales[i] = m->Int32Constant(ScaleFactorMatcher::kMatchedFactors[i]);
scales[2] = m->Int32Constant(4); }
scales[3] = m->Int32Constant(8);
} }
}; };
@ -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 compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -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 { class AddressingModeMatcher {
public: public:
AddressingModeMatcher(X64OperandGenerator* g, Node* base, Node* index) AddressingModeMatcher(X64OperandGenerator* g, Node* base, Node* index)
@ -99,15 +108,11 @@ class AddressingModeMatcher {
} }
} }
// Adjust mode to actual scale factor. // Adjust mode to actual scale factor.
mode_ = GetMode(mode_, matcher.power()); mode_ = AdjustAddressingMode(mode_, matcher.power());
} }
DCHECK_NE(kMode_None, mode_); 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 SetInputs(InstructionOperand** inputs) {
size_t input_count = 0; size_t input_count = 0;
// Compute inputs_ and input_count. // Compute inputs_ and input_count.
@ -464,18 +469,48 @@ void InstructionSelector::VisitInt64Sub(Node* node) {
static void VisitMul(InstructionSelector* selector, Node* node, static void VisitMul(InstructionSelector* selector, Node* node,
ArchOpcode opcode) { ArchOpcode opcode) {
X64OperandGenerator g(selector); X64OperandGenerator g(selector);
Int32BinopMatcher m(node); LeaMultiplyMatcher lea(node);
Node* left = m.left().node(); // Try to match lea.
Node* right = m.right().node(); if (lea.Matches()) {
if (g.CanBeImmediate(right)) { switch (opcode) {
selector->Emit(opcode, g.DefineAsRegister(node), g.Use(left), case kX64Imul32:
g.UseImmediate(right)); opcode = kX64Lea32;
} else { break;
if (g.CanBeBetterLeftOperand(right)) { case kX64Imul:
std::swap(left, right); 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));
} }
} }

View File

@ -447,6 +447,7 @@
'../../src/compiler/node-aux-data.h', '../../src/compiler/node-aux-data.h',
'../../src/compiler/node-cache.cc', '../../src/compiler/node-cache.cc',
'../../src/compiler/node-cache.h', '../../src/compiler/node-cache.h',
'../../src/compiler/node-matchers.cc',
'../../src/compiler/node-matchers.h', '../../src/compiler/node-matchers.h',
'../../src/compiler/node-properties-inl.h', '../../src/compiler/node-properties-inl.h',
'../../src/compiler/node-properties.h', '../../src/compiler/node-properties.h',