Commit 43216574 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce NumberFloor simplified operator.

The NumberFloor operator matches exactly the semantics of the Math.floor
builtin on Numbers. It uses hardware rounding instructions if available,
but provides a full fallback solution that is compatible with Math.floor.
The lowering is optimizable based on types if needed later, i.e. we
already optimize it for the case that the input is already an Integer
(in the EcmaScript sense, including NaN and -0), but we could add more
optimizations, like combining NumberFloor and NumberDivide in the
future, if necessary.

R=jarin@chromium.org
BUG=v8:2890,v8:4059
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#35090}
parent 7b342a23
......@@ -132,11 +132,9 @@ Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
// ES6 draft 08-24-14, section 20.2.2.16.
Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::Number()) &&
machine()->Float64RoundDown().IsSupported()) {
// Math.floor(a:number) -> Float64RoundDown(a)
Node* value =
graph()->NewNode(machine()->Float64RoundDown().op(), r.left());
if (r.InputsMatchOne(Type::Number())) {
// Math.floor(a:number) -> NumberFloor(a)
Node* value = graph()->NewNode(simplified()->NumberFloor(), r.left());
return Replace(value);
}
return NoChange();
......
......@@ -183,6 +183,7 @@
V(NumberShiftLeft) \
V(NumberShiftRight) \
V(NumberShiftRightLogical) \
V(NumberFloor) \
V(NumberToInt32) \
V(NumberToUint32) \
V(NumberIsHoleNaN) \
......
......@@ -649,6 +649,7 @@ struct TypedLoweringPhase {
data->info()->is_deoptimization_enabled()
? JSIntrinsicLowering::kDeoptimizationEnabled
: JSIntrinsicLowering::kDeoptimizationDisabled);
SimplifiedOperatorReducer simple_reducer(data->jsgraph());
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->common(), data->machine());
AddReducer(data, &graph_reducer, &dead_code_elimination);
......@@ -659,6 +660,7 @@ struct TypedLoweringPhase {
AddReducer(data, &graph_reducer, &typed_lowering);
AddReducer(data, &graph_reducer, &intrinsic_lowering);
AddReducer(data, &graph_reducer, &load_elimination);
AddReducer(data, &graph_reducer, &simple_reducer);
AddReducer(data, &graph_reducer, &common_reducer);
graph_reducer.ReduceGraph();
}
......
......@@ -962,6 +962,11 @@ class RepresentationSelector {
}
break;
}
case IrOpcode::kNumberFloor: {
VisitUnop(node, UseInfo::Float64(), MachineRepresentation::kFloat64);
if (lower()) DeferReplacement(node, lowering->Float64Floor(node));
break;
}
case IrOpcode::kNumberToInt32: {
// Just change representation if necessary.
VisitUnop(node, UseInfo::TruncatingWord32(),
......@@ -1519,6 +1524,132 @@ void SimplifiedLowering::DoStoreBuffer(Node* node) {
NodeProperties::ChangeOp(node, machine()->CheckedStore(rep));
}
Node* SimplifiedLowering::Float64Floor(Node* const node) {
Node* const one = jsgraph()->Float64Constant(1.0);
Node* const zero = jsgraph()->Float64Constant(0.0);
Node* const minus_one = jsgraph()->Float64Constant(-1.0);
Node* const minus_zero = jsgraph()->Float64Constant(-0.0);
Node* const two_52 = jsgraph()->Float64Constant(4503599627370496.0E0);
Node* const minus_two_52 = jsgraph()->Float64Constant(-4503599627370496.0E0);
Node* const input = node->InputAt(0);
// Use fast hardware instruction if available.
if (machine()->Float64RoundDown().IsSupported()) {
return graph()->NewNode(machine()->Float64RoundDown().op(), input);
}
// General case for floor.
//
// if 0.0 < input then
// if 2^52 <= input then
// input
// else
// let temp1 = (2^52 + input) - 2^52 in
// if input < temp1 then
// temp1 - 1
// else
// temp1
// else
// if input == 0 then
// input
// else
// if input <= -2^52 then
// input
// else
// let temp1 = -0 - input in
// let temp2 = (2^52 + temp1) - 2^52 in
// if temp2 < temp1 then
// -1 - temp2
// else
// -0 - temp2
//
// Note: We do not use the Diamond helper class here, because it really hurts
// readability with nested diamonds.
Node* check0 = graph()->NewNode(machine()->Float64LessThan(), zero, input);
Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kTrue), check0,
graph()->start());
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* vtrue0;
{
Node* check1 =
graph()->NewNode(machine()->Float64LessThanOrEqual(), two_52, input);
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* vtrue1 = input;
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* vfalse1;
{
Node* temp1 = graph()->NewNode(
machine()->Float64Sub(),
graph()->NewNode(machine()->Float64Add(), two_52, input), two_52);
vfalse1 = graph()->NewNode(
common()->Select(MachineRepresentation::kFloat64),
graph()->NewNode(machine()->Float64LessThan(), input, temp1),
graph()->NewNode(machine()->Float64Sub(), temp1, one), temp1);
}
if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
vtrue0 = graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
vtrue1, vfalse1, if_true0);
}
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* vfalse0;
{
Node* check1 = graph()->NewNode(machine()->Float64Equal(), input, zero);
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check1, if_false0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* vtrue1 = input;
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* vfalse1;
{
Node* check2 = graph()->NewNode(machine()->Float64LessThanOrEqual(),
input, minus_two_52);
Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check2, if_false1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* vtrue2 = input;
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
Node* vfalse2;
{
Node* temp1 =
graph()->NewNode(machine()->Float64Sub(), minus_zero, input);
Node* temp2 = graph()->NewNode(
machine()->Float64Sub(),
graph()->NewNode(machine()->Float64Add(), two_52, temp1), two_52);
vfalse2 = graph()->NewNode(
common()->Select(MachineRepresentation::kFloat64),
graph()->NewNode(machine()->Float64LessThan(), temp2, temp1),
graph()->NewNode(machine()->Float64Sub(), minus_one, temp2),
graph()->NewNode(machine()->Float64Sub(), minus_zero, temp2));
}
if_false1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
vfalse1 =
graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
vtrue2, vfalse2, if_false1);
}
if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
vfalse0 =
graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
vtrue1, vfalse1, if_false0);
}
Node* merge0 = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
return graph()->NewNode(common()->Phi(MachineRepresentation::kFloat64, 2),
vtrue0, vfalse0, merge0);
}
Node* SimplifiedLowering::Int32Div(Node* const node) {
Int32BinopMatcher m(node);
Node* const zero = jsgraph()->Int32Constant(0);
......
......@@ -55,6 +55,7 @@ class SimplifiedLowering final {
// position information via the SourcePositionWrapper like all other reducers.
SourcePositionTable* source_positions_;
Node* Float64Floor(Node* const node);
Node* Int32Div(Node* const node);
Node* Int32Mod(Node* const node);
Node* Uint32Div(Node* const node);
......
......@@ -9,14 +9,14 @@
#include "src/compiler/node-matchers.h"
#include "src/compiler/operator-properties.h"
#include "src/conversions-inl.h"
#include "src/type-cache.h"
namespace v8 {
namespace internal {
namespace compiler {
SimplifiedOperatorReducer::SimplifiedOperatorReducer(JSGraph* jsgraph)
: jsgraph_(jsgraph) {}
: jsgraph_(jsgraph), type_cache_(TypeCache::Get()) {}
SimplifiedOperatorReducer::~SimplifiedOperatorReducer() {}
......@@ -89,6 +89,8 @@ Reduction SimplifiedOperatorReducer::Reduce(Node* node) {
if (m.HasValue()) return ReplaceNumber(FastUI2D(m.Value()));
break;
}
case IrOpcode::kNumberFloor:
return ReduceNumberFloor(node);
case IrOpcode::kReferenceEqual:
return ReduceReferenceEqual(node);
default:
......@@ -97,6 +99,15 @@ Reduction SimplifiedOperatorReducer::Reduce(Node* node) {
return NoChange();
}
Reduction SimplifiedOperatorReducer::ReduceNumberFloor(Node* node) {
DCHECK_EQ(IrOpcode::kNumberFloor, node->opcode());
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetType(input);
if (input_type->Is(type_cache_.kIntegerOrMinusZeroOrNaN)) {
return Replace(input);
}
return NoChange();
}
Reduction SimplifiedOperatorReducer::ReduceReferenceEqual(Node* node) {
DCHECK_EQ(IrOpcode::kReferenceEqual, node->opcode());
......
......@@ -9,6 +9,10 @@
namespace v8 {
namespace internal {
// Forward declarations.
class TypeCache;
namespace compiler {
// Forward declarations.
......@@ -25,6 +29,7 @@ class SimplifiedOperatorReducer final : public Reducer {
Reduction Reduce(Node* node) final;
private:
Reduction ReduceNumberFloor(Node* node);
Reduction ReduceReferenceEqual(Node* node);
Reduction Change(Node* node, const Operator* op, Node* a);
......@@ -42,6 +47,7 @@ class SimplifiedOperatorReducer final : public Reducer {
SimplifiedOperatorBuilder* simplified() const;
JSGraph* const jsgraph_;
TypeCache const& type_cache_;
DISALLOW_COPY_AND_ASSIGN(SimplifiedOperatorReducer);
};
......
......@@ -173,6 +173,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
V(NumberShiftLeft, Operator::kNoProperties, 2) \
V(NumberShiftRight, Operator::kNoProperties, 2) \
V(NumberShiftRightLogical, Operator::kNoProperties, 2) \
V(NumberFloor, Operator::kNoProperties, 1) \
V(NumberToInt32, Operator::kNoProperties, 1) \
V(NumberToUint32, Operator::kNoProperties, 1) \
V(NumberIsHoleNaN, Operator::kNoProperties, 1) \
......
......@@ -143,6 +143,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* NumberShiftLeft();
const Operator* NumberShiftRight();
const Operator* NumberShiftRightLogical();
const Operator* NumberFloor();
const Operator* NumberToInt32();
const Operator* NumberToUint32();
const Operator* NumberIsHoleNaN();
......
......@@ -240,6 +240,7 @@ class Typer::Visitor : public Reducer {
static Type* ToNumber(Type*, Typer*);
static Type* ToObject(Type*, Typer*);
static Type* ToString(Type*, Typer*);
static Type* NumberFloor(Type*, Typer*);
static Type* NumberToInt32(Type*, Typer*);
static Type* NumberToUint32(Type*, Typer*);
......@@ -487,6 +488,12 @@ Type* Typer::Visitor::ToString(Type* type, Typer* t) {
return Type::String();
}
// static
Type* Typer::Visitor::NumberFloor(Type* type, Typer* t) {
DCHECK(type->Is(Type::Number()));
if (type->Is(t->cache_.kIntegerOrMinusZeroOrNaN)) return type;
return t->cache_.kIntegerOrMinusZeroOrNaN;
}
Type* Typer::Visitor::NumberToInt32(Type* type, Typer* t) {
// TODO(neis): DCHECK(type->Is(Type::Number()));
......@@ -1715,6 +1722,9 @@ Type* Typer::Visitor::TypeNumberShiftRightLogical(Node* node) {
return Type::Unsigned32();
}
Type* Typer::Visitor::TypeNumberFloor(Node* node) {
return TypeUnaryOp(node, NumberFloor);
}
Type* Typer::Visitor::TypeNumberToInt32(Node* node) {
return TypeUnaryOp(node, NumberToInt32);
......
......@@ -677,6 +677,11 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::Unsigned32());
CheckUpperIs(node, Type::Unsigned32());
break;
case IrOpcode::kNumberFloor:
// Number -> Number
CheckValueInputIs(node, 0, Type::Number());
CheckUpperIs(node, Type::Number());
break;
case IrOpcode::kNumberToInt32:
// Number -> Signed32
CheckValueInputIs(node, 0, Type::Number());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment