[turbofan] Support for typed lowering of "prototype" load from functions.
Add initial support to optimize certain "prototype" loads from known JSFunctions which have a prototype. This includes an appropriate typing rule plus a matching rule for typed lowering. R=jarin@chromium.org Review URL: https://codereview.chromium.org/1482213002 Cr-Commit-Position: refs/heads/master@{#32390}
This commit is contained in:
parent
7730edcc12
commit
f7226a798a
@ -975,6 +975,27 @@ Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) {
|
||||
ReplaceWithValue(node, value, effect);
|
||||
return Replace(value);
|
||||
}
|
||||
// Optimize "prototype" property of functions.
|
||||
if (name.is_identical_to(factory()->prototype_string()) &&
|
||||
receiver_type->IsConstant() &&
|
||||
receiver_type->AsConstant()->Value()->IsJSFunction()) {
|
||||
// TODO(turbofan): This lowering might not kick in if we ever lower
|
||||
// the C++ accessor for "prototype" in an earlier optimization pass.
|
||||
Handle<JSFunction> function =
|
||||
Handle<JSFunction>::cast(receiver_type->AsConstant()->Value());
|
||||
if (function->has_initial_map()) {
|
||||
// We need to add a code dependency on the initial map of the {function}
|
||||
// in order to be notified about changes to the "prototype" of {function},
|
||||
// so it doesn't make sense to continue unless deoptimization is enabled.
|
||||
if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
||||
Handle<Map> initial_map(function->initial_map(), isolate());
|
||||
dependencies()->AssumeInitialMapCantChange(initial_map);
|
||||
Node* value =
|
||||
jsgraph()->Constant(handle(initial_map->prototype(), isolate()));
|
||||
ReplaceWithValue(node, value);
|
||||
return Replace(value);
|
||||
}
|
||||
}
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
|
@ -1262,7 +1262,37 @@ Type* Typer::Visitor::TypeJSLoadProperty(Node* node) {
|
||||
}
|
||||
|
||||
|
||||
Type* Typer::Visitor::TypeJSLoadNamed(Node* node) { return Type::Any(); }
|
||||
Type* Typer::Visitor::TypeJSLoadNamed(Node* node) {
|
||||
Factory* const f = isolate()->factory();
|
||||
Handle<Name> name = NamedAccessOf(node->op()).name();
|
||||
if (name.is_identical_to(f->prototype_string())) {
|
||||
Type* receiver = Operand(node, 0);
|
||||
if (receiver->Is(Type::None())) return Type::None();
|
||||
if (receiver->IsConstant() &&
|
||||
receiver->AsConstant()->Value()->IsJSFunction()) {
|
||||
Handle<JSFunction> function =
|
||||
Handle<JSFunction>::cast(receiver->AsConstant()->Value());
|
||||
if (function->has_prototype()) {
|
||||
// We need to add a code dependency on the initial map of the {function}
|
||||
// in order to be notified about changes to "prototype" of {function},
|
||||
// so we can only infer a constant type if deoptimization is enabled.
|
||||
if (flags() & kDeoptimizationEnabled) {
|
||||
JSFunction::EnsureHasInitialMap(function);
|
||||
Handle<Map> initial_map(function->initial_map(), isolate());
|
||||
dependencies()->AssumeInitialMapCantChange(initial_map);
|
||||
return Type::Constant(handle(initial_map->prototype(), isolate()),
|
||||
zone());
|
||||
}
|
||||
}
|
||||
} else if (receiver->IsClass() &&
|
||||
receiver->AsClass()->Map()->IsJSFunctionMap()) {
|
||||
Handle<Map> map = receiver->AsClass()->Map();
|
||||
return map->has_non_instance_prototype() ? Type::Primitive(zone())
|
||||
: Type::Receiver(zone());
|
||||
}
|
||||
}
|
||||
return Type::Any();
|
||||
}
|
||||
|
||||
|
||||
Type* Typer::Visitor::TypeJSLoadGlobal(Node* node) { return Type::Any(); }
|
||||
|
@ -952,6 +952,27 @@ TEST_F(JSTypedLoweringTest, JSLoadNamedStringLength) {
|
||||
}
|
||||
|
||||
|
||||
TEST_F(JSTypedLoweringTest, JSLoadNamedFunctionPrototype) {
|
||||
VectorSlotPair feedback;
|
||||
Handle<Name> name = factory()->prototype_string();
|
||||
Handle<JSFunction> function = isolate()->object_function();
|
||||
Handle<JSObject> function_prototype(JSObject::cast(function->prototype()));
|
||||
Node* const receiver = Parameter(Type::Constant(function, zone()), 0);
|
||||
Node* const vector = Parameter(Type::Internal(), 1);
|
||||
Node* const context = Parameter(Type::Internal(), 2);
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) {
|
||||
Reduction const r = Reduce(
|
||||
graph()->NewNode(javascript()->LoadNamed(language_mode, name, feedback),
|
||||
receiver, vector, context, EmptyFrameState(),
|
||||
EmptyFrameState(), effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(), IsHeapConstant(function_prototype));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// JSAdd
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user