[turbofan] Add CompilationDependency for a function's prototype property.

Introduce a CompilationDependency that let's us optimize the lookup of
a function's "prototype" property. This is basically the same as
InitialMapDependency, except that if the function's initial map doesn't
exist yet, it is created after compilation.

Bug: v8:7790, chromium:875175
Change-Id: I62834f1815b3cef282fa67e6d64a6ee0e3777929
Reviewed-on: https://chromium-review.googlesource.com/1184714
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55299}
This commit is contained in:
Georg Neis 2018-08-22 11:32:12 +02:00 committed by Commit Bot
parent 3f1e2346b4
commit 4b214d6fa2
6 changed files with 69 additions and 16 deletions

View File

@ -48,6 +48,40 @@ class InitialMapDependency final : public CompilationDependencies::Dependency {
MapRef initial_map_;
};
class PrototypePropertyDependency final
: public CompilationDependencies::Dependency {
public:
// TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the prototype.
PrototypePropertyDependency(const JSFunctionRef& function,
const ObjectRef& prototype)
: function_(function), prototype_(prototype) {
DCHECK(function_.has_prototype());
DCHECK(!function_.PrototypeRequiresRuntimeLookup());
DCHECK(function_.prototype().equals(prototype_));
}
bool IsValid() const override {
Handle<JSFunction> function = function_.object<JSFunction>();
return function->has_prototype_slot() && function->has_prototype() &&
!function->PrototypeRequiresRuntimeLookup() &&
function->prototype() == *prototype_.object();
}
void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid());
Handle<JSFunction> function = function_.object<JSFunction>();
if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function);
Handle<Map> initial_map(function->initial_map(), function_.isolate());
DependentCode::InstallDependency(function_.isolate(), code, initial_map,
DependentCode::kInitialMapChangedGroup);
}
private:
JSFunctionRef function_;
ObjectRef prototype_;
};
class StableMapDependency final : public CompilationDependencies::Dependency {
public:
explicit StableMapDependency(const MapRef& map) : map_(map) {
@ -261,6 +295,14 @@ MapRef CompilationDependencies::DependOnInitialMap(
return map;
}
ObjectRef CompilationDependencies::DependOnPrototypeProperty(
const JSFunctionRef& function) {
ObjectRef prototype = function.prototype();
dependencies_.push_front(
new (zone_) PrototypePropertyDependency(function, prototype));
return prototype;
}
void CompilationDependencies::DependOnStableMap(const MapRef& map) {
if (map.CanTransition()) {
dependencies_.push_front(new (zone_) StableMapDependency(map));

View File

@ -36,6 +36,10 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
// stays the initial map.
MapRef DependOnInitialMap(const JSFunctionRef& function);
// Return the "prototype" property of the given function and record the
// assumption that it doesn't change.
ObjectRef DependOnPrototypeProperty(const JSFunctionRef& function);
// Record the assumption that {map} stays stable.
void DependOnStableMap(const MapRef& map);

View File

@ -75,6 +75,9 @@ class JSFunctionData : public JSObjectData {
public:
ObjectData* const global_proxy;
ObjectData* const initial_map; // Can be nullptr.
bool const has_prototype;
ObjectData* const prototype; // Can be nullptr.
bool const PrototypeRequiresRuntimeLookup;
ObjectData* const shared;
JSFunctionData(JSHeapBroker* broker_, Handle<JSFunction> object_,
@ -301,6 +304,9 @@ JSFunctionData::JSFunctionData(JSHeapBroker* broker_,
initial_map(object_->has_prototype_slot() && object_->has_initial_map()
? GET_OR_CREATE(initial_map)
: nullptr),
has_prototype(object_->has_prototype_slot() && object_->has_prototype()),
prototype(has_prototype ? GET_OR_CREATE(prototype) : nullptr),
PrototypeRequiresRuntimeLookup(object_->PrototypeRequiresRuntimeLookup()),
shared(GET_OR_CREATE(shared)) {
if (initial_map != nullptr &&
initial_map->AsMap()->instance_type == JS_ARRAY_TYPE) {
@ -966,7 +972,10 @@ HANDLE_ACCESSOR_C(HeapNumber, double, value)
HANDLE_ACCESSOR(JSArray, Object, length)
BIMODAL_ACCESSOR_C(JSFunction, bool, has_prototype)
BIMODAL_ACCESSOR_C(JSFunction, bool, PrototypeRequiresRuntimeLookup)
BIMODAL_ACCESSOR(JSFunction, Map, initial_map)
BIMODAL_ACCESSOR(JSFunction, Object, prototype)
HANDLE_ACCESSOR_C(JSFunction, bool, IsConstructor)
HANDLE_ACCESSOR(JSFunction, JSGlobalProxy, global_proxy)
HANDLE_ACCESSOR(JSFunction, SharedFunctionInfo, shared)

View File

@ -171,7 +171,9 @@ class JSFunctionRef : public JSObjectRef {
bool IsConstructor() const;
bool has_initial_map() const;
MapRef initial_map() const;
bool has_prototype() const;
ObjectRef prototype() const;
bool PrototypeRequiresRuntimeLookup() const;
JSGlobalProxyRef global_proxy() const;
int InitialMapInstanceSizeWithMinSlack() const;
SharedFunctionInfoRef shared() const;

View File

@ -1103,21 +1103,17 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
if (m.Value()->IsJSFunction() &&
p.name().is_identical_to(factory()->prototype_string())) {
// Optimize "prototype" property of functions.
Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
if (!function->has_prototype_slot() || !function->has_initial_map()) {
JSFunctionRef function = m.Ref(js_heap_broker()).AsJSFunction();
// TODO(neis): Remove the has_prototype_slot condition once the broker is
// always enabled.
if (!function.map().has_prototype_slot() || !function.has_prototype() ||
function.PrototypeRequiresRuntimeLookup()) {
return NoChange();
}
if (!function->PrototypeRequiresRuntimeLookup()) {
// 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}.
dependencies()->DependOnInitialMap(
JSFunctionRef(js_heap_broker(), function));
Handle<Object> prototype(function->prototype(), isolate());
Node* value = jsgraph()->Constant(prototype);
ReplaceWithValue(node, value);
return Replace(value);
}
ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function);
Node* value = jsgraph()->Constant(prototype);
ReplaceWithValue(node, value);
return Replace(value);
} else if (m.Value()->IsString() &&
p.name().is_identical_to(factory()->length_string())) {
// Constant-fold "length" property on constant strings.

View File

@ -2448,7 +2448,7 @@ bool JSFunction::PrototypeRequiresRuntimeLookup() {
Object* JSFunction::instance_prototype() {
DCHECK(has_instance_prototype());
if (has_initial_map()) return initial_map()->prototype();
// When there is no initial map and the prototype is a JSObject, the
// When there is no initial map and the prototype is a JSReceiver, the
// initial map field is used for the prototype field.
return prototype_or_initial_map();
}
@ -2456,7 +2456,7 @@ Object* JSFunction::instance_prototype() {
Object* JSFunction::prototype() {
DCHECK(has_prototype());
// If the function's prototype property has been set to a non-JSObject
// If the function's prototype property has been set to a non-JSReceiver
// value, that value is stored in the constructor field of the map.
if (map()->has_non_instance_prototype()) {
Object* prototype = map()->GetConstructor();