From 99f8d57f3c5d967bc3b10356b22d7ec7be55e89f Mon Sep 17 00:00:00 2001 From: bmeurer Date: Thu, 12 Mar 2015 07:07:28 -0700 Subject: [PATCH] [turbofan] Introduce optional Float64Min and Float64Max machine operators. 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} --- src/compiler/arm/instruction-selector-arm.cc | 6 + src/compiler/arm64/instruction-selector-arm64.cc | 6 + src/compiler/common-operator-reducer.cc | 124 ++++++++++++++++++--- src/compiler/common-operator-reducer.h | 21 +++- src/compiler/ia32/code-generator-ia32.cc | 18 +++ src/compiler/ia32/instruction-codes-ia32.h | 4 + src/compiler/ia32/instruction-selector-ia32.cc | 26 +++++ src/compiler/instruction-selector.cc | 6 + src/compiler/machine-operator.cc | 2 + src/compiler/machine-operator.h | 20 +++- src/compiler/opcodes.h | 2 + src/compiler/pipeline.cc | 6 +- src/compiler/simplified-lowering.cc | 1 + src/compiler/typer.cc | 10 ++ src/compiler/verifier.cc | 2 + src/compiler/x64/code-generator-x64.cc | 12 ++ src/compiler/x64/instruction-codes-x64.h | 4 + src/compiler/x64/instruction-selector-x64.cc | 26 +++++ src/ia32/assembler-ia32.cc | 18 +++ src/ia32/assembler-ia32.h | 17 +++ src/ia32/disasm-ia32.cc | 16 +++ src/x64/assembler-x64.cc | 40 +++++++ src/x64/assembler-x64.h | 17 +++ src/x64/disasm-x64.cc | 14 +++ test/cctest/test-disasm-ia32.cc | 8 ++ test/cctest/test-disasm-x64.cc | 10 +- .../compiler/common-operator-reducer-unittest.cc | 64 ++++++++++- .../compiler/machine-operator-unittest.cc | 3 +- test/unittests/compiler/node-test-utils.cc | 2 + test/unittests/compiler/node-test-utils.h | 4 + 30 files changed, 478 insertions(+), 31 deletions(-) diff --git a/src/compiler/arm/instruction-selector-arm.cc b/src/compiler/arm/instruction-selector-arm.cc index 248444c..20276ed 100644 --- a/src/compiler/arm/instruction-selector-arm.cc +++ b/src/compiler/arm/instruction-selector-arm.cc @@ -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))); diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index b880507..7f8e9ea 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -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); } diff --git a/src/compiler/common-operator-reducer.cc b/src/compiler/common-operator-reducer.cc index c3cbcde..b07383d 100644 --- a/src/compiler/common-operator-reducer.cc +++ b/src/compiler/common-operator-reducer.cc @@ -4,8 +4,12 @@ #include "src/compiler/common-operator-reducer.h" +#include + #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 diff --git a/src/compiler/common-operator-reducer.h b/src/compiler/common-operator-reducer.h index 10543db..dfcbe29 100644 --- a/src/compiler/common-operator-reducer.h +++ b/src/compiler/common-operator-reducer.h @@ -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 diff --git a/src/compiler/ia32/code-generator-ia32.cc b/src/compiler/ia32/code-generator-ia32.cc index 7a63ff0..fe86e3a 100644 --- a/src/compiler/ia32/code-generator-ia32.cc +++ b/src/compiler/ia32/code-generator-ia32.cc @@ -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; diff --git a/src/compiler/ia32/instruction-codes-ia32.h b/src/compiler/ia32/instruction-codes-ia32.h index f9acab8..e3a7ac1 100644 --- a/src/compiler/ia32/instruction-codes-ia32.h +++ b/src/compiler/ia32/instruction-codes-ia32.h @@ -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) \ diff --git a/src/compiler/ia32/instruction-selector-ia32.cc b/src/compiler/ia32/instruction-selector-ia32.cc index 6f15150..8ec091a 100644 --- a/src/compiler/ia32/instruction-selector-ia32.cc +++ b/src/compiler/ia32/instruction-selector-ia32.cc @@ -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 | diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index 89c66ab..fb1fa23 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -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: diff --git a/src/compiler/machine-operator.cc b/src/compiler/machine-operator.cc index 0b6d390..2351223 100644 --- a/src/compiler/machine-operator.cc +++ b/src/compiler/machine-operator.cc @@ -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) diff --git a/src/compiler/machine-operator.h b/src/compiler/machine-operator.h index a92ce39..409f033 100644 --- a/src/compiler/machine-operator.h +++ b/src/compiler/machine-operator.h @@ -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 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(); diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index 27d19ce..99ea8e4 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -248,6 +248,8 @@ V(Float64Mul) \ V(Float64Div) \ V(Float64Mod) \ + V(Float64Max) \ + V(Float64Min) \ V(Float64Sqrt) \ V(Float64RoundDown) \ V(Float64RoundTruncate) \ diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc index a96e6bd..247478f 100644 --- a/src/compiler/pipeline.cc +++ b/src/compiler/pipeline.cc @@ -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); diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 49c4985..1d6320e 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -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: diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index 9934741..3f9b105 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -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()); } diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index e066d21..46cf780 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -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: diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc index 9d946b8..0027d49 100644 --- a/src/compiler/x64/code-generator-x64.cc +++ b/src/compiler/x64/code-generator-x64.cc @@ -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()); diff --git a/src/compiler/x64/instruction-codes-x64.h b/src/compiler/x64/instruction-codes-x64.h index 3bdba12..4c9d335 100644 --- a/src/compiler/x64/instruction-codes-x64.h +++ b/src/compiler/x64/instruction-codes-x64.h @@ -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) \ diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc index 50fe0df..ac06da3 100644 --- a/src/compiler/x64/instruction-selector-x64.cc +++ b/src/compiler/x64/instruction-selector-x64.cc @@ -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 | diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 05d1bdd..2a3384d 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -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); diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index 31a0395..7edcf52 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -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. diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc index 08ffdde..f1fba34 100644 --- a/src/ia32/disasm-ia32.cc +++ b/src/ia32/disasm-ia32.cc @@ -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 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; diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 2873e73..1eb0547 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -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) { diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 87ec1eb..bf3572d 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -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); diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc index ba33da7..4f5e74f 100644 --- a/src/x64/disasm-x64.cc +++ b/src/x64/disasm-x64.cc @@ -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: diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc index 215ca76..16b9007 100644 --- a/test/cctest/test-disasm-ia32.cc +++ b/test/cctest/test-disasm-ia32.cc @@ -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)); } } diff --git a/test/cctest/test-disasm-x64.cc b/test/cctest/test-disasm-x64.cc index 3b78b64..8986ba7 100644 --- a/test/cctest/test-disasm-x64.cc +++ b/test/cctest/test-disasm-x64.cc @@ -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)); } } diff --git a/test/unittests/compiler/common-operator-reducer-unittest.cc b/test/unittests/compiler/common-operator-reducer-unittest.cc index 1f6044b..3b60e5b 100644 --- a/test/unittests/compiler/common-operator-reducer-unittest.cc +++ b/test/unittests/compiler/common-operator-reducer-unittest.cc @@ -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_; }; @@ -78,9 +91,14 @@ TEST_F(CommonOperatorReducerTest, RedundantPhi) { 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 diff --git a/test/unittests/compiler/machine-operator-unittest.cc b/test/unittests/compiler/machine-operator-unittest.cc index fcdb0ae..8244c37 100644 --- a/test/unittests/compiler/machine-operator-unittest.cc +++ b/test/unittests/compiler/machine-operator-unittest.cc @@ -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 }; diff --git a/test/unittests/compiler/node-test-utils.cc b/test/unittests/compiler/node-test-utils.cc index 715fe56..a84a3f1 100644 --- a/test/unittests/compiler/node-test-utils.cc +++ b/test/unittests/compiler/node-test-utils.cc @@ -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) diff --git a/test/unittests/compiler/node-test-utils.h b/test/unittests/compiler/node-test-utils.h index a941ddb..0cf34d7 100644 --- a/test/unittests/compiler/node-test-utils.h +++ b/test/unittests/compiler/node-test-utils.h @@ -203,6 +203,10 @@ Matcher IsChangeUint32ToUint64(const Matcher& input_matcher); Matcher IsTruncateFloat64ToFloat32(const Matcher& input_matcher); Matcher IsTruncateFloat64ToInt32(const Matcher& input_matcher); Matcher IsTruncateInt64ToInt32(const Matcher& input_matcher); +Matcher IsFloat64Max(const Matcher& lhs_matcher, + const Matcher& rhs_matcher); +Matcher IsFloat64Min(const Matcher& lhs_matcher, + const Matcher& rhs_matcher); Matcher IsFloat64Sub(const Matcher& lhs_matcher, const Matcher& rhs_matcher); Matcher IsFloat64Sqrt(const Matcher& input_matcher); -- 2.7.4