[turbofan] Optimize array destructuring

This CL introduces type narrowing and constant folding reducers
to constant fold code that comes out of inlined destructuring
of arrays. In particular, array iterator introduces code that
contains a phi of a temporary array that blocks escape analysis.
The phi comes from conditional that can be evaluated statically
(i.e., constant folded), so with better constant folding we
allow escape analysis to get rid of the temporary array.

On a quick micro-benchmark below, we see more than 6x improvement.
This is close to the hand-optimized version - if we replace
body of f with 'return b + a', we get 220ms (versus 218ms with
destructuring).

function f(a, b) {
  [b, a] = [a, b];
  return a + b;
}

function sum(count) {
  let s = 0;
  for (let i = 0; i < count; i++) {
    s += f(1, 2);
  }
  return s;
}

// Warm up
sum(1e5); sum(1e5);
console.time("destructure array");
sum(1e8);
console.timeEnd("destructure array");

console.timeEnd: destructure array, 213.526000

console.timeEnd: destructure array, 1503.537000

Bug: v8:7728
Change-Id: Ib7aec1d5897989e6adb1af1eddd516d8b3866db5
Reviewed-on: https://chromium-review.googlesource.com/1047672
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53048}
This commit is contained in:
Jaroslav Sevcik 2018-05-07 22:45:11 +02:00 committed by Commit Bot
parent 9a49396c7c
commit 3fe7d698b8
14 changed files with 500 additions and 232 deletions

View File

@ -1575,6 +1575,8 @@ v8_source_set("v8_base") {
"src/compiler/common-operator.h",
"src/compiler/compiler-source-position-table.cc",
"src/compiler/compiler-source-position-table.h",
"src/compiler/constant-folding-reducer.cc",
"src/compiler/constant-folding-reducer.h",
"src/compiler/control-equivalence.cc",
"src/compiler/control-equivalence.h",
"src/compiler/control-flow-optimizer.cc",
@ -1725,6 +1727,8 @@ v8_source_set("v8_base") {
"src/compiler/store-store-elimination.h",
"src/compiler/type-cache.cc",
"src/compiler/type-cache.h",
"src/compiler/type-narrowing-reducer.cc",
"src/compiler/type-narrowing-reducer.h",
"src/compiler/typed-optimization.cc",
"src/compiler/typed-optimization.h",
"src/compiler/typer.cc",

View File

@ -0,0 +1,64 @@
// Copyright 2018 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.
#include "src/compiler/constant-folding-reducer.h"
#include "src/compiler/js-graph.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
ConstantFoldingReducer::ConstantFoldingReducer(Editor* editor, JSGraph* jsgraph)
: AdvancedReducer(editor), jsgraph_(jsgraph) {}
ConstantFoldingReducer::~ConstantFoldingReducer() {}
Reduction ConstantFoldingReducer::Reduce(Node* node) {
// Check if the output type is a singleton. In that case we already know the
// result value and can simply replace the node if it's eliminable.
if (!NodeProperties::IsConstant(node) && NodeProperties::IsTyped(node) &&
node->op()->HasProperty(Operator::kEliminatable)) {
// TODO(v8:5303): We must not eliminate FinishRegion here. This special
// case can be removed once we have separate operators for value and
// effect regions.
if (node->opcode() == IrOpcode::kFinishRegion) return NoChange();
// We can only constant-fold nodes here, that are known to not cause any
// side-effect, may it be a JavaScript observable side-effect or a possible
// eager deoptimization exit (i.e. {node} has an operator that doesn't have
// the Operator::kNoDeopt property).
Type upper = NodeProperties::GetType(node);
if (!upper.IsNone()) {
Node* replacement = nullptr;
if (upper.IsHeapConstant()) {
replacement = jsgraph()->Constant(upper.AsHeapConstant()->Value());
} else if (upper.Is(Type::MinusZero())) {
Factory* factory = jsgraph()->isolate()->factory();
replacement = jsgraph()->Constant(factory->minus_zero_value());
} else if (upper.Is(Type::NaN())) {
replacement = jsgraph()->NaNConstant();
} else if (upper.Is(Type::Null())) {
replacement = jsgraph()->NullConstant();
} else if (upper.Is(Type::PlainNumber()) && upper.Min() == upper.Max()) {
replacement = jsgraph()->Constant(upper.Min());
} else if (upper.Is(Type::Undefined())) {
replacement = jsgraph()->UndefinedConstant();
}
if (replacement) {
// Make sure the node has a type.
if (!NodeProperties::IsTyped(replacement)) {
NodeProperties::SetType(replacement, upper);
}
ReplaceWithValue(node, replacement);
return Changed(replacement);
}
}
}
return NoChange();
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,39 @@
// Copyright 2018 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.
#ifndef V8_COMPILER_CONSTANT_FOLDING_REDUCER_H_
#define V8_COMPILER_CONSTANT_FOLDING_REDUCER_H_
#include "src/compiler/graph-reducer.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class JSGraph;
class V8_EXPORT_PRIVATE ConstantFoldingReducer final
: public NON_EXPORTED_BASE(AdvancedReducer) {
public:
ConstantFoldingReducer(Editor* editor, JSGraph* jsgraph);
~ConstantFoldingReducer() final;
const char* reducer_name() const override { return "ConstantFoldingReducer"; }
Reduction Reduce(Node* node) final;
private:
JSGraph* jsgraph() const { return jsgraph_; }
JSGraph* const jsgraph_;
DISALLOW_COPY_AND_ASSIGN(ConstantFoldingReducer);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_CONSTANT_FOLDING_REDUCER_H_

View File

@ -24,6 +24,7 @@ OperationTyper::OperationTyper(Isolate* isolate, Zone* zone)
Type truncating_to_zero = Type::MinusZeroOrNaN();
DCHECK(!truncating_to_zero.Maybe(Type::Integral32()));
singleton_empty_string_ = Type::HeapConstant(factory->empty_string(), zone);
singleton_NaN_string_ = Type::HeapConstant(factory->NaN_string(), zone);
singleton_zero_string_ = Type::HeapConstant(factory->zero_string(), zone);
singleton_false_ = Type::HeapConstant(factory->false_value(), zone);
@ -31,6 +32,16 @@ OperationTyper::OperationTyper(Isolate* isolate, Zone* zone)
singleton_the_hole_ = Type::HeapConstant(factory->the_hole_value(), zone);
signed32ish_ = Type::Union(Type::Signed32(), truncating_to_zero, zone);
unsigned32ish_ = Type::Union(Type::Unsigned32(), truncating_to_zero, zone);
falsish_ = Type::Union(
Type::Undetectable(),
Type::Union(Type::Union(singleton_false_, cache_.kZeroish, zone),
Type::Union(singleton_empty_string_, Type::Hole(), zone),
zone),
zone);
truish_ = Type::Union(
singleton_true_,
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone), zone);
}
Type OperationTyper::Merge(Type left, Type right) {
@ -1153,6 +1164,16 @@ Type OperationTyper::ConvertTaggedHoleToUndefined(Type input) {
return input;
}
Type OperationTyper::ToBoolean(Type type) {
if (type.Is(Type::Boolean())) return type;
if (type.Is(falsish_)) return singleton_false_;
if (type.Is(truish_)) return singleton_true_;
if (type.Is(Type::Number())) {
return NumberToBoolean(type);
}
return Type::Boolean();
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -35,10 +35,11 @@ class V8_EXPORT_PRIVATE OperationTyper {
Type ToPrimitive(Type type);
Type ToNumber(Type type);
Type ToNumeric(Type type);
Type ToBoolean(Type type);
Type WeakenRange(Type current_range, Type previous_range);
// Number unary operators.
// Unary operators.
#define DECLARE_METHOD(Name) Type Name(Type type);
SIMPLIFIED_NUMBER_UNOP_LIST(DECLARE_METHOD)
SIMPLIFIED_SPECULATIVE_NUMBER_UNOP_LIST(DECLARE_METHOD)
@ -102,6 +103,9 @@ class V8_EXPORT_PRIVATE OperationTyper {
Type singleton_the_hole_;
Type signed32ish_;
Type unsigned32ish_;
Type singleton_empty_string_;
Type truish_;
Type falsish_;
};
} // namespace compiler

View File

@ -21,6 +21,7 @@
#include "src/compiler/code-generator.h"
#include "src/compiler/common-operator-reducer.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/constant-folding-reducer.h"
#include "src/compiler/control-flow-optimizer.h"
#include "src/compiler/dead-code-elimination.h"
#include "src/compiler/effect-control-linearizer.h"
@ -61,6 +62,7 @@
#include "src/compiler/simplified-operator-reducer.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/store-store-elimination.h"
#include "src/compiler/type-narrowing-reducer.h"
#include "src/compiler/typed-optimization.h"
#include "src/compiler/typer.h"
#include "src/compiler/value-numbering-reducer.h"
@ -1199,6 +1201,8 @@ struct TypedLoweringPhase {
&graph_reducer, data->info()->dependencies(), data->jsgraph(),
data->native_context(), temp_zone);
JSTypedLowering typed_lowering(&graph_reducer, data->jsgraph(), temp_zone);
ConstantFoldingReducer constant_folding_reducer(&graph_reducer,
data->jsgraph());
TypedOptimization typed_optimization(
&graph_reducer, data->info()->dependencies(), data->jsgraph());
SimplifiedOperatorReducer simple_reducer(&graph_reducer, data->jsgraph());
@ -1208,6 +1212,7 @@ struct TypedLoweringPhase {
temp_zone);
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &create_lowering);
AddReducer(data, &graph_reducer, &constant_folding_reducer);
AddReducer(data, &graph_reducer, &typed_optimization);
AddReducer(data, &graph_reducer, &typed_lowering);
AddReducer(data, &graph_reducer, &simple_reducer);
@ -1426,10 +1431,16 @@ struct LoadEliminationPhase {
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
data->common(), data->machine(),
temp_zone);
ConstantFoldingReducer constant_folding_reducer(&graph_reducer,
data->jsgraph());
TypeNarrowingReducer type_narrowing_reducer(&graph_reducer,
data->jsgraph());
AddReducer(data, &graph_reducer, &branch_condition_elimination);
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &redundancy_elimination);
AddReducer(data, &graph_reducer, &load_elimination);
AddReducer(data, &graph_reducer, &type_narrowing_reducer);
AddReducer(data, &graph_reducer, &constant_folding_reducer);
AddReducer(data, &graph_reducer, &checkpoint_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
AddReducer(data, &graph_reducer, &value_numbering);

View File

@ -0,0 +1,86 @@
// Copyright 2018 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.
#include "src/compiler/type-narrowing-reducer.h"
#include "src/compiler/js-graph.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
TypeNarrowingReducer::TypeNarrowingReducer(Editor* editor, JSGraph* jsgraph)
: AdvancedReducer(editor),
jsgraph_(jsgraph),
op_typer_(jsgraph->isolate(), zone()) {}
TypeNarrowingReducer::~TypeNarrowingReducer() {}
Reduction TypeNarrowingReducer::Reduce(Node* node) {
Type new_type = Type::Any();
switch (node->opcode()) {
case IrOpcode::kNumberLessThan: {
// TODO(turbofan) Reuse the logic from typer.cc (by integrating relational
// comparisons with the operation typer).
Type left_type = NodeProperties::GetType(node->InputAt(0));
Type right_type = NodeProperties::GetType(node->InputAt(1));
if (left_type.Is(Type::PlainNumber()) &&
right_type.Is(Type::PlainNumber())) {
Factory* const factory = jsgraph()->isolate()->factory();
if (left_type.Max() < right_type.Min()) {
new_type = Type::HeapConstant(factory->true_value(), zone());
} else if (left_type.Min() >= right_type.Max()) {
new_type = Type::HeapConstant(factory->false_value(), zone());
}
}
break;
}
case IrOpcode::kTypeGuard: {
new_type = op_typer_.TypeTypeGuard(
node->op(), NodeProperties::GetType(node->InputAt(0)));
break;
}
#define DECLARE_CASE(Name) \
case IrOpcode::k##Name: { \
new_type = op_typer_.Name(NodeProperties::GetType(node->InputAt(0)), \
NodeProperties::GetType(node->InputAt(1))); \
break; \
}
SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE)
DECLARE_CASE(SameValue)
#undef DECLARE_CASE
#define DECLARE_CASE(Name) \
case IrOpcode::k##Name: { \
new_type = op_typer_.Name(NodeProperties::GetType(node->InputAt(0))); \
break; \
}
SIMPLIFIED_NUMBER_UNOP_LIST(DECLARE_CASE)
DECLARE_CASE(ToBoolean)
#undef DECLARE_CASE
default:
return NoChange();
}
Type original_type = NodeProperties::GetType(node);
Type restricted = Type::Intersect(new_type, original_type, zone());
if (!original_type.Is(restricted)) {
NodeProperties::SetType(node, restricted);
return Changed(node);
}
return NoChange();
}
Graph* TypeNarrowingReducer::graph() const { return jsgraph()->graph(); }
Zone* TypeNarrowingReducer::zone() const { return graph()->zone(); }
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,44 @@
// Copyright 2018 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.
#ifndef V8_COMPILER_TYPE_NARROWING_REDUCER_H_
#define V8_COMPILER_TYPE_NARROWING_REDUCER_H_
#include "src/base/compiler-specific.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/operation-typer.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class JSGraph;
class V8_EXPORT_PRIVATE TypeNarrowingReducer final
: public NON_EXPORTED_BASE(AdvancedReducer) {
public:
TypeNarrowingReducer(Editor* editor, JSGraph* jsgraph);
~TypeNarrowingReducer() final;
const char* reducer_name() const override { return "TypeNarrowingReducer"; }
Reduction Reduce(Node* node) final;
private:
JSGraph* jsgraph() const { return jsgraph_; }
Graph* graph() const;
Zone* zone() const;
JSGraph* const jsgraph_;
OperationTyper op_typer_;
DISALLOW_COPY_AND_ASSIGN(TypeNarrowingReducer);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_TYPE_NARROWING_REDUCER_H_

View File

@ -30,48 +30,6 @@ TypedOptimization::TypedOptimization(Editor* editor,
TypedOptimization::~TypedOptimization() {}
Reduction TypedOptimization::Reduce(Node* node) {
// Check if the output type is a singleton. In that case we already know the
// result value and can simply replace the node if it's eliminable.
if (!NodeProperties::IsConstant(node) && NodeProperties::IsTyped(node) &&
node->op()->HasProperty(Operator::kEliminatable)) {
// TODO(v8:5303): We must not eliminate FinishRegion here. This special
// case can be removed once we have separate operators for value and
// effect regions.
if (node->opcode() == IrOpcode::kFinishRegion) return NoChange();
// We can only constant-fold nodes here, that are known to not cause any
// side-effect, may it be a JavaScript observable side-effect or a possible
// eager deoptimization exit (i.e. {node} has an operator that doesn't have
// the Operator::kNoDeopt property).
Type upper = NodeProperties::GetType(node);
if (!upper.IsNone()) {
if (upper.IsHeapConstant()) {
Node* replacement =
jsgraph()->Constant(upper.AsHeapConstant()->Value());
ReplaceWithValue(node, replacement);
return Changed(replacement);
} else if (upper.Is(Type::MinusZero())) {
Node* replacement = jsgraph()->Constant(factory()->minus_zero_value());
ReplaceWithValue(node, replacement);
return Changed(replacement);
} else if (upper.Is(Type::NaN())) {
Node* replacement = jsgraph()->NaNConstant();
ReplaceWithValue(node, replacement);
return Changed(replacement);
} else if (upper.Is(Type::Null())) {
Node* replacement = jsgraph()->NullConstant();
ReplaceWithValue(node, replacement);
return Changed(replacement);
} else if (upper.Is(Type::PlainNumber()) && upper.Min() == upper.Max()) {
Node* replacement = jsgraph()->Constant(upper.Min());
ReplaceWithValue(node, replacement);
return Changed(replacement);
} else if (upper.Is(Type::Undefined())) {
Node* replacement = jsgraph()->UndefinedConstant();
ReplaceWithValue(node, replacement);
return Changed(replacement);
}
}
}
switch (node->opcode()) {
case IrOpcode::kConvertReceiver:
return ReduceConvertReceiver(node);

View File

@ -40,23 +40,10 @@ Typer::Typer(Isolate* isolate, Flags flags, Graph* graph)
decorator_(nullptr),
cache_(TypeCache::Get()),
operation_typer_(isolate, zone()) {
Zone* zone = this->zone();
Factory* const factory = isolate->factory();
singleton_empty_string_ = Type::HeapConstant(factory->empty_string(), zone);
singleton_false_ = operation_typer_.singleton_false();
singleton_true_ = operation_typer_.singleton_true();
falsish_ = Type::Union(
Type::Undetectable(),
Type::Union(Type::Union(singleton_false_, cache_.kZeroish, zone),
Type::Union(singleton_empty_string_, Type::Hole(), zone),
zone),
zone);
truish_ = Type::Union(
singleton_true_,
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone), zone);
decorator_ = new (zone) Decorator(this);
decorator_ = new (zone()) Decorator(this);
graph_->AddDecorator(decorator_);
}
@ -489,13 +476,7 @@ Type Typer::Visitor::ToPrimitive(Type type, Typer* t) {
}
Type Typer::Visitor::ToBoolean(Type type, Typer* t) {
if (type.Is(Type::Boolean())) return type;
if (type.Is(t->falsish_)) return t->singleton_false_;
if (type.Is(t->truish_)) return t->singleton_true_;
if (type.Is(Type::Number())) {
return t->operation_typer()->NumberToBoolean(type);
}
return Type::Boolean();
return t->operation_typer()->ToBoolean(type);
}

View File

@ -50,11 +50,8 @@ class V8_EXPORT_PRIVATE Typer {
TypeCache const& cache_;
OperationTyper operation_typer_;
Type singleton_empty_string_;
Type singleton_false_;
Type singleton_true_;
Type falsish_;
Type truish_;
DISALLOW_COPY_AND_ASSIGN(Typer);
};

View File

@ -91,6 +91,7 @@ v8_source_set("unittests_sources") {
"compiler/common-operator-reducer-unittest.cc",
"compiler/common-operator-unittest.cc",
"compiler/compiler-test-utils.h",
"compiler/constant-folding-reducer-unittest.cc",
"compiler/control-equivalence-unittest.cc",
"compiler/control-flow-optimizer-unittest.cc",
"compiler/dead-code-elimination-unittest.cc",

View File

@ -0,0 +1,223 @@
// Copyright 2016 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.
#include "src/compiler/constant-folding-reducer.h"
#include "src/code-factory.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/isolate-inl.h"
#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "testing/gmock-support.h"
using testing::IsNaN;
namespace v8 {
namespace internal {
namespace compiler {
namespace constant_folding_reducer_unittest {
namespace {
const double kFloat64Values[] = {
-V8_INFINITY, -4.23878e+275, -5.82632e+265, -6.60355e+220,
-6.26172e+212, -2.56222e+211, -4.82408e+201, -1.84106e+157,
-1.63662e+127, -1.55772e+100, -1.67813e+72, -2.3382e+55,
-3.179e+30, -1.441e+09, -1.0647e+09, -7.99361e+08,
-5.77375e+08, -2.20984e+08, -32757, -13171,
-9970, -3984, -107, -105,
-92, -77, -61, -0.000208163,
-1.86685e-06, -1.17296e-10, -9.26358e-11, -5.08004e-60,
-1.74753e-65, -1.06561e-71, -5.67879e-79, -5.78459e-130,
-2.90989e-171, -7.15489e-243, -3.76242e-252, -1.05639e-263,
-4.40497e-267, -2.19666e-273, -4.9998e-276, -5.59821e-278,
-2.03855e-282, -5.99335e-283, -7.17554e-284, -3.11744e-309,
-0.0, 0.0, 2.22507e-308, 1.30127e-270,
7.62898e-260, 4.00313e-249, 3.16829e-233, 1.85244e-228,
2.03544e-129, 1.35126e-110, 1.01182e-106, 5.26333e-94,
1.35292e-90, 2.85394e-83, 1.78323e-77, 5.4967e-57,
1.03207e-25, 4.57401e-25, 1.58738e-05, 2,
125, 2310, 9636, 14802,
17168, 28945, 29305, 4.81336e+07,
1.41207e+08, 4.65962e+08, 1.40499e+09, 2.12648e+09,
8.80006e+30, 1.4446e+45, 1.12164e+54, 2.48188e+89,
6.71121e+102, 3.074e+112, 4.9699e+152, 5.58383e+166,
4.30654e+172, 7.08824e+185, 9.6586e+214, 2.028e+223,
6.63277e+243, 1.56192e+261, 1.23202e+269, 5.72883e+289,
8.5798e+290, 1.40256e+294, 1.79769e+308, V8_INFINITY};
const double kIntegerValues[] = {-V8_INFINITY, INT_MIN, -1000.0, -42.0,
-1.0, 0.0, 1.0, 42.0,
1000.0, INT_MAX, UINT_MAX, V8_INFINITY};
} // namespace
class ConstantFoldingReducerTest : public TypedGraphTest {
public:
ConstantFoldingReducerTest()
: TypedGraphTest(3), simplified_(zone()), deps_(isolate(), zone()) {}
~ConstantFoldingReducerTest() override {}
protected:
Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone());
JSOperatorBuilder javascript(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, simplified(),
&machine);
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(zone(), graph());
ConstantFoldingReducer reducer(&graph_reducer, &jsgraph);
return reducer.Reduce(node);
}
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
private:
SimplifiedOperatorBuilder simplified_;
CompilationDependencies deps_;
};
TEST_F(ConstantFoldingReducerTest, ParameterWithMinusZero) {
{
Reduction r = Reduce(
Parameter(Type::NewConstant(factory()->minus_zero_value(), zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(-0.0));
}
{
Reduction r = Reduce(Parameter(Type::MinusZero()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(-0.0));
}
{
Reduction r = Reduce(Parameter(Type::Union(
Type::MinusZero(), Type::NewConstant(factory()->NewNumber(0), zone()),
zone())));
EXPECT_FALSE(r.Changed());
}
}
TEST_F(ConstantFoldingReducerTest, ParameterWithNull) {
Handle<HeapObject> null = factory()->null_value();
{
Reduction r = Reduce(Parameter(Type::NewConstant(null, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(null));
}
{
Reduction r = Reduce(Parameter(Type::Null()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(null));
}
}
TEST_F(ConstantFoldingReducerTest, ParameterWithNaN) {
const double kNaNs[] = {-std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::signaling_NaN()};
TRACED_FOREACH(double, nan, kNaNs) {
Handle<Object> constant = factory()->NewNumber(nan);
Reduction r = Reduce(Parameter(Type::NewConstant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r =
Reduce(Parameter(Type::NewConstant(factory()->nan_value(), zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r = Reduce(Parameter(Type::NaN()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
}
TEST_F(ConstantFoldingReducerTest, ParameterWithPlainNumber) {
TRACED_FOREACH(double, value, kFloat64Values) {
Handle<Object> constant = factory()->NewNumber(value);
Reduction r = Reduce(Parameter(Type::NewConstant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(value));
}
TRACED_FOREACH(double, value, kIntegerValues) {
Reduction r = Reduce(Parameter(Type::Range(value, value, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(value));
}
}
TEST_F(ConstantFoldingReducerTest, ParameterWithUndefined) {
Handle<HeapObject> undefined = factory()->undefined_value();
{
Reduction r = Reduce(Parameter(Type::Undefined()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(undefined));
}
{
Reduction r = Reduce(Parameter(Type::NewConstant(undefined, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(undefined));
}
}
// -----------------------------------------------------------------------------
// ToBoolean
TEST_F(ConstantFoldingReducerTest, ToBooleanWithFalsish) {
Node* input = Parameter(
Type::Union(
Type::MinusZero(),
Type::Union(
Type::NaN(),
Type::Union(
Type::Null(),
Type::Union(
Type::Undefined(),
Type::Union(
Type::Undetectable(),
Type::Union(Type::NewConstant(
factory()->false_value(), zone()),
Type::Range(0.0, 0.0, zone()), zone()),
zone()),
zone()),
zone()),
zone()),
zone()),
0);
Reduction r = Reduce(graph()->NewNode(simplified()->ToBoolean(), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(ConstantFoldingReducerTest, ToBooleanWithTruish) {
Node* input = Parameter(
Type::Union(
Type::NewConstant(factory()->true_value(), zone()),
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
zone()),
0);
Reduction r = Reduce(graph()->NewNode(simplified()->ToBoolean(), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(ConstantFoldingReducerTest, ToBooleanWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0);
Reduction r = Reduce(graph()->NewNode(simplified()->ToBoolean(), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
} // namespace constant_folding_reducer_unittest
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -24,41 +24,6 @@ namespace internal {
namespace compiler {
namespace typed_optimization_unittest {
namespace {
const double kFloat64Values[] = {
-V8_INFINITY, -4.23878e+275, -5.82632e+265, -6.60355e+220,
-6.26172e+212, -2.56222e+211, -4.82408e+201, -1.84106e+157,
-1.63662e+127, -1.55772e+100, -1.67813e+72, -2.3382e+55,
-3.179e+30, -1.441e+09, -1.0647e+09, -7.99361e+08,
-5.77375e+08, -2.20984e+08, -32757, -13171,
-9970, -3984, -107, -105,
-92, -77, -61, -0.000208163,
-1.86685e-06, -1.17296e-10, -9.26358e-11, -5.08004e-60,
-1.74753e-65, -1.06561e-71, -5.67879e-79, -5.78459e-130,
-2.90989e-171, -7.15489e-243, -3.76242e-252, -1.05639e-263,
-4.40497e-267, -2.19666e-273, -4.9998e-276, -5.59821e-278,
-2.03855e-282, -5.99335e-283, -7.17554e-284, -3.11744e-309,
-0.0, 0.0, 2.22507e-308, 1.30127e-270,
7.62898e-260, 4.00313e-249, 3.16829e-233, 1.85244e-228,
2.03544e-129, 1.35126e-110, 1.01182e-106, 5.26333e-94,
1.35292e-90, 2.85394e-83, 1.78323e-77, 5.4967e-57,
1.03207e-25, 4.57401e-25, 1.58738e-05, 2,
125, 2310, 9636, 14802,
17168, 28945, 29305, 4.81336e+07,
1.41207e+08, 4.65962e+08, 1.40499e+09, 2.12648e+09,
8.80006e+30, 1.4446e+45, 1.12164e+54, 2.48188e+89,
6.71121e+102, 3.074e+112, 4.9699e+152, 5.58383e+166,
4.30654e+172, 7.08824e+185, 9.6586e+214, 2.028e+223,
6.63277e+243, 1.56192e+261, 1.23202e+269, 5.72883e+289,
8.5798e+290, 1.40256e+294, 1.79769e+308, V8_INFINITY};
const double kIntegerValues[] = {-V8_INFINITY, INT_MIN, -1000.0, -42.0,
-1.0, 0.0, 1.0, 42.0,
1000.0, INT_MAX, UINT_MAX, V8_INFINITY};
} // namespace
class TypedOptimizationTest : public TypedGraphTest {
public:
TypedOptimizationTest()
@ -84,139 +49,9 @@ class TypedOptimizationTest : public TypedGraphTest {
CompilationDependencies deps_;
};
TEST_F(TypedOptimizationTest, ParameterWithMinusZero) {
{
Reduction r = Reduce(
Parameter(Type::NewConstant(factory()->minus_zero_value(), zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(-0.0));
}
{
Reduction r = Reduce(Parameter(Type::MinusZero()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(-0.0));
}
{
Reduction r = Reduce(Parameter(Type::Union(
Type::MinusZero(), Type::NewConstant(factory()->NewNumber(0), zone()),
zone())));
EXPECT_FALSE(r.Changed());
}
}
TEST_F(TypedOptimizationTest, ParameterWithNull) {
Handle<HeapObject> null = factory()->null_value();
{
Reduction r = Reduce(Parameter(Type::NewConstant(null, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(null));
}
{
Reduction r = Reduce(Parameter(Type::Null()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(null));
}
}
TEST_F(TypedOptimizationTest, ParameterWithNaN) {
const double kNaNs[] = {-std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::signaling_NaN()};
TRACED_FOREACH(double, nan, kNaNs) {
Handle<Object> constant = factory()->NewNumber(nan);
Reduction r = Reduce(Parameter(Type::NewConstant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r =
Reduce(Parameter(Type::NewConstant(factory()->nan_value(), zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r = Reduce(Parameter(Type::NaN()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
}
TEST_F(TypedOptimizationTest, ParameterWithPlainNumber) {
TRACED_FOREACH(double, value, kFloat64Values) {
Handle<Object> constant = factory()->NewNumber(value);
Reduction r = Reduce(Parameter(Type::NewConstant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(value));
}
TRACED_FOREACH(double, value, kIntegerValues) {
Reduction r = Reduce(Parameter(Type::Range(value, value, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(value));
}
}
TEST_F(TypedOptimizationTest, ParameterWithUndefined) {
Handle<HeapObject> undefined = factory()->undefined_value();
{
Reduction r = Reduce(Parameter(Type::Undefined()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(undefined));
}
{
Reduction r = Reduce(Parameter(Type::NewConstant(undefined, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(undefined));
}
}
// -----------------------------------------------------------------------------
// ToBoolean
TEST_F(TypedOptimizationTest, ToBooleanWithFalsish) {
Node* input = Parameter(
Type::Union(
Type::MinusZero(),
Type::Union(
Type::NaN(),
Type::Union(
Type::Null(),
Type::Union(
Type::Undefined(),
Type::Union(
Type::Undetectable(),
Type::Union(Type::NewConstant(
factory()->false_value(), zone()),
Type::Range(0.0, 0.0, zone()), zone()),
zone()),
zone()),
zone()),
zone()),
zone()),
0);
Reduction r = Reduce(graph()->NewNode(simplified()->ToBoolean(), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(TypedOptimizationTest, ToBooleanWithTruish) {
Node* input = Parameter(
Type::Union(
Type::NewConstant(factory()->true_value(), zone()),
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
zone()),
0);
Reduction r = Reduce(graph()->NewNode(simplified()->ToBoolean(), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(TypedOptimizationTest, ToBooleanWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0);
Reduction r = Reduce(graph()->NewNode(simplified()->ToBoolean(), input));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(TypedOptimizationTest, ToBooleanWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Reduction r = Reduce(graph()->NewNode(simplified()->ToBoolean(), input));