[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:
parent
3f1e2346b4
commit
4b214d6fa2
@ -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));
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user