[turbofan] x64 lea multiplication matching
authordcarney@chromium.org <dcarney@chromium.org>
Tue, 30 Sep 2014 09:46:30 +0000 (09:46 +0000)
committerdcarney@chromium.org <dcarney@chromium.org>
Tue, 30 Sep 2014 09:46:30 +0000 (09:46 +0000)
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

BUILD.gn
src/compiler/node-matchers.cc [new file with mode: 0644]
src/compiler/node-matchers.h
src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-codes-x64.h
src/compiler/x64/instruction-selector-x64-unittest.cc
src/compiler/x64/instruction-selector-x64.cc
tools/gyp/v8.gyp

index cbb9cdfb48ea87a9af48cdfffa75177f1e76bb65..5ba74eb4561cff8d26d6948e613932b3c877d2e6 100644 (file)
--- a/BUILD.gn
+++ b/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",
diff --git a/src/compiler/node-matchers.cc b/src/compiler/node-matchers.cc
new file mode 100644 (file)
index 0000000..4d7fb84
--- /dev/null
@@ -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
index 6019cba3a593fa9a64a9bc2cbf7cd4a37719ecbb..a88e7740d342eaab925a098fae6a5a4bdf19af9e 100644 (file)
@@ -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];
+
+  explicit ScaleFactorMatcher(Node* node);
 
-  bool Matches() { return left_ != NULL; }
-  int Power() {
+  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
index e6dfaf1e7785051423cf910210c20c894e299400..7bf26a4c91b4563a6b44b86a3d631e3f4a6b7047 100644 (file)
@@ -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));
index 336c592aba15704f0c5ff18b0f4476465129b916..676716453fd3e8a972f744f37b1e78e525231915 100644 (file)
@@ -68,6 +68,8 @@ namespace compiler {
   V(X64Movq)                       \
   V(X64Movsd)                      \
   V(X64Movss)                      \
+  V(X64Lea32)                      \
+  V(X64Lea)                        \
   V(X64Push)                       \
   V(X64StoreWriteBarrier)
 
index f5545a789d9200992d536e603f624ad7a2a36543..cc2fedb3fcf3707acc20f54f02fac34145a69864 100644 (file)
@@ -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
index 843f0883944b4216e56e157835709d64f07b9ac6..ab159a2bbca6b54d635a37e51859cc0bda7f7286 100644 (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 {
  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));
+  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 {
-    if (g.CanBeBetterLeftOperand(right)) {
-      std::swap(left, right);
+    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));
   }
 }
 
index 27bb9fb3c8e5105a47ba3f9e0cd54ce9151f826e..0d86c7d6ded4036e741d1db0e9efd3fda7e47dcb 100644 (file)
         '../../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',