Commit 85d2db87 authored by danno's avatar danno Committed by Commit bot

[turbofan] Use "leal" in even more situations

Achieve more than parity with modes currently handled on ia32 in preparation for
porting the entire mechanism to ia32, including supporting mul of constants 3,
5 and 9 with "leal" instructions.

TEST=unittests

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

Cr-Commit-Position: refs/heads/master@{#25677}
parent 5866c5c2
This diff is collapsed.
......@@ -93,8 +93,14 @@ class X64OperandConverter : public InstructionOperandConverter {
int32_t disp = InputInt32(NextOffset(offset));
return Operand(base, index, scale, disp);
}
case kMode_M1:
case kMode_M1: {
Register base = InputRegister(NextOffset(offset));
int32_t disp = 0;
return Operand(base, disp);
}
case kMode_M2:
UNREACHABLE(); // Should use kModeMR with more compact encoding instead
return Operand(no_reg, 0);
case kMode_M4:
case kMode_M8: {
Register index = InputRegister(NextOffset(offset));
......@@ -737,17 +743,26 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
break;
case kX64Lea32: {
AddressingMode mode = AddressingModeField::decode(instr->opcode());
// Shorten "leal" to "addl" or "subl" if the register allocation just
// happens to work out for operations with immediate operands where the
// non-constant input register is the same as output register. The
// "addl"/"subl" forms in these cases are faster based on empirical
// measurements.
if (mode == kMode_MRI && i.InputRegister(0).is(i.OutputRegister())) {
int32_t constant_summand = i.InputInt32(1);
if (constant_summand > 0) {
__ addl(i.OutputRegister(), Immediate(constant_summand));
} else if (constant_summand < 0) {
__ subl(i.OutputRegister(), Immediate(-constant_summand));
// Shorten "leal" to "addl", "subl" or "shll" if the register allocation
// and addressing mode just happens to work out. The "addl"/"subl" forms
// in these cases are faster based on measurements.
if (i.InputRegister(0).is(i.OutputRegister())) {
if (mode == kMode_MRI) {
int32_t constant_summand = i.InputInt32(1);
if (constant_summand > 0) {
__ addl(i.OutputRegister(), Immediate(constant_summand));
} else if (constant_summand < 0) {
__ subl(i.OutputRegister(), Immediate(-constant_summand));
}
} else if (mode == kMode_MR1 || mode == kMode_M2) {
// Using "addl %r1, %r1" is generally faster than "shll %r1, 1"
__ addl(i.OutputRegister(), i.InputRegister(1));
} else if (mode == kMode_M4) {
__ shll(i.OutputRegister(), Immediate(2));
} else if (mode == kMode_M8) {
__ shll(i.OutputRegister(), Immediate(3));
} else {
__ leal(i.OutputRegister(), i.MemoryOperand());
}
} else {
__ leal(i.OutputRegister(), i.MemoryOperand());
......
......@@ -33,6 +33,56 @@ class X64OperandGenerator FINAL : public OperandGenerator {
}
}
AddressingMode GenerateMemoryOperandInputs(Node* index, int scale_exponent,
Node* base, Node* displacement,
InstructionOperand* inputs[],
size_t* input_count) {
AddressingMode mode = kMode_MRI;
if (base != NULL) {
inputs[(*input_count)++] = UseRegister(base);
if (index != NULL) {
DCHECK(scale_exponent >= 0 && scale_exponent <= 3);
inputs[(*input_count)++] = UseRegister(index);
if (displacement != NULL) {
inputs[(*input_count)++] = UseImmediate(displacement);
static const AddressingMode kMRnI_modes[] = {kMode_MR1I, kMode_MR2I,
kMode_MR4I, kMode_MR8I};
mode = kMRnI_modes[scale_exponent];
} else {
static const AddressingMode kMRn_modes[] = {kMode_MR1, kMode_MR2,
kMode_MR4, kMode_MR8};
mode = kMRn_modes[scale_exponent];
}
} else {
if (displacement == NULL) {
mode = kMode_MR;
} else {
inputs[(*input_count)++] = UseImmediate(displacement);
mode = kMode_MRI;
}
}
} else {
DCHECK(index != NULL);
DCHECK(scale_exponent >= 0 && scale_exponent <= 3);
inputs[(*input_count)++] = UseRegister(index);
if (displacement != NULL) {
inputs[(*input_count)++] = UseImmediate(displacement);
static const AddressingMode kMnI_modes[] = {kMode_M1I, kMode_M2I,
kMode_M4I, kMode_M8I};
mode = kMnI_modes[scale_exponent];
} else {
static const AddressingMode kMn_modes[] = {kMode_M1, kMode_MR1,
kMode_M4, kMode_M8};
mode = kMn_modes[scale_exponent];
if (mode == kMode_MR1) {
// [%r1 + %r1*1] has a smaller encoding than [%r1*2+0]
inputs[(*input_count)++] = UseRegister(index);
}
}
}
return mode;
}
bool CanBeBetterLeftOperand(Node* node) const {
return !selector()->IsLive(node);
}
......@@ -408,10 +458,39 @@ void VisitWord64Shift(InstructionSelector* selector, Node* node,
}
}
void EmitLea(InstructionSelector* selector, InstructionCode opcode,
Node* result, Node* index, int scale, Node* base,
Node* displacement) {
X64OperandGenerator g(selector);
InstructionOperand* inputs[4];
size_t input_count = 0;
AddressingMode mode = g.GenerateMemoryOperandInputs(
index, scale, base, displacement, inputs, &input_count);
DCHECK_NE(0, static_cast<int>(input_count));
DCHECK_GE(arraysize(inputs), input_count);
InstructionOperand* outputs[1];
outputs[0] = g.DefineAsRegister(result);
opcode = AddressingModeField::encode(mode) | opcode;
selector->Emit(opcode, 1, outputs, input_count, inputs);
}
} // namespace
void InstructionSelector::VisitWord32Shl(Node* node) {
Int32ScaleMatcher m(node, true);
if (m.matches()) {
Node* index = node->InputAt(0);
Node* base = m.power_of_two_plus_one() ? index : NULL;
EmitLea(this, kX64Lea32, node, index, m.scale(), base, NULL);
return;
}
VisitWord32Shift(this, node, kX64Shl32);
}
......@@ -474,84 +553,19 @@ void InstructionSelector::VisitWord64Ror(Node* node) {
}
namespace {
AddressingMode GenerateMemoryOperandInputs(X64OperandGenerator* g, Node* scaled,
int scale_exponent, Node* offset,
Node* constant,
InstructionOperand* inputs[],
size_t* input_count) {
AddressingMode mode = kMode_MRI;
if (offset != NULL) {
inputs[(*input_count)++] = g->UseRegister(offset);
if (scaled != NULL) {
DCHECK(scale_exponent >= 0 && scale_exponent <= 3);
inputs[(*input_count)++] = g->UseRegister(scaled);
if (constant != NULL) {
inputs[(*input_count)++] = g->UseImmediate(constant);
static const AddressingMode kMRnI_modes[] = {kMode_MR1I, kMode_MR2I,
kMode_MR4I, kMode_MR8I};
mode = kMRnI_modes[scale_exponent];
} else {
static const AddressingMode kMRn_modes[] = {kMode_MR1, kMode_MR2,
kMode_MR4, kMode_MR8};
mode = kMRn_modes[scale_exponent];
}
} else {
if (constant == NULL) {
mode = kMode_MR;
} else {
inputs[(*input_count)++] = g->UseImmediate(constant);
mode = kMode_MRI;
}
}
} else {
DCHECK(scaled != NULL);
DCHECK(scale_exponent >= 0 && scale_exponent <= 3);
inputs[(*input_count)++] = g->UseRegister(scaled);
if (constant != NULL) {
inputs[(*input_count)++] = g->UseImmediate(constant);
static const AddressingMode kMnI_modes[] = {kMode_M1I, kMode_M2I,
kMode_M4I, kMode_M8I};
mode = kMnI_modes[scale_exponent];
} else {
static const AddressingMode kMn_modes[] = {kMode_M1, kMode_M2, kMode_M4,
kMode_M8};
mode = kMn_modes[scale_exponent];
}
}
return mode;
}
} // namespace
void InstructionSelector::VisitInt32Add(Node* node) {
// Try to match the Add to a leal pattern
ScaledWithOffset32Matcher m(node);
X64OperandGenerator g(this);
// It's possible to use a "leal", but it may not be smaller/cheaper. In the
// case that there are only two operands to the add and one of them isn't
// live, use a plain "addl".
if (m.matches() && (m.constant() == NULL || g.CanBeImmediate(m.constant()))) {
InstructionOperand* inputs[4];
size_t input_count = 0;
AddressingMode mode = GenerateMemoryOperandInputs(
&g, m.scaled(), m.scale_exponent(), m.offset(), m.constant(), inputs,
&input_count);
DCHECK_NE(0, static_cast<int>(input_count));
DCHECK_GE(arraysize(inputs), input_count);
InstructionOperand* outputs[1];
outputs[0] = g.DefineAsRegister(node);
InstructionCode opcode = AddressingModeField::encode(mode) | kX64Lea32;
Emit(opcode, 1, outputs, input_count, inputs);
// Try to match the Add to a leal pattern
BaseWithIndexAndDisplacement32Matcher m(node);
if (m.matches() &&
(m.displacement() == NULL || g.CanBeImmediate(m.displacement()))) {
EmitLea(this, kX64Lea32, node, m.index(), m.scale(), m.base(),
m.displacement());
return;
}
// No leal pattern match, use addl
VisitBinop(this, node, kX64Add32);
}
......@@ -646,6 +660,13 @@ void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode opcode) {
void InstructionSelector::VisitInt32Mul(Node* node) {
Int32ScaleMatcher m(node, true);
if (m.matches()) {
Node* index = node->InputAt(0);
Node* base = m.power_of_two_plus_one() ? index : NULL;
EmitLea(this, kX64Lea32, node, index, m.scale(), base, NULL);
return;
}
VisitMul(this, node, kX64Imul32);
}
......
......@@ -846,6 +846,150 @@ TEST_F(InstructionSelectorTest, Uint32MulHigh) {
}
TEST_F(InstructionSelectorTest, Int32Mul2BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(2);
Node* const n = m.Int32Mul(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MR1, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, Int32Mul3BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(3);
Node* const n = m.Int32Mul(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MR2, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, Int32Mul4BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(4);
Node* const n = m.Int32Mul(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_M4, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
}
TEST_F(InstructionSelectorTest, Int32Mul5BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(5);
Node* const n = m.Int32Mul(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MR4, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, Int32Mul8BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(8);
Node* const n = m.Int32Mul(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_M8, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
}
TEST_F(InstructionSelectorTest, Int32Mul9BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(9);
Node* const n = m.Int32Mul(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MR8, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1)));
}
// -----------------------------------------------------------------------------
// Word32Shl.
TEST_F(InstructionSelectorTest, Int32Shl1BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(1);
Node* const n = m.Word32Shl(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MR1, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, Int32Shl2BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(2);
Node* const n = m.Word32Shl(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_M4, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
}
TEST_F(InstructionSelectorTest, Int32Shl4BecomesLea) {
StreamBuilder m(this, kMachUint32, kMachUint32, kMachUint32);
Node* const p0 = m.Parameter(0);
Node* const c1 = m.Int32Constant(3);
Node* const n = m.Word32Shl(p0, c1);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_M8, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
}
// -----------------------------------------------------------------------------
// Word64Shl.
......
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