From 75ac43928b87d090c402ccc9d78ccc8a77ca2a08 Mon Sep 17 00:00:00 2001 From: "sigurds@chromium.org" Date: Thu, 30 Oct 2014 14:15:20 +0000 Subject: [PATCH] Add floor, ceil, round (truncate) instructions for ia32, x64 (if SSE4.1) and add floor, ceil, round (truncate and away from zero) for arm64. R=bmeurer@chromium.org, dcarney@chromium.org, mstarzinger@chromium.org, rodolph.perfetta@arm.com TEST=test/mjsunit/asm/math-floor.js,test/mjsunit/asm/math-ceil.js,test/unittest/compiler/js-builtin-reducer-unittest.cc Review URL: https://codereview.chromium.org/677433002 Cr-Commit-Position: refs/heads/master@{#25018} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25018 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/compiler/arm/code-generator-arm.cc | 12 ++ src/compiler/arm/instruction-codes-arm.h | 4 + src/compiler/arm/instruction-selector-arm.cc | 53 +++++- src/compiler/arm64/code-generator-arm64.cc | 12 ++ src/compiler/arm64/instruction-codes-arm64.h | 4 + .../arm64/instruction-selector-arm64.cc | 38 +++- src/compiler/ia32/code-generator-ia32.cc | 18 ++ src/compiler/ia32/instruction-codes-ia32.h | 3 + .../ia32/instruction-selector-ia32.cc | 39 +++- src/compiler/instruction-selector.cc | 13 ++ src/compiler/js-builtin-reducer.cc | 30 ++++ src/compiler/js-builtin-reducer.h | 2 + .../mips/instruction-selector-mips.cc | 16 ++ src/compiler/raw-machine-assembler.cc | 5 +- src/compiler/raw-machine-assembler.h | 12 +- src/compiler/x64/code-generator-x64.cc | 18 ++ src/compiler/x64/instruction-codes-x64.h | 3 + src/compiler/x64/instruction-selector-x64.cc | 37 +++- test/cctest/compiler/codegen-tester.h | 11 +- test/cctest/compiler/test-run-machops.cc | 168 ++++++++++++++++++ test/mjsunit/asm/math-ceil.js | 38 ++++ test/mjsunit/asm/math-floor.js | 38 ++++ .../compiler/js-builtin-reducer-unittest.cc | 78 +++++++- test/unittests/compiler/node-test-utils.cc | 4 + test/unittests/compiler/node-test-utils.h | 4 + 25 files changed, 639 insertions(+), 21 deletions(-) create mode 100644 test/mjsunit/asm/math-ceil.js create mode 100644 test/mjsunit/asm/math-floor.js diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc index c0e50042a..814f5e7da 100644 --- a/src/compiler/arm/code-generator-arm.cc +++ b/src/compiler/arm/code-generator-arm.cc @@ -363,6 +363,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArmVsqrtF64: __ vsqrt(i.OutputFloat64Register(), i.InputFloat64Register(0)); break; + case kArmVfloorF64: + __ vrintm(i.OutputFloat64Register(), i.InputFloat64Register(0)); + break; + case kArmVceilF64: + __ vrintp(i.OutputFloat64Register(), i.InputFloat64Register(0)); + break; + case kArmVroundTruncateF64: + __ vrintz(i.OutputFloat64Register(), i.InputFloat64Register(0)); + break; + case kArmVroundTiesAwayF64: + __ vrinta(i.OutputFloat64Register(), i.InputFloat64Register(0)); + break; case kArmVnegF64: __ vneg(i.OutputFloat64Register(), i.InputFloat64Register(0)); break; diff --git a/src/compiler/arm/instruction-codes-arm.h b/src/compiler/arm/instruction-codes-arm.h index 3c9748214..04559a39c 100644 --- a/src/compiler/arm/instruction-codes-arm.h +++ b/src/compiler/arm/instruction-codes-arm.h @@ -44,6 +44,10 @@ namespace compiler { V(ArmVmodF64) \ V(ArmVnegF64) \ V(ArmVsqrtF64) \ + V(ArmVfloorF64) \ + V(ArmVceilF64) \ + V(ArmVroundTruncateF64) \ + V(ArmVroundTiesAwayF64) \ V(ArmVcvtF32F64) \ V(ArmVcvtF64F32) \ V(ArmVcvtF64S32) \ diff --git a/src/compiler/arm/instruction-selector-arm.cc b/src/compiler/arm/instruction-selector-arm.cc index 56b1bf0e5..3f3aff454 100644 --- a/src/compiler/arm/instruction-selector-arm.cc +++ b/src/compiler/arm/instruction-selector-arm.cc @@ -100,6 +100,10 @@ class ArmOperandGenerator : public OperandGenerator { case kArmVmodF64: case kArmVnegF64: case kArmVsqrtF64: + case kArmVfloorF64: + case kArmVceilF64: + case kArmVroundTruncateF64: + case kArmVroundTiesAwayF64: case kArmVcvtF32F64: case kArmVcvtF64F32: case kArmVcvtF64S32: @@ -115,6 +119,14 @@ class ArmOperandGenerator : public OperandGenerator { }; +static void VisitRRFloat64(InstructionSelector* selector, ArchOpcode opcode, + Node* node) { + ArmOperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + + static void VisitRRRFloat64(InstructionSelector* selector, ArchOpcode opcode, Node* node) { ArmOperandGenerator g(selector); @@ -826,6 +838,30 @@ void InstructionSelector::VisitFloat64Sqrt(Node* node) { } +void InstructionSelector::VisitFloat64Floor(Node* node) { + DCHECK(CpuFeatures::IsSupported(ARMv8)); + VisitRRFloat64(this, kArmVfloorF64, node); +} + + +void InstructionSelector::VisitFloat64Ceil(Node* node) { + DCHECK(CpuFeatures::IsSupported(ARMv8)); + VisitRRFloat64(this, kArmVceilF64, node); +} + + +void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { + DCHECK(CpuFeatures::IsSupported(ARMv8)); + VisitRRFloat64(this, kArmVroundTruncateF64, node); +} + + +void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { + DCHECK(CpuFeatures::IsSupported(ARMv8)); + VisitRRFloat64(this, kArmVroundTiesAwayF64, node); +} + + void InstructionSelector::VisitCall(Node* node) { ArmOperandGenerator g(this); CallDescriptor* descriptor = OpParameter(node); @@ -1139,10 +1175,19 @@ void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { // static MachineOperatorBuilder::Flags InstructionSelector::SupportedMachineOperatorFlags() { - return MachineOperatorBuilder::kInt32DivIsSafe | - MachineOperatorBuilder::kInt32ModIsSafe | - MachineOperatorBuilder::kUint32DivIsSafe | - MachineOperatorBuilder::kUint32ModIsSafe; + MachineOperatorBuilder::Flags flags = + MachineOperatorBuilder::kInt32DivIsSafe | + MachineOperatorBuilder::kInt32ModIsSafe | + MachineOperatorBuilder::kUint32DivIsSafe | + MachineOperatorBuilder::kUint32ModIsSafe; + + if (CpuFeatures::IsSupported(ARMv8)) { + flags |= MachineOperatorBuilder::kFloat64Floor | + MachineOperatorBuilder::kFloat64Ceil | + MachineOperatorBuilder::kFloat64RoundTruncate | + MachineOperatorBuilder::kFloat64RoundTiesAway; + } + return flags; } } // namespace compiler diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index c3a4f4085..38c6531c5 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -222,6 +222,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArchTruncateDoubleToI: __ TruncateDoubleToI(i.OutputRegister(), i.InputDoubleRegister(0)); break; + case kArm64Float64Ceil: + __ Frintp(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + case kArm64Float64Floor: + __ Frintm(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + case kArm64Float64RoundTruncate: + __ Frintz(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; + case kArm64Float64RoundTiesAway: + __ Frinta(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); + break; case kArm64Add: __ Add(i.OutputRegister(), i.InputRegister(0), i.InputOperand2_64(1)); break; diff --git a/src/compiler/arm64/instruction-codes-arm64.h b/src/compiler/arm64/instruction-codes-arm64.h index 63756d14a..ab14a7cc6 100644 --- a/src/compiler/arm64/instruction-codes-arm64.h +++ b/src/compiler/arm64/instruction-codes-arm64.h @@ -78,6 +78,10 @@ namespace compiler { V(Arm64Float64Div) \ V(Arm64Float64Mod) \ V(Arm64Float64Sqrt) \ + V(Arm64Float64Floor) \ + V(Arm64Float64Ceil) \ + V(Arm64Float64RoundTruncate) \ + V(Arm64Float64RoundTiesAway) \ V(Arm64Float32ToFloat64) \ V(Arm64Float64ToFloat32) \ V(Arm64Float64ToInt32) \ diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index 40cb20753..39cd6ca77 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -86,6 +86,14 @@ class Arm64OperandGenerator FINAL : public OperandGenerator { }; +static void VisitRRFloat64(InstructionSelector* selector, ArchOpcode opcode, + Node* node) { + Arm64OperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + + static void VisitRRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) { Arm64OperandGenerator g(selector); @@ -899,9 +907,27 @@ void InstructionSelector::VisitFloat64Mod(Node* node) { void InstructionSelector::VisitFloat64Sqrt(Node* node) { - Arm64OperandGenerator g(this); - Emit(kArm64Float64Sqrt, g.DefineAsRegister(node), - g.UseRegister(node->InputAt(0))); + VisitRRFloat64(this, kArm64Float64Sqrt, node); +} + + +void InstructionSelector::VisitFloat64Floor(Node* node) { + VisitRRFloat64(this, kArm64Float64Floor, node); +} + + +void InstructionSelector::VisitFloat64Ceil(Node* node) { + VisitRRFloat64(this, kArm64Float64Ceil, node); +} + + +void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { + VisitRRFloat64(this, kArm64Float64RoundTruncate, node); +} + + +void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { + VisitRRFloat64(this, kArm64Float64RoundTiesAway, node); } @@ -1317,9 +1343,11 @@ void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { // static MachineOperatorBuilder::Flags InstructionSelector::SupportedMachineOperatorFlags() { - return MachineOperatorBuilder::kNoFlags; + return MachineOperatorBuilder::kFloat64Floor | + MachineOperatorBuilder::kFloat64Ceil | + MachineOperatorBuilder::kFloat64RoundTruncate | + MachineOperatorBuilder::kFloat64RoundTiesAway; } - } // namespace compiler } // namespace internal } // namespace v8 diff --git a/src/compiler/ia32/code-generator-ia32.cc b/src/compiler/ia32/code-generator-ia32.cc index d7098ae8d..00444838a 100644 --- a/src/compiler/ia32/code-generator-ia32.cc +++ b/src/compiler/ia32/code-generator-ia32.cc @@ -353,6 +353,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kSSEFloat64Sqrt: __ sqrtsd(i.OutputDoubleRegister(), i.InputOperand(0)); break; + case kSSEFloat64Floor: { + CpuFeatureScope sse_scope(masm(), SSE4_1); + __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + v8::internal::Assembler::kRoundDown); + break; + } + case kSSEFloat64Ceil: { + CpuFeatureScope sse_scope(masm(), SSE4_1); + __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + v8::internal::Assembler::kRoundUp); + break; + } + case kSSEFloat64RoundTruncate: { + CpuFeatureScope sse_scope(masm(), SSE4_1); + __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + v8::internal::Assembler::kRoundToZero); + break; + } case kSSECvtss2sd: __ cvtss2sd(i.OutputDoubleRegister(), i.InputOperand(0)); break; diff --git a/src/compiler/ia32/instruction-codes-ia32.h b/src/compiler/ia32/instruction-codes-ia32.h index 251a4894e..c922a3d9a 100644 --- a/src/compiler/ia32/instruction-codes-ia32.h +++ b/src/compiler/ia32/instruction-codes-ia32.h @@ -36,6 +36,9 @@ namespace compiler { V(SSEFloat64Div) \ V(SSEFloat64Mod) \ V(SSEFloat64Sqrt) \ + V(SSEFloat64Floor) \ + V(SSEFloat64Ceil) \ + V(SSEFloat64RoundTruncate) \ V(SSECvtss2sd) \ V(SSECvtsd2ss) \ V(SSEFloat64ToInt32) \ diff --git a/src/compiler/ia32/instruction-selector-ia32.cc b/src/compiler/ia32/instruction-selector-ia32.cc index e1a9cb7f6..a5e99c68a 100644 --- a/src/compiler/ia32/instruction-selector-ia32.cc +++ b/src/compiler/ia32/instruction-selector-ia32.cc @@ -142,6 +142,14 @@ class AddressingModeMatcher { }; +static void VisitRRFloat64(InstructionSelector* selector, ArchOpcode opcode, + Node* node) { + IA32OperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + + void InstructionSelector::VisitLoad(Node* node) { MachineType rep = RepresentationOf(OpParameter(node)); MachineType typ = TypeOf(OpParameter(node)); @@ -596,6 +604,29 @@ void InstructionSelector::VisitFloat64Sqrt(Node* node) { } +void InstructionSelector::VisitFloat64Floor(Node* node) { + DCHECK(CpuFeatures::IsSupported(SSE4_1)); + VisitRRFloat64(this, kSSEFloat64Floor, node); +} + + +void InstructionSelector::VisitFloat64Ceil(Node* node) { + DCHECK(CpuFeatures::IsSupported(SSE4_1)); + VisitRRFloat64(this, kSSEFloat64Ceil, node); +} + + +void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { + DCHECK(CpuFeatures::IsSupported(SSE4_1)); + VisitRRFloat64(this, kSSEFloat64RoundTruncate, node); +} + + +void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { + UNREACHABLE(); +} + + void InstructionSelector::VisitCall(Node* node) { IA32OperandGenerator g(this); CallDescriptor* descriptor = OpParameter(node); @@ -881,9 +912,13 @@ void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { // static MachineOperatorBuilder::Flags InstructionSelector::SupportedMachineOperatorFlags() { - return MachineOperatorBuilder::kNoFlags; + if (CpuFeatures::IsSupported(SSE4_1)) { + return MachineOperatorBuilder::kFloat64Floor | + MachineOperatorBuilder::kFloat64Ceil | + MachineOperatorBuilder::kFloat64RoundTruncate; + } + return MachineOperatorBuilder::Flag::kNoFlags; } - } // namespace compiler } // namespace internal } // namespace v8 diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index 729f16392..122bb5ff2 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -603,6 +603,10 @@ MachineType InstructionSelector::GetMachineType(Node* node) { case IrOpcode::kFloat64Div: case IrOpcode::kFloat64Mod: case IrOpcode::kFloat64Sqrt: + case IrOpcode::kFloat64Floor: + case IrOpcode::kFloat64Ceil: + case IrOpcode::kFloat64RoundTruncate: + case IrOpcode::kFloat64RoundTiesAway: return kMachFloat64; case IrOpcode::kFloat64Equal: case IrOpcode::kFloat64LessThan: @@ -792,11 +796,20 @@ void InstructionSelector::VisitNode(Node* node) { return VisitFloat64LessThan(node); case IrOpcode::kFloat64LessThanOrEqual: return VisitFloat64LessThanOrEqual(node); + case IrOpcode::kFloat64Floor: + return MarkAsDouble(node), VisitFloat64Floor(node); + case IrOpcode::kFloat64Ceil: + return MarkAsDouble(node), VisitFloat64Ceil(node); + case IrOpcode::kFloat64RoundTruncate: + return MarkAsDouble(node), VisitFloat64RoundTruncate(node); + case IrOpcode::kFloat64RoundTiesAway: + return MarkAsDouble(node), VisitFloat64RoundTiesAway(node); case IrOpcode::kLoadStackPointer: return VisitLoadStackPointer(node); default: V8_Fatal(__FILE__, __LINE__, "Unexpected operator #%d:%s @ node #%d", node->opcode(), node->op()->mnemonic(), node->id()); + break; } } diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc index b40288d5a..dbaa29300 100644 --- a/src/compiler/js-builtin-reducer.cc +++ b/src/compiler/js-builtin-reducer.cc @@ -191,6 +191,32 @@ Reduction JSBuiltinReducer::ReduceMathFround(Node* node) { } +// ES6 draft 10-14-14, section 20.2.2.16. +Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) { + if (!machine()->HasFloat64Floor()) return NoChange(); + JSCallReduction r(node); + if (r.InputsMatchOne(Type::Number())) { + // Math.floor(a:number) -> Float64Floor(a) + Node* value = graph()->NewNode(machine()->Float64Floor(), r.left()); + return Replace(value); + } + return NoChange(); +} + + +// ES6 draft 10-14-14, section 20.2.2.10. +Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) { + if (!machine()->HasFloat64Ceil()) return NoChange(); + JSCallReduction r(node); + if (r.InputsMatchOne(Type::Number())) { + // Math.ceil(a:number) -> Float64Ceil(a) + Node* value = graph()->NewNode(machine()->Float64Ceil(), r.left()); + return Replace(value); + } + return NoChange(); +} + + Reduction JSBuiltinReducer::Reduce(Node* node) { JSCallReduction r(node); @@ -207,6 +233,10 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { return ReplaceWithPureReduction(node, ReduceMathImul(node)); case kMathFround: return ReplaceWithPureReduction(node, ReduceMathFround(node)); + case kMathFloor: + return ReplaceWithPureReduction(node, ReduceMathFloor(node)); + case kMathCeil: + return ReplaceWithPureReduction(node, ReduceMathCeil(node)); default: break; } diff --git a/src/compiler/js-builtin-reducer.h b/src/compiler/js-builtin-reducer.h index f3b862f52..4b3be29ca 100644 --- a/src/compiler/js-builtin-reducer.h +++ b/src/compiler/js-builtin-reducer.h @@ -35,6 +35,8 @@ class JSBuiltinReducer FINAL : public Reducer { Reduction ReduceMathMax(Node* node); Reduction ReduceMathImul(Node* node); Reduction ReduceMathFround(Node* node); + Reduction ReduceMathFloor(Node* node); + Reduction ReduceMathCeil(Node* node); JSGraph* jsgraph_; SimplifiedOperatorBuilder simplified_; diff --git a/src/compiler/mips/instruction-selector-mips.cc b/src/compiler/mips/instruction-selector-mips.cc index 9be6bb6d6..637d98b78 100644 --- a/src/compiler/mips/instruction-selector-mips.cc +++ b/src/compiler/mips/instruction-selector-mips.cc @@ -409,6 +409,22 @@ void InstructionSelector::VisitFloat64Sqrt(Node* node) { } +void InstructionSelector::VisitFloat64Floor(Node* node) { UNREACHABLE(); } + + +void InstructionSelector::VisitFloat64Ceil(Node* node) { UNREACHABLE(); } + + +void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { + UNREACHABLE(); +} + + +void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { + UNREACHABLE(); +} + + void InstructionSelector::VisitCall(Node* node) { MipsOperandGenerator g(this); CallDescriptor* descriptor = OpParameter(node); diff --git a/src/compiler/raw-machine-assembler.cc b/src/compiler/raw-machine-assembler.cc index ba545c1a2..1d8b187b0 100644 --- a/src/compiler/raw-machine-assembler.cc +++ b/src/compiler/raw-machine-assembler.cc @@ -13,10 +13,11 @@ namespace compiler { RawMachineAssembler::RawMachineAssembler(Graph* graph, MachineSignature* machine_sig, - MachineType word) + MachineType word, + MachineOperatorBuilder::Flags flags) : GraphBuilder(graph), schedule_(new (zone()) Schedule(zone())), - machine_(word), + machine_(word, flags), common_(zone()), machine_sig_(machine_sig), call_descriptor_( diff --git a/src/compiler/raw-machine-assembler.h b/src/compiler/raw-machine-assembler.h index dfe83fa5b..01fa509e2 100644 --- a/src/compiler/raw-machine-assembler.h +++ b/src/compiler/raw-machine-assembler.h @@ -45,7 +45,9 @@ class RawMachineAssembler : public GraphBuilder { }; RawMachineAssembler(Graph* graph, MachineSignature* machine_sig, - MachineType word = kMachPtr); + MachineType word = kMachPtr, + MachineOperatorBuilder::Flags flags = + MachineOperatorBuilder::Flag::kNoFlags); virtual ~RawMachineAssembler() {} Isolate* isolate() const { return zone()->isolate(); } @@ -380,6 +382,14 @@ class RawMachineAssembler : public GraphBuilder { Node* TruncateInt64ToInt32(Node* a) { return NewNode(machine()->TruncateInt64ToInt32(), a); } + Node* Float64Floor(Node* a) { return NewNode(machine()->Float64Floor(), a); } + Node* Float64Ceil(Node* a) { return NewNode(machine()->Float64Ceil(), a); } + Node* Float64RoundTruncate(Node* a) { + return NewNode(machine()->Float64RoundTruncate(), a); + } + Node* Float64RoundTiesAway(Node* a) { + return NewNode(machine()->Float64RoundTiesAway(), a); + } // Parameters. Node* Parameter(size_t index); diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc index de9710323..5a54e5637 100644 --- a/src/compiler/x64/code-generator-x64.cc +++ b/src/compiler/x64/code-generator-x64.cc @@ -407,6 +407,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { __ sqrtsd(i.OutputDoubleRegister(), i.InputOperand(0)); } break; + case kSSEFloat64Floor: { + CpuFeatureScope sse_scope(masm(), SSE4_1); + __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + v8::internal::Assembler::kRoundDown); + break; + } + case kSSEFloat64Ceil: { + CpuFeatureScope sse_scope(masm(), SSE4_1); + __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + v8::internal::Assembler::kRoundUp); + break; + } + case kSSEFloat64RoundTruncate: { + CpuFeatureScope sse_scope(masm(), SSE4_1); + __ roundsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0), + v8::internal::Assembler::kRoundToZero); + break; + } case kSSECvtss2sd: if (instr->InputAt(0)->IsDoubleRegister()) { __ cvtss2sd(i.OutputDoubleRegister(), i.InputDoubleRegister(0)); diff --git a/src/compiler/x64/instruction-codes-x64.h b/src/compiler/x64/instruction-codes-x64.h index 835baf5fa..6859e90bc 100644 --- a/src/compiler/x64/instruction-codes-x64.h +++ b/src/compiler/x64/instruction-codes-x64.h @@ -52,6 +52,9 @@ namespace compiler { V(SSEFloat64Div) \ V(SSEFloat64Mod) \ V(SSEFloat64Sqrt) \ + V(SSEFloat64Floor) \ + V(SSEFloat64Ceil) \ + V(SSEFloat64RoundTruncate) \ V(SSECvtss2sd) \ V(SSECvtsd2ss) \ V(SSEFloat64ToInt32) \ diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc index 41674a37e..1dc409aa4 100644 --- a/src/compiler/x64/instruction-selector-x64.cc +++ b/src/compiler/x64/instruction-selector-x64.cc @@ -120,6 +120,14 @@ class AddressingModeMatcher { }; +static void VisitRRFloat64(InstructionSelector* selector, ArchOpcode opcode, + Node* node) { + X64OperandGenerator g(selector); + selector->Emit(opcode, g.DefineAsRegister(node), + g.UseRegister(node->InputAt(0))); +} + + void InstructionSelector::VisitLoad(Node* node) { MachineType rep = RepresentationOf(OpParameter(node)); MachineType typ = TypeOf(OpParameter(node)); @@ -723,6 +731,29 @@ void InstructionSelector::VisitFloat64Sqrt(Node* node) { } +void InstructionSelector::VisitFloat64Floor(Node* node) { + DCHECK(CpuFeatures::IsSupported(SSE4_1)); + VisitRRFloat64(this, kSSEFloat64Floor, node); +} + + +void InstructionSelector::VisitFloat64Ceil(Node* node) { + DCHECK(CpuFeatures::IsSupported(SSE4_1)); + VisitRRFloat64(this, kSSEFloat64Ceil, node); +} + + +void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { + DCHECK(CpuFeatures::IsSupported(SSE4_1)); + VisitRRFloat64(this, kSSEFloat64RoundTruncate, node); +} + + +void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { + UNREACHABLE(); +} + + void InstructionSelector::VisitCall(Node* node) { X64OperandGenerator g(this); CallDescriptor* descriptor = OpParameter(node); @@ -1112,9 +1143,13 @@ void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { // static MachineOperatorBuilder::Flags InstructionSelector::SupportedMachineOperatorFlags() { + if (CpuFeatures::IsSupported(SSE4_1)) { + return MachineOperatorBuilder::kFloat64Floor | + MachineOperatorBuilder::kFloat64Ceil | + MachineOperatorBuilder::kFloat64RoundTruncate; + } return MachineOperatorBuilder::kNoFlags; } - } // namespace compiler } // namespace internal } // namespace v8 diff --git a/test/cctest/compiler/codegen-tester.h b/test/cctest/compiler/codegen-tester.h index 6af5c78aa..72d658f36 100644 --- a/test/cctest/compiler/codegen-tester.h +++ b/test/cctest/compiler/codegen-tester.h @@ -7,6 +7,7 @@ #include "src/v8.h" +#include "src/compiler/instruction-selector.h" #include "src/compiler/pipeline.h" #include "src/compiler/raw-machine-assembler.h" #include "src/simulator.h" @@ -23,7 +24,9 @@ class MachineAssemblerTester : public HandleAndZoneScope, public: MachineAssemblerTester(MachineType return_type, MachineType p0, MachineType p1, MachineType p2, MachineType p3, - MachineType p4) + MachineType p4, + MachineOperatorBuilder::Flags flags = + MachineOperatorBuilder::Flag::kNoFlags) : HandleAndZoneScope(), CallHelper( main_isolate(), @@ -31,7 +34,7 @@ class MachineAssemblerTester : public HandleAndZoneScope, MachineAssembler( new (main_zone()) Graph(main_zone()), MakeMachineSignature(main_zone(), return_type, p0, p1, p2, p3, p4), - kMachPtr) {} + kMachPtr, flags) {} Node* LoadFromPointer(void* address, MachineType rep, int32_t offset = 0) { return this->Load(rep, this->PointerConstant(address), @@ -89,8 +92,8 @@ class RawMachineAssemblerTester MachineType p3 = kMachNone, MachineType p4 = kMachNone) : MachineAssemblerTester( - ReturnValueTraits::Representation(), p0, p1, p2, p3, - p4) {} + ReturnValueTraits::Representation(), p0, p1, p2, p3, p4, + InstructionSelector::SupportedMachineOperatorFlags()) {} template void Run(const Ci& ci, const Fn& fn) { diff --git a/test/cctest/compiler/test-run-machops.cc b/test/cctest/compiler/test-run-machops.cc index 18a8bd89b..271967d7c 100644 --- a/test/cctest/compiler/test-run-machops.cc +++ b/test/cctest/compiler/test-run-machops.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include @@ -4536,4 +4537,171 @@ TEST(RunFloat32Constant) { } } + +static double two_30 = 1 << 30; // 2^30 is a smi boundary. +static double two_52 = two_30 * (1 << 22); // 2^52 is a precision boundary. +static double kValues[] = {0.1, + 0.2, + 0.49999999999999994, + 0.5, + 0.7, + 1.0 - std::numeric_limits::epsilon(), + -0.1, + -0.49999999999999994, + -0.5, + -0.7, + 1.1, + 1.0 + std::numeric_limits::epsilon(), + 1.5, + 1.7, + -1, + -1 + std::numeric_limits::epsilon(), + -1 - std::numeric_limits::epsilon(), + -1.1, + -1.5, + -1.7, + std::numeric_limits::min(), + -std::numeric_limits::min(), + std::numeric_limits::max(), + -std::numeric_limits::max(), + std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + two_30, + two_30 + 0.1, + two_30 + 0.5, + two_30 + 0.7, + two_30 - 1, + two_30 - 1 + 0.1, + two_30 - 1 + 0.5, + two_30 - 1 + 0.7, + -two_30, + -two_30 + 0.1, + -two_30 + 0.5, + -two_30 + 0.7, + -two_30 + 1, + -two_30 + 1 + 0.1, + -two_30 + 1 + 0.5, + -two_30 + 1 + 0.7, + two_52, + two_52 + 0.1, + two_52 + 0.5, + two_52 + 0.5, + two_52 + 0.7, + two_52 + 0.7, + two_52 - 1, + two_52 - 1 + 0.1, + two_52 - 1 + 0.5, + two_52 - 1 + 0.7, + -two_52, + -two_52 + 0.1, + -two_52 + 0.5, + -two_52 + 0.7, + -two_52 + 1, + -two_52 + 1 + 0.1, + -two_52 + 1 + 0.5, + -two_52 + 1 + 0.7, + two_30, + two_30 - 0.1, + two_30 - 0.5, + two_30 - 0.7, + two_30 - 1, + two_30 - 1 - 0.1, + two_30 - 1 - 0.5, + two_30 - 1 - 0.7, + -two_30, + -two_30 - 0.1, + -two_30 - 0.5, + -two_30 - 0.7, + -two_30 + 1, + -two_30 + 1 - 0.1, + -two_30 + 1 - 0.5, + -two_30 + 1 - 0.7, + two_52, + two_52 - 0.1, + two_52 - 0.5, + two_52 - 0.5, + two_52 - 0.7, + two_52 - 0.7, + two_52 - 1, + two_52 - 1 - 0.1, + two_52 - 1 - 0.5, + two_52 - 1 - 0.7, + -two_52, + -two_52 - 0.1, + -two_52 - 0.5, + -two_52 - 0.7, + -two_52 + 1, + -two_52 + 1 - 0.1, + -two_52 + 1 - 0.5, + -two_52 + 1 - 0.7}; + + +TEST(RunFloat64Floor) { + double input = -1.0; + double result = 0.0; + RawMachineAssemblerTester m; + if (!m.machine()->HasFloat64Floor()) return; + m.StoreToPointer(&result, kMachFloat64, + m.Float64Floor(m.LoadFromPointer(&input, kMachFloat64))); + m.Return(m.Int32Constant(0)); + for (size_t i = 0; i < arraysize(kValues); ++i) { + input = kValues[i]; + CHECK_EQ(0, m.Call()); + double expected = std::floor(kValues[i]); + CHECK_EQ(expected, result); + } +} + + +TEST(RunFloat64Ceil) { + double input = -1.0; + double result = 0.0; + RawMachineAssemblerTester m; + if (!m.machine()->HasFloat64Ceil()) return; + m.StoreToPointer(&result, kMachFloat64, + m.Float64Ceil(m.LoadFromPointer(&input, kMachFloat64))); + m.Return(m.Int32Constant(0)); + for (size_t i = 0; i < arraysize(kValues); ++i) { + input = kValues[i]; + CHECK_EQ(0, m.Call()); + double expected = std::ceil(kValues[i]); + CHECK_EQ(expected, result); + } +} + + +TEST(RunFloat64RoundTruncate) { + double input = -1.0; + double result = 0.0; + RawMachineAssemblerTester m; + if (!m.machine()->HasFloat64Ceil()) return; + m.StoreToPointer( + &result, kMachFloat64, + m.Float64RoundTruncate(m.LoadFromPointer(&input, kMachFloat64))); + m.Return(m.Int32Constant(0)); + for (size_t i = 0; i < arraysize(kValues); ++i) { + input = kValues[i]; + CHECK_EQ(0, m.Call()); + double expected = trunc(kValues[i]); + CHECK_EQ(expected, result); + } +} + + +TEST(RunFloat64RoundTiesAway) { + double input = -1.0; + double result = 0.0; + RawMachineAssemblerTester m; + if (!m.machine()->HasFloat64RoundTiesAway()) return; + m.StoreToPointer( + &result, kMachFloat64, + m.Float64RoundTiesAway(m.LoadFromPointer(&input, kMachFloat64))); + m.Return(m.Int32Constant(0)); + for (size_t i = 0; i < arraysize(kValues); ++i) { + input = kValues[i]; + CHECK_EQ(0, m.Call()); + double expected = round(kValues[i]); + CHECK_EQ(expected, result); + } +} #endif // V8_TURBOFAN_TARGET diff --git a/test/mjsunit/asm/math-ceil.js b/test/mjsunit/asm/math-ceil.js new file mode 100644 index 000000000..edb94937b --- /dev/null +++ b/test/mjsunit/asm/math-ceil.js @@ -0,0 +1,38 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function Module(stdlib) { + "use asm"; + + var ceil = stdlib.Math.ceil; + + // f: double -> float + function f(a) { + a = +a; + return ceil(a); + } + + return { f: f }; +} + +var f = Module({ Math: Math }).f; + +assertTrue(isNaN(f(NaN))); +assertTrue(isNaN(f(undefined))); +assertTrue(isNaN(f(function() {}))); + +assertEquals(0, f(0)); +assertEquals(+0, f(+0)); +assertEquals(-0, f(-0)); +assertEquals(1, f(0.49999)); +assertEquals(1, f(0.6)); +assertEquals(1, f(0.5)); +assertEquals(-0, f(-0.1)); +assertEquals(-0, f(-0.5)); +assertEquals(-0, f(-0.6)); +assertEquals(-1, f(-1.6)); +assertEquals(-0, f(-0.50001)); + +assertEquals("Infinity", String(f(Infinity))); +assertEquals("-Infinity", String(f(-Infinity))); diff --git a/test/mjsunit/asm/math-floor.js b/test/mjsunit/asm/math-floor.js new file mode 100644 index 000000000..e8c3f341b --- /dev/null +++ b/test/mjsunit/asm/math-floor.js @@ -0,0 +1,38 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function Module(stdlib) { + "use asm"; + + var floor = stdlib.Math.floor; + + // f: double -> float + function f(a) { + a = +a; + return floor(a); + } + + return { f: f }; +} + +var f = Module({ Math: Math }).f; + +assertTrue(isNaN(f(NaN))); +assertTrue(isNaN(f(undefined))); +assertTrue(isNaN(f(function() {}))); + +assertEquals(0, f(0)); +assertEquals(+0, f(+0)); +assertEquals(-0, f(-0)); +assertEquals(0, f(0.49999)); +assertEquals(+0, f(0.6)); +assertEquals(+0, f(0.5)); +assertEquals(-1, f(-0.1)); +assertEquals(-1, f(-0.5)); +assertEquals(-1, f(-0.6)); +assertEquals(-2, f(-1.6)); +assertEquals(-1, f(-0.50001)); + +assertEquals("Infinity", String(f(Infinity))); +assertEquals("-Infinity", String(f(-Infinity))); diff --git a/test/unittests/compiler/js-builtin-reducer-unittest.cc b/test/unittests/compiler/js-builtin-reducer-unittest.cc index 26cabc3f7..6cfcc26dd 100644 --- a/test/unittests/compiler/js-builtin-reducer-unittest.cc +++ b/test/unittests/compiler/js-builtin-reducer-unittest.cc @@ -21,8 +21,9 @@ class JSBuiltinReducerTest : public TypedGraphTest { JSBuiltinReducerTest() : javascript_(zone()) {} protected: - Reduction Reduce(Node* node) { - MachineOperatorBuilder machine; + Reduction Reduce(Node* node, MachineOperatorBuilder::Flags flags = + MachineOperatorBuilder::Flag::kNoFlags) { + MachineOperatorBuilder machine(kMachPtr, flags); JSGraph jsgraph(graph(), common(), javascript(), &machine); JSBuiltinReducer reducer(&jsgraph); return reducer.Reduce(node); @@ -237,6 +238,79 @@ TEST_F(JSBuiltinReducerTest, MathFround) { } } + +// ----------------------------------------------------------------------------- +// Math.floor + + +TEST_F(JSBuiltinReducerTest, MathFloorAvailable) { + Handle f = MathFunction("floor"); + + TRACED_FOREACH(Type*, t0, kNumberTypes) { + Node* p0 = Parameter(t0, 0); + Node* fun = HeapConstant(Unique::CreateUninitialized(f)); + Node* call = + graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS), + fun, UndefinedConstant(), p0); + Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kFloat64Floor); + + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsFloat64Floor(p0)); + } +} + + +TEST_F(JSBuiltinReducerTest, MathFloorUnavailable) { + Handle f = MathFunction("floor"); + + TRACED_FOREACH(Type*, t0, kNumberTypes) { + Node* p0 = Parameter(t0, 0); + Node* fun = HeapConstant(Unique::CreateUninitialized(f)); + Node* call = + graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS), + fun, UndefinedConstant(), p0); + Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kNoFlags); + + ASSERT_FALSE(r.Changed()); + } +} + + +// ----------------------------------------------------------------------------- +// Math.ceil + + +TEST_F(JSBuiltinReducerTest, MathCeilAvailable) { + Handle f = MathFunction("ceil"); + + TRACED_FOREACH(Type*, t0, kNumberTypes) { + Node* p0 = Parameter(t0, 0); + Node* fun = HeapConstant(Unique::CreateUninitialized(f)); + Node* call = + graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS), + fun, UndefinedConstant(), p0); + Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kFloat64Ceil); + + ASSERT_TRUE(r.Changed()); + EXPECT_THAT(r.replacement(), IsFloat64Ceil(p0)); + } +} + + +TEST_F(JSBuiltinReducerTest, MathCeilUnavailable) { + Handle f = MathFunction("ceil"); + + TRACED_FOREACH(Type*, t0, kNumberTypes) { + Node* p0 = Parameter(t0, 0); + Node* fun = HeapConstant(Unique::CreateUninitialized(f)); + Node* call = + graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS), + fun, UndefinedConstant(), p0); + Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kNoFlags); + + ASSERT_FALSE(r.Changed()); + } +} } // namespace compiler } // namespace internal } // namespace v8 diff --git a/test/unittests/compiler/node-test-utils.cc b/test/unittests/compiler/node-test-utils.cc index a7308f999..4c1e10b9e 100644 --- a/test/unittests/compiler/node-test-utils.cc +++ b/test/unittests/compiler/node-test-utils.cc @@ -949,6 +949,10 @@ IS_UNOP_MATCHER(TruncateFloat64ToFloat32) IS_UNOP_MATCHER(TruncateFloat64ToInt32) IS_UNOP_MATCHER(TruncateInt64ToInt32) IS_UNOP_MATCHER(Float64Sqrt) +IS_UNOP_MATCHER(Float64Floor) +IS_UNOP_MATCHER(Float64Ceil) +IS_UNOP_MATCHER(Float64RoundTruncate) +IS_UNOP_MATCHER(Float64RoundTiesAway) #undef IS_UNOP_MATCHER } // namespace compiler diff --git a/test/unittests/compiler/node-test-utils.h b/test/unittests/compiler/node-test-utils.h index da05a108e..7f153bd97 100644 --- a/test/unittests/compiler/node-test-utils.h +++ b/test/unittests/compiler/node-test-utils.h @@ -149,6 +149,10 @@ Matcher IsTruncateFloat64ToFloat32(const Matcher& input_matcher); Matcher IsTruncateFloat64ToInt32(const Matcher& input_matcher); Matcher IsTruncateInt64ToInt32(const Matcher& input_matcher); Matcher IsFloat64Sqrt(const Matcher& input_matcher); +Matcher IsFloat64Floor(const Matcher& input_matcher); +Matcher IsFloat64Ceil(const Matcher& input_matcher); +Matcher IsFloat64RoundTruncate(const Matcher& input_matcher); +Matcher IsFloat64RoundTiesAway(const Matcher& input_matcher); } // namespace compiler } // namespace internal -- 2.34.1