[arm64] Optimize fcmp when lhs operand is #0.0
authorpierre.langlois <pierre.langlois@arm.com>
Wed, 23 Sep 2015 09:33:27 +0000 (02:33 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 23 Sep 2015 09:33:35 +0000 (09:33 +0000)
This patch checks the type of the lhs operand of a floating point
comparison, and commutes the operands if it is #0.0.  It allows us to
optimize a comparison with zero, as the fcmp instruction accepts #0.0 as
rhs operand.

Code before for "0.0 < 0.123":
------------------------------
fmov d1, xzr
ldr d0, pc+96
fcmp d1, d0
b.lo #+0xc

Code after:
-----------
ldr d0, pc+92
fcmp d0, #0.0
b.gt #+0xc

Before this patch, we used unsigned condition codes for floating point
comparisons, but the unordered case was not correctly commuted.

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

Cr-Commit-Position: refs/heads/master@{#30881}

src/compiler/arm/code-generator-arm.cc
src/compiler/arm64/code-generator-arm64.cc
src/compiler/arm64/instruction-selector-arm64.cc
src/compiler/ia32/code-generator-ia32.cc
src/compiler/instruction-codes.h
src/compiler/instruction-selector-impl.h
src/compiler/instruction.cc
src/compiler/ppc/code-generator-ppc.cc
src/compiler/x64/code-generator-x64.cc
src/compiler/x87/code-generator-x87.cc
test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc

index ead028c39792fff8ae268a5203d48503387cf5f1..6cb32f421ba020ff9bebdd6b3b59788482fc7514 100644 (file)
@@ -224,8 +224,7 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
       return vs;
     case kNotOverflow:
       return vc;
-    case kUnorderedEqual:
-    case kUnorderedNotEqual:
+    default:
       break;
   }
   UNREACHABLE();
index b4a60046038588caf7b0bd350d76da8e0270e1e7..2b51cd09c6102720b077432a65bc11793a891a4a 100644 (file)
@@ -259,6 +259,22 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
       return ls;
     case kUnsignedGreaterThan:
       return hi;
+    case kFloatLessThanOrUnordered:
+      return lt;
+    case kFloatGreaterThanOrEqual:
+      return ge;
+    case kFloatLessThanOrEqual:
+      return ls;
+    case kFloatGreaterThanOrUnordered:
+      return hi;
+    case kFloatLessThan:
+      return lo;
+    case kFloatGreaterThanOrEqualOrUnordered:
+      return hs;
+    case kFloatLessThanOrEqualOrUnordered:
+      return le;
+    case kFloatGreaterThan:
+      return gt;
     case kOverflow:
       return vs;
     case kNotOverflow:
index c24cecf4605dc68b091358c0d217e5720604936a..7a5b84275acd05008f13de95121235ee1b8f8f15 100644 (file)
@@ -1671,7 +1671,7 @@ void VisitWord64Test(InstructionSelector* selector, Node* node,
 }
 
 
-// Shared routine for multiple float64 compare operations.
+// Shared routine for multiple float32 compare operations.
 void VisitFloat32Compare(InstructionSelector* selector, Node* node,
                          FlagsContinuation* cont) {
   Arm64OperandGenerator g(selector);
@@ -1679,6 +1679,10 @@ void VisitFloat32Compare(InstructionSelector* selector, Node* node,
   if (m.right().Is(0.0f)) {
     VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.left().node()),
                  g.UseImmediate(m.right().node()), cont);
+  } else if (m.left().Is(0.0f)) {
+    cont->Commute();
+    VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.right().node()),
+                 g.UseImmediate(m.left().node()), cont);
   } else {
     VisitCompare(selector, kArm64Float32Cmp, g.UseRegister(m.left().node()),
                  g.UseRegister(m.right().node()), cont);
@@ -1694,6 +1698,10 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node,
   if (m.right().Is(0.0)) {
     VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.left().node()),
                  g.UseImmediate(m.right().node()), cont);
+  } else if (m.left().Is(0.0)) {
+    cont->Commute();
+    VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.right().node()),
+                 g.UseImmediate(m.left().node()), cont);
   } else {
     VisitCompare(selector, kArm64Float64Cmp, g.UseRegister(m.left().node()),
                  g.UseRegister(m.right().node()), cont);
@@ -1765,19 +1773,19 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
         cont.OverwriteAndNegateIfEqual(kEqual);
         return VisitFloat32Compare(this, value, &cont);
       case IrOpcode::kFloat32LessThan:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThan);
+        cont.OverwriteAndNegateIfEqual(kFloatLessThan);
         return VisitFloat32Compare(this, value, &cont);
       case IrOpcode::kFloat32LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
+        cont.OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
         return VisitFloat32Compare(this, value, &cont);
       case IrOpcode::kFloat64Equal:
         cont.OverwriteAndNegateIfEqual(kEqual);
         return VisitFloat64Compare(this, value, &cont);
       case IrOpcode::kFloat64LessThan:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThan);
+        cont.OverwriteAndNegateIfEqual(kFloatLessThan);
         return VisitFloat64Compare(this, value, &cont);
       case IrOpcode::kFloat64LessThanOrEqual:
-        cont.OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
+        cont.OverwriteAndNegateIfEqual(kFloatLessThanOrEqual);
         return VisitFloat64Compare(this, value, &cont);
       case IrOpcode::kProjection:
         // Check if this is the overflow output projection of an
@@ -2018,13 +2026,13 @@ void InstructionSelector::VisitFloat32Equal(Node* node) {
 
 
 void InstructionSelector::VisitFloat32LessThan(Node* node) {
-  FlagsContinuation cont(kUnsignedLessThan, node);
+  FlagsContinuation cont(kFloatLessThan, node);
   VisitFloat32Compare(this, node, &cont);
 }
 
 
 void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) {
-  FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
+  FlagsContinuation cont(kFloatLessThanOrEqual, node);
   VisitFloat32Compare(this, node, &cont);
 }
 
@@ -2036,13 +2044,13 @@ void InstructionSelector::VisitFloat64Equal(Node* node) {
 
 
 void InstructionSelector::VisitFloat64LessThan(Node* node) {
-  FlagsContinuation cont(kUnsignedLessThan, node);
+  FlagsContinuation cont(kFloatLessThan, node);
   VisitFloat64Compare(this, node, &cont);
 }
 
 
 void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) {
-  FlagsContinuation cont(kUnsignedLessThanOrEqual, node);
+  FlagsContinuation cont(kFloatLessThanOrEqual, node);
   VisitFloat64Compare(this, node, &cont);
 }
 
index e9b3a9949a28cf00ea484b84708e5e8131d574ab..d4fe21505c4fd7971c1ea97580680cf698a58819 100644 (file)
@@ -1026,6 +1026,9 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
     case kNotOverflow:
       __ j(no_overflow, tlabel);
       break;
+    default:
+      UNREACHABLE();
+      break;
   }
   // Add a jump if not falling through to the next block.
   if (!branch->fallthru) __ jmp(flabel);
@@ -1096,6 +1099,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
     case kNotOverflow:
       cc = no_overflow;
       break;
+    default:
+      UNREACHABLE();
+      break;
   }
   __ bind(&check);
   if (reg.is_byte_register()) {
index 88471e6c04dd6efabf1f9e9adff3d19755c9adfc..cb47be64467ddd1f9f13639fbb567b1799c1cb7a 100644 (file)
@@ -114,6 +114,14 @@ enum FlagsCondition {
   kUnsignedGreaterThanOrEqual,
   kUnsignedLessThanOrEqual,
   kUnsignedGreaterThan,
+  kFloatLessThanOrUnordered,
+  kFloatGreaterThanOrEqual,
+  kFloatLessThanOrEqual,
+  kFloatGreaterThanOrUnordered,
+  kFloatLessThan,
+  kFloatGreaterThanOrEqualOrUnordered,
+  kFloatLessThanOrEqualOrUnordered,
+  kFloatGreaterThan,
   kUnorderedEqual,
   kUnorderedNotEqual,
   kOverflow,
@@ -124,6 +132,8 @@ inline FlagsCondition NegateFlagsCondition(FlagsCondition condition) {
   return static_cast<FlagsCondition>(condition ^ 1);
 }
 
+FlagsCondition CommuteFlagsCondition(FlagsCondition condition);
+
 std::ostream& operator<<(std::ostream& os, const FlagsCondition& fc);
 
 // The InstructionCode is an opaque, target-specific integer that encodes
@@ -139,8 +149,8 @@ typedef int32_t InstructionCode;
 typedef BitField<ArchOpcode, 0, 8> ArchOpcodeField;
 typedef BitField<AddressingMode, 8, 5> AddressingModeField;
 typedef BitField<FlagsMode, 13, 2> FlagsModeField;
-typedef BitField<FlagsCondition, 15, 4> FlagsConditionField;
-typedef BitField<int, 19, 13> MiscField;
+typedef BitField<FlagsCondition, 15, 5> FlagsConditionField;
+typedef BitField<int, 20, 12> MiscField;
 
 }  // namespace compiler
 }  // namespace internal
index e22f5458697b852f59300dc5e4011298673da45e..88283d489877caeef91ed8e431c82e19a007d9fa 100644 (file)
@@ -291,41 +291,7 @@ class FlagsContinuation final {
 
   void Commute() {
     DCHECK(!IsNone());
-    switch (condition_) {
-      case kEqual:
-      case kNotEqual:
-      case kOverflow:
-      case kNotOverflow:
-        return;
-      case kSignedLessThan:
-        condition_ = kSignedGreaterThan;
-        return;
-      case kSignedGreaterThanOrEqual:
-        condition_ = kSignedLessThanOrEqual;
-        return;
-      case kSignedLessThanOrEqual:
-        condition_ = kSignedGreaterThanOrEqual;
-        return;
-      case kSignedGreaterThan:
-        condition_ = kSignedLessThan;
-        return;
-      case kUnsignedLessThan:
-        condition_ = kUnsignedGreaterThan;
-        return;
-      case kUnsignedGreaterThanOrEqual:
-        condition_ = kUnsignedLessThanOrEqual;
-        return;
-      case kUnsignedLessThanOrEqual:
-        condition_ = kUnsignedGreaterThanOrEqual;
-        return;
-      case kUnsignedGreaterThan:
-        condition_ = kUnsignedLessThan;
-        return;
-      case kUnorderedEqual:
-      case kUnorderedNotEqual:
-        return;
-    }
-    UNREACHABLE();
+    condition_ = CommuteFlagsCondition(condition_);
   }
 
   void OverwriteAndNegateIfEqual(FlagsCondition condition) {
index 1f6f7c9333e1d5e5feacd9a71e22b0f72d1dc0e1..0fbb94979e41522d4df8d2472372559f4dab5160 100644 (file)
@@ -11,6 +11,54 @@ namespace v8 {
 namespace internal {
 namespace compiler {
 
+
+FlagsCondition CommuteFlagsCondition(FlagsCondition condition) {
+  switch (condition) {
+    case kSignedLessThan:
+      return kSignedGreaterThan;
+    case kSignedGreaterThanOrEqual:
+      return kSignedLessThanOrEqual;
+    case kSignedLessThanOrEqual:
+      return kSignedGreaterThanOrEqual;
+    case kSignedGreaterThan:
+      return kSignedLessThan;
+    case kUnsignedLessThan:
+      return kUnsignedGreaterThan;
+    case kUnsignedGreaterThanOrEqual:
+      return kUnsignedLessThanOrEqual;
+    case kUnsignedLessThanOrEqual:
+      return kUnsignedGreaterThanOrEqual;
+    case kUnsignedGreaterThan:
+      return kUnsignedLessThan;
+    case kFloatLessThanOrUnordered:
+      return kFloatGreaterThanOrUnordered;
+    case kFloatGreaterThanOrEqual:
+      return kFloatLessThanOrEqual;
+    case kFloatLessThanOrEqual:
+      return kFloatGreaterThanOrEqual;
+    case kFloatGreaterThanOrUnordered:
+      return kFloatLessThanOrUnordered;
+    case kFloatLessThan:
+      return kFloatGreaterThan;
+    case kFloatGreaterThanOrEqualOrUnordered:
+      return kFloatLessThanOrEqualOrUnordered;
+    case kFloatLessThanOrEqualOrUnordered:
+      return kFloatGreaterThanOrEqualOrUnordered;
+    case kFloatGreaterThan:
+      return kFloatLessThan;
+    case kEqual:
+    case kNotEqual:
+    case kOverflow:
+    case kNotOverflow:
+    case kUnorderedEqual:
+    case kUnorderedNotEqual:
+      return condition;
+  }
+  UNREACHABLE();
+  return condition;
+}
+
+
 std::ostream& operator<<(std::ostream& os,
                          const PrintableInstructionOperand& printable) {
   const InstructionOperand& op = printable.op_;
@@ -300,6 +348,22 @@ std::ostream& operator<<(std::ostream& os, const FlagsCondition& fc) {
       return os << "unsigned less than or equal";
     case kUnsignedGreaterThan:
       return os << "unsigned greater than";
+    case kFloatLessThanOrUnordered:
+      return os << "less than or unordered (FP)";
+    case kFloatGreaterThanOrEqual:
+      return os << "greater than or equal (FP)";
+    case kFloatLessThanOrEqual:
+      return os << "less than or equal (FP)";
+    case kFloatGreaterThanOrUnordered:
+      return os << "greater than or unordered (FP)";
+    case kFloatLessThan:
+      return os << "less than (FP)";
+    case kFloatGreaterThanOrEqualOrUnordered:
+      return os << "greater than, equal or unordered (FP)";
+    case kFloatLessThanOrEqualOrUnordered:
+      return os << "less than, equal or unordered (FP)";
+    case kFloatGreaterThan:
+      return os << "greater than (FP)";
     case kUnorderedEqual:
       return os << "unordered equal";
     case kUnorderedNotEqual:
index 4fc6bc017672128507089b57421869407cd42dbf..df776fac682f0bf0124f226f8f96b3c1affbc895 100644 (file)
@@ -187,8 +187,7 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
 #else
       return ge;
 #endif
-    case kUnorderedEqual:
-    case kUnorderedNotEqual:
+    default:
       break;
   }
   UNREACHABLE();
index 06befa83f4c53144037a45d8f77728d1055e4594..4c991718f8b641996361ef2cfafed6e70adb589b 100644 (file)
@@ -1383,6 +1383,9 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
     case kNotOverflow:
       __ j(no_overflow, tlabel);
       break;
+    default:
+      UNREACHABLE();
+      break;
   }
   if (!branch->fallthru) __ jmp(flabel, flabel_distance);
 }
@@ -1452,6 +1455,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
     case kNotOverflow:
       cc = no_overflow;
       break;
+    default:
+      UNREACHABLE();
+      break;
   }
   __ bind(&check);
   __ setcc(cc, reg);
index 596b7ab775343b251f55e1014dfce84ddcbe78e0..4f90a80d8ae1a69ab6a18d9b82c9c97489396f52 100644 (file)
@@ -1282,6 +1282,9 @@ void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
     case kNotOverflow:
       __ j(no_overflow, tlabel);
       break;
+    default:
+      UNREACHABLE();
+      break;
   }
   // Add a jump if not falling through to the next block.
   if (!branch->fallthru) __ jmp(flabel);
@@ -1352,6 +1355,9 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr,
     case kNotOverflow:
       cc = no_overflow;
       break;
+    default:
+      UNREACHABLE();
+      break;
   }
   __ bind(&check);
   if (reg.is_byte_register()) {
index 71c2d44d2f51722fe2bf6fec1dd2a5f7390006ec..c26ad726d4212f22ddc4e2bfa33739bef7907427 100644 (file)
@@ -235,10 +235,19 @@ const FPCmp kFPCmpInstructions[] = {
      kEqual},
     {{&RawMachineAssembler::Float64LessThan, "Float64LessThan",
       kArm64Float64Cmp, kMachFloat64},
-     kUnsignedLessThan},
+     kFloatLessThan},
     {{&RawMachineAssembler::Float64LessThanOrEqual, "Float64LessThanOrEqual",
       kArm64Float64Cmp, kMachFloat64},
-     kUnsignedLessThanOrEqual}};
+     kFloatLessThanOrEqual},
+    {{&RawMachineAssembler::Float32Equal, "Float32Equal", kArm64Float32Cmp,
+      kMachFloat32},
+     kEqual},
+    {{&RawMachineAssembler::Float32LessThan, "Float32LessThan",
+      kArm64Float32Cmp, kMachFloat32},
+     kFloatLessThan},
+    {{&RawMachineAssembler::Float32LessThanOrEqual, "Float32LessThanOrEqual",
+      kArm64Float32Cmp, kMachFloat32},
+     kFloatLessThanOrEqual}};
 
 
 struct Conversion {
@@ -1913,7 +1922,11 @@ TEST_P(InstructionSelectorFPCmpTest, Parameter) {
 TEST_P(InstructionSelectorFPCmpTest, WithImmediateZeroOnRight) {
   const FPCmp cmp = GetParam();
   StreamBuilder m(this, kMachInt32, cmp.mi.machine_type);
-  m.Return((m.*cmp.mi.constructor)(m.Parameter(0), m.Float64Constant(0.0)));
+  if (cmp.mi.machine_type == kMachFloat64) {
+    m.Return((m.*cmp.mi.constructor)(m.Parameter(0), m.Float64Constant(0.0)));
+  } else {
+    m.Return((m.*cmp.mi.constructor)(m.Parameter(0), m.Float32Constant(0.0f)));
+  }
   Stream s = m.Build();
   ASSERT_EQ(1U, s.size());
   EXPECT_EQ(cmp.mi.arch_opcode, s[0]->arch_opcode());
@@ -1925,24 +1938,29 @@ TEST_P(InstructionSelectorFPCmpTest, WithImmediateZeroOnRight) {
 }
 
 
-INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorFPCmpTest,
-                        ::testing::ValuesIn(kFPCmpInstructions));
-
-
-TEST_F(InstructionSelectorTest, Float64EqualWithImmediateZeroOnLeft) {
-  StreamBuilder m(this, kMachInt32, kMachFloat64);
-  m.Return(m.Float64Equal(m.Float64Constant(0.0), m.Parameter(0)));
+TEST_P(InstructionSelectorFPCmpTest, WithImmediateZeroOnLeft) {
+  const FPCmp cmp = GetParam();
+  StreamBuilder m(this, kMachInt32, cmp.mi.machine_type);
+  if (cmp.mi.machine_type == kMachFloat64) {
+    m.Return((m.*cmp.mi.constructor)(m.Float64Constant(0.0), m.Parameter(0)));
+  } else {
+    m.Return((m.*cmp.mi.constructor)(m.Float32Constant(0.0f), m.Parameter(0)));
+  }
   Stream s = m.Build();
   ASSERT_EQ(1U, s.size());
-  EXPECT_EQ(kArm64Float64Cmp, s[0]->arch_opcode());
+  EXPECT_EQ(cmp.mi.arch_opcode, s[0]->arch_opcode());
   EXPECT_EQ(2U, s[0]->InputCount());
   EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate());
   EXPECT_EQ(1U, s[0]->OutputCount());
   EXPECT_EQ(kFlags_set, s[0]->flags_mode());
-  EXPECT_EQ(kEqual, s[0]->flags_condition());
+  EXPECT_EQ(CommuteFlagsCondition(cmp.cond), s[0]->flags_condition());
 }
 
 
+INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorFPCmpTest,
+                        ::testing::ValuesIn(kFPCmpInstructions));
+
+
 // -----------------------------------------------------------------------------
 // Conversions.