Commit 4f1cc515 authored by danno's avatar danno Committed by Commit bot

[turbofan]: remove optimization of adds/subs to inc and dec

They generally cause regressions on most modern Intel chips. Replace them with
addl/subl.

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

Cr-Commit-Position: refs/heads/master@{#25466}
parent ac3c4d40
......@@ -427,29 +427,34 @@ 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()))) {
// The add can be represented as a "leal", but there may be a smaller
// representation that is better and no more expensive.
if (m.offset() != NULL) {
if (m.scaled() == NULL) {
if (!IsLive(m.offset())) {
// If the add is of the form (r1 + immediate) and the non-constant
// input to the add is owned by the add, then it doesn't need to be
// preserved across the operation, so use more compact,
// source-register-overwriting versions when they are available and
// smaller, e.g. "incl" and "decl".
int32_t value =
m.constant() == NULL ? 0 : OpParameter<int32_t>(m.constant());
if (value == 1) {
Emit(kX64Inc32, g.DefineSameAsFirst(node),
g.UseRegister(m.offset()));
if (m.constant() == NULL) {
if (m.scaled() != NULL && m.scale_exponent() == 0) {
if (!IsLive(m.offset())) {
Emit(kX64Add32, g.DefineSameAsFirst(node),
g.UseRegister(m.offset()), g.Use(m.scaled()));
return;
} else if (value == -1) {
Emit(kX64Dec32, g.DefineSameAsFirst(node),
g.UseRegister(m.offset()));
} else if (!IsLive(m.scaled())) {
Emit(kX64Add32, g.DefineSameAsFirst(node),
g.UseRegister(m.scaled()), g.Use(m.offset()));
return;
}
}
} else {
if (m.scale_exponent() == 0) {
if (m.scaled() == NULL || m.offset() == NULL) {
Node* non_constant = m.scaled() == NULL ? m.offset() : m.scaled();
if (!IsLive(non_constant)) {
Emit(kX64Add32, g.DefineSameAsFirst(node),
g.UseRegister(non_constant), g.UseImmediate(m.constant()));
return;
}
}
}
}
}
......@@ -487,21 +492,7 @@ void InstructionSelector::VisitInt32Sub(Node* node) {
Emit(kX64Neg32, g.DefineSameAsFirst(node), g.UseRegister(m.right().node()));
} else {
if (m.right().HasValue() && g.CanBeImmediate(m.right().node())) {
// If the Non-constant input is owned by the subtract, using a "decl" or
// "incl" that overwrites that input is smaller and probably an overall
// win.
if (!IsLive(m.left().node())) {
if (m.right().Value() == 1) {
Emit(kX64Dec32, g.DefineSameAsFirst(node),
g.UseRegister(m.left().node()));
return;
}
if (m.right().Value() == -1) {
Emit(kX64Inc32, g.DefineSameAsFirst(node),
g.UseRegister(m.left().node()));
return;
}
} else {
if (IsLive(m.left().node())) {
// Special handling for subtraction of constants where the non-constant
// input is used elsewhere. To eliminate the gap move before the sub to
// copy the destination register, use a "leal" instead.
......
......@@ -249,10 +249,9 @@ TEST_F(InstructionSelectorTest, Int32AddWithInt32ParametersLea) {
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
Node* const a0 = m.Int32Add(p0, p1);
USE(a0);
// Additional uses of input to add chooses lea
Node* const a1 = m.Int32Add(p0, p1);
m.Return(m.Int32Add(a0, a1));
Node* const a1 = m.Int32Div(p0, p1);
m.Return(m.Int32Div(a0, a1));
Stream s = m.Build();
ASSERT_EQ(3U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
......@@ -266,20 +265,21 @@ TEST_F(InstructionSelectorTest, Int32AddConstantAsLeaSingle) {
StreamBuilder m(this, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const c0 = m.Int32Constant(15);
// If there is only a single use of an add's input, still use lea and not add,
// it is faster.
m.Return(m.Int32Add(p0, c0));
// If there is only a single use of an add's input, use an "addl" not a
// "leal", it is faster.
Node* const v0 = m.Int32Add(p0, c0);
m.Return(v0);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
EXPECT_EQ(kX64Add32, s[0]->arch_opcode());
EXPECT_EQ(kMode_None, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
}
TEST_F(InstructionSelectorTest, Int32AddConstantAsInc) {
TEST_F(InstructionSelectorTest, Int32AddConstantAsAdd) {
StreamBuilder m(this, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const c0 = m.Int32Constant(1);
......@@ -288,26 +288,11 @@ TEST_F(InstructionSelectorTest, Int32AddConstantAsInc) {
m.Return(m.Int32Add(p0, c0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Inc32, s[0]->arch_opcode());
EXPECT_EQ(kMode_None, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
}
TEST_F(InstructionSelectorTest, Int32AddConstantAsDec) {
StreamBuilder m(this, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const c0 = m.Int32Constant(-1);
// If there is only a single use of an add's input and the immediate constant
// for the add is -11, use dec.
m.Return(m.Int32Add(p0, c0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Dec32, s[0]->arch_opcode());
EXPECT_EQ(kX64Add32, s[0]->arch_opcode());
EXPECT_EQ(kMode_None, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
}
......@@ -317,10 +302,9 @@ TEST_F(InstructionSelectorTest, Int32AddConstantAsLeaDouble) {
Node* const c0 = m.Int32Constant(15);
// A second use of an add's input uses lea
Node* const a0 = m.Int32Add(p0, c0);
USE(a0);
m.Return(m.Int32Add(p0, c0));
m.Return(m.Int32Div(a0, p0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
ASSERT_EQ(2U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
......@@ -333,13 +317,12 @@ TEST_F(InstructionSelectorTest, Int32AddCommutedConstantAsLeaSingle) {
StreamBuilder m(this, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const c0 = m.Int32Constant(15);
// If there is only a single use of an add's input, still use lea... it's
// generally faster than the add to reduce register pressure.
// If there is only a single use of an add's input, use "addl"
m.Return(m.Int32Add(c0, p0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
EXPECT_EQ(kX64Add32, s[0]->arch_opcode());
EXPECT_EQ(kMode_None, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
......@@ -353,9 +336,9 @@ TEST_F(InstructionSelectorTest, Int32AddCommutedConstantAsLeaDouble) {
// A second use of an add's input uses lea
Node* const a0 = m.Int32Add(c0, p0);
USE(a0);
m.Return(m.Int32Add(c0, p0));
m.Return(m.Int32Div(a0, p0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
ASSERT_EQ(2U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
......@@ -364,6 +347,39 @@ TEST_F(InstructionSelectorTest, Int32AddCommutedConstantAsLeaDouble) {
}
TEST_F(InstructionSelectorTest, Int32AddSimpleAsAdd) {
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
// If one of the add's operands is only used once, use an "addl".
m.Return(m.Int32Add(p0, p1));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Add32, s[0]->arch_opcode());
EXPECT_EQ(kMode_None, 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(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, Int32AddSimpleAsLea) {
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
// If all of of the add's operands are used multiple times, use an "leal".
Node* const v1 = m.Int32Add(p0, p1);
m.Return(m.Int32Add(m.Int32Add(v1, p1), p0));
Stream s = m.Build();
ASSERT_EQ(3U, 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(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, Int32AddScaled2Mul) {
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
......@@ -690,35 +706,38 @@ TEST_F(InstructionSelectorTest, Int32AddScaled8ShlWithConstant) {
}
TEST_F(InstructionSelectorTest, Int32SubConstantAsInc) {
TEST_F(InstructionSelectorTest, Int32SubConstantAsSub) {
StreamBuilder m(this, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const c0 = m.Int32Constant(-1);
// If there is only a single use of an add's input and the immediate constant
// for the add is 1, use inc.
// If there is only a single use of on of the sub's non-constant input, use a
// "subl" instruction.
m.Return(m.Int32Sub(p0, c0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Inc32, s[0]->arch_opcode());
EXPECT_EQ(kX64Sub32, s[0]->arch_opcode());
EXPECT_EQ(kMode_None, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
}
TEST_F(InstructionSelectorTest, Int32SubConstantAsDec) {
TEST_F(InstructionSelectorTest, Int32SubConstantAsLea) {
StreamBuilder m(this, kMachInt32, kMachInt32);
Node* const p0 = m.Parameter(0);
Node* const c0 = m.Int32Constant(1);
// If there is only a single use of an sub's input and the immediate constant
// for the add is 1, use dec.
m.Return(m.Int32Sub(p0, c0));
Node* const c0 = m.Int32Constant(-1);
// If there are multiple uses of on of the sub's non-constant input, use a
// "leal" instruction.
Node* const v0 = m.Int32Sub(p0, c0);
m.Return(m.Int32Div(p0, v0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Dec32, s[0]->arch_opcode());
EXPECT_EQ(kMode_None, s[0]->addressing_mode());
ASSERT_EQ(1U, s[0]->InputCount());
ASSERT_EQ(2U, s.size());
EXPECT_EQ(kX64Lea32, s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
ASSERT_EQ(2U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
}
......
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