[turbofan] Introduce optional Float64Min and Float64Max machine operators.
authorbmeurer <bmeurer@chromium.org>
Thu, 12 Mar 2015 14:07:28 +0000 (07:07 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 12 Mar 2015 14:07:39 +0000 (14:07 +0000)
Basically recognize certain x < y ? x : y constructs and turn that into
Float64Min/Float64Max operations, if the target machine supports that.
On x86 we lower to (v)minsd/(v)maxsd.

R=dcarney@chromium.org

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

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

30 files changed:
src/compiler/arm/instruction-selector-arm.cc
src/compiler/arm64/instruction-selector-arm64.cc
src/compiler/common-operator-reducer.cc
src/compiler/common-operator-reducer.h
src/compiler/ia32/code-generator-ia32.cc
src/compiler/ia32/instruction-codes-ia32.h
src/compiler/ia32/instruction-selector-ia32.cc
src/compiler/instruction-selector.cc
src/compiler/machine-operator.cc
src/compiler/machine-operator.h
src/compiler/opcodes.h
src/compiler/pipeline.cc
src/compiler/simplified-lowering.cc
src/compiler/typer.cc
src/compiler/verifier.cc
src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-codes-x64.h
src/compiler/x64/instruction-selector-x64.cc
src/ia32/assembler-ia32.cc
src/ia32/assembler-ia32.h
src/ia32/disasm-ia32.cc
src/x64/assembler-x64.cc
src/x64/assembler-x64.h
src/x64/disasm-x64.cc
test/cctest/test-disasm-ia32.cc
test/cctest/test-disasm-x64.cc
test/unittests/compiler/common-operator-reducer-unittest.cc
test/unittests/compiler/machine-operator-unittest.cc
test/unittests/compiler/node-test-utils.cc
test/unittests/compiler/node-test-utils.h

index 248444cd599c617caf29349b7226eee0511deae3..20276ede7e62364fb5c4f6bff3f6780007224f32 100644 (file)
@@ -1000,6 +1000,12 @@ void InstructionSelector::VisitFloat64Mod(Node* node) {
 }
 
 
+void InstructionSelector::VisitFloat64Max(Node* node) { UNREACHABLE(); }
+
+
+void InstructionSelector::VisitFloat64Min(Node* node) { UNREACHABLE(); }
+
+
 void InstructionSelector::VisitFloat64Sqrt(Node* node) {
   ArmOperandGenerator g(this);
   Emit(kArmVsqrtF64, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
index b8805078138c45e02077f90228d62b90ef301eba..7f8e9ea3bdd5d17522e0e72c1cf68ac468629053 100644 (file)
@@ -1069,6 +1069,12 @@ void InstructionSelector::VisitFloat64Mod(Node* node) {
 }
 
 
+void InstructionSelector::VisitFloat64Max(Node* node) { UNREACHABLE(); }
+
+
+void InstructionSelector::VisitFloat64Min(Node* node) { UNREACHABLE(); }
+
+
 void InstructionSelector::VisitFloat64Sqrt(Node* node) {
   VisitRRFloat64(this, kArm64Float64Sqrt, node);
 }
index c3cbcdefc7be70234035d507228c593cd049a638..b07383d21c5ed7b554273d24f98d933ab8808d98 100644 (file)
@@ -4,8 +4,12 @@
 
 #include "src/compiler/common-operator-reducer.h"
 
+#include <algorithm>
+
 #include "src/compiler/common-operator.h"
+#include "src/compiler/js-graph.h"
 #include "src/compiler/node.h"
+#include "src/compiler/node-matchers.h"
 
 namespace v8 {
 namespace internal {
@@ -14,29 +18,119 @@ namespace compiler {
 Reduction CommonOperatorReducer::Reduce(Node* node) {
   switch (node->opcode()) {
     case IrOpcode::kEffectPhi:
-    case IrOpcode::kPhi: {
-      int const input_count = node->InputCount();
-      if (input_count > 1) {
-        Node* const replacement = node->InputAt(0);
-        for (int i = 1; i < input_count - 1; ++i) {
-          if (node->InputAt(i) != replacement) return NoChange();
-        }
-        return Replace(replacement);
-      }
+      return ReduceEffectPhi(node);
+    case IrOpcode::kPhi:
+      return ReducePhi(node);
+    case IrOpcode::kSelect:
+      return ReduceSelect(node);
+    default:
       break;
+  }
+  return NoChange();
+}
+
+
+Reduction CommonOperatorReducer::ReduceEffectPhi(Node* node) {
+  DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
+  int const input_count = node->InputCount();
+  if (input_count > 1) {
+    Node* const replacement = node->InputAt(0);
+    for (int i = 1; i < input_count - 1; ++i) {
+      if (node->InputAt(i) != replacement) return NoChange();
     }
-    case IrOpcode::kSelect: {
-      if (node->InputAt(1) == node->InputAt(2)) {
-        return Replace(node->InputAt(1));
+    return Replace(replacement);
+  }
+  return NoChange();
+}
+
+
+Reduction CommonOperatorReducer::ReducePhi(Node* node) {
+  DCHECK_EQ(IrOpcode::kPhi, node->opcode());
+  int const input_count = node->InputCount();
+  if (input_count == 3) {
+    Node* vtrue = NodeProperties::GetValueInput(node, 0);
+    Node* vfalse = NodeProperties::GetValueInput(node, 1);
+    Node* merge = NodeProperties::GetControlInput(node);
+    Node* if_true = NodeProperties::GetControlInput(merge, 0);
+    Node* if_false = NodeProperties::GetControlInput(merge, 1);
+    if (if_true->opcode() != IrOpcode::kIfTrue) {
+      std::swap(if_true, if_false);
+      std::swap(vtrue, vfalse);
+    }
+    if (if_true->opcode() == IrOpcode::kIfTrue &&
+        if_false->opcode() == IrOpcode::kIfFalse &&
+        if_true->InputAt(0) == if_false->InputAt(0)) {
+      Node* branch = if_true->InputAt(0);
+      Node* cond = branch->InputAt(0);
+      if (cond->opcode() == IrOpcode::kFloat64LessThan) {
+        if (cond->InputAt(0) == vtrue && cond->InputAt(1) == vfalse &&
+            machine()->HasFloat64Min()) {
+          node->set_op(machine()->Float64Min());
+          node->ReplaceInput(0, vtrue);
+          node->ReplaceInput(1, vfalse);
+          node->TrimInputCount(2);
+          return Changed(node);
+        } else if (cond->InputAt(0) == vfalse && cond->InputAt(1) == vtrue &&
+                   machine()->HasFloat64Max()) {
+          node->set_op(machine()->Float64Max());
+          node->ReplaceInput(0, vtrue);
+          node->ReplaceInput(1, vfalse);
+          node->TrimInputCount(2);
+          return Changed(node);
+        }
       }
-      break;
     }
-    default:
-      break;
+  }
+  if (input_count > 1) {
+    Node* const replacement = node->InputAt(0);
+    for (int i = 1; i < input_count - 1; ++i) {
+      if (node->InputAt(i) != replacement) return NoChange();
+    }
+    return Replace(replacement);
+  }
+  return NoChange();
+}
+
+
+Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
+  DCHECK_EQ(IrOpcode::kSelect, node->opcode());
+  Node* cond = NodeProperties::GetValueInput(node, 0);
+  Node* vtrue = NodeProperties::GetValueInput(node, 1);
+  Node* vfalse = NodeProperties::GetValueInput(node, 2);
+  if (vtrue == vfalse) return Replace(vtrue);
+  if (cond->opcode() == IrOpcode::kFloat64LessThan) {
+    if (cond->InputAt(0) == vtrue && cond->InputAt(1) == vfalse &&
+        machine()->HasFloat64Min()) {
+      node->set_op(machine()->Float64Min());
+      node->ReplaceInput(0, vtrue);
+      node->ReplaceInput(1, vfalse);
+      node->TrimInputCount(2);
+      return Changed(node);
+    } else if (cond->InputAt(0) == vfalse && cond->InputAt(1) == vtrue &&
+               machine()->HasFloat64Max()) {
+      node->set_op(machine()->Float64Max());
+      node->ReplaceInput(0, vtrue);
+      node->ReplaceInput(1, vfalse);
+      node->TrimInputCount(2);
+      return Changed(node);
+    }
   }
   return NoChange();
 }
 
+
+CommonOperatorBuilder* CommonOperatorReducer::common() const {
+  return jsgraph()->common();
+}
+
+
+Graph* CommonOperatorReducer::graph() const { return jsgraph()->graph(); }
+
+
+MachineOperatorBuilder* CommonOperatorReducer::machine() const {
+  return jsgraph()->machine();
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index 10543db8c68234783ab21ce434627ea6c0eb65cc..dfcbe295bc47ed9c0fb5361bb2bfdc9574f2bd5a 100644 (file)
@@ -11,13 +11,32 @@ namespace v8 {
 namespace internal {
 namespace compiler {
 
+// Forward declarations.
+class CommonOperatorBuilder;
+class Graph;
+class JSGraph;
+class MachineOperatorBuilder;
+
+
 // Performs strength reduction on nodes that have common operators.
 class CommonOperatorReducer FINAL : public Reducer {
  public:
-  CommonOperatorReducer() {}
+  explicit CommonOperatorReducer(JSGraph* jsgraph) : jsgraph_(jsgraph) {}
   ~CommonOperatorReducer() FINAL {}
 
   Reduction Reduce(Node* node) FINAL;
+
+ private:
+  Reduction ReduceEffectPhi(Node* node);
+  Reduction ReducePhi(Node* node);
+  Reduction ReduceSelect(Node* node);
+
+  CommonOperatorBuilder* common() const;
+  Graph* graph() const;
+  JSGraph* jsgraph() const { return jsgraph_; }
+  MachineOperatorBuilder* machine() const;
+
+  JSGraph* const jsgraph_;
 };
 
 }  // namespace compiler
index 7a63ff044348f806b965f68491ed76a9d0757cbb..fe86e3aaac635e3d2b91e695ed30d94f245cd434 100644 (file)
@@ -462,6 +462,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kSSEFloat64Div:
       __ divsd(i.InputDoubleRegister(0), i.InputOperand(1));
       break;
+    case kSSEFloat64Max:
+      __ maxsd(i.InputDoubleRegister(0), i.InputOperand(1));
+      break;
+    case kSSEFloat64Min:
+      __ minsd(i.InputDoubleRegister(0), i.InputOperand(1));
+      break;
     case kSSEFloat64Mod: {
       // TODO(dcarney): alignment is wrong.
       __ sub(esp, Immediate(kDoubleSize));
@@ -567,6 +573,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
                 i.InputOperand(1));
       break;
     }
+    case kAVXFloat64Max: {
+      CpuFeatureScope avx_scope(masm(), AVX);
+      __ vmaxsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+                i.InputOperand(1));
+      break;
+    }
+    case kAVXFloat64Min: {
+      CpuFeatureScope avx_scope(masm(), AVX);
+      __ vminsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
+                i.InputOperand(1));
+      break;
+    }
     case kIA32Movsxbl:
       __ movsx_b(i.OutputRegister(), i.MemoryOperand());
       break;
index f9acab8b124ad44545d812e0c3220c867ca7835c..e3a7ac1f83ca7ec6b1fd7f864516e64126e1bea9 100644 (file)
@@ -36,6 +36,8 @@ namespace compiler {
   V(SSEFloat64Mul)                 \
   V(SSEFloat64Div)                 \
   V(SSEFloat64Mod)                 \
+  V(SSEFloat64Max)                 \
+  V(SSEFloat64Min)                 \
   V(SSEFloat64Sqrt)                \
   V(SSEFloat64Round)               \
   V(SSECvtss2sd)                   \
@@ -53,6 +55,8 @@ namespace compiler {
   V(AVXFloat64Sub)                 \
   V(AVXFloat64Mul)                 \
   V(AVXFloat64Div)                 \
+  V(AVXFloat64Max)                 \
+  V(AVXFloat64Min)                 \
   V(IA32Movsxbl)                   \
   V(IA32Movzxbl)                   \
   V(IA32Movb)                      \
index 6f15150f7f9b1d7619c66c72a2d54f22864d5bc8..8ec091a0f6b4776bb981d727507320c4e47ac64d 100644 (file)
@@ -702,6 +702,30 @@ void InstructionSelector::VisitFloat64Mod(Node* node) {
 }
 
 
+void InstructionSelector::VisitFloat64Max(Node* node) {
+  IA32OperandGenerator g(this);
+  if (IsSupported(AVX)) {
+    Emit(kAVXFloat64Max, g.DefineAsRegister(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  } else {
+    Emit(kSSEFloat64Max, g.DefineSameAsFirst(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  }
+}
+
+
+void InstructionSelector::VisitFloat64Min(Node* node) {
+  IA32OperandGenerator g(this);
+  if (IsSupported(AVX)) {
+    Emit(kAVXFloat64Min, g.DefineAsRegister(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  } else {
+    Emit(kSSEFloat64Min, g.DefineSameAsFirst(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  }
+}
+
+
 void InstructionSelector::VisitFloat64Sqrt(Node* node) {
   IA32OperandGenerator g(this);
   Emit(kSSEFloat64Sqrt, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
@@ -1135,6 +1159,8 @@ void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
 MachineOperatorBuilder::Flags
 InstructionSelector::SupportedMachineOperatorFlags() {
   MachineOperatorBuilder::Flags flags =
+      MachineOperatorBuilder::kFloat64Max |
+      MachineOperatorBuilder::kFloat64Min |
       MachineOperatorBuilder::kWord32ShiftIsSafe;
   if (CpuFeatures::IsSupported(SSE4_1)) {
     flags |= MachineOperatorBuilder::kFloat64RoundDown |
index 89c66aba3db1362d0bd549410ec2b6df97cc3a02..fb1fa2396685a60b106b1e423371b4f1936fc2c0 100644 (file)
@@ -689,6 +689,8 @@ MachineType InstructionSelector::GetMachineType(Node* node) {
     case IrOpcode::kFloat64Mul:
     case IrOpcode::kFloat64Div:
     case IrOpcode::kFloat64Mod:
+    case IrOpcode::kFloat64Max:
+    case IrOpcode::kFloat64Min:
     case IrOpcode::kFloat64Sqrt:
     case IrOpcode::kFloat64RoundDown:
     case IrOpcode::kFloat64RoundTruncate:
@@ -892,6 +894,10 @@ void InstructionSelector::VisitNode(Node* node) {
       return MarkAsDouble(node), VisitFloat64Div(node);
     case IrOpcode::kFloat64Mod:
       return MarkAsDouble(node), VisitFloat64Mod(node);
+    case IrOpcode::kFloat64Min:
+      return MarkAsDouble(node), VisitFloat64Min(node);
+    case IrOpcode::kFloat64Max:
+      return MarkAsDouble(node), VisitFloat64Max(node);
     case IrOpcode::kFloat64Sqrt:
       return MarkAsDouble(node), VisitFloat64Sqrt(node);
     case IrOpcode::kFloat64Equal:
index 0b6d3903babfd8d0b306b9fcf2cf1bf656295353..2351223890e7d266c55c18883795e48a2406daec 100644 (file)
@@ -133,6 +133,8 @@ CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const* op) {
   V(Float64ExtractHighWord32, Operator::kNoProperties, 1, 0, 1)               \
   V(Float64InsertLowWord32, Operator::kNoProperties, 2, 0, 1)                 \
   V(Float64InsertHighWord32, Operator::kNoProperties, 2, 0, 1)                \
+  V(Float64Max, Operator::kNoProperties, 2, 0, 1)                             \
+  V(Float64Min, Operator::kNoProperties, 2, 0, 1)                             \
   V(LoadStackPointer, Operator::kNoProperties, 0, 0, 1)
 
 
index a92ce39536f6bc1669cbb41cf04cf4fac2584638..409f033cc8cb6594e7069cc537724e89336bdd44 100644 (file)
@@ -74,12 +74,14 @@ class MachineOperatorBuilder FINAL : public ZoneObject {
   // for operations that are unsupported by some back-ends.
   enum Flag {
     kNoFlags = 0u,
-    kFloat64RoundDown = 1u << 0,
-    kFloat64RoundTruncate = 1u << 1,
-    kFloat64RoundTiesAway = 1u << 2,
-    kInt32DivIsSafe = 1u << 3,
-    kUint32DivIsSafe = 1u << 4,
-    kWord32ShiftIsSafe = 1u << 5
+    kFloat64Max = 1u << 0,
+    kFloat64Min = 1u << 1,
+    kFloat64RoundDown = 1u << 2,
+    kFloat64RoundTruncate = 1u << 3,
+    kFloat64RoundTiesAway = 1u << 4,
+    kInt32DivIsSafe = 1u << 5,
+    kUint32DivIsSafe = 1u << 6,
+    kWord32ShiftIsSafe = 1u << 7
   };
   typedef base::Flags<Flag, unsigned> Flags;
 
@@ -166,6 +168,12 @@ class MachineOperatorBuilder FINAL : public ZoneObject {
   const Operator* Float64LessThan();
   const Operator* Float64LessThanOrEqual();
 
+  // Floating point min/max complying to IEEE 754.
+  const Operator* Float64Max();
+  const Operator* Float64Min();
+  bool HasFloat64Max() { return flags_ & kFloat64Max; }
+  bool HasFloat64Min() { return flags_ & kFloat64Min; }
+
   // Floating point rounding.
   const Operator* Float64RoundDown();
   const Operator* Float64RoundTruncate();
index 27d19cecbc24a9211ff0eccfa69f0367779c3a08..99ea8e48ec832b88cd35303d1e11c9fb48ad3fd0 100644 (file)
   V(Float64Mul)                 \
   V(Float64Div)                 \
   V(Float64Mod)                 \
+  V(Float64Max)                 \
+  V(Float64Min)                 \
   V(Float64Sqrt)                \
   V(Float64RoundDown)           \
   V(Float64RoundTruncate)       \
index a96e6bd34f74ebf0cad023926a2ccb19d2bee25a..247478f5dda639436f6341d4152fb867448f9a40 100644 (file)
@@ -494,7 +494,7 @@ struct TypedLoweringPhase {
     JSTypedLowering typed_lowering(data->jsgraph(), temp_zone);
     JSIntrinsicLowering intrinsic_lowering(data->jsgraph());
     SimplifiedOperatorReducer simple_reducer(data->jsgraph());
-    CommonOperatorReducer common_reducer;
+    CommonOperatorReducer common_reducer(data->jsgraph());
     GraphReducer graph_reducer(data->graph(), temp_zone);
     AddReducer(data, &graph_reducer, &vn_reducer);
     AddReducer(data, &graph_reducer, &builtin_reducer);
@@ -520,7 +520,7 @@ struct SimplifiedLoweringPhase {
     ValueNumberingReducer vn_reducer(temp_zone);
     SimplifiedOperatorReducer simple_reducer(data->jsgraph());
     MachineOperatorReducer machine_reducer(data->jsgraph());
-    CommonOperatorReducer common_reducer;
+    CommonOperatorReducer common_reducer(data->jsgraph());
     GraphReducer graph_reducer(data->graph(), temp_zone);
     AddReducer(data, &graph_reducer, &vn_reducer);
     AddReducer(data, &graph_reducer, &simple_reducer);
@@ -553,7 +553,7 @@ struct ChangeLoweringPhase {
     SimplifiedOperatorReducer simple_reducer(data->jsgraph());
     ChangeLowering lowering(data->jsgraph());
     MachineOperatorReducer machine_reducer(data->jsgraph());
-    CommonOperatorReducer common_reducer;
+    CommonOperatorReducer common_reducer(data->jsgraph());
     GraphReducer graph_reducer(data->graph(), temp_zone);
     AddReducer(data, &graph_reducer, &vn_reducer);
     AddReducer(data, &graph_reducer, &simple_reducer);
index 49c4985e66779e5837b914244396837db7d43624..1d6320ee3541a88aac7a7e90e024ddd2b835cf58 100644 (file)
@@ -1028,6 +1028,7 @@ class RepresentationSelector {
       case IrOpcode::kFloat64Mul:
       case IrOpcode::kFloat64Div:
       case IrOpcode::kFloat64Mod:
+      case IrOpcode::kFloat64Min:
         return VisitFloat64Binop(node);
       case IrOpcode::kFloat64Sqrt:
       case IrOpcode::kFloat64RoundDown:
index 9934741767140ae4fff9b878d5385edcb7c9c736..3f9b105a7133f47bf9ed6449432ba15dd3c620ac 100644 (file)
@@ -2066,6 +2066,16 @@ Bounds Typer::Visitor::TypeFloat64Mod(Node* node) {
 }
 
 
+Bounds Typer::Visitor::TypeFloat64Max(Node* node) {
+  return Bounds(Type::Number());
+}
+
+
+Bounds Typer::Visitor::TypeFloat64Min(Node* node) {
+  return Bounds(Type::Number());
+}
+
+
 Bounds Typer::Visitor::TypeFloat64Sqrt(Node* node) {
   return Bounds(Type::Number());
 }
index e066d21ceae6537a27af1e9b853edc0f73c669df..46cf780627c9da8c96e63c4425e8bda5d5df72fe 100644 (file)
@@ -792,6 +792,8 @@ void Verifier::Visitor::Check(Node* node) {
     case IrOpcode::kFloat64Mul:
     case IrOpcode::kFloat64Div:
     case IrOpcode::kFloat64Mod:
+    case IrOpcode::kFloat64Max:
+    case IrOpcode::kFloat64Min:
     case IrOpcode::kFloat64Sqrt:
     case IrOpcode::kFloat64RoundDown:
     case IrOpcode::kFloat64RoundTruncate:
index 9d946b8094291b1e2143f241d2fa9531189e309d..0027d49bec5a6f443568e4392ac761ca4c1bd6d3 100644 (file)
@@ -741,6 +741,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ addq(rsp, Immediate(kDoubleSize));
       break;
     }
+    case kSSEFloat64Max:
+      ASSEMBLE_DOUBLE_BINOP(maxsd);
+      break;
+    case kSSEFloat64Min:
+      ASSEMBLE_DOUBLE_BINOP(minsd);
+      break;
     case kSSEFloat64Sqrt:
       if (instr->InputAt(0)->IsDoubleRegister()) {
         __ sqrtsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
@@ -847,6 +853,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
     case kAVXFloat64Div:
       ASSEMBLE_AVX_DOUBLE_BINOP(vdivsd);
       break;
+    case kAVXFloat64Max:
+      ASSEMBLE_AVX_DOUBLE_BINOP(vmaxsd);
+      break;
+    case kAVXFloat64Min:
+      ASSEMBLE_AVX_DOUBLE_BINOP(vminsd);
+      break;
     case kX64Movsxbl:
       ASSEMBLE_MOVX(movsxbl);
       __ AssertZeroExtended(i.OutputRegister());
index 3bdba127d8b9834ae97a4e10442f49a7f8dd90b1..4c9d335353585deae9b5d2620e8f0361de471216 100644 (file)
@@ -54,6 +54,8 @@ namespace compiler {
   V(SSEFloat64Mod)                 \
   V(SSEFloat64Sqrt)                \
   V(SSEFloat64Round)               \
+  V(SSEFloat64Max)                 \
+  V(SSEFloat64Min)                 \
   V(SSECvtss2sd)                   \
   V(SSECvtsd2ss)                   \
   V(SSEFloat64ToInt32)             \
@@ -69,6 +71,8 @@ namespace compiler {
   V(AVXFloat64Sub)                 \
   V(AVXFloat64Mul)                 \
   V(AVXFloat64Div)                 \
+  V(AVXFloat64Max)                 \
+  V(AVXFloat64Min)                 \
   V(X64Movsxbl)                    \
   V(X64Movzxbl)                    \
   V(X64Movb)                       \
index 50fe0df1904b8cb77ae3b476d42233e12252d221..ac06da397a52751445addbca8144eeaaac2c60a7 100644 (file)
@@ -900,6 +900,30 @@ void InstructionSelector::VisitFloat64Mod(Node* node) {
 }
 
 
+void InstructionSelector::VisitFloat64Max(Node* node) {
+  X64OperandGenerator g(this);
+  if (IsSupported(AVX)) {
+    Emit(kAVXFloat64Max, g.DefineAsRegister(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  } else {
+    Emit(kSSEFloat64Max, g.DefineSameAsFirst(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  }
+}
+
+
+void InstructionSelector::VisitFloat64Min(Node* node) {
+  X64OperandGenerator g(this);
+  if (IsSupported(AVX)) {
+    Emit(kAVXFloat64Min, g.DefineAsRegister(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  } else {
+    Emit(kSSEFloat64Min, g.DefineSameAsFirst(node),
+         g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
+  }
+}
+
+
 void InstructionSelector::VisitFloat64Sqrt(Node* node) {
   X64OperandGenerator g(this);
   Emit(kSSEFloat64Sqrt, g.DefineAsRegister(node), g.Use(node->InputAt(0)));
@@ -1449,6 +1473,8 @@ void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
 MachineOperatorBuilder::Flags
 InstructionSelector::SupportedMachineOperatorFlags() {
   MachineOperatorBuilder::Flags flags =
+      MachineOperatorBuilder::kFloat64Max |
+      MachineOperatorBuilder::kFloat64Min |
       MachineOperatorBuilder::kWord32ShiftIsSafe;
   if (CpuFeatures::IsSupported(SSE4_1)) {
     flags |= MachineOperatorBuilder::kFloat64RoundDown |
index 05d1bdd4f2178c73885a431aa5142ec276469d22..2a3384d68d6fbe965a1285e34108715163c45e69 100644 (file)
@@ -2235,6 +2235,24 @@ void Assembler::punpckhdq(XMMRegister dst, XMMRegister src) {
 }
 
 
+void Assembler::maxsd(XMMRegister dst, const Operand& src) {
+  EnsureSpace ensure_space(this);
+  EMIT(0xF2);
+  EMIT(0x0F);
+  EMIT(0x5F);
+  emit_sse_operand(dst, src);
+}
+
+
+void Assembler::minsd(XMMRegister dst, const Operand& src) {
+  EnsureSpace ensure_space(this);
+  EMIT(0xF2);
+  EMIT(0x0F);
+  EMIT(0x5D);
+  emit_sse_operand(dst, src);
+}
+
+
 void Assembler::cmpltsd(XMMRegister dst, XMMRegister src) {
   EnsureSpace ensure_space(this);
   EMIT(0xF2);
index 31a0395b27d513962672c27c9f9568af82fa9acc..7edcf527f2ee03aeb2d70be2ccbb31752f7f7fcb 100644 (file)
@@ -1022,6 +1022,11 @@ class Assembler : public AssemblerBase {
   void punpckldq(XMMRegister dst, XMMRegister src);
   void punpckhdq(XMMRegister dst, XMMRegister src);
 
+  void maxsd(XMMRegister dst, XMMRegister src) { maxsd(dst, Operand(src)); }
+  void maxsd(XMMRegister dst, const Operand& src);
+  void minsd(XMMRegister dst, XMMRegister src) { minsd(dst, Operand(src)); }
+  void minsd(XMMRegister dst, const Operand& src);
+
   void movdqa(XMMRegister dst, const Operand& src);
   void movdqa(const Operand& dst, XMMRegister src);
   void movdqu(XMMRegister dst, const Operand& src);
@@ -1246,6 +1251,18 @@ class Assembler : public AssemblerBase {
   void vdivsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
     vsd(0x5e, dst, src1, src2);
   }
+  void vmaxsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
+    vmaxsd(dst, src1, Operand(src2));
+  }
+  void vmaxsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
+    vsd(0x5f, dst, src1, src2);
+  }
+  void vminsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
+    vminsd(dst, src1, Operand(src2));
+  }
+  void vminsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
+    vsd(0x5d, dst, src1, src2);
+  }
   void vsd(byte op, XMMRegister dst, XMMRegister src1, const Operand& src2);
 
   // Prefetch src position into cache level.
index 08ffdde5b4e7632e82d5973975c47f8ac40fd25e..f1fba341c67161827437538c604b5f19bf5e24d8 100644 (file)
@@ -826,11 +826,21 @@ int DisassemblerIA32::AVXInstruction(byte* data) {
                        NameOfXMMRegister(vvvv));
         current += PrintRightXMMOperand(current);
         break;
+      case 0x5d:
+        AppendToBuffer("vminsd %s,%s,", NameOfXMMRegister(regop),
+                       NameOfXMMRegister(vvvv));
+        current += PrintRightXMMOperand(current);
+        break;
       case 0x5e:
         AppendToBuffer("vdivsd %s,%s,", NameOfXMMRegister(regop),
                        NameOfXMMRegister(vvvv));
         current += PrintRightXMMOperand(current);
         break;
+      case 0x5f:
+        AppendToBuffer("vmaxsd %s,%s,", NameOfXMMRegister(regop),
+                       NameOfXMMRegister(vvvv));
+        current += PrintRightXMMOperand(current);
+        break;
       default:
         UnimplementedInstruction();
     }
@@ -1758,7 +1768,13 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
               case 0x58: mnem = "addsd"; break;
               case 0x59: mnem = "mulsd"; break;
               case 0x5C: mnem = "subsd"; break;
+              case 0x5D:
+                mnem = "minsd";
+                break;
               case 0x5E: mnem = "divsd"; break;
+              case 0x5F:
+                mnem = "maxsd";
+                break;
             }
             data += 3;
             int mod, regop, rm;
index 2873e73db07ddf3bc013307d3ecc9a8d77d289d2..1eb054772356d76e87092a3a0f3fda0ca3ed927b 100644 (file)
@@ -3314,6 +3314,46 @@ void Assembler::punpckhdq(XMMRegister dst, XMMRegister src) {
 }
 
 
+void Assembler::maxsd(XMMRegister dst, XMMRegister src) {
+  EnsureSpace ensure_space(this);
+  emit(0xF2);
+  emit_optional_rex_32(dst, src);
+  emit(0x0F);
+  emit(0x5F);
+  emit_sse_operand(dst, src);
+}
+
+
+void Assembler::maxsd(XMMRegister dst, const Operand& src) {
+  EnsureSpace ensure_space(this);
+  emit(0xF2);
+  emit_optional_rex_32(dst, src);
+  emit(0x0F);
+  emit(0x5F);
+  emit_sse_operand(dst, src);
+}
+
+
+void Assembler::minsd(XMMRegister dst, XMMRegister src) {
+  EnsureSpace ensure_space(this);
+  emit(0xF2);
+  emit_optional_rex_32(dst, src);
+  emit(0x0F);
+  emit(0x5D);
+  emit_sse_operand(dst, src);
+}
+
+
+void Assembler::minsd(XMMRegister dst, const Operand& src) {
+  EnsureSpace ensure_space(this);
+  emit(0xF2);
+  emit_optional_rex_32(dst, src);
+  emit(0x0F);
+  emit(0x5D);
+  emit_sse_operand(dst, src);
+}
+
+
 // AVX instructions
 void Assembler::vfmasd(byte op, XMMRegister dst, XMMRegister src1,
                        XMMRegister src2) {
index 87ec1eb6b6b1529361f2e6680a849bc4d3be4399..bf3572dc548c3e2ee99867471a5a89840294c8ca 100644 (file)
@@ -1144,6 +1144,11 @@ class Assembler : public AssemblerBase {
   void punpckldq(XMMRegister dst, XMMRegister src);
   void punpckhdq(XMMRegister dst, XMMRegister src);
 
+  void maxsd(XMMRegister dst, XMMRegister src);
+  void maxsd(XMMRegister dst, const Operand& src);
+  void minsd(XMMRegister dst, XMMRegister src);
+  void minsd(XMMRegister dst, const Operand& src);
+
   // SSE 4.1 instruction
   void extractps(Register dst, XMMRegister src, byte imm8);
 
@@ -1329,6 +1334,18 @@ class Assembler : public AssemblerBase {
   void vdivsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
     vsd(0x5e, dst, src1, src2);
   }
+  void vmaxsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
+    vsd(0x5f, dst, src1, src2);
+  }
+  void vmaxsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
+    vsd(0x5f, dst, src1, src2);
+  }
+  void vminsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
+    vsd(0x5d, dst, src1, src2);
+  }
+  void vminsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
+    vsd(0x5d, dst, src1, src2);
+  }
   void vsd(byte op, XMMRegister dst, XMMRegister src1, XMMRegister src2);
   void vsd(byte op, XMMRegister dst, XMMRegister src1, const Operand& src2);
 
index ba33da779caaef08b780a30712d150d881c9eb3c..4f5e74fa833075f24ba97ac44d773d2abb48833c 100644 (file)
@@ -956,11 +956,21 @@ int DisassemblerX64::AVXInstruction(byte* data) {
                        NameOfXMMRegister(vvvv));
         current += PrintRightXMMOperand(current);
         break;
+      case 0x5d:
+        AppendToBuffer("vminsd %s,%s,", NameOfXMMRegister(regop),
+                       NameOfXMMRegister(vvvv));
+        current += PrintRightXMMOperand(current);
+        break;
       case 0x5e:
         AppendToBuffer("vdivsd %s,%s,", NameOfXMMRegister(regop),
                        NameOfXMMRegister(vvvv));
         current += PrintRightXMMOperand(current);
         break;
+      case 0x5f:
+        AppendToBuffer("vmaxsd %s,%s,", NameOfXMMRegister(regop),
+                       NameOfXMMRegister(vvvv));
+        current += PrintRightXMMOperand(current);
+        break;
       default:
         UnimplementedInstruction();
     }
@@ -1543,10 +1553,14 @@ const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
       return "mulsd";
     case 0x5A:  // F2 prefix.
       return "cvtsd2ss";
+    case 0x5D:  // F2 prefix.
+      return "minsd";
     case 0x5C:  // F2 prefix.
       return "subsd";
     case 0x5E:  // F2 prefix.
       return "divsd";
+    case 0x5F:  // F2 prefix.
+      return "maxsd";
     case 0xA2:
       return "cpuid";
     case 0xA5:
index 215ca76c1d8f5a391328464571845d6dd8bf0809..16b9007fbdd56257f74ec84107d4154c40022b10 100644 (file)
@@ -442,6 +442,10 @@ TEST(DisasmIa320) {
     __ subsd(xmm1, Operand(ebx, ecx, times_4, 10000));
     __ divsd(xmm1, xmm0);
     __ divsd(xmm1, Operand(ebx, ecx, times_4, 10000));
+    __ minsd(xmm1, xmm0);
+    __ minsd(xmm1, Operand(ebx, ecx, times_4, 10000));
+    __ maxsd(xmm1, xmm0);
+    __ maxsd(xmm1, Operand(ebx, ecx, times_4, 10000));
     __ ucomisd(xmm0, xmm1);
     __ cmpltsd(xmm0, xmm1);
 
@@ -499,6 +503,10 @@ TEST(DisasmIa320) {
       __ vsubsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
       __ vdivsd(xmm0, xmm1, xmm2);
       __ vdivsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+      __ vminsd(xmm0, xmm1, xmm2);
+      __ vminsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
+      __ vmaxsd(xmm0, xmm1, xmm2);
+      __ vmaxsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
     }
   }
 
index 3b78b640ae5d2719759a3e51570eeec812b6911d..8986ba76a0d2a7df3289754f0f4517bdb61e7ade 100644 (file)
@@ -436,6 +436,10 @@ TEST(DisasmX64) {
     __ subsd(xmm1, Operand(rbx, rcx, times_4, 10000));
     __ divsd(xmm1, xmm0);
     __ divsd(xmm1, Operand(rbx, rcx, times_4, 10000));
+    __ minsd(xmm1, xmm0);
+    __ minsd(xmm1, Operand(rbx, rcx, times_4, 10000));
+    __ maxsd(xmm1, xmm0);
+    __ maxsd(xmm1, Operand(rbx, rcx, times_4, 10000));
     __ ucomisd(xmm0, xmm1);
 
     __ andpd(xmm0, xmm1);
@@ -493,7 +497,11 @@ TEST(DisasmX64) {
       __ vsubsd(xmm0, xmm1, xmm2);
       __ vsubsd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
       __ vdivsd(xmm0, xmm1, xmm2);
-      __ vdivsd(xmm0, xmm1, Operand(rbx, rcx, times_4, 10000));
+      __ vdivsd(xmm0, xmm1, Operand(rbx, rcx, times_2, 10000));
+      __ vminsd(xmm8, xmm1, xmm2);
+      __ vminsd(xmm9, xmm1, Operand(rbx, rcx, times_8, 10000));
+      __ vmaxsd(xmm8, xmm1, xmm2);
+      __ vmaxsd(xmm9, xmm1, Operand(rbx, rcx, times_1, 10000));
     }
   }
 
index 1f6044b97cc5c4b8cf318404b49b0e1a582d7cb0..3b60e5b9bd32b2218b1fe33e6af30bccbbae61ad 100644 (file)
@@ -4,9 +4,13 @@
 
 #include "src/compiler/common-operator.h"
 #include "src/compiler/common-operator-reducer.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/machine-operator.h"
 #include "src/compiler/machine-type.h"
 #include "src/compiler/operator.h"
 #include "test/unittests/compiler/graph-unittest.h"
+#include "test/unittests/compiler/node-test-utils.h"
 
 namespace v8 {
 namespace internal {
@@ -15,14 +19,23 @@ namespace compiler {
 class CommonOperatorReducerTest : public GraphTest {
  public:
   explicit CommonOperatorReducerTest(int num_parameters = 1)
-      : GraphTest(num_parameters) {}
+      : GraphTest(num_parameters), machine_(zone()) {}
   ~CommonOperatorReducerTest() OVERRIDE {}
 
  protected:
-  Reduction Reduce(Node* node) {
-    CommonOperatorReducer reducer;
+  Reduction Reduce(Node* node, MachineOperatorBuilder::Flags flags =
+                                   MachineOperatorBuilder::kNoFlags) {
+    JSOperatorBuilder javascript(zone());
+    MachineOperatorBuilder machine(zone(), kMachPtr, flags);
+    JSGraph jsgraph(isolate(), graph(), common(), &javascript, &machine);
+    CommonOperatorReducer reducer(&jsgraph);
     return reducer.Reduce(node);
   }
+
+  MachineOperatorBuilder* machine() { return &machine_; }
+
+ private:
+  MachineOperatorBuilder machine_;
 };
 
 
@@ -77,10 +90,15 @@ TEST_F(CommonOperatorReducerTest, RedundantPhi) {
   TRACED_FORRANGE(int, input_count, 2, kMaxInputs - 1) {
     int const value_input_count = input_count - 1;
     TRACED_FOREACH(MachineType, type, kMachineTypes) {
+      for (int i = 0; i < value_input_count; ++i) {
+        inputs[i] = graph()->start();
+      }
+      Node* merge = graph()->NewNode(common()->Merge(value_input_count),
+                                     value_input_count, inputs);
       for (int i = 0; i < value_input_count; ++i) {
         inputs[i] = input;
       }
-      inputs[value_input_count] = graph()->start();
+      inputs[value_input_count] = merge;
       Reduction r = Reduce(graph()->NewNode(
           common()->Phi(type, value_input_count), input_count, inputs));
       ASSERT_TRUE(r.Changed());
@@ -90,6 +108,27 @@ TEST_F(CommonOperatorReducerTest, RedundantPhi) {
 }
 
 
+TEST_F(CommonOperatorReducerTest, PhiToFloat64MaxOrFloat64Min) {
+  Node* p0 = Parameter(0);
+  Node* p1 = Parameter(1);
+  Node* check = graph()->NewNode(machine()->Float64LessThan(), p0, p1);
+  Node* branch = graph()->NewNode(common()->Branch(), check, graph()->start());
+  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
+  Reduction r1 =
+      Reduce(graph()->NewNode(common()->Phi(kMachFloat64, 2), p1, p0, merge),
+             MachineOperatorBuilder::kFloat64Max);
+  ASSERT_TRUE(r1.Changed());
+  EXPECT_THAT(r1.replacement(), IsFloat64Max(p1, p0));
+  Reduction r2 =
+      Reduce(graph()->NewNode(common()->Phi(kMachFloat64, 2), p0, p1, merge),
+             MachineOperatorBuilder::kFloat64Min);
+  ASSERT_TRUE(r2.Changed());
+  EXPECT_THAT(r2.replacement(), IsFloat64Min(p0, p1));
+}
+
+
 // -----------------------------------------------------------------------------
 // Select
 
@@ -106,6 +145,23 @@ TEST_F(CommonOperatorReducerTest, RedundantSelect) {
   }
 }
 
+
+TEST_F(CommonOperatorReducerTest, SelectToFloat64MaxOrFloat64Min) {
+  Node* p0 = Parameter(0);
+  Node* p1 = Parameter(1);
+  Node* check = graph()->NewNode(machine()->Float64LessThan(), p0, p1);
+  Reduction r1 =
+      Reduce(graph()->NewNode(common()->Select(kMachFloat64), check, p1, p0),
+             MachineOperatorBuilder::kFloat64Max);
+  ASSERT_TRUE(r1.Changed());
+  EXPECT_THAT(r1.replacement(), IsFloat64Max(p1, p0));
+  Reduction r2 =
+      Reduce(graph()->NewNode(common()->Select(kMachFloat64), check, p0, p1),
+             MachineOperatorBuilder::kFloat64Min);
+  ASSERT_TRUE(r2.Changed());
+  EXPECT_THAT(r2.replacement(), IsFloat64Min(p0, p1));
+}
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index fcdb0aeef67414a673d01dc8b6caa47b53bc3f83..8244c3791412b35df3e9250f892491e4ac49548c 100644 (file)
@@ -209,7 +209,8 @@ const PureOperator kPureOperators[] = {
     PURE(Float64RoundTiesAway, 1, 0, 1), PURE(Float64ExtractLowWord32, 1, 0, 1),
     PURE(Float64ExtractHighWord32, 1, 0, 1),
     PURE(Float64InsertLowWord32, 2, 0, 1),
-    PURE(Float64InsertHighWord32, 2, 0, 1)
+    PURE(Float64InsertHighWord32, 2, 0, 1), PURE(Float64Max, 2, 0, 1),
+    PURE(Float64Min, 2, 0, 1)
 #undef PURE
 };
 
index 715fe56d5c50b4b136dcd608270cacf9ad8dd788..a84a3f1da9ff1d8a0dccbbf2ea7711e074fe30db 100644 (file)
@@ -1590,6 +1590,8 @@ IS_BINOP_MATCHER(Int32MulHigh)
 IS_BINOP_MATCHER(Int32LessThan)
 IS_BINOP_MATCHER(Uint32LessThan)
 IS_BINOP_MATCHER(Uint32LessThanOrEqual)
+IS_BINOP_MATCHER(Float64Max)
+IS_BINOP_MATCHER(Float64Min)
 IS_BINOP_MATCHER(Float64Sub)
 IS_BINOP_MATCHER(Float64InsertLowWord32)
 IS_BINOP_MATCHER(Float64InsertHighWord32)
index a941ddbc0f0e44cdd17dfdbe5f848f4c9117e3a2..0cf34d7473567a8d529a2b1adc8bfa762ab5ec42 100644 (file)
@@ -203,6 +203,10 @@ Matcher<Node*> IsChangeUint32ToUint64(const Matcher<Node*>& input_matcher);
 Matcher<Node*> IsTruncateFloat64ToFloat32(const Matcher<Node*>& input_matcher);
 Matcher<Node*> IsTruncateFloat64ToInt32(const Matcher<Node*>& input_matcher);
 Matcher<Node*> IsTruncateInt64ToInt32(const Matcher<Node*>& input_matcher);
+Matcher<Node*> IsFloat64Max(const Matcher<Node*>& lhs_matcher,
+                            const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsFloat64Min(const Matcher<Node*>& lhs_matcher,
+                            const Matcher<Node*>& rhs_matcher);
 Matcher<Node*> IsFloat64Sub(const Matcher<Node*>& lhs_matcher,
                             const Matcher<Node*>& rhs_matcher);
 Matcher<Node*> IsFloat64Sqrt(const Matcher<Node*>& input_matcher);