[turbofan] Negated immediates for ARM64 add/sub
authorm.m.capewell@googlemail.com <m.m.capewell@googlemail.com>
Mon, 6 Oct 2014 10:39:23 +0000 (10:39 +0000)
committerm.m.capewell@googlemail.com <m.m.capewell@googlemail.com>
Mon, 6 Oct 2014 10:39:23 +0000 (10:39 +0000)
Add ARM64 instruction selector support for negating the sense of an arithmetic
instruction when its immediate is negative.

BUG=
R=bmeurer@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24407 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/compiler/arm64/instruction-selector-arm64.cc
test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc

index 589d469..3a2a820 100644 (file)
@@ -44,6 +44,10 @@ class Arm64OperandGenerator FINAL : public OperandGenerator {
       value = OpParameter<int64_t>(node);
     else
       return false;
+    return CanBeImmediate(value, mode);
+  }
+
+  bool CanBeImmediate(int64_t value, ImmediateMode mode) {
     unsigned ignored;
     switch (mode) {
       case kLogical32Imm:
@@ -55,7 +59,6 @@ class Arm64OperandGenerator FINAL : public OperandGenerator {
         return Assembler::IsImmLogical(static_cast<uint64_t>(value), 64,
                                        &ignored, &ignored, &ignored);
       case kArithmeticImm:
-        // TODO(dcarney): -values can be handled by instruction swapping
         return Assembler::IsImmAddSub(value);
       case kShift32Imm:
         return 0 <= value && value < 32;
@@ -155,6 +158,22 @@ static void VisitBinop(InstructionSelector* selector, Node* node,
 }
 
 
+template <typename Matcher>
+static void VisitAddSub(InstructionSelector* selector, Node* node,
+                        ArchOpcode opcode, ArchOpcode negate_opcode) {
+  Arm64OperandGenerator g(selector);
+  Matcher m(node);
+  if (m.right().HasValue() && (m.right().Value() < 0) &&
+      g.CanBeImmediate(-m.right().Value(), kArithmeticImm)) {
+    selector->Emit(negate_opcode, g.DefineAsRegister(node),
+                   g.UseRegister(m.left().node()),
+                   g.TempImmediate(-m.right().Value()));
+  } else {
+    VisitBinop<Matcher>(selector, node, opcode, kArithmeticImm);
+  }
+}
+
+
 void InstructionSelector::VisitLoad(Node* node) {
   MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
   MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
@@ -442,7 +461,7 @@ void InstructionSelector::VisitInt32Add(Node* node) {
          g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
     return;
   }
-  VisitBinop<Int32BinopMatcher>(this, node, kArm64Add32, kArithmeticImm);
+  VisitAddSub<Int32BinopMatcher>(this, node, kArm64Add32, kArm64Sub32);
 }
 
 
@@ -465,7 +484,7 @@ void InstructionSelector::VisitInt64Add(Node* node) {
          g.UseRegister(mright.right().node()), g.UseRegister(m.left().node()));
     return;
   }
-  VisitBinop<Int64BinopMatcher>(this, node, kArm64Add, kArithmeticImm);
+  VisitAddSub<Int64BinopMatcher>(this, node, kArm64Add, kArm64Sub);
 }
 
 
@@ -486,7 +505,7 @@ void InstructionSelector::VisitInt32Sub(Node* node) {
     Emit(kArm64Neg32, g.DefineAsRegister(node),
          g.UseRegister(m.right().node()));
   } else {
-    VisitBinop<Int32BinopMatcher>(this, node, kArm64Sub32, kArithmeticImm);
+    VisitAddSub<Int32BinopMatcher>(this, node, kArm64Sub32, kArm64Add32);
   }
 }
 
@@ -507,7 +526,7 @@ void InstructionSelector::VisitInt64Sub(Node* node) {
   if (m.left().Is(0)) {
     Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node()));
   } else {
-    VisitBinop<Int64BinopMatcher>(this, node, kArm64Sub, kArithmeticImm);
+    VisitAddSub<Int64BinopMatcher>(this, node, kArm64Sub, kArm64Add);
   }
 }
 
index 6287a71..dc74a7b 100644 (file)
@@ -84,11 +84,26 @@ static const uint32_t kLogicalImmediates[] = {
 
 
 // ARM64 arithmetic instructions.
-static const MachInst2 kAddSubInstructions[] = {
-    {&RawMachineAssembler::Int32Add, "Int32Add", kArm64Add32, kMachInt32},
-    {&RawMachineAssembler::Int64Add, "Int64Add", kArm64Add, kMachInt64},
-    {&RawMachineAssembler::Int32Sub, "Int32Sub", kArm64Sub32, kMachInt32},
-    {&RawMachineAssembler::Int64Sub, "Int64Sub", kArm64Sub, kMachInt64}};
+struct AddSub {
+  MachInst2 mi;
+  ArchOpcode negate_arch_opcode;
+};
+
+
+std::ostream& operator<<(std::ostream& os, const AddSub& op) {
+  return os << op.mi;
+}
+
+
+static const AddSub kAddSubInstructions[] = {
+    {{&RawMachineAssembler::Int32Add, "Int32Add", kArm64Add32, kMachInt32},
+     kArm64Sub32},
+    {{&RawMachineAssembler::Int64Add, "Int64Add", kArm64Add, kMachInt64},
+     kArm64Sub},
+    {{&RawMachineAssembler::Int32Sub, "Int32Sub", kArm64Sub32, kMachInt32},
+     kArm64Add32},
+    {{&RawMachineAssembler::Int64Sub, "Int64Sub", kArm64Sub, kMachInt64},
+     kArm64Add}};
 
 
 // ARM64 Add/Sub immediates: 12-bit immediate optionally shifted by 12.
@@ -288,32 +303,32 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorLogicalTest,
 // -----------------------------------------------------------------------------
 // Add and Sub instructions.
 
-typedef InstructionSelectorTestWithParam<MachInst2>
-    InstructionSelectorAddSubTest;
+typedef InstructionSelectorTestWithParam<AddSub> InstructionSelectorAddSubTest;
 
 
 TEST_P(InstructionSelectorAddSubTest, Parameter) {
-  const MachInst2 dpi = GetParam();
-  const MachineType type = dpi.machine_type;
+  const AddSub dpi = GetParam();
+  const MachineType type = dpi.mi.machine_type;
   StreamBuilder m(this, type, type, type);
-  m.Return((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1)));
+  m.Return((m.*dpi.mi.constructor)(m.Parameter(0), m.Parameter(1)));
   Stream s = m.Build();
   ASSERT_EQ(1U, s.size());
-  EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode());
+  EXPECT_EQ(dpi.mi.arch_opcode, s[0]->arch_opcode());
   EXPECT_EQ(2U, s[0]->InputCount());
   EXPECT_EQ(1U, s[0]->OutputCount());
 }
 
 
 TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) {
-  const MachInst2 dpi = GetParam();
-  const MachineType type = dpi.machine_type;
+  const AddSub dpi = GetParam();
+  const MachineType type = dpi.mi.machine_type;
   TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
     StreamBuilder m(this, type, type);
-    m.Return((m.*dpi.constructor)(m.Parameter(0), BuildConstant(m, type, imm)));
+    m.Return(
+        (m.*dpi.mi.constructor)(m.Parameter(0), BuildConstant(m, type, imm)));
     Stream s = m.Build();
     ASSERT_EQ(1U, s.size());
-    EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode());
+    EXPECT_EQ(dpi.mi.arch_opcode, s[0]->arch_opcode());
     ASSERT_EQ(2U, s[0]->InputCount());
     EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
     EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1)));
@@ -322,20 +337,52 @@ TEST_P(InstructionSelectorAddSubTest, ImmediateOnRight) {
 }
 
 
-TEST_P(InstructionSelectorAddSubTest, ImmediateOnLeft) {
-  const MachInst2 dpi = GetParam();
-  const MachineType type = dpi.machine_type;
-
+TEST_P(InstructionSelectorAddSubTest, NegImmediateOnRight) {
+  const AddSub dpi = GetParam();
+  const MachineType type = dpi.mi.machine_type;
   TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
+    if (imm == 0) continue;
     StreamBuilder m(this, type, type);
-    m.Return((m.*dpi.constructor)(BuildConstant(m, type, imm), m.Parameter(0)));
+    m.Return(
+        (m.*dpi.mi.constructor)(m.Parameter(0), BuildConstant(m, type, -imm)));
     Stream s = m.Build();
+    ASSERT_EQ(1U, s.size());
+    EXPECT_EQ(dpi.negate_arch_opcode, s[0]->arch_opcode());
+    ASSERT_EQ(2U, s[0]->InputCount());
+    ASSERT_TRUE(s[0]->InputAt(1)->IsImmediate());
+    EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1)));
+    EXPECT_EQ(1U, s[0]->OutputCount());
+  }
+}
+
 
-    // Add can support an immediate on the left by commuting, but Sub can't
-    // commute. We test zero-on-left Sub later.
-    if (strstr(dpi.constructor_name, "Add") != NULL) {
+INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorAddSubTest,
+                        ::testing::ValuesIn(kAddSubInstructions));
+
+
+TEST_F(InstructionSelectorTest, AddImmediateOnLeft) {
+  {
+    // 32-bit add.
+    TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
+      StreamBuilder m(this, kMachInt32, kMachInt32);
+      m.Return(m.Int32Add(m.Int32Constant(imm), m.Parameter(0)));
+      Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
-      EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode());
+      EXPECT_EQ(kArm64Add32, s[0]->arch_opcode());
+      ASSERT_EQ(2U, s[0]->InputCount());
+      EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
+      EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1)));
+      EXPECT_EQ(1U, s[0]->OutputCount());
+    }
+  }
+  {
+    // 64-bit add.
+    TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
+      StreamBuilder m(this, kMachInt64, kMachInt64);
+      m.Return(m.Int64Add(m.Int64Constant(imm), m.Parameter(0)));
+      Stream s = m.Build();
+      ASSERT_EQ(1U, s.size());
+      EXPECT_EQ(kArm64Add, s[0]->arch_opcode());
       ASSERT_EQ(2U, s[0]->InputCount());
       EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
       EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1)));
@@ -345,10 +392,6 @@ TEST_P(InstructionSelectorAddSubTest, ImmediateOnLeft) {
 }
 
 
-INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorAddSubTest,
-                        ::testing::ValuesIn(kAddSubInstructions));
-
-
 TEST_F(InstructionSelectorTest, SubZeroOnLeft) {
   // Subtraction with zero on the left maps to Neg.
   {
@@ -376,6 +419,42 @@ TEST_F(InstructionSelectorTest, SubZeroOnLeft) {
 }
 
 
+TEST_F(InstructionSelectorTest, AddNegImmediateOnLeft) {
+  {
+    // 32-bit add.
+    TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
+      if (imm == 0) continue;
+      StreamBuilder m(this, kMachInt32, kMachInt32);
+      m.Return(m.Int32Add(m.Int32Constant(-imm), m.Parameter(0)));
+      Stream s = m.Build();
+
+      ASSERT_EQ(1U, s.size());
+      EXPECT_EQ(kArm64Sub32, s[0]->arch_opcode());
+      ASSERT_EQ(2U, s[0]->InputCount());
+      ASSERT_TRUE(s[0]->InputAt(1)->IsImmediate());
+      EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1)));
+      EXPECT_EQ(1U, s[0]->OutputCount());
+    }
+  }
+  {
+    // 64-bit add.
+    TRACED_FOREACH(int32_t, imm, kAddSubImmediates) {
+      if (imm == 0) continue;
+      StreamBuilder m(this, kMachInt64, kMachInt64);
+      m.Return(m.Int64Add(m.Int64Constant(-imm), m.Parameter(0)));
+      Stream s = m.Build();
+
+      ASSERT_EQ(1U, s.size());
+      EXPECT_EQ(kArm64Sub, s[0]->arch_opcode());
+      ASSERT_EQ(2U, s[0]->InputCount());
+      ASSERT_TRUE(s[0]->InputAt(1)->IsImmediate());
+      EXPECT_EQ(imm, s.ToInt64(s[0]->InputAt(1)));
+      EXPECT_EQ(1U, s[0]->OutputCount());
+    }
+  }
+}
+
+
 // -----------------------------------------------------------------------------
 // Data processing controlled branches.