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

[turbofan] Separate typed optimizations from JSTypedLowering.

Introduce a new TypedOptimization reducer that contains the type
based optimization reduction steps, which are not (directly)
related to lowering JavaScript operators based on types (which is
what JSTypedLowering is supposed to do).

This also addresses a chicken-and-egg problem that we see in the
Octane/Mandreel benchmark where type based constant folding isn't
applied to the numeric comparison operators introduced by the
JSTypedLowering itself, and thus gives up to 10% speedup for the
benchmark.

BUG=v8:5267

Review-Url: https://codereview.chromium.org/2280673003
Cr-Commit-Position: refs/heads/master@{#38928}
parent 58cfe4d6
......@@ -1135,6 +1135,8 @@ v8_source_set("v8_base") {
"src/compiler/type-hint-analyzer.h",
"src/compiler/type-hints.cc",
"src/compiler/type-hints.h",
"src/compiler/typed-optimization.cc",
"src/compiler/typed-optimization.h",
"src/compiler/typer.cc",
"src/compiler/typer.h",
"src/compiler/unwinding-info-writer.h",
......
......@@ -430,8 +430,6 @@ JSTypedLowering::JSTypedLowering(Editor* editor,
dependencies_(dependencies),
flags_(flags),
jsgraph_(jsgraph),
true_type_(Type::Constant(factory()->true_value(), graph()->zone())),
false_type_(Type::Constant(factory()->false_value(), graph()->zone())),
the_hole_type_(
Type::Constant(factory()->the_hole_value(), graph()->zone())),
type_cache_(TypeCache::Get()) {
......@@ -1927,194 +1925,7 @@ Reduction JSTypedLowering::ReduceJSGeneratorRestoreRegister(Node* node) {
return Changed(element);
}
Reduction JSTypedLowering::ReducePhi(Node* node) {
// Try to narrow the type of the Phi {node}, which might be more precise now
// after lowering based on types, i.e. a SpeculativeNumberAdd has a more
// precise type than the JSAdd that was in the graph when the Typer was run.
DCHECK_EQ(IrOpcode::kPhi, node->opcode());
int arity = node->op()->ValueInputCount();
Type* type = NodeProperties::GetType(node->InputAt(0));
for (int i = 1; i < arity; ++i) {
type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)),
graph()->zone());
}
Type* const node_type = NodeProperties::GetType(node);
if (!node_type->Is(type)) {
type = Type::Intersect(node_type, type, graph()->zone());
NodeProperties::SetType(node, type);
return Changed(node);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceSelect(Node* node) {
DCHECK_EQ(IrOpcode::kSelect, node->opcode());
Node* const condition = NodeProperties::GetValueInput(node, 0);
Type* const condition_type = NodeProperties::GetType(condition);
Node* const vtrue = NodeProperties::GetValueInput(node, 1);
Type* const vtrue_type = NodeProperties::GetType(vtrue);
Node* const vfalse = NodeProperties::GetValueInput(node, 2);
Type* const vfalse_type = NodeProperties::GetType(vfalse);
if (condition_type->Is(true_type_)) {
// Select(condition:true, vtrue, vfalse) => vtrue
return Replace(vtrue);
}
if (condition_type->Is(false_type_)) {
// Select(condition:false, vtrue, vfalse) => vfalse
return Replace(vfalse);
}
if (vtrue_type->Is(true_type_) && vfalse_type->Is(false_type_)) {
// Select(condition, vtrue:true, vfalse:false) => condition
return Replace(condition);
}
if (vtrue_type->Is(false_type_) && vfalse_type->Is(true_type_)) {
// Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition)
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
}
return NoChange();
}
namespace {
MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
if (object_type->IsConstant() &&
object_type->AsConstant()->Value()->IsHeapObject()) {
Handle<Map> object_map(
Handle<HeapObject>::cast(object_type->AsConstant()->Value())->map());
if (object_map->is_stable()) return object_map;
} else if (object_type->IsClass()) {
Handle<Map> object_map = object_type->AsClass()->Map();
if (object_map->is_stable()) return object_map;
}
return MaybeHandle<Map>();
}
} // namespace
Reduction JSTypedLowering::ReduceCheckMaps(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
// The CheckMaps(o, ...map...) can be eliminated if map is stable and
// either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) we can add a code dependency on the stability of map
// (to guard the Constant type information).
Node* const object = NodeProperties::GetValueInput(node, 0);
Type* const object_type = NodeProperties::GetType(object);
Node* const effect = NodeProperties::GetEffectInput(node);
Handle<Map> object_map;
if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
Node* const map = NodeProperties::GetValueInput(node, i);
Type* const map_type = NodeProperties::GetType(map);
if (map_type->IsConstant() &&
map_type->AsConstant()->Value().is_identical_to(object_map)) {
if (object_map->CanTransition()) {
DCHECK(flags() & kDeoptimizationEnabled);
dependencies()->AssumeMapStable(object_map);
}
return Replace(effect);
}
}
}
return NoChange();
}
Reduction JSTypedLowering::ReduceCheckString(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::String())) {
ReplaceWithValue(node, input);
return Replace(input);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceLoadField(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
Node* const object = NodeProperties::GetValueInput(node, 0);
Type* const object_type = NodeProperties::GetType(object);
FieldAccess const& access = FieldAccessOf(node->op());
if (access.base_is_tagged == kTaggedBase &&
access.offset == HeapObject::kMapOffset) {
// We can replace LoadField[Map](o) with map if is stable and either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) deoptimization is enabled and we can add a code dependency on the
// stability of map (to guard the Constant type information).
Handle<Map> object_map;
if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
if (object_map->CanTransition()) {
if (flags() & kDeoptimizationEnabled) {
dependencies()->AssumeMapStable(object_map);
} else {
return NoChange();
}
}
Node* const value = jsgraph()->HeapConstant(object_map);
ReplaceWithValue(node, value);
return Replace(value);
}
}
return NoChange();
}
Reduction JSTypedLowering::ReduceNumberRoundop(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
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 JSTypedLowering::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)) {
// 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->IsInhabited()) {
if (upper->IsConstant()) {
Node* replacement = jsgraph()->Constant(upper->AsConstant()->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::kJSEqual:
return ReduceJSEqual(node, false);
......@@ -2187,21 +1998,6 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSGeneratorRestoreContinuation(node);
case IrOpcode::kJSGeneratorRestoreRegister:
return ReduceJSGeneratorRestoreRegister(node);
case IrOpcode::kPhi:
return ReducePhi(node);
case IrOpcode::kSelect:
return ReduceSelect(node);
case IrOpcode::kCheckMaps:
return ReduceCheckMaps(node);
case IrOpcode::kCheckString:
return ReduceCheckString(node);
case IrOpcode::kNumberCeil:
case IrOpcode::kNumberFloor:
case IrOpcode::kNumberRound:
case IrOpcode::kNumberTrunc:
return ReduceNumberRoundop(node);
case IrOpcode::kLoadField:
return ReduceLoadField(node);
default:
break;
}
......
......@@ -75,12 +75,6 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSGeneratorStore(Node* node);
Reduction ReduceJSGeneratorRestoreContinuation(Node* node);
Reduction ReduceJSGeneratorRestoreRegister(Node* node);
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceCheckString(Node* node);
Reduction ReduceLoadField(Node* node);
Reduction ReduceNumberRoundop(Node* node);
Reduction ReduceSelect(Node* node);
Reduction ReducePhi(Node* node);
Reduction ReduceNumberBinop(Node* node);
Reduction ReduceInt32Binop(Node* node);
Reduction ReduceUI32Shift(Node* node, Signedness signedness);
......@@ -100,8 +94,6 @@ class JSTypedLowering final : public AdvancedReducer {
Flags flags_;
JSGraph* jsgraph_;
Type* shifted_int32_ranges_[4];
Type* const true_type_;
Type* const false_type_;
Type* const the_hole_type_;
TypeCache const& type_cache_;
};
......
......@@ -63,6 +63,7 @@
#include "src/compiler/store-store-elimination.h"
#include "src/compiler/tail-call-optimization.h"
#include "src/compiler/type-hint-analyzer.h"
#include "src/compiler/typed-optimization.h"
#include "src/compiler/typer.h"
#include "src/compiler/value-numbering-reducer.h"
#include "src/compiler/verifier.h"
......@@ -922,6 +923,12 @@ struct TypedLoweringPhase {
JSTypedLowering typed_lowering(&graph_reducer, data->info()->dependencies(),
typed_lowering_flags, data->jsgraph(),
temp_zone);
TypedOptimization typed_optimization(
&graph_reducer, data->info()->dependencies(),
data->info()->is_deoptimization_enabled()
? TypedOptimization::kDeoptimizationEnabled
: TypedOptimization::kNoFlags,
data->jsgraph());
SimplifiedOperatorReducer simple_reducer(&graph_reducer, data->jsgraph());
CheckpointElimination checkpoint_elimination(&graph_reducer);
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
......@@ -931,6 +938,7 @@ struct TypedLoweringPhase {
if (data->info()->is_deoptimization_enabled()) {
AddReducer(data, &graph_reducer, &create_lowering);
}
AddReducer(data, &graph_reducer, &typed_optimization);
AddReducer(data, &graph_reducer, &typed_lowering);
AddReducer(data, &graph_reducer, &simple_reducer);
AddReducer(data, &graph_reducer, &checkpoint_elimination);
......
// 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/typed-optimization.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/isolate-inl.h"
#include "src/type-cache.h"
namespace v8 {
namespace internal {
namespace compiler {
TypedOptimization::TypedOptimization(Editor* editor,
CompilationDependencies* dependencies,
Flags flags, JSGraph* jsgraph)
: AdvancedReducer(editor),
dependencies_(dependencies),
flags_(flags),
jsgraph_(jsgraph),
true_type_(Type::Constant(factory()->true_value(), graph()->zone())),
false_type_(Type::Constant(factory()->false_value(), graph()->zone())),
type_cache_(TypeCache::Get()) {}
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)) {
// 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->IsInhabited()) {
if (upper->IsConstant()) {
Node* replacement = jsgraph()->Constant(upper->AsConstant()->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::kCheckMaps:
return ReduceCheckMaps(node);
case IrOpcode::kCheckString:
return ReduceCheckString(node);
case IrOpcode::kLoadField:
return ReduceLoadField(node);
case IrOpcode::kNumberCeil:
case IrOpcode::kNumberFloor:
case IrOpcode::kNumberRound:
case IrOpcode::kNumberTrunc:
return ReduceNumberRoundop(node);
case IrOpcode::kPhi:
return ReducePhi(node);
case IrOpcode::kSelect:
return ReduceSelect(node);
default:
break;
}
return NoChange();
}
namespace {
MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
if (object_type->IsConstant() &&
object_type->AsConstant()->Value()->IsHeapObject()) {
Handle<Map> object_map(
Handle<HeapObject>::cast(object_type->AsConstant()->Value())->map());
if (object_map->is_stable()) return object_map;
} else if (object_type->IsClass()) {
Handle<Map> object_map = object_type->AsClass()->Map();
if (object_map->is_stable()) return object_map;
}
return MaybeHandle<Map>();
}
} // namespace
Reduction TypedOptimization::ReduceCheckMaps(Node* node) {
// The CheckMaps(o, ...map...) can be eliminated if map is stable and
// either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) we can add a code dependency on the stability of map
// (to guard the Constant type information).
Node* const object = NodeProperties::GetValueInput(node, 0);
Type* const object_type = NodeProperties::GetType(object);
Node* const effect = NodeProperties::GetEffectInput(node);
Handle<Map> object_map;
if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
Node* const map = NodeProperties::GetValueInput(node, i);
Type* const map_type = NodeProperties::GetType(map);
if (map_type->IsConstant() &&
map_type->AsConstant()->Value().is_identical_to(object_map)) {
if (object_map->CanTransition()) {
dependencies()->AssumeMapStable(object_map);
}
return Replace(effect);
}
}
}
return NoChange();
}
Reduction TypedOptimization::ReduceCheckString(Node* node) {
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* const input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::String())) {
ReplaceWithValue(node, input);
return Replace(input);
}
return NoChange();
}
Reduction TypedOptimization::ReduceLoadField(Node* node) {
Node* const object = NodeProperties::GetValueInput(node, 0);
Type* const object_type = NodeProperties::GetType(object);
FieldAccess const& access = FieldAccessOf(node->op());
if (access.base_is_tagged == kTaggedBase &&
access.offset == HeapObject::kMapOffset) {
// We can replace LoadField[Map](o) with map if is stable and either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) deoptimization is enabled and we can add a code dependency on the
// stability of map (to guard the Constant type information).
Handle<Map> object_map;
if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
if (object_map->CanTransition()) {
if (flags() & kDeoptimizationEnabled) {
dependencies()->AssumeMapStable(object_map);
} else {
return NoChange();
}
}
Node* const value = jsgraph()->HeapConstant(object_map);
ReplaceWithValue(node, value);
return Replace(value);
}
}
return NoChange();
}
Reduction TypedOptimization::ReduceNumberRoundop(Node* node) {
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 TypedOptimization::ReducePhi(Node* node) {
// Try to narrow the type of the Phi {node}, which might be more precise now
// after lowering based on types, i.e. a SpeculativeNumberAdd has a more
// precise type than the JSAdd that was in the graph when the Typer was run.
DCHECK_EQ(IrOpcode::kPhi, node->opcode());
int arity = node->op()->ValueInputCount();
Type* type = NodeProperties::GetType(node->InputAt(0));
for (int i = 1; i < arity; ++i) {
type = Type::Union(type, NodeProperties::GetType(node->InputAt(i)),
graph()->zone());
}
Type* const node_type = NodeProperties::GetType(node);
if (!node_type->Is(type)) {
type = Type::Intersect(node_type, type, graph()->zone());
NodeProperties::SetType(node, type);
return Changed(node);
}
return NoChange();
}
Reduction TypedOptimization::ReduceSelect(Node* node) {
DCHECK_EQ(IrOpcode::kSelect, node->opcode());
Node* const condition = NodeProperties::GetValueInput(node, 0);
Type* const condition_type = NodeProperties::GetType(condition);
Node* const vtrue = NodeProperties::GetValueInput(node, 1);
Type* const vtrue_type = NodeProperties::GetType(vtrue);
Node* const vfalse = NodeProperties::GetValueInput(node, 2);
Type* const vfalse_type = NodeProperties::GetType(vfalse);
if (condition_type->Is(true_type_)) {
// Select(condition:true, vtrue, vfalse) => vtrue
return Replace(vtrue);
}
if (condition_type->Is(false_type_)) {
// Select(condition:false, vtrue, vfalse) => vfalse
return Replace(vfalse);
}
if (vtrue_type->Is(true_type_) && vfalse_type->Is(false_type_)) {
// Select(condition, vtrue:true, vfalse:false) => condition
return Replace(condition);
}
if (vtrue_type->Is(false_type_) && vfalse_type->Is(true_type_)) {
// Select(condition, vtrue:false, vfalse:true) => BooleanNot(condition)
node->TrimInputCount(1);
NodeProperties::ChangeOp(node, simplified()->BooleanNot());
return Changed(node);
}
// Try to narrow the type of the Select {node}, which might be more precise
// now after lowering based on types.
Type* type = Type::Union(vtrue_type, vfalse_type, graph()->zone());
Type* const node_type = NodeProperties::GetType(node);
if (!node_type->Is(type)) {
type = Type::Intersect(node_type, type, graph()->zone());
NodeProperties::SetType(node, type);
return Changed(node);
}
return NoChange();
}
Factory* TypedOptimization::factory() const { return isolate()->factory(); }
Graph* TypedOptimization::graph() const { return jsgraph()->graph(); }
Isolate* TypedOptimization::isolate() const { return jsgraph()->isolate(); }
SimplifiedOperatorBuilder* TypedOptimization::simplified() const {
return jsgraph()->simplified();
}
} // namespace compiler
} // namespace internal
} // namespace v8
// 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.
#ifndef V8_COMPILER_TYPED_OPTIMIZATION_H_
#define V8_COMPILER_TYPED_OPTIMIZATION_H_
#include "src/base/flags.h"
#include "src/compiler/graph-reducer.h"
namespace v8 {
namespace internal {
// Forward declarations.
class CompilationDependencies;
class Factory;
class Isolate;
class TypeCache;
namespace compiler {
// Forward declarations.
class JSGraph;
class SimplifiedOperatorBuilder;
class TypedOptimization final : public AdvancedReducer {
public:
// Flags that control the mode of operation.
enum Flag {
kNoFlags = 0u,
kDeoptimizationEnabled = 1u << 0,
};
typedef base::Flags<Flag> Flags;
TypedOptimization(Editor* editor, CompilationDependencies* dependencies,
Flags flags, JSGraph* jsgraph);
~TypedOptimization();
Reduction Reduce(Node* node) final;
private:
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceCheckString(Node* node);
Reduction ReduceLoadField(Node* node);
Reduction ReduceNumberRoundop(Node* node);
Reduction ReducePhi(Node* node);
Reduction ReduceSelect(Node* node);
CompilationDependencies* dependencies() const { return dependencies_; }
Factory* factory() const;
Flags flags() const { return flags_; }
Graph* graph() const;
Isolate* isolate() const;
JSGraph* jsgraph() const { return jsgraph_; }
SimplifiedOperatorBuilder* simplified() const;
CompilationDependencies* const dependencies_;
Flags const flags_;
JSGraph* const jsgraph_;
Type* const true_type_;
Type* const false_type_;
TypeCache const& type_cache_;
DISALLOW_COPY_AND_ASSIGN(TypedOptimization);
};
DEFINE_OPERATORS_FOR_FLAGS(TypedOptimization::Flags)
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_TYPED_OPTIMIZATION_H_
......@@ -723,6 +723,8 @@
'compiler/type-hint-analyzer.h',
'compiler/type-hints.cc',
'compiler/type-hints.h',
'compiler/typed-optimization.cc',
'compiler/typed-optimization.h',
'compiler/typer.cc',
'compiler/typer.h',
'compiler/unwinding-info-writer.h',
......
......@@ -33,36 +33,8 @@ const ExternalArrayType kExternalArrayTypes[] = {
kExternalInt16Array, kExternalUint32Array, kExternalInt32Array,
kExternalFloat32Array, kExternalFloat64Array};
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 size_t kIndices[] = {0, 1, 42, 100, 1024};
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};
Type* const kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
Type::Number(), Type::String(), Type::Object()};
......@@ -111,100 +83,6 @@ class JSTypedLoweringTest : public TypedGraphTest {
};
// -----------------------------------------------------------------------------
// Constant propagation
TEST_F(JSTypedLoweringTest, ParameterWithMinusZero) {
{
Reduction r = Reduce(
Parameter(Type::Constant(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::Constant(factory()->NewNumber(0), zone()), zone())));
EXPECT_FALSE(r.Changed());
}
}
TEST_F(JSTypedLoweringTest, ParameterWithNull) {
Handle<HeapObject> null = factory()->null_value();
{
Reduction r = Reduce(Parameter(Type::Constant(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(JSTypedLoweringTest, 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::Constant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r =
Reduce(Parameter(Type::Constant(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(JSTypedLoweringTest, ParameterWithPlainNumber) {
TRACED_FOREACH(double, value, kFloat64Values) {
Handle<Object> constant = factory()->NewNumber(value);
Reduction r = Reduce(Parameter(Type::Constant(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(JSTypedLoweringTest, 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::Constant(undefined, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(undefined));
}
}
// -----------------------------------------------------------------------------
// JSToBoolean
......@@ -219,60 +97,6 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithFalsish) {
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::Constant(factory()->false_value(), zone()),
Type::Range(0.0, 0.0, zone()), zone()),
zone()),
zone()),
zone()),
zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithTruish) {
Node* input = Parameter(
Type::Union(
Type::Constant(factory()->true_value(), zone()),
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumber) {
Node* input = Parameter(Type::OrderedNumber(), 0);
Node* context = Parameter(Type::Any(), 1);
......
// 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/typed-optimization.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 {
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};
Type* const kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
Type::Number(), Type::String(), Type::Object()};
} // namespace
class TypedOptimizationTest : public TypedGraphTest {
public:
TypedOptimizationTest()
: TypedGraphTest(3), javascript_(zone()), deps_(isolate(), zone()) {}
~TypedOptimizationTest() override {}
protected:
Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone());
SimplifiedOperatorBuilder simplified(zone());
JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified,
&machine);
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(zone(), graph());
TypedOptimization reducer(&graph_reducer, &deps_,
TypedOptimization::kDeoptimizationEnabled,
&jsgraph);
return reducer.Reduce(node);
}
JSOperatorBuilder* javascript() { return &javascript_; }
private:
JSOperatorBuilder javascript_;
CompilationDependencies deps_;
};
TEST_F(TypedOptimizationTest, ParameterWithMinusZero) {
{
Reduction r = Reduce(
Parameter(Type::Constant(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::Constant(factory()->NewNumber(0), zone()), zone())));
EXPECT_FALSE(r.Changed());
}
}
TEST_F(TypedOptimizationTest, ParameterWithNull) {
Handle<HeapObject> null = factory()->null_value();
{
Reduction r = Reduce(Parameter(Type::Constant(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::Constant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r =
Reduce(Parameter(Type::Constant(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::Constant(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::Constant(undefined, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsHeapConstant(undefined));
}
}
TEST_F(TypedOptimizationTest, JSToBooleanWithFalsish) {
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::Constant(factory()->false_value(), zone()),
Type::Range(0.0, 0.0, zone()), zone()),
zone()),
zone()),
zone()),
zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(TypedOptimizationTest, JSToBooleanWithTruish) {
Node* input = Parameter(
Type::Union(
Type::Constant(factory()->true_value(), zone()),
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(TypedOptimizationTest, JSToBooleanWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(
javascript()->ToBoolean(ToBooleanHint::kAny), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -75,6 +75,7 @@
'compiler/simplified-operator-unittest.cc',
'compiler/state-values-utils-unittest.cc',
'compiler/tail-call-optimization-unittest.cc',
'compiler/typed-optimization-unittest.cc',
'compiler/typer-unittest.cc',
'compiler/value-numbering-reducer-unittest.cc',
'compiler/zone-pool-unittest.cc',
......
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