[turbofan] Pseudo-inline 'instanceof'

This patch extends the typed lowering with a specialized version of 'instanceof' that is used if the "class", i.e. the constructor function, is a known constant.

Unittests check that replacement occurs as intended. Functional correctness is ensured by extensive unit tests covering instanceof already in the testsuite.

TESTS=unittests/JSTypedLoweringTest.{JSInstanceOfSpecializationWithSmiCheck,JSInstanceOfSpecializationWithoutSmiCheck,JSInstanceOfNoSpecialization}

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

Cr-Commit-Position: refs/heads/master@{#31916}
This commit is contained in:
sigurds 2015-11-10 04:19:37 -08:00 committed by Commit bot
parent 8e09ee1dba
commit 45787501e5
7 changed files with 236 additions and 7 deletions

View File

@ -130,6 +130,14 @@ FieldAccess AccessBuilder::ForMapInstanceType() {
}
// static
FieldAccess AccessBuilder::ForMapPrototype() {
FieldAccess access = {kTaggedBase, Map::kPrototypeOffset, Handle<Name>(),
Type::TaggedPointer(), kMachAnyTagged};
return access;
}
// static
FieldAccess AccessBuilder::ForStringLength() {
FieldAccess access = {kTaggedBase, String::kLengthOffset, Handle<Name>(),

View File

@ -61,6 +61,9 @@ class AccessBuilder final : public AllStatic {
// Provides access to Map::instance_type() field.
static FieldAccess ForMapInstanceType();
// Provides access to Map::prototype() field.
static FieldAccess ForMapPrototype();
// Provides access to String::length() field.
static FieldAccess ForStringLength();

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#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-typed-lowering.h"
@ -22,8 +23,13 @@ namespace compiler {
// - relax effects from generic but not-side-effecting operations
JSTypedLowering::JSTypedLowering(Editor* editor, JSGraph* jsgraph, Zone* zone)
: AdvancedReducer(editor), jsgraph_(jsgraph) {
JSTypedLowering::JSTypedLowering(Editor* editor,
CompilationDependencies* dependencies,
Flags flags, JSGraph* jsgraph, Zone* zone)
: AdvancedReducer(editor),
dependencies_(dependencies),
flags_(flags),
jsgraph_(jsgraph) {
for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) {
double min = kMinInt / (1 << k);
double max = kMaxInt / (1 << k);
@ -1059,6 +1065,117 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
}
Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) {
DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
// If deoptimization is disabled, we cannot optimize.
if (!(flags() & kDeoptimizationEnabled)) return NoChange();
JSBinopReduction r(this, node);
Node* effect = r.effect();
Node* control = r.control();
if (r.right_type()->IsConstant() &&
r.right_type()->AsConstant()->Value()->IsJSFunction()) {
Handle<JSFunction> function =
Handle<JSFunction>::cast(r.right_type()->AsConstant()->Value());
Handle<SharedFunctionInfo> shared(function->shared(), isolate());
if (!function->map()->has_non_instance_prototype()) {
JSFunction::EnsureHasInitialMap(function);
DCHECK(function->has_initial_map());
Handle<Map> initial_map(function->initial_map(), isolate());
this->dependencies()->AssumeInitialMapCantChange(initial_map);
Node* prototype =
jsgraph()->Constant(handle(initial_map->prototype(), isolate()));
Node* if_is_smi = nullptr;
Node* e_is_smi = nullptr;
// If the left hand side is an object, no smi check is needed.
if (r.left_type()->Maybe(Type::TaggedSigned())) {
Node* is_smi = graph()->NewNode(simplified()->ObjectIsSmi(), r.left());
Node* branch_is_smi = graph()->NewNode(
common()->Branch(BranchHint::kFalse), is_smi, control);
if_is_smi = graph()->NewNode(common()->IfTrue(), branch_is_smi);
e_is_smi = effect;
control = graph()->NewNode(common()->IfFalse(), branch_is_smi);
}
Node* object_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
r.left(), effect, control);
// Loop through the {object}s prototype chain looking for the {prototype}.
Node* loop = control =
graph()->NewNode(common()->Loop(2), control, control);
Node* loop_effect = effect =
graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
Node* loop_object_map = graph()->NewNode(common()->Phi(kMachAnyTagged, 2),
object_map, r.left(), loop);
Node* object_prototype = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapPrototype()),
loop_object_map, loop_effect, control);
// Check if object prototype is equal to function prototype.
Node* eq_proto =
graph()->NewNode(simplified()->ReferenceEqual(r.right_type()),
object_prototype, prototype);
Node* branch_eq_proto = graph()->NewNode(
common()->Branch(BranchHint::kFalse), eq_proto, control);
Node* if_eq_proto = graph()->NewNode(common()->IfTrue(), branch_eq_proto);
Node* e_eq_proto = effect;
control = graph()->NewNode(common()->IfFalse(), branch_eq_proto);
// If not, check if object prototype is the null prototype.
Node* null_proto =
graph()->NewNode(simplified()->ReferenceEqual(r.right_type()),
object_prototype, jsgraph()->NullConstant());
Node* branch_null_proto = graph()->NewNode(
common()->Branch(BranchHint::kFalse), null_proto, control);
Node* if_null_proto =
graph()->NewNode(common()->IfTrue(), branch_null_proto);
Node* e_null_proto = effect;
control = graph()->NewNode(common()->IfFalse(), branch_null_proto);
Node* load_object_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
object_prototype, effect, control);
// Close the loop.
loop_effect->ReplaceInput(1, effect);
loop_object_map->ReplaceInput(1, load_object_map);
loop->ReplaceInput(1, control);
control =
graph()->NewNode(common()->Merge(2), if_eq_proto, if_null_proto);
effect = graph()->NewNode(common()->EffectPhi(2), e_eq_proto,
e_null_proto, control);
Node* result = graph()->NewNode(common()->Phi(kTypeBool, 2),
jsgraph()->TrueConstant(),
jsgraph()->FalseConstant(), control);
if (if_is_smi != nullptr) {
DCHECK(e_is_smi != nullptr);
control = graph()->NewNode(common()->Merge(2), if_is_smi, control);
effect =
graph()->NewNode(common()->EffectPhi(2), e_is_smi, effect, control);
result = graph()->NewNode(common()->Phi(kTypeBool, 2),
jsgraph()->FalseConstant(), result, control);
}
ReplaceWithValue(node, result, effect, control);
return Changed(result);
}
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSLoadContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
ContextAccess const& access = ContextAccessOf(node->op());
@ -2028,6 +2145,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSLoadProperty(node);
case IrOpcode::kJSStoreProperty:
return ReduceJSStoreProperty(node);
case IrOpcode::kJSInstanceOf:
return ReduceJSInstanceOf(node);
case IrOpcode::kJSLoadContext:
return ReduceJSLoadContext(node);
case IrOpcode::kJSStoreContext:
@ -2175,6 +2294,11 @@ MachineOperatorBuilder* JSTypedLowering::machine() const {
return jsgraph()->machine();
}
CompilationDependencies* JSTypedLowering::dependencies() const {
return dependencies_;
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -5,6 +5,7 @@
#ifndef V8_COMPILER_JS_TYPED_LOWERING_H_
#define V8_COMPILER_JS_TYPED_LOWERING_H_
#include "src/base/flags.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/opcodes.h"
@ -12,6 +13,7 @@ namespace v8 {
namespace internal {
// Forward declarations.
class CompilationDependencies;
class Factory;
@ -28,7 +30,15 @@ class SimplifiedOperatorBuilder;
// Lowers JS-level operators to simplified operators based on types.
class JSTypedLowering final : public AdvancedReducer {
public:
JSTypedLowering(Editor* editor, JSGraph* jsgraph, Zone* zone);
// Flags that control the mode of operation.
enum Flag {
kNoFlags = 0u,
kDeoptimizationEnabled = 1u << 0,
};
typedef base::Flags<Flag> Flags;
JSTypedLowering(Editor* editor, CompilationDependencies* dependencies,
Flags flags, JSGraph* jsgraph, Zone* zone);
~JSTypedLowering() final {}
Reduction Reduce(Node* node) final;
@ -44,6 +54,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSLoadProperty(Node* node);
Reduction ReduceJSStoreProperty(Node* node);
Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSStoreContext(Node* node);
Reduction ReduceJSEqual(Node* node, bool invert);
@ -87,15 +98,21 @@ class JSTypedLowering final : public AdvancedReducer {
CommonOperatorBuilder* common() const;
SimplifiedOperatorBuilder* simplified() const;
MachineOperatorBuilder* machine() const;
CompilationDependencies* dependencies() const;
Flags flags() const { return flags_; }
// Limits up to which context allocations are inlined.
static const int kFunctionContextAllocationLimit = 16;
static const int kBlockContextAllocationLimit = 16;
CompilationDependencies* dependencies_;
Flags flags_;
JSGraph* jsgraph_;
Type* shifted_int32_ranges_[4];
};
DEFINE_OPERATORS_FOR_FLAGS(JSTypedLowering::Flags)
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -591,7 +591,11 @@ struct TypedLoweringPhase {
data->common());
LoadElimination load_elimination(&graph_reducer);
JSBuiltinReducer builtin_reducer(&graph_reducer, data->jsgraph());
JSTypedLowering typed_lowering(&graph_reducer, data->jsgraph(), temp_zone);
JSTypedLowering typed_lowering(&graph_reducer, data->info()->dependencies(),
data->info()->is_deoptimization_enabled()
? JSTypedLowering::kDeoptimizationEnabled
: JSTypedLowering::kNoFlags,
data->jsgraph(), temp_zone);
JSIntrinsicLowering intrinsic_lowering(
&graph_reducer, data->jsgraph(),
data->info()->is_deoptimization_enabled()

View File

@ -5,6 +5,7 @@
// TODO(jochen): Remove this after the setting is turned on globally.
#define V8_IMMINENT_DEPRECATION_WARNINGS
#include "src/compilation-dependencies.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-typed-lowering.h"
#include "src/compiler/machine-operator.h"
@ -42,6 +43,7 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
machine(main_zone()),
simplified(main_zone()),
common(main_zone()),
deps(main_isolate(), main_zone()),
graph(main_zone()),
typer(main_isolate(), &graph),
context_node(NULL) {
@ -57,6 +59,7 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
MachineOperatorBuilder machine;
SimplifiedOperatorBuilder simplified;
CommonOperatorBuilder common;
CompilationDependencies deps;
Graph graph;
Typer typer;
Node* context_node;
@ -94,7 +97,9 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
&machine);
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(main_zone(), &graph);
JSTypedLowering reducer(&graph_reducer, &jsgraph, main_zone());
JSTypedLowering reducer(&graph_reducer, &deps,
JSTypedLowering::kDeoptimizationEnabled, &jsgraph,
main_zone());
Reduction reduction = reducer.Reduce(node);
if (reduction.Changed()) return reduction.replacement();
return node;

View File

@ -74,7 +74,8 @@ const LanguageMode kLanguageModes[] = {SLOPPY, STRICT, STRONG};
class JSTypedLoweringTest : public TypedGraphTest {
public:
JSTypedLoweringTest() : TypedGraphTest(3), javascript_(zone()) {}
JSTypedLoweringTest()
: TypedGraphTest(3), javascript_(zone()), deps_(isolate(), zone()) {}
~JSTypedLoweringTest() override {}
protected:
@ -85,7 +86,9 @@ class JSTypedLoweringTest : public TypedGraphTest {
&machine);
// TODO(titzer): mock the GraphReducer here for better unit testing.
GraphReducer graph_reducer(zone(), graph());
JSTypedLowering reducer(&graph_reducer, &jsgraph, zone());
JSTypedLowering reducer(&graph_reducer, &deps_,
JSTypedLowering::kDeoptimizationEnabled, &jsgraph,
zone());
return reducer.Reduce(node);
}
@ -116,6 +119,7 @@ class JSTypedLoweringTest : public TypedGraphTest {
private:
JSOperatorBuilder javascript_;
CompilationDependencies deps_;
};
@ -1168,6 +1172,70 @@ TEST_F(JSTypedLoweringTest, JSCreateWithContext) {
_));
}
// -----------------------------------------------------------------------------
// JSInstanceOf
// Test that instanceOf is reduced if and only if the right-hand side is a
// function constant. Functional correctness is ensured elsewhere.
TEST_F(JSTypedLoweringTest, JSInstanceOfSpecializationWithoutSmiCheck) {
Node* const context = Parameter(Type::Any());
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
// Reduce if left-hand side is known to be an object.
Node* instanceOf =
graph()->NewNode(javascript()->InstanceOf(), Parameter(Type::Object(), 0),
HeapConstant(isolate()->object_function()), context,
frame_state, effect, control);
Node* dummy = graph()->NewNode(javascript()->ToObject(), instanceOf, context,
frame_state, effect, control);
Reduction r = Reduce(instanceOf);
ASSERT_TRUE(r.Changed());
ASSERT_EQ(r.replacement(), dummy->InputAt(0));
ASSERT_NE(instanceOf, dummy->InputAt(0));
}
TEST_F(JSTypedLoweringTest, JSInstanceOfSpecializationWithSmiCheck) {
Node* const context = Parameter(Type::Any());
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
// Reduce if left-hand side could be a Smi.
Node* instanceOf =
graph()->NewNode(javascript()->InstanceOf(), Parameter(Type::Any(), 0),
HeapConstant(isolate()->object_function()), context,
frame_state, effect, control);
Node* dummy = graph()->NewNode(javascript()->ToObject(), instanceOf, context,
frame_state, effect, control);
Reduction r = Reduce(instanceOf);
ASSERT_TRUE(r.Changed());
ASSERT_EQ(r.replacement(), dummy->InputAt(0));
ASSERT_NE(instanceOf, dummy->InputAt(0));
}
TEST_F(JSTypedLoweringTest, JSInstanceOfNoSpecialization) {
Node* const context = Parameter(Type::Any());
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
// Do not reduce if right-hand side is not a function constant.
Node* instanceOf = graph()->NewNode(
javascript()->InstanceOf(), Parameter(Type::Any(), 0),
Parameter(Type::Any()), context, frame_state, effect, control);
Node* dummy = graph()->NewNode(javascript()->ToObject(), instanceOf, context,
frame_state, effect, control);
Reduction r = Reduce(instanceOf);
ASSERT_FALSE(r.Changed());
ASSERT_EQ(instanceOf, dummy->InputAt(0));
}
} // namespace compiler
} // namespace internal
} // namespace v8