[compiler] Make JSFunction bg-serialized
This wraps up the transition away from kSerialized ref kinds. Since JSFunctionRef is a complex type, we don't attempt full consistency on the background thread. Instead, we serialize functions on the background in a partially-racy manner, in which consistency between different JSFunction fields is *not* guaranteed. Consistency is later verified through a new compilation dependency kind during finalization. Bug: v8:7790, v8:12004 Change-Id: Ic2b78af9c9fe183c8769d323132bb304b151dc75 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2968404 Commit-Queue: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#75789}
This commit is contained in:
parent
18289533db
commit
0dba97f8dc
@ -21,14 +21,17 @@ namespace compiler {
|
||||
|
||||
CompilationDependencies::CompilationDependencies(JSHeapBroker* broker,
|
||||
Zone* zone)
|
||||
: zone_(zone), broker_(broker), dependencies_(zone) {}
|
||||
: zone_(zone), broker_(broker), dependencies_(zone) {
|
||||
broker->set_dependencies(this);
|
||||
}
|
||||
|
||||
class InitialMapDependency final : public CompilationDependency {
|
||||
public:
|
||||
InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map)
|
||||
InitialMapDependency(JSHeapBroker* broker, const JSFunctionRef& function,
|
||||
const MapRef& initial_map)
|
||||
: function_(function), initial_map_(initial_map) {
|
||||
DCHECK(function_.has_initial_map());
|
||||
DCHECK(function_.initial_map().equals(initial_map_));
|
||||
DCHECK(function_.has_initial_map(broker->dependencies()));
|
||||
DCHECK(function_.initial_map(broker->dependencies()).equals(initial_map_));
|
||||
}
|
||||
|
||||
bool IsValid() const override {
|
||||
@ -51,19 +54,22 @@ class InitialMapDependency final : public CompilationDependency {
|
||||
|
||||
class PrototypePropertyDependency final : public CompilationDependency {
|
||||
public:
|
||||
PrototypePropertyDependency(const JSFunctionRef& function,
|
||||
PrototypePropertyDependency(JSHeapBroker* broker,
|
||||
const JSFunctionRef& function,
|
||||
const ObjectRef& prototype)
|
||||
: function_(function), prototype_(prototype) {
|
||||
DCHECK(function_.has_prototype());
|
||||
DCHECK(!function_.PrototypeRequiresRuntimeLookup());
|
||||
DCHECK(function_.prototype().equals(prototype_));
|
||||
DCHECK(function_.has_instance_prototype(broker->dependencies()));
|
||||
DCHECK(!function_.PrototypeRequiresRuntimeLookup(broker->dependencies()));
|
||||
DCHECK(function_.instance_prototype(broker->dependencies())
|
||||
.equals(prototype_));
|
||||
}
|
||||
|
||||
bool IsValid() const override {
|
||||
Handle<JSFunction> function = function_.object();
|
||||
return function->has_prototype_slot() && function->has_prototype() &&
|
||||
return function->has_prototype_slot() &&
|
||||
function->has_instance_prototype() &&
|
||||
!function->PrototypeRequiresRuntimeLookup() &&
|
||||
function->prototype() == *prototype_.object();
|
||||
function->instance_prototype() == *prototype_.object();
|
||||
}
|
||||
|
||||
void PrepareInstall() const override {
|
||||
@ -338,6 +344,21 @@ class OwnConstantDictionaryPropertyDependency final
|
||||
ObjectRef const value_;
|
||||
};
|
||||
|
||||
class ConsistentJSFunctionViewDependency final : public CompilationDependency {
|
||||
public:
|
||||
explicit ConsistentJSFunctionViewDependency(const JSFunctionRef& function)
|
||||
: function_(function) {}
|
||||
|
||||
bool IsValid() const override {
|
||||
return function_.IsConsistentWithHeapState();
|
||||
}
|
||||
|
||||
void Install(Handle<Code> code) const override {}
|
||||
|
||||
private:
|
||||
const JSFunctionRef function_;
|
||||
};
|
||||
|
||||
class TransitionDependency final : public CompilationDependency {
|
||||
public:
|
||||
explicit TransitionDependency(const MapRef& map) : map_(map) {
|
||||
@ -388,8 +409,7 @@ class FieldRepresentationDependency final : public CompilationDependency {
|
||||
Representation representation)
|
||||
: owner_(owner),
|
||||
descriptor_(descriptor),
|
||||
representation_(representation) {
|
||||
}
|
||||
representation_(representation) {}
|
||||
|
||||
bool IsValid() const override {
|
||||
DisallowGarbageCollection no_heap_allocation;
|
||||
@ -639,17 +659,17 @@ void CompilationDependencies::RecordDependency(
|
||||
MapRef CompilationDependencies::DependOnInitialMap(
|
||||
const JSFunctionRef& function) {
|
||||
DCHECK(!function.IsNeverSerializedHeapObject());
|
||||
MapRef map = function.initial_map();
|
||||
RecordDependency(zone_->New<InitialMapDependency>(function, map));
|
||||
MapRef map = function.initial_map(this);
|
||||
RecordDependency(zone_->New<InitialMapDependency>(broker_, function, map));
|
||||
return map;
|
||||
}
|
||||
|
||||
ObjectRef CompilationDependencies::DependOnPrototypeProperty(
|
||||
const JSFunctionRef& function) {
|
||||
DCHECK(!function.IsNeverSerializedHeapObject());
|
||||
ObjectRef prototype = function.prototype();
|
||||
ObjectRef prototype = function.instance_prototype(this);
|
||||
RecordDependency(
|
||||
zone_->New<PrototypePropertyDependency>(function, prototype));
|
||||
zone_->New<PrototypePropertyDependency>(broker_, function, prototype));
|
||||
return prototype;
|
||||
}
|
||||
|
||||
@ -859,7 +879,9 @@ void CompilationDependencies::DependOnStablePrototypeChains(
|
||||
// Implemented according to ES6 section 7.3.2 GetV (V, P).
|
||||
base::Optional<JSFunctionRef> constructor =
|
||||
broker_->target_native_context().GetConstructorFunction(receiver_map);
|
||||
if (constructor.has_value()) receiver_map = constructor->initial_map();
|
||||
if (constructor.has_value()) {
|
||||
receiver_map = constructor->initial_map(this);
|
||||
}
|
||||
}
|
||||
DependOnStablePrototypeChain(this, receiver_map, last_prototype);
|
||||
}
|
||||
@ -882,6 +904,12 @@ void CompilationDependencies::DependOnElementsKinds(
|
||||
CHECK_EQ(current.nested_site().AsSmi(), 0);
|
||||
}
|
||||
|
||||
void CompilationDependencies::DependOnConsistentJSFunctionView(
|
||||
const JSFunctionRef& function) {
|
||||
DCHECK(broker_->is_concurrent_inlining());
|
||||
RecordDependency(zone_->New<ConsistentJSFunctionViewDependency>(function));
|
||||
}
|
||||
|
||||
SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
|
||||
int instance_size)
|
||||
: instance_size_(instance_size),
|
||||
@ -893,13 +921,13 @@ SlackTrackingPrediction
|
||||
CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
|
||||
const JSFunctionRef& function) {
|
||||
MapRef initial_map = DependOnInitialMap(function);
|
||||
int instance_size = function.InitialMapInstanceSizeWithMinSlack();
|
||||
int instance_size = function.InitialMapInstanceSizeWithMinSlack(this);
|
||||
// Currently, we always install the prediction dependency. If this turns out
|
||||
// to be too expensive, we can only install the dependency if slack
|
||||
// tracking is active.
|
||||
RecordDependency(zone_->New<InitialMapInstanceSizePredictionDependency>(
|
||||
function, instance_size));
|
||||
DCHECK_LE(instance_size, function.initial_map().instance_size());
|
||||
DCHECK_LE(instance_size, function.initial_map(this).instance_size());
|
||||
return SlackTrackingPrediction(initial_map, instance_size);
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,8 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
|
||||
// Like DependOnElementsKind but also applies to all nested allocation sites.
|
||||
void DependOnElementsKinds(const AllocationSiteRef& site);
|
||||
|
||||
void DependOnConsistentJSFunctionView(const JSFunctionRef& function);
|
||||
|
||||
// Predict the final instance size for {function}'s initial map and record
|
||||
// the assumption that this prediction is correct. In addition, register
|
||||
// the initial map dependency. This method returns the {function}'s the
|
||||
|
@ -725,64 +725,90 @@ class JSBoundFunctionData : public JSObjectData {
|
||||
class JSFunctionData : public JSObjectData {
|
||||
public:
|
||||
JSFunctionData(JSHeapBroker* broker, ObjectData** storage,
|
||||
Handle<JSFunction> object);
|
||||
Handle<JSFunction> object,
|
||||
ObjectDataKind kind = kSerializedHeapObject)
|
||||
: JSObjectData(broker, storage, object, kind) {
|
||||
Serialize(broker);
|
||||
}
|
||||
|
||||
bool has_feedback_vector() const { return has_feedback_vector_; }
|
||||
bool has_initial_map() const { return has_initial_map_; }
|
||||
bool has_prototype() const { return has_prototype_; }
|
||||
bool IsConsistentWithHeapState(JSHeapBroker* broker) const;
|
||||
|
||||
bool recorded_dependency() const { return recorded_dependency_; }
|
||||
void set_recorded_dependency() { recorded_dependency_ = true; }
|
||||
|
||||
bool has_feedback_vector() const {
|
||||
CHECK(serialized_);
|
||||
return has_feedback_vector_;
|
||||
}
|
||||
bool has_initial_map() const {
|
||||
CHECK(serialized_);
|
||||
return has_initial_map_;
|
||||
}
|
||||
bool has_instance_prototype() const {
|
||||
CHECK(serialized_);
|
||||
return has_instance_prototype_;
|
||||
}
|
||||
bool PrototypeRequiresRuntimeLookup() const {
|
||||
CHECK(serialized_);
|
||||
return PrototypeRequiresRuntimeLookup_;
|
||||
}
|
||||
|
||||
void Serialize(JSHeapBroker* broker);
|
||||
bool serialized() const { return serialized_; }
|
||||
|
||||
void SerializeCodeAndFeedback(JSHeapBroker* broker);
|
||||
bool serialized_code_and_feedback() const {
|
||||
return serialized_code_and_feedback_;
|
||||
ObjectData* context() const {
|
||||
CHECK(serialized_);
|
||||
return context_;
|
||||
}
|
||||
ObjectData* native_context() const {
|
||||
CHECK(serialized_);
|
||||
return native_context_;
|
||||
}
|
||||
MapData* initial_map() const {
|
||||
CHECK(serialized_);
|
||||
return initial_map_;
|
||||
}
|
||||
ObjectData* instance_prototype() const {
|
||||
CHECK(serialized_);
|
||||
return instance_prototype_;
|
||||
}
|
||||
ObjectData* shared() const {
|
||||
CHECK(serialized_);
|
||||
return shared_;
|
||||
}
|
||||
|
||||
ObjectData* context() const { return context_; }
|
||||
ObjectData* native_context() const { return native_context_; }
|
||||
ObjectData* initial_map() const { return initial_map_; }
|
||||
ObjectData* prototype() const { return prototype_; }
|
||||
ObjectData* shared() const { return shared_; }
|
||||
ObjectData* raw_feedback_cell() const {
|
||||
DCHECK(serialized_code_and_feedback());
|
||||
CHECK(serialized_);
|
||||
return feedback_cell_;
|
||||
}
|
||||
ObjectData* feedback_vector() const {
|
||||
DCHECK(serialized_code_and_feedback());
|
||||
CHECK(serialized_);
|
||||
return feedback_vector_;
|
||||
}
|
||||
ObjectData* code() const {
|
||||
DCHECK(serialized_code_and_feedback());
|
||||
DCHECK(!broker()->is_concurrent_inlining());
|
||||
return code_;
|
||||
}
|
||||
int initial_map_instance_size_with_min_slack() const {
|
||||
CHECK(serialized_);
|
||||
return initial_map_instance_size_with_min_slack_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool has_feedback_vector_;
|
||||
bool has_initial_map_;
|
||||
bool has_prototype_;
|
||||
bool PrototypeRequiresRuntimeLookup_;
|
||||
|
||||
void Serialize(JSHeapBroker* broker);
|
||||
bool serialized_ = false;
|
||||
bool serialized_code_and_feedback_ = false;
|
||||
|
||||
bool recorded_dependency_ = false;
|
||||
|
||||
bool has_feedback_vector_ = false;
|
||||
ObjectData* prototype_or_initial_map_ = nullptr;
|
||||
bool has_initial_map_ = false;
|
||||
bool has_instance_prototype_ = false;
|
||||
bool PrototypeRequiresRuntimeLookup_ = false;
|
||||
|
||||
ObjectData* context_ = nullptr;
|
||||
ObjectData* native_context_ = nullptr;
|
||||
ObjectData* initial_map_ = nullptr;
|
||||
ObjectData* prototype_ = nullptr;
|
||||
ObjectData* native_context_ = nullptr; // Derives from context_.
|
||||
MapData* initial_map_ = nullptr; // Derives from prototype_or_initial_map_.
|
||||
ObjectData* instance_prototype_ =
|
||||
nullptr; // Derives from prototype_or_initial_map_.
|
||||
ObjectData* shared_ = nullptr;
|
||||
ObjectData* feedback_vector_ = nullptr;
|
||||
ObjectData* feedback_vector_ = nullptr; // Derives from feedback_cell.
|
||||
ObjectData* feedback_cell_ = nullptr;
|
||||
ObjectData* code_ = nullptr;
|
||||
int initial_map_instance_size_with_min_slack_;
|
||||
int initial_map_instance_size_with_min_slack_; // Derives from
|
||||
// prototype_or_initial_map_.
|
||||
ObjectData* function_data_ = nullptr;
|
||||
};
|
||||
|
||||
class RegExpBoilerplateDescriptionData : public HeapObjectData {
|
||||
@ -1061,6 +1087,189 @@ class MapData : public HeapObjectData {
|
||||
bool serialized_for_element_store_ = false;
|
||||
};
|
||||
|
||||
// IMPORTANT: Keep this sync'd with JSFunctionData::IsConsistentWithHeapState.
|
||||
void JSFunctionData::Serialize(JSHeapBroker* broker) {
|
||||
CHECK(!serialized_);
|
||||
CHECK(!broker->ObjectMayBeUninitialized(HeapObject::cast(*object())));
|
||||
|
||||
TraceScope tracer(broker, this, "JSFunctionData::Serialize");
|
||||
Handle<JSFunction> function = Handle<JSFunction>::cast(object());
|
||||
|
||||
// This function may run on the background thread and thus must be individual
|
||||
// fields in a thread-safe manner. Consistency between fields is *not*
|
||||
// guaranteed here, instead we verify it in `IsConsistentWithHeapState`,
|
||||
// called during job finalization. Relaxed loads are thus okay: we're
|
||||
// guaranteed to see an initialized JSFunction object, and after
|
||||
// initialization fields remain in a valid state.
|
||||
|
||||
Context context = function->context(kRelaxedLoad);
|
||||
context_ = broker->GetOrCreateData(context, kAssumeMemoryFence);
|
||||
CHECK(context_->IsContext());
|
||||
|
||||
native_context_ = broker->GetOrCreateData(context.map().native_context(),
|
||||
kAssumeMemoryFence);
|
||||
CHECK(native_context_->IsNativeContext());
|
||||
|
||||
SharedFunctionInfo shared = function->shared(kRelaxedLoad);
|
||||
shared_ = broker->GetOrCreateData(shared, kAssumeMemoryFence);
|
||||
|
||||
function_data_ = broker->GetOrCreateData(shared.function_data(kAcquireLoad),
|
||||
kAssumeMemoryFence);
|
||||
|
||||
if (function->has_prototype_slot()) {
|
||||
prototype_or_initial_map_ = broker->GetOrCreateData(
|
||||
function->prototype_or_initial_map(kAcquireLoad), kAssumeMemoryFence);
|
||||
|
||||
has_initial_map_ = prototype_or_initial_map_->IsMap();
|
||||
if (has_initial_map_) {
|
||||
initial_map_ = prototype_or_initial_map_->AsMap();
|
||||
|
||||
MapRef initial_map_ref = TryMakeRef<Map>(broker, initial_map_).value();
|
||||
if (initial_map_ref.IsInobjectSlackTrackingInProgress()) {
|
||||
initial_map_instance_size_with_min_slack_ =
|
||||
initial_map_ref.object()->InstanceSizeFromSlack(
|
||||
initial_map_ref.object()->ComputeMinObjectSlack(
|
||||
broker->isolate()));
|
||||
} else {
|
||||
initial_map_instance_size_with_min_slack_ =
|
||||
initial_map_ref.instance_size();
|
||||
}
|
||||
|
||||
if (!initial_map_->should_access_heap() &&
|
||||
!broker->is_concurrent_inlining()) {
|
||||
// TODO(neis): This is currently only needed for native_context's
|
||||
// object_function, as used by GetObjectCreateMap. If no further use
|
||||
// sites show up, we should move this into NativeContextData::Serialize.
|
||||
initial_map_->SerializePrototype(broker);
|
||||
initial_map_->SerializeConstructor(broker);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_initial_map_) {
|
||||
has_instance_prototype_ = true;
|
||||
instance_prototype_ = broker->GetOrCreateData(
|
||||
Handle<Map>::cast(initial_map_->object())->prototype(),
|
||||
kAssumeMemoryFence);
|
||||
} else if (prototype_or_initial_map_->IsHeapObject() &&
|
||||
!Handle<HeapObject>::cast(prototype_or_initial_map_->object())
|
||||
->IsTheHole()) {
|
||||
has_instance_prototype_ = true;
|
||||
instance_prototype_ = prototype_or_initial_map_;
|
||||
}
|
||||
}
|
||||
|
||||
PrototypeRequiresRuntimeLookup_ = function->PrototypeRequiresRuntimeLookup();
|
||||
|
||||
FeedbackCell feedback_cell = function->raw_feedback_cell(kAcquireLoad);
|
||||
feedback_cell_ = broker->GetOrCreateData(feedback_cell, kAssumeMemoryFence);
|
||||
|
||||
ObjectData* maybe_feedback_vector = broker->GetOrCreateData(
|
||||
feedback_cell.value(kAcquireLoad), kAssumeMemoryFence);
|
||||
if (shared.is_compiled() && maybe_feedback_vector->IsFeedbackVector()) {
|
||||
has_feedback_vector_ = true;
|
||||
feedback_vector_ = maybe_feedback_vector;
|
||||
}
|
||||
|
||||
serialized_ = true;
|
||||
}
|
||||
|
||||
// IMPORTANT: Keep this sync'd with JSFunctionData::Serialize.
|
||||
bool JSFunctionData::IsConsistentWithHeapState(JSHeapBroker* broker) const {
|
||||
CHECK(serialized_);
|
||||
|
||||
Handle<JSFunction> f = Handle<JSFunction>::cast(object());
|
||||
|
||||
if (*context_->object() != f->context()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::context");
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_EQ(*native_context_->object(), f->native_context());
|
||||
CHECK_EQ(*shared_->object(), f->shared());
|
||||
|
||||
if (*function_data_->object() !=
|
||||
Handle<SharedFunctionInfo>::cast(shared_->object())
|
||||
->function_data(kAcquireLoad)) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::function_data");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (f->has_prototype_slot()) {
|
||||
if (*prototype_or_initial_map_->object() !=
|
||||
f->prototype_or_initial_map(kAcquireLoad)) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::prototype_or_initial_map");
|
||||
return false;
|
||||
}
|
||||
if (has_initial_map_ != f->has_initial_map()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::has_initial_map");
|
||||
return false;
|
||||
}
|
||||
if (has_instance_prototype_ != f->has_instance_prototype()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::has_instance_prototype");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
DCHECK(!has_initial_map_);
|
||||
DCHECK(!has_instance_prototype_);
|
||||
}
|
||||
|
||||
if (has_initial_map()) {
|
||||
if (*initial_map_->object() != f->initial_map()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::initial_map");
|
||||
return false;
|
||||
}
|
||||
if (initial_map_instance_size_with_min_slack_ !=
|
||||
f->ComputeInstanceSizeWithMinSlack(f->GetIsolate())) {
|
||||
TRACE_BROKER_MISSING(broker,
|
||||
"JSFunction::ComputeInstanceSizeWithMinSlack");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
DCHECK_NULL(initial_map_);
|
||||
}
|
||||
|
||||
if (has_instance_prototype_) {
|
||||
if (*instance_prototype_->object() != f->instance_prototype()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::instance_prototype");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
DCHECK_NULL(instance_prototype_);
|
||||
}
|
||||
|
||||
if (PrototypeRequiresRuntimeLookup_ != f->PrototypeRequiresRuntimeLookup()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::PrototypeRequiresRuntimeLookup");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*feedback_cell_->object() != f->raw_feedback_cell()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::raw_feedback_cell");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (has_feedback_vector_ != f->has_feedback_vector()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::has_feedback_vector");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (has_feedback_vector_) {
|
||||
if (*feedback_vector_->object() != f->feedback_vector()) {
|
||||
TRACE_BROKER_MISSING(broker, "JSFunction::feedback_vector");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
DCHECK_NULL(feedback_vector_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JSFunctionRef::IsConsistentWithHeapState() const {
|
||||
DCHECK(broker()->is_concurrent_inlining());
|
||||
DCHECK(broker()->IsMainThread());
|
||||
return data()->AsJSFunction()->IsConsistentWithHeapState(broker());
|
||||
}
|
||||
|
||||
AllocationSiteData::AllocationSiteData(JSHeapBroker* broker,
|
||||
ObjectData** storage,
|
||||
Handle<AllocationSite> object)
|
||||
@ -1228,75 +1437,6 @@ MapData::MapData(JSHeapBroker* broker, ObjectData** storage, Handle<Map> object,
|
||||
}
|
||||
}
|
||||
|
||||
JSFunctionData::JSFunctionData(JSHeapBroker* broker, ObjectData** storage,
|
||||
Handle<JSFunction> object)
|
||||
: JSObjectData(broker, storage, object),
|
||||
has_feedback_vector_(object->has_feedback_vector()),
|
||||
has_initial_map_(object->has_prototype_slot() &&
|
||||
object->has_initial_map()),
|
||||
has_prototype_(object->has_prototype_slot() && object->has_prototype()),
|
||||
PrototypeRequiresRuntimeLookup_(
|
||||
object->PrototypeRequiresRuntimeLookup()) {}
|
||||
|
||||
void JSFunctionData::Serialize(JSHeapBroker* broker) {
|
||||
if (serialized_) return;
|
||||
serialized_ = true;
|
||||
|
||||
TraceScope tracer(broker, this, "JSFunctionData::Serialize");
|
||||
Handle<JSFunction> function = Handle<JSFunction>::cast(object());
|
||||
|
||||
DCHECK_NULL(context_);
|
||||
DCHECK_NULL(native_context_);
|
||||
DCHECK_NULL(initial_map_);
|
||||
DCHECK_NULL(prototype_);
|
||||
DCHECK_NULL(shared_);
|
||||
|
||||
context_ = broker->GetOrCreateData(function->context());
|
||||
native_context_ = broker->GetOrCreateData(function->native_context());
|
||||
shared_ = broker->GetOrCreateData(function->shared());
|
||||
|
||||
initial_map_ = has_initial_map()
|
||||
? broker->GetOrCreateData(function->initial_map())
|
||||
: nullptr;
|
||||
prototype_ = has_prototype() ? broker->GetOrCreateData(function->prototype())
|
||||
: nullptr;
|
||||
|
||||
if (initial_map_ != nullptr) {
|
||||
initial_map_instance_size_with_min_slack_ =
|
||||
function->ComputeInstanceSizeWithMinSlack(broker->isolate());
|
||||
}
|
||||
if (initial_map_ != nullptr && !initial_map_->should_access_heap()) {
|
||||
initial_map_->AsMap()->SerializeConstructor(broker);
|
||||
// TODO(neis): This is currently only needed for native_context's
|
||||
// object_function, as used by GetObjectCreateMap. If no further use sites
|
||||
// show up, we should move this into NativeContextData::Serialize.
|
||||
initial_map_->AsMap()->SerializePrototype(broker);
|
||||
}
|
||||
}
|
||||
|
||||
void JSFunctionData::SerializeCodeAndFeedback(JSHeapBroker* broker) {
|
||||
DCHECK(serialized_);
|
||||
if (serialized_code_and_feedback_) return;
|
||||
serialized_code_and_feedback_ = true;
|
||||
|
||||
TraceScope tracer(broker, this, "JSFunctionData::SerializeCodeAndFeedback");
|
||||
Handle<JSFunction> function = Handle<JSFunction>::cast(object());
|
||||
|
||||
DCHECK_NULL(feedback_cell_);
|
||||
DCHECK_NULL(feedback_vector_);
|
||||
DCHECK_NULL(code_);
|
||||
if (!broker->is_concurrent_inlining()) {
|
||||
// This is conditionalized because Code objects are never serialized now.
|
||||
// We only need to represent the code object in serialized data when
|
||||
// we're unable to perform direct heap accesses.
|
||||
code_ = broker->GetOrCreateData(function->code(kAcquireLoad));
|
||||
}
|
||||
feedback_cell_ = broker->GetOrCreateData(function->raw_feedback_cell());
|
||||
feedback_vector_ = has_feedback_vector()
|
||||
? broker->GetOrCreateData(function->feedback_vector())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
class DescriptorArrayData : public HeapObjectData {
|
||||
public:
|
||||
DescriptorArrayData(JSHeapBroker* broker, ObjectData** storage,
|
||||
@ -1527,8 +1667,6 @@ bool JSBoundFunctionData::Serialize(JSHeapBroker* broker) {
|
||||
if (bound_target_function_->IsJSBoundFunction()) {
|
||||
serialized_nested =
|
||||
bound_target_function_->AsJSBoundFunction()->Serialize(broker);
|
||||
} else if (bound_target_function_->IsJSFunction()) {
|
||||
bound_target_function_->AsJSFunction()->Serialize(broker);
|
||||
}
|
||||
}
|
||||
if (!serialized_nested) {
|
||||
@ -1855,6 +1993,8 @@ void JSObjectData::SerializeElements(JSHeapBroker* broker) {
|
||||
}
|
||||
|
||||
void MapData::SerializeConstructor(JSHeapBroker* broker) {
|
||||
CHECK(!broker->is_concurrent_inlining());
|
||||
|
||||
if (serialized_constructor_) return;
|
||||
serialized_constructor_ = true;
|
||||
|
||||
@ -1866,6 +2006,8 @@ void MapData::SerializeConstructor(JSHeapBroker* broker) {
|
||||
}
|
||||
|
||||
void MapData::SerializeBackPointer(JSHeapBroker* broker) {
|
||||
CHECK(!broker->is_concurrent_inlining());
|
||||
|
||||
if (serialized_backpointer_) return;
|
||||
serialized_backpointer_ = true;
|
||||
|
||||
@ -1877,6 +2019,8 @@ void MapData::SerializeBackPointer(JSHeapBroker* broker) {
|
||||
}
|
||||
|
||||
bool MapData::TrySerializePrototype(JSHeapBroker* broker) {
|
||||
CHECK(!broker->is_concurrent_inlining());
|
||||
|
||||
if (serialized_prototype_) return true;
|
||||
|
||||
TraceScope tracer(broker, this, "MapData::SerializePrototype");
|
||||
@ -2503,10 +2647,12 @@ bool MapRef::supports_fast_array_resize() const {
|
||||
return data()->AsMap()->supports_fast_array_resize();
|
||||
}
|
||||
|
||||
int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const {
|
||||
int JSFunctionRef::InitialMapInstanceSizeWithMinSlack(
|
||||
CompilationDependencies* dependencies) const {
|
||||
if (data_->should_access_heap()) {
|
||||
return object()->ComputeInstanceSizeWithMinSlack(broker()->isolate());
|
||||
}
|
||||
RecordDependencyIfNeeded(dependencies);
|
||||
return data()->AsJSFunction()->initial_map_instance_size_with_min_slack();
|
||||
}
|
||||
|
||||
@ -2899,18 +3045,6 @@ FixedArrayRef JSBoundFunctionRef::bound_arguments() const {
|
||||
// Immutable after initialization.
|
||||
BIMODAL_ACCESSOR_WITH_FLAG_C(JSDataView, size_t, byte_length)
|
||||
|
||||
BIMODAL_ACCESSOR_C(JSFunction, bool, has_feedback_vector)
|
||||
BIMODAL_ACCESSOR_C(JSFunction, bool, has_initial_map)
|
||||
BIMODAL_ACCESSOR_C(JSFunction, bool, has_prototype)
|
||||
BIMODAL_ACCESSOR_C(JSFunction, bool, PrototypeRequiresRuntimeLookup)
|
||||
BIMODAL_ACCESSOR(JSFunction, Context, context)
|
||||
BIMODAL_ACCESSOR(JSFunction, NativeContext, native_context)
|
||||
BIMODAL_ACCESSOR(JSFunction, Map, initial_map)
|
||||
BIMODAL_ACCESSOR(JSFunction, Object, prototype)
|
||||
BIMODAL_ACCESSOR(JSFunction, SharedFunctionInfo, shared)
|
||||
BIMODAL_ACCESSOR(JSFunction, FeedbackCell, raw_feedback_cell)
|
||||
BIMODAL_ACCESSOR(JSFunction, FeedbackVector, feedback_vector)
|
||||
|
||||
BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field2, elements_kind,
|
||||
Map::Bits2::ElementsKindBits)
|
||||
BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field3, is_dictionary_map,
|
||||
@ -3229,16 +3363,15 @@ void NativeContextRef::Serialize() {
|
||||
// then, we *must* iterate them and create refs at serialization-time (even
|
||||
// though NativeContextRef itself is never-serialized).
|
||||
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
|
||||
#define SERIALIZE_MEMBER(type, name) \
|
||||
{ \
|
||||
ObjectData* member_data = broker()->GetOrCreateData(object()->name()); \
|
||||
if (member_data->IsMap() && !InstanceTypeChecker::IsContext( \
|
||||
member_data->AsMap()->instance_type())) { \
|
||||
member_data->AsMap()->SerializeConstructor(broker()); \
|
||||
} \
|
||||
if (member_data->IsJSFunction()) { \
|
||||
member_data->AsJSFunction()->Serialize(broker()); \
|
||||
} \
|
||||
#define SERIALIZE_MEMBER(type, name) \
|
||||
{ \
|
||||
ObjectData* member_data = broker()->GetOrCreateData(object()->name()); \
|
||||
if (member_data->IsMap() && \
|
||||
!InstanceTypeChecker::IsContext( \
|
||||
member_data->AsMap()->instance_type()) && \
|
||||
!broker()->is_concurrent_inlining()) { \
|
||||
member_data->AsMap()->SerializeConstructor(broker()); \
|
||||
} \
|
||||
}
|
||||
BROKER_NATIVE_CONTEXT_FIELDS(SERIALIZE_MEMBER)
|
||||
#undef SERIALIZE_MEMBER
|
||||
@ -3246,7 +3379,8 @@ void NativeContextRef::Serialize() {
|
||||
for (int i = Context::FIRST_FUNCTION_MAP_INDEX;
|
||||
i <= Context::LAST_FUNCTION_MAP_INDEX; i++) {
|
||||
MapData* member_data = broker()->GetOrCreateData(object()->get(i))->AsMap();
|
||||
if (!InstanceTypeChecker::IsContext(member_data->instance_type())) {
|
||||
if (!InstanceTypeChecker::IsContext(member_data->instance_type()) &&
|
||||
!broker()->is_concurrent_inlining()) {
|
||||
member_data->SerializeConstructor(broker());
|
||||
}
|
||||
}
|
||||
@ -3749,13 +3883,12 @@ ObjectData* ObjectRef::data() const {
|
||||
CHECK_NE(data_->kind(), kUnserializedHeapObject);
|
||||
return data_;
|
||||
case JSHeapBroker::kSerialized:
|
||||
case JSHeapBroker::kRetired:
|
||||
#ifdef DEBUG
|
||||
data_->used_status = ObjectData::Usage::kDataUsed;
|
||||
#endif // DEBUG
|
||||
CHECK_NE(data_->kind(), kUnserializedHeapObject);
|
||||
return data_;
|
||||
case JSHeapBroker::kRetired:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3765,18 +3898,6 @@ Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker,
|
||||
return AdvancedReducer::NoChange();
|
||||
}
|
||||
|
||||
void JSFunctionRef::Serialize() {
|
||||
if (data_->should_access_heap()) return;
|
||||
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
|
||||
data()->AsJSFunction()->Serialize(broker());
|
||||
}
|
||||
|
||||
void JSFunctionRef::SerializeCodeAndFeedback() {
|
||||
if (data_->should_access_heap()) return;
|
||||
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
|
||||
data()->AsJSFunction()->SerializeCodeAndFeedback(broker());
|
||||
}
|
||||
|
||||
bool JSBoundFunctionRef::Serialize() {
|
||||
if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
|
||||
return true;
|
||||
@ -3785,24 +3906,48 @@ bool JSBoundFunctionRef::Serialize() {
|
||||
return data()->AsJSBoundFunction()->Serialize(broker());
|
||||
}
|
||||
|
||||
bool JSFunctionRef::serialized() const {
|
||||
if (data_->should_access_heap()) return true;
|
||||
if (data_->AsJSFunction()->serialized()) return true;
|
||||
TRACE_BROKER_MISSING(broker(), "data for JSFunction " << this);
|
||||
return false;
|
||||
void JSFunctionRef::RecordDependencyIfNeeded(
|
||||
CompilationDependencies* dependencies) const {
|
||||
CHECK_NOT_NULL(dependencies);
|
||||
if (broker()->is_concurrent_inlining() &&
|
||||
!data()->AsJSFunction()->recorded_dependency()) {
|
||||
dependencies->DependOnConsistentJSFunctionView(*this);
|
||||
data()->AsJSFunction()->set_recorded_dependency();
|
||||
}
|
||||
}
|
||||
|
||||
bool JSFunctionRef::serialized_code_and_feedback() const {
|
||||
if (data_->should_access_heap()) return true;
|
||||
return data()->AsJSFunction()->serialized_code_and_feedback();
|
||||
}
|
||||
|
||||
CodeRef JSFunctionRef::code() const {
|
||||
if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
|
||||
return MakeRefAssumeMemoryFence(broker(), object()->code(kAcquireLoad));
|
||||
#define JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(result, name) \
|
||||
result##Ref JSFunctionRef::name(CompilationDependencies* dependencies) \
|
||||
const { \
|
||||
IF_ACCESS_FROM_HEAP(result, name); \
|
||||
RecordDependencyIfNeeded(dependencies); \
|
||||
return result##Ref(broker(), data()->AsJSFunction()->name()); \
|
||||
}
|
||||
|
||||
return CodeRef(broker(), ObjectRef::data()->AsJSFunction()->code());
|
||||
#define JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP_C(result, name) \
|
||||
result JSFunctionRef::name(CompilationDependencies* dependencies) const { \
|
||||
IF_ACCESS_FROM_HEAP_C(name); \
|
||||
RecordDependencyIfNeeded(dependencies); \
|
||||
return data()->AsJSFunction()->name(); \
|
||||
}
|
||||
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP_C(bool, has_feedback_vector)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP_C(bool, has_initial_map)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP_C(bool, has_instance_prototype)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP_C(bool, PrototypeRequiresRuntimeLookup)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(Context, context)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(NativeContext, native_context)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(Map, initial_map)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(Object, instance_prototype)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(SharedFunctionInfo, shared)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(FeedbackCell, raw_feedback_cell)
|
||||
JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP(FeedbackVector, feedback_vector)
|
||||
|
||||
#undef JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP
|
||||
#undef JSFUNCTION_BIMODAL_ACCESSOR_WITH_DEP_C
|
||||
|
||||
CodeRef JSFunctionRef::code() const {
|
||||
return MakeRefAssumeMemoryFence(broker(), object()->code(kAcquireLoad));
|
||||
}
|
||||
|
||||
base::Optional<FunctionTemplateInfoRef>
|
||||
@ -3886,15 +4031,15 @@ bool MapRef::serialized_own_descriptor(InternalIndex descriptor_index) const {
|
||||
|
||||
void MapRef::SerializeBackPointer() {
|
||||
if (data_->should_access_heap() || broker()->is_concurrent_inlining()) return;
|
||||
CHECK_IMPLIES(!FLAG_turbo_concurrent_get_property_access_info,
|
||||
broker()->mode() == JSHeapBroker::kSerializing);
|
||||
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
|
||||
data()->AsMap()->SerializeBackPointer(broker());
|
||||
}
|
||||
|
||||
bool MapRef::TrySerializePrototype() {
|
||||
if (data_->should_access_heap()) return true;
|
||||
CHECK_IMPLIES(!FLAG_turbo_concurrent_get_property_access_info,
|
||||
broker()->mode() == JSHeapBroker::kSerializing);
|
||||
if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
|
||||
return true;
|
||||
}
|
||||
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
|
||||
return data()->AsMap()->TrySerializePrototype(broker());
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ enum class RefSerializationKind {
|
||||
V(JSArray, RefSerializationKind::kBackgroundSerialized) \
|
||||
V(JSBoundFunction, RefSerializationKind::kBackgroundSerialized) \
|
||||
V(JSDataView, RefSerializationKind::kBackgroundSerialized) \
|
||||
V(JSFunction, RefSerializationKind::kSerialized) \
|
||||
V(JSFunction, RefSerializationKind::kBackgroundSerialized) \
|
||||
V(JSGlobalObject, RefSerializationKind::kBackgroundSerialized) \
|
||||
V(JSGlobalProxy, RefSerializationKind::kBackgroundSerialized) \
|
||||
V(JSTypedArray, RefSerializationKind::kBackgroundSerialized) \
|
||||
@ -404,28 +404,34 @@ class V8_EXPORT_PRIVATE JSFunctionRef : public JSObjectRef {
|
||||
|
||||
Handle<JSFunction> object() const;
|
||||
|
||||
bool has_feedback_vector() const;
|
||||
bool has_initial_map() const;
|
||||
bool has_prototype() const;
|
||||
bool PrototypeRequiresRuntimeLookup() const;
|
||||
// Returns true, iff the serialized JSFunctionData contents are consistent
|
||||
// with the state of the underlying JSFunction object. Must be called from
|
||||
// the main thread.
|
||||
bool IsConsistentWithHeapState() const;
|
||||
|
||||
void Serialize();
|
||||
bool serialized() const;
|
||||
// TODO(jgruber): Consider more fine-grained dependencies that keep track of
|
||||
// which fields were actually inspected during compilation.
|
||||
bool has_feedback_vector(CompilationDependencies* dependencies) const;
|
||||
bool has_initial_map(CompilationDependencies* dependencies) const;
|
||||
bool PrototypeRequiresRuntimeLookup(
|
||||
CompilationDependencies* dependencies) const;
|
||||
bool has_instance_prototype(CompilationDependencies* dependencies) const;
|
||||
ObjectRef instance_prototype(CompilationDependencies* dependencies) const;
|
||||
MapRef initial_map(CompilationDependencies* dependencies) const;
|
||||
ContextRef context(CompilationDependencies* dependencies) const;
|
||||
NativeContextRef native_context(CompilationDependencies* dependencies) const;
|
||||
SharedFunctionInfoRef shared(CompilationDependencies* dependencies) const;
|
||||
int InitialMapInstanceSizeWithMinSlack(
|
||||
CompilationDependencies* dependencies) const;
|
||||
FeedbackVectorRef feedback_vector(
|
||||
CompilationDependencies* dependencies) const;
|
||||
FeedbackCellRef raw_feedback_cell(
|
||||
CompilationDependencies* dependencies) const;
|
||||
|
||||
// The following are available only after calling Serialize().
|
||||
ObjectRef prototype() const;
|
||||
MapRef initial_map() const;
|
||||
ContextRef context() const;
|
||||
NativeContextRef native_context() const;
|
||||
SharedFunctionInfoRef shared() const;
|
||||
int InitialMapInstanceSizeWithMinSlack() const;
|
||||
|
||||
void SerializeCodeAndFeedback();
|
||||
bool serialized_code_and_feedback() const;
|
||||
|
||||
FeedbackVectorRef feedback_vector() const;
|
||||
FeedbackCellRef raw_feedback_cell() const;
|
||||
CodeRef code() const;
|
||||
|
||||
private:
|
||||
void RecordDependencyIfNeeded(CompilationDependencies* dependencies) const;
|
||||
};
|
||||
|
||||
class RegExpBoilerplateDescriptionRef : public HeapObjectRef {
|
||||
|
@ -59,6 +59,7 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
||||
reducer->ZoneForGraphAssembler(),
|
||||
[reducer](Node* n) { reducer->RevisitForGraphAssembler(n); },
|
||||
nullptr, kMarkLoopExits),
|
||||
dependencies_(reducer->dependencies()),
|
||||
node_(node),
|
||||
outermost_catch_scope_(
|
||||
CatchScope::Outermost(reducer->ZoneForGraphAssembler())),
|
||||
@ -656,9 +657,11 @@ class JSCallReducerAssembler : public JSGraphAssembler {
|
||||
|
||||
JSOperatorBuilder* javascript() const { return jsgraph()->javascript(); }
|
||||
|
||||
private:
|
||||
Node* const node_;
|
||||
CompilationDependencies* dependencies() const { return dependencies_; }
|
||||
|
||||
private:
|
||||
CompilationDependencies* const dependencies_;
|
||||
Node* const node_;
|
||||
CatchScope outermost_catch_scope_;
|
||||
Node* outermost_handler_;
|
||||
CatchScope* catch_scope_;
|
||||
@ -2153,7 +2156,7 @@ TNode<Object> PromiseBuiltinReducerAssembler::ReducePromiseConstructor(
|
||||
DCHECK_EQ(target, NewTargetInput());
|
||||
|
||||
SharedFunctionInfoRef promise_shared =
|
||||
native_context.promise_function().shared();
|
||||
native_context.promise_function().shared(dependencies());
|
||||
|
||||
PromiseCtorFrameStateParams frame_state_params{jsgraph(), promise_shared,
|
||||
node_ptr(), context,
|
||||
@ -2739,8 +2742,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
|
||||
HeapObjectMatcher m(target);
|
||||
if (m.HasResolvedValue() && m.Ref(broker()).IsJSFunction()) {
|
||||
JSFunctionRef function = m.Ref(broker()).AsJSFunction();
|
||||
if (!function.serialized()) return NoChange();
|
||||
context = jsgraph()->Constant(function.context());
|
||||
context = jsgraph()->Constant(function.context(dependencies()));
|
||||
} else {
|
||||
context = effect = graph()->NewNode(
|
||||
simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
|
||||
@ -4286,12 +4288,11 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
|
||||
}
|
||||
|
||||
bool JSCallReducer::IsBuiltinOrApiFunction(JSFunctionRef function) const {
|
||||
if (!function.serialized()) return false;
|
||||
// TODO(neis): Add a way to check if function template info isn't serialized
|
||||
// and add a warning in such cases. Currently we can't tell if function
|
||||
// template info doesn't exist or wasn't serialized.
|
||||
return function.shared().HasBuiltinId() ||
|
||||
function.shared().function_template_info().has_value();
|
||||
return function.shared(dependencies()).HasBuiltinId() ||
|
||||
function.shared(dependencies()).function_template_info().has_value();
|
||||
}
|
||||
|
||||
Reduction JSCallReducer::ReduceJSCall(Node* node) {
|
||||
@ -4310,14 +4311,13 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
|
||||
ObjectRef target_ref = m.Ref(broker());
|
||||
if (target_ref.IsJSFunction()) {
|
||||
JSFunctionRef function = target_ref.AsJSFunction();
|
||||
if (!function.serialized()) return NoChange();
|
||||
|
||||
// Don't inline cross native context.
|
||||
if (!function.native_context().equals(native_context())) {
|
||||
if (!function.native_context(dependencies()).equals(native_context())) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
return ReduceJSCall(node, function.shared());
|
||||
return ReduceJSCall(node, function.shared(dependencies()));
|
||||
} else if (target_ref.IsJSBoundFunction()) {
|
||||
JSBoundFunctionRef function = target_ref.AsJSBoundFunction();
|
||||
base::Optional<JSReceiverRef> bound_target_function =
|
||||
@ -5012,23 +5012,22 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
|
||||
|
||||
if (target_ref.IsJSFunction()) {
|
||||
JSFunctionRef function = target_ref.AsJSFunction();
|
||||
if (!function.serialized()) return NoChange();
|
||||
|
||||
// Do not reduce constructors with break points.
|
||||
// If this state changes during background compilation, the compilation
|
||||
// job will be aborted from the main thread (see
|
||||
// Debug::PrepareFunctionForDebugExecution()).
|
||||
if (function.shared().HasBreakInfo()) return NoChange();
|
||||
SharedFunctionInfoRef sfi = function.shared(dependencies());
|
||||
if (sfi.HasBreakInfo()) return NoChange();
|
||||
|
||||
// Don't inline cross native context.
|
||||
if (!function.native_context().equals(native_context())) {
|
||||
if (!function.native_context(dependencies()).equals(native_context())) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// Check for known builtin functions.
|
||||
Builtin builtin = function.shared().HasBuiltinId()
|
||||
? function.shared().builtin_id()
|
||||
: Builtin::kNoBuiltinId;
|
||||
Builtin builtin =
|
||||
sfi.HasBuiltinId() ? sfi.builtin_id() : Builtin::kNoBuiltinId;
|
||||
switch (builtin) {
|
||||
case Builtin::kArrayConstructor: {
|
||||
// TODO(bmeurer): Deal with Array subclasses here.
|
||||
@ -5068,7 +5067,8 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
|
||||
case Builtin::kPromiseConstructor:
|
||||
return ReducePromiseConstructor(node);
|
||||
case Builtin::kTypedArrayConstructor:
|
||||
return ReduceTypedArrayConstructor(node, function.shared());
|
||||
return ReduceTypedArrayConstructor(node,
|
||||
function.shared(dependencies()));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -7006,7 +7006,8 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
|
||||
// doesn't escape to user JavaScript. So bake this information
|
||||
// into the graph such that subsequent passes can use the
|
||||
// information for further optimizations.
|
||||
MapRef promise_map = native_context().promise_function().initial_map();
|
||||
MapRef promise_map =
|
||||
native_context().promise_function().initial_map(dependencies());
|
||||
effect = graph()->NewNode(
|
||||
simplified()->MapGuard(ZoneHandleSet<Map>(promise_map.object())), promise,
|
||||
effect, control);
|
||||
@ -7969,7 +7970,7 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) {
|
||||
// check as well as the lowered builtin call rely on a known location of the
|
||||
// lastIndex field.
|
||||
Handle<Map> regexp_initial_map =
|
||||
native_context().regexp_function().initial_map().object();
|
||||
native_context().regexp_function().initial_map(dependencies()).object();
|
||||
|
||||
MapInference inference(broker(), regexp, effect);
|
||||
if (!inference.Is(regexp_initial_map)) return inference.NoChange();
|
||||
@ -8069,7 +8070,7 @@ Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
|
||||
|
||||
// Create the artificial frame state in the middle of the Number constructor.
|
||||
SharedFunctionInfoRef shared_info =
|
||||
native_context().number_function().shared();
|
||||
native_context().number_function().shared(dependencies());
|
||||
Node* stack_parameters[] = {receiver};
|
||||
int stack_parameter_count = arraysize(stack_parameters);
|
||||
Node* continuation_frame_state =
|
||||
@ -8115,6 +8116,10 @@ Reduction JSCallReducer::ReduceBigIntAsUintN(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
CompilationDependencies* JSCallReducer::dependencies() const {
|
||||
return broker()->dependencies();
|
||||
}
|
||||
|
||||
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
|
||||
|
||||
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
|
||||
|
@ -48,14 +48,12 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
|
||||
using Flags = base::Flags<Flag>;
|
||||
|
||||
JSCallReducer(Editor* editor, JSGraph* jsgraph, JSHeapBroker* broker,
|
||||
Zone* temp_zone, Flags flags,
|
||||
CompilationDependencies* dependencies)
|
||||
Zone* temp_zone, Flags flags)
|
||||
: AdvancedReducer(editor),
|
||||
jsgraph_(jsgraph),
|
||||
broker_(broker),
|
||||
temp_zone_(temp_zone),
|
||||
flags_(flags),
|
||||
dependencies_(dependencies) {}
|
||||
flags_(flags) {}
|
||||
|
||||
const char* reducer_name() const override { return "JSCallReducer"; }
|
||||
|
||||
@ -72,6 +70,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
|
||||
|
||||
bool has_wasm_calls() const { return has_wasm_calls_; }
|
||||
|
||||
CompilationDependencies* dependencies() const;
|
||||
|
||||
private:
|
||||
Reduction ReduceBooleanConstructor(Node* node);
|
||||
Reduction ReduceCallApiFunction(Node* node,
|
||||
@ -256,13 +256,11 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
|
||||
JSOperatorBuilder* javascript() const;
|
||||
SimplifiedOperatorBuilder* simplified() const;
|
||||
Flags flags() const { return flags_; }
|
||||
CompilationDependencies* dependencies() const { return dependencies_; }
|
||||
|
||||
JSGraph* const jsgraph_;
|
||||
JSHeapBroker* const broker_;
|
||||
Zone* const temp_zone_;
|
||||
Flags const flags_;
|
||||
CompilationDependencies* const dependencies_;
|
||||
std::set<Node*> waitlist_;
|
||||
|
||||
// For preventing infinite recursion via ReduceJSCallWithArrayLikeOrSpread.
|
||||
|
@ -389,17 +389,17 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
|
||||
DCHECK(closure_type.AsHeapConstant()->Ref().IsJSFunction());
|
||||
JSFunctionRef js_function =
|
||||
closure_type.AsHeapConstant()->Ref().AsJSFunction();
|
||||
if (!js_function.has_initial_map()) return NoChange();
|
||||
if (!js_function.has_initial_map(dependencies())) return NoChange();
|
||||
|
||||
SlackTrackingPrediction slack_tracking_prediction =
|
||||
dependencies()->DependOnInitialMapInstanceSizePrediction(js_function);
|
||||
|
||||
MapRef initial_map = js_function.initial_map();
|
||||
MapRef initial_map = js_function.initial_map(dependencies());
|
||||
DCHECK(initial_map.instance_type() == JS_GENERATOR_OBJECT_TYPE ||
|
||||
initial_map.instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE);
|
||||
|
||||
// Allocate a register file.
|
||||
SharedFunctionInfoRef shared = js_function.shared();
|
||||
SharedFunctionInfoRef shared = js_function.shared(dependencies());
|
||||
DCHECK(shared.HasBytecodeArray());
|
||||
int parameter_count_no_receiver = shared.internal_formal_parameter_count();
|
||||
int length = parameter_count_no_receiver +
|
||||
@ -1060,7 +1060,8 @@ Reduction JSCreateLowering::ReduceJSCreatePromise(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kJSCreatePromise, node->opcode());
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
|
||||
MapRef promise_map = native_context().promise_function().initial_map();
|
||||
MapRef promise_map =
|
||||
native_context().promise_function().initial_map(dependencies());
|
||||
|
||||
AllocationBuilder a(jsgraph(), effect, graph()->start());
|
||||
a.Allocate(promise_map.instance_size());
|
||||
@ -1140,7 +1141,7 @@ Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralObject(Node* node) {
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
|
||||
// Retrieve the initial map for the object.
|
||||
MapRef map = native_context().object_function().initial_map();
|
||||
MapRef map = native_context().object_function().initial_map(dependencies());
|
||||
DCHECK(!map.is_dictionary_map());
|
||||
DCHECK(!map.IsInobjectSlackTrackingInProgress());
|
||||
Node* js_object_map = jsgraph()->Constant(map);
|
||||
@ -1313,10 +1314,12 @@ Reduction JSCreateLowering::ReduceJSCreateBlockContext(Node* node) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
base::Optional<MapRef> GetObjectCreateMap(JSHeapBroker* broker,
|
||||
HeapObjectRef prototype) {
|
||||
MapRef standard_map =
|
||||
broker->target_native_context().object_function().initial_map();
|
||||
broker->target_native_context().object_function().initial_map(
|
||||
broker->dependencies());
|
||||
if (prototype.equals(standard_map.prototype().value())) {
|
||||
return standard_map;
|
||||
}
|
||||
@ -1329,6 +1332,7 @@ base::Optional<MapRef> GetObjectCreateMap(JSHeapBroker* broker,
|
||||
}
|
||||
return base::Optional<MapRef>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reduction JSCreateLowering::ReduceJSCreateObject(Node* node) {
|
||||
@ -1886,7 +1890,8 @@ base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteralElements(
|
||||
|
||||
Node* JSCreateLowering::AllocateLiteralRegExp(
|
||||
Node* effect, Node* control, RegExpBoilerplateDescriptionRef boilerplate) {
|
||||
MapRef initial_map = native_context().regexp_function().initial_map();
|
||||
MapRef initial_map =
|
||||
native_context().regexp_function().initial_map(dependencies());
|
||||
|
||||
// Sanity check that JSRegExp object layout hasn't changed.
|
||||
STATIC_ASSERT(JSRegExp::kDataOffset == JSObject::kHeaderSize);
|
||||
|
@ -404,6 +404,16 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
|
||||
bool ObjectMayBeUninitialized(Object object) const;
|
||||
bool ObjectMayBeUninitialized(HeapObject object) const;
|
||||
|
||||
void set_dependencies(CompilationDependencies* dependencies) {
|
||||
DCHECK_NOT_NULL(dependencies);
|
||||
DCHECK_NULL(dependencies_);
|
||||
dependencies_ = dependencies;
|
||||
}
|
||||
CompilationDependencies* dependencies() const {
|
||||
DCHECK_NOT_NULL(dependencies_);
|
||||
return dependencies_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class HeapObjectRef;
|
||||
friend class ObjectRef;
|
||||
@ -513,6 +523,8 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
|
||||
};
|
||||
ZoneMultimap<SerializedFunction, HintsVector> serialized_functions_;
|
||||
|
||||
CompilationDependencies* dependencies_ = nullptr;
|
||||
|
||||
// The MapUpdater mutex is used in recursive patterns; for example,
|
||||
// ComputePropertyAccessInfo may call itself recursively. Thus we need to
|
||||
// emulate a recursive mutex, which we do by checking if this heap broker
|
||||
|
@ -37,7 +37,6 @@ Reduction JSHeapCopyReducer::Reduce(Node* node) {
|
||||
}
|
||||
case IrOpcode::kHeapConstant: {
|
||||
ObjectRef object = MakeRef(broker(), HeapConstantOf(node->op()));
|
||||
if (object.IsJSFunction()) object.AsJSFunction().Serialize();
|
||||
if (object.IsJSObject()) {
|
||||
object.AsJSObject().SerializeObjectCreateMap();
|
||||
}
|
||||
|
@ -50,21 +50,15 @@ bool CanConsiderForInlining(JSHeapBroker* broker,
|
||||
|
||||
bool CanConsiderForInlining(JSHeapBroker* broker,
|
||||
JSFunctionRef const& function) {
|
||||
if (!function.has_feedback_vector()) {
|
||||
if (!function.has_feedback_vector(broker->dependencies())) {
|
||||
TRACE("Cannot consider " << function
|
||||
<< " for inlining (no feedback vector)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!function.serialized() || !function.serialized_code_and_feedback()) {
|
||||
TRACE_BROKER_MISSING(
|
||||
broker, "data for " << function << " (cannot consider for inlining)");
|
||||
TRACE("Cannot consider " << function << " for inlining (missing data)");
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanConsiderForInlining(broker, function.shared(),
|
||||
function.feedback_vector());
|
||||
return CanConsiderForInlining(
|
||||
broker, function.shared(broker->dependencies()),
|
||||
function.feedback_vector(broker->dependencies()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -81,7 +75,7 @@ JSInliningHeuristic::Candidate JSInliningHeuristic::CollectFunctions(
|
||||
out.functions[0] = m.Ref(broker()).AsJSFunction();
|
||||
JSFunctionRef function = out.functions[0].value();
|
||||
if (CanConsiderForInlining(broker(), function)) {
|
||||
out.bytecode[0] = function.shared().GetBytecodeArray();
|
||||
out.bytecode[0] = function.shared(dependencies()).GetBytecodeArray();
|
||||
out.num_functions = 1;
|
||||
return out;
|
||||
}
|
||||
@ -102,7 +96,7 @@ JSInliningHeuristic::Candidate JSInliningHeuristic::CollectFunctions(
|
||||
out.functions[n] = m.Ref(broker()).AsJSFunction();
|
||||
JSFunctionRef function = out.functions[n].value();
|
||||
if (CanConsiderForInlining(broker(), function)) {
|
||||
out.bytecode[n] = function.shared().GetBytecodeArray();
|
||||
out.bytecode[n] = function.shared(dependencies()).GetBytecodeArray();
|
||||
}
|
||||
}
|
||||
out.num_functions = value_input_count;
|
||||
@ -179,9 +173,10 @@ Reduction JSInliningHeuristic::Reduce(Node* node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SharedFunctionInfoRef shared = candidate.functions[i].has_value()
|
||||
? candidate.functions[i].value().shared()
|
||||
: candidate.shared_info.value();
|
||||
SharedFunctionInfoRef shared =
|
||||
candidate.functions[i].has_value()
|
||||
? candidate.functions[i].value().shared(dependencies())
|
||||
: candidate.shared_info.value();
|
||||
candidate.can_inline_function[i] = candidate.bytecode[i].has_value();
|
||||
CHECK_IMPLIES(candidate.can_inline_function[i], shared.IsInlineable());
|
||||
// Do not allow direct recursion i.e. f() -> f(). We still allow indirect
|
||||
@ -794,9 +789,10 @@ void JSInliningHeuristic::PrintCandidates() {
|
||||
<< candidate.node->id() << " with frequency " << candidate.frequency
|
||||
<< ", " << candidate.num_functions << " target(s):" << std::endl;
|
||||
for (int i = 0; i < candidate.num_functions; ++i) {
|
||||
SharedFunctionInfoRef shared = candidate.functions[i].has_value()
|
||||
? candidate.functions[i]->shared()
|
||||
: candidate.shared_info.value();
|
||||
SharedFunctionInfoRef shared =
|
||||
candidate.functions[i].has_value()
|
||||
? candidate.functions[i]->shared(dependencies())
|
||||
: candidate.shared_info.value();
|
||||
os << " - target: " << shared;
|
||||
if (candidate.bytecode[i].has_value()) {
|
||||
os << ", bytecode size: " << candidate.bytecode[i]->length();
|
||||
@ -819,6 +815,10 @@ void JSInliningHeuristic::PrintCandidates() {
|
||||
|
||||
Graph* JSInliningHeuristic::graph() const { return jsgraph()->graph(); }
|
||||
|
||||
CompilationDependencies* JSInliningHeuristic::dependencies() const {
|
||||
return broker()->dependencies();
|
||||
}
|
||||
|
||||
CommonOperatorBuilder* JSInliningHeuristic::common() const {
|
||||
return jsgraph()->common();
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ class JSInliningHeuristic final : public AdvancedReducer {
|
||||
JSGraph* jsgraph() const { return jsgraph_; }
|
||||
// TODO(neis): Make heap broker a component of JSGraph?
|
||||
JSHeapBroker* broker() const { return broker_; }
|
||||
CompilationDependencies* dependencies() const;
|
||||
Isolate* isolate() const { return jsgraph_->isolate(); }
|
||||
SimplifiedOperatorBuilder* simplified() const;
|
||||
Mode mode() const { return mode_; }
|
||||
|
@ -305,7 +305,7 @@ base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
|
||||
JSFunctionRef function = match.Ref(broker()).AsJSFunction();
|
||||
|
||||
// The function might have not been called yet.
|
||||
if (!function.has_feedback_vector()) {
|
||||
if (!function.has_feedback_vector(broker()->dependencies())) {
|
||||
return base::nullopt;
|
||||
}
|
||||
|
||||
@ -317,11 +317,12 @@ base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
|
||||
// TODO(turbofan): We might want to revisit this restriction later when we
|
||||
// have a need for this, and we know how to model different native contexts
|
||||
// in the same graph in a compositional way.
|
||||
if (!function.native_context().equals(broker()->target_native_context())) {
|
||||
if (!function.native_context(broker()->dependencies())
|
||||
.equals(broker()->target_native_context())) {
|
||||
return base::nullopt;
|
||||
}
|
||||
|
||||
return function.shared();
|
||||
return function.shared(broker()->dependencies());
|
||||
}
|
||||
|
||||
// This reducer can also handle calls where the target is statically known to
|
||||
@ -355,11 +356,12 @@ FeedbackCellRef JSInliner::DetermineCallContext(Node* node,
|
||||
if (match.HasResolvedValue() && match.Ref(broker()).IsJSFunction()) {
|
||||
JSFunctionRef function = match.Ref(broker()).AsJSFunction();
|
||||
// This was already ensured by DetermineCallTarget
|
||||
CHECK(function.has_feedback_vector());
|
||||
CHECK(function.has_feedback_vector(broker()->dependencies()));
|
||||
|
||||
// The inlinee specializes to the context from the JSFunction object.
|
||||
*context_out = jsgraph()->Constant(function.context());
|
||||
return function.raw_feedback_cell();
|
||||
*context_out =
|
||||
jsgraph()->Constant(function.context(broker()->dependencies()));
|
||||
return function.raw_feedback_cell(broker()->dependencies());
|
||||
}
|
||||
|
||||
if (match.IsJSCreateClosure()) {
|
||||
|
@ -649,12 +649,12 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
|
||||
// Optimize if we currently know the "prototype" property.
|
||||
|
||||
JSFunctionRef function = m.Ref(broker()).AsJSFunction();
|
||||
if (!function.serialized()) return NoChange();
|
||||
|
||||
// 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()) {
|
||||
if (!function.map().has_prototype_slot() ||
|
||||
!function.has_instance_prototype(dependencies()) ||
|
||||
function.PrototypeRequiresRuntimeLookup(dependencies())) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
@ -1478,11 +1478,11 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
|
||||
name.equals(MakeRef(broker(), factory()->prototype_string()))) {
|
||||
// Optimize "prototype" property of functions.
|
||||
JSFunctionRef function = object.AsJSFunction();
|
||||
if (!function.serialized()) return NoChange();
|
||||
// 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()) {
|
||||
if (!function.map().has_prototype_slot() ||
|
||||
!function.has_instance_prototype(dependencies()) ||
|
||||
function.PrototypeRequiresRuntimeLookup(dependencies())) {
|
||||
return NoChange();
|
||||
}
|
||||
ObjectRef prototype = dependencies()->DependOnPrototypeProperty(function);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "src/codegen/interface-descriptors-inl.h"
|
||||
#include "src/compiler/access-builder.h"
|
||||
#include "src/compiler/allocation-builder.h"
|
||||
#include "src/compiler/compilation-dependencies.h"
|
||||
#include "src/compiler/graph-assembler.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/js-heap-broker.h"
|
||||
@ -1622,13 +1623,9 @@ Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
|
||||
// Only optimize [[Construct]] here if {function} is a Constructor.
|
||||
if (!function.map().is_constructor()) return NoChange();
|
||||
|
||||
if (!function.serialized()) {
|
||||
TRACE_BROKER_MISSING(broker(), "data for function " << function);
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// Patch {node} to an indirect call via the {function}s construct stub.
|
||||
bool use_builtin_construct_stub = function.shared().construct_as_builtin();
|
||||
bool use_builtin_construct_stub =
|
||||
function.shared(dependencies()).construct_as_builtin();
|
||||
CodeRef code = MakeRef(
|
||||
broker(), use_builtin_construct_stub
|
||||
? BUILTIN_CODE(isolate(), JSBuiltinsConstructStub)
|
||||
@ -1704,12 +1701,7 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
|
||||
if (target_type.IsHeapConstant() &&
|
||||
target_type.AsHeapConstant()->Ref().IsJSFunction()) {
|
||||
function = target_type.AsHeapConstant()->Ref().AsJSFunction();
|
||||
|
||||
if (!function->serialized()) {
|
||||
TRACE_BROKER_MISSING(broker(), "data for function " << *function);
|
||||
return NoChange();
|
||||
}
|
||||
shared = function->shared();
|
||||
shared = function->shared(dependencies());
|
||||
} else if (target->opcode() == IrOpcode::kJSCreateClosure) {
|
||||
CreateClosureParameters const& ccp =
|
||||
JSCreateClosureNode{target}.Parameters();
|
||||
@ -1737,12 +1729,13 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
|
||||
// require data from a foreign native context.
|
||||
if (is_sloppy(shared->language_mode()) && !shared->native() &&
|
||||
!receiver_type.Is(Type::Receiver())) {
|
||||
if (!function.has_value() || !function->native_context().equals(
|
||||
broker()->target_native_context())) {
|
||||
if (!function.has_value() ||
|
||||
!function->native_context(dependencies())
|
||||
.equals(broker()->target_native_context())) {
|
||||
return NoChange();
|
||||
}
|
||||
Node* global_proxy =
|
||||
jsgraph()->Constant(function->native_context().global_proxy_object());
|
||||
Node* global_proxy = jsgraph()->Constant(
|
||||
function->native_context(dependencies()).global_proxy_object());
|
||||
receiver = effect =
|
||||
graph()->NewNode(simplified()->ConvertReceiver(convert_mode),
|
||||
receiver, global_proxy, effect, control);
|
||||
@ -2469,18 +2462,18 @@ Reduction JSTypedLowering::Reduce(Node* node) {
|
||||
|
||||
Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); }
|
||||
|
||||
|
||||
Graph* JSTypedLowering::graph() const { return jsgraph()->graph(); }
|
||||
|
||||
CompilationDependencies* JSTypedLowering::dependencies() const {
|
||||
return broker()->dependencies();
|
||||
}
|
||||
|
||||
Isolate* JSTypedLowering::isolate() const { return jsgraph()->isolate(); }
|
||||
|
||||
|
||||
JSOperatorBuilder* JSTypedLowering::javascript() const {
|
||||
return jsgraph()->javascript();
|
||||
}
|
||||
|
||||
|
||||
CommonOperatorBuilder* JSTypedLowering::common() const {
|
||||
return jsgraph()->common();
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace compiler {
|
||||
|
||||
// Forward declarations.
|
||||
class CommonOperatorBuilder;
|
||||
class CompilationDependencies;
|
||||
class JSGraph;
|
||||
class JSOperatorBuilder;
|
||||
class SimplifiedOperatorBuilder;
|
||||
@ -93,6 +94,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
|
||||
Graph* graph() const;
|
||||
JSGraph* jsgraph() const { return jsgraph_; }
|
||||
JSHeapBroker* broker() const { return broker_; }
|
||||
CompilationDependencies* dependencies() const;
|
||||
Isolate* isolate() const;
|
||||
JSOperatorBuilder* javascript() const;
|
||||
CommonOperatorBuilder* common() const;
|
||||
|
@ -332,12 +332,9 @@ base::Optional<MapRef> NodeProperties::GetJSCreateMap(JSHeapBroker* broker,
|
||||
mnewtarget.Ref(broker).IsJSFunction()) {
|
||||
ObjectRef target = mtarget.Ref(broker);
|
||||
JSFunctionRef newtarget = mnewtarget.Ref(broker).AsJSFunction();
|
||||
if (newtarget.map().has_prototype_slot() && newtarget.has_initial_map()) {
|
||||
if (!newtarget.serialized()) {
|
||||
TRACE_BROKER_MISSING(broker, "initial map on " << newtarget);
|
||||
return base::nullopt;
|
||||
}
|
||||
MapRef initial_map = newtarget.initial_map();
|
||||
if (newtarget.map().has_prototype_slot() &&
|
||||
newtarget.has_initial_map(broker->dependencies())) {
|
||||
MapRef initial_map = newtarget.initial_map(broker->dependencies());
|
||||
if (initial_map.GetConstructor().equals(target)) {
|
||||
DCHECK(target.AsJSFunction().map().is_constructor());
|
||||
DCHECK(newtarget.map().is_constructor());
|
||||
@ -406,10 +403,11 @@ NodeProperties::InferMapsResult NodeProperties::InferMapsUnsafe(
|
||||
}
|
||||
case IrOpcode::kJSCreatePromise: {
|
||||
if (IsSame(receiver, effect)) {
|
||||
*maps_return = ZoneHandleSet<Map>(broker->target_native_context()
|
||||
.promise_function()
|
||||
.initial_map()
|
||||
.object());
|
||||
*maps_return =
|
||||
ZoneHandleSet<Map>(broker->target_native_context()
|
||||
.promise_function()
|
||||
.initial_map(broker->dependencies())
|
||||
.object());
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
|
@ -1353,11 +1353,11 @@ struct GraphBuilderPhase {
|
||||
JSFunctionRef closure = MakeRef(data->broker(), data->info()->closure());
|
||||
CallFrequency frequency(1.0f);
|
||||
BuildGraphFromBytecode(
|
||||
data->broker(), temp_zone, closure.shared(),
|
||||
closure.raw_feedback_cell(), data->info()->osr_offset(),
|
||||
data->jsgraph(), frequency, data->source_positions(),
|
||||
SourcePosition::kNotInlined, data->info()->code_kind(), flags,
|
||||
&data->info()->tick_counter(),
|
||||
data->broker(), temp_zone, closure.shared(data->dependencies()),
|
||||
closure.raw_feedback_cell(data->dependencies()),
|
||||
data->info()->osr_offset(), data->jsgraph(), frequency,
|
||||
data->source_positions(), SourcePosition::kNotInlined,
|
||||
data->info()->code_kind(), flags, &data->info()->tick_counter(),
|
||||
ObserveNodeInfo{data->observe_node_manager(),
|
||||
data->info()->node_observer()});
|
||||
}
|
||||
@ -1385,8 +1385,7 @@ struct InliningPhase {
|
||||
call_reducer_flags |= JSCallReducer::kInlineJSToWasmCalls;
|
||||
}
|
||||
JSCallReducer call_reducer(&graph_reducer, data->jsgraph(), data->broker(),
|
||||
temp_zone, call_reducer_flags,
|
||||
data->dependencies());
|
||||
temp_zone, call_reducer_flags);
|
||||
JSContextSpecialization context_specialization(
|
||||
&graph_reducer, data->jsgraph(), data->broker(),
|
||||
data->specialization_context(),
|
||||
|
@ -1069,9 +1069,7 @@ SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
|
||||
CompilationSubject(closure, broker_->isolate(), zone()))),
|
||||
arguments_(zone()) {
|
||||
closure_hints_.AddConstant(closure, zone(), broker_);
|
||||
JSFunctionRef closure_ref = MakeRef(broker, closure);
|
||||
closure_ref.Serialize();
|
||||
closure_ref.SerializeCodeAndFeedback();
|
||||
MakeRef(broker, closure);
|
||||
|
||||
TRACE_BROKER(broker_, "Hints for <closure>: " << closure_hints_);
|
||||
TRACE_BROKER(broker_, "Initial environment:\n" << *environment_);
|
||||
@ -1098,9 +1096,7 @@ SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
|
||||
Handle<JSFunction> closure;
|
||||
if (function.closure().ToHandle(&closure)) {
|
||||
closure_hints_.AddConstant(closure, zone(), broker);
|
||||
JSFunctionRef closure_ref = MakeRef(broker, closure);
|
||||
closure_ref.Serialize();
|
||||
closure_ref.SerializeCodeAndFeedback();
|
||||
MakeRef(broker, closure);
|
||||
} else {
|
||||
closure_hints_.AddVirtualClosure(function.virtual_closure(), zone(),
|
||||
broker);
|
||||
@ -2073,7 +2069,6 @@ void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct(
|
||||
if (!callee->IsJSFunction()) return;
|
||||
|
||||
JSFunctionRef function = MakeRef(broker(), Handle<JSFunction>::cast(callee));
|
||||
function.Serialize();
|
||||
Callee new_callee(function.object());
|
||||
ProcessCalleeForCallOrConstruct(new_callee, new_target, *actual_arguments,
|
||||
speculation_mode, padding, result_hints);
|
||||
@ -2499,7 +2494,7 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
|
||||
if (arguments.size() >= 2) {
|
||||
for (auto constant : arguments[1].constants()) {
|
||||
if (constant->IsJSFunction()) {
|
||||
MakeRef(broker(), Handle<JSFunction>::cast(constant)).Serialize();
|
||||
MakeRef(broker(), Handle<JSFunction>::cast(constant));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2701,7 +2696,6 @@ void SerializerForBackgroundCompilation::ProcessHintsForFunctionBind(
|
||||
if (constant->IsJSFunction()) {
|
||||
JSFunctionRef function =
|
||||
MakeRef(broker(), Handle<JSFunction>::cast(constant));
|
||||
function.Serialize();
|
||||
ProcessMapForFunctionBind(function.map());
|
||||
} else if (constant->IsJSBoundFunction()) {
|
||||
JSBoundFunctionRef function =
|
||||
@ -3014,7 +3008,8 @@ void SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
|
||||
kMissingArgumentsAreUndefined, result_hints);
|
||||
|
||||
// For JSCallReducer::ReduceCallApiFunction.
|
||||
Handle<SharedFunctionInfo> sfi = function.shared().object();
|
||||
Handle<SharedFunctionInfo> sfi =
|
||||
function.shared(dependencies()).object();
|
||||
if (sfi->IsApiFunction()) {
|
||||
FunctionTemplateInfoRef fti_ref =
|
||||
MakeRef(broker(), sfi->get_api_func_data());
|
||||
@ -3231,10 +3226,11 @@ void SerializerForBackgroundCompilation::ProcessNamedAccess(
|
||||
feedback.name().equals(MakeRef(
|
||||
broker(), broker()->isolate()->factory()->prototype_string()))) {
|
||||
JSFunctionRef function = object.AsJSFunction();
|
||||
function.Serialize();
|
||||
if (result_hints != nullptr && function.has_prototype()) {
|
||||
result_hints->AddConstant(function.prototype().object(), zone(),
|
||||
broker());
|
||||
if (result_hints != nullptr &&
|
||||
function.has_instance_prototype(dependencies())) {
|
||||
result_hints->AddConstant(
|
||||
function.instance_prototype(dependencies()).object(), zone(),
|
||||
broker());
|
||||
}
|
||||
}
|
||||
// TODO(neis): Also record accumulator hint for string.length and maybe
|
||||
@ -3394,12 +3390,12 @@ void SerializerForBackgroundCompilation::ProcessConstantForOrdinaryHasInstance(
|
||||
constructor.AsJSBoundFunction().bound_target_function().value(),
|
||||
walk_prototypes);
|
||||
} else if (constructor.IsJSFunction()) {
|
||||
constructor.AsJSFunction().Serialize();
|
||||
*walk_prototypes =
|
||||
*walk_prototypes ||
|
||||
(constructor.map().has_prototype_slot() &&
|
||||
constructor.AsJSFunction().has_prototype() &&
|
||||
!constructor.AsJSFunction().PrototypeRequiresRuntimeLookup());
|
||||
constructor.AsJSFunction().has_instance_prototype(dependencies()) &&
|
||||
!constructor.AsJSFunction().PrototypeRequiresRuntimeLookup(
|
||||
dependencies()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3428,9 +3424,8 @@ void SerializerForBackgroundCompilation::ProcessConstantForInstanceOf(
|
||||
CHECK(constant.has_value());
|
||||
if (constant->IsJSFunction()) {
|
||||
JSFunctionRef function = constant->AsJSFunction();
|
||||
function.Serialize();
|
||||
if (function.shared().HasBuiltinId() &&
|
||||
function.shared().builtin_id() ==
|
||||
if (function.shared(dependencies()).HasBuiltinId() &&
|
||||
function.shared(dependencies()).builtin_id() ==
|
||||
Builtin::kFunctionPrototypeHasInstance) {
|
||||
// For JSCallReducer::ReduceFunctionPrototypeHasInstance.
|
||||
ProcessConstantForOrdinaryHasInstance(constructor_heap_object,
|
||||
|
@ -1510,14 +1510,10 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
|
||||
return Type::NonInternal();
|
||||
}
|
||||
JSFunctionRef function = fun.AsHeapConstant()->Ref().AsJSFunction();
|
||||
if (!function.serialized()) {
|
||||
TRACE_BROKER_MISSING(t->broker(), "data for function " << function);
|
||||
if (!function.shared(t->broker()->dependencies()).HasBuiltinId()) {
|
||||
return Type::NonInternal();
|
||||
}
|
||||
if (!function.shared().HasBuiltinId()) {
|
||||
return Type::NonInternal();
|
||||
}
|
||||
switch (function.shared().builtin_id()) {
|
||||
switch (function.shared(t->broker()->dependencies()).builtin_id()) {
|
||||
case Builtin::kMathRandom:
|
||||
return Type::PlainNumber();
|
||||
case Builtin::kMathFloor:
|
||||
|
@ -168,8 +168,12 @@ Address JSFunction::code_entry_point() const {
|
||||
|
||||
// TODO(ishell): Why relaxed read but release store?
|
||||
DEF_GETTER(JSFunction, shared, SharedFunctionInfo) {
|
||||
return SharedFunctionInfo::cast(
|
||||
RELAXED_READ_FIELD(*this, kSharedFunctionInfoOffset));
|
||||
return shared(cage_base, kRelaxedLoad);
|
||||
}
|
||||
|
||||
DEF_RELAXED_GETTER(JSFunction, shared, SharedFunctionInfo) {
|
||||
return TaggedField<SharedFunctionInfo,
|
||||
kSharedFunctionInfoOffset>::Relaxed_Load(cage_base, *this);
|
||||
}
|
||||
|
||||
void JSFunction::set_shared(SharedFunctionInfo value, WriteBarrierMode mode) {
|
||||
@ -200,6 +204,10 @@ Context JSFunction::context() {
|
||||
return TaggedField<Context, kContextOffset>::load(*this);
|
||||
}
|
||||
|
||||
DEF_RELAXED_GETTER(JSFunction, context, Context) {
|
||||
return TaggedField<Context, kContextOffset>::Relaxed_Load(cage_base, *this);
|
||||
}
|
||||
|
||||
bool JSFunction::has_context() const {
|
||||
return TaggedField<HeapObject, kContextOffset>::load(*this).IsContext();
|
||||
}
|
||||
@ -258,8 +266,9 @@ DEF_GETTER(JSFunction, PrototypeRequiresRuntimeLookup, bool) {
|
||||
|
||||
DEF_GETTER(JSFunction, instance_prototype, HeapObject) {
|
||||
DCHECK(has_instance_prototype(cage_base));
|
||||
if (has_initial_map(cage_base))
|
||||
if (has_initial_map(cage_base)) {
|
||||
return initial_map(cage_base).prototype(cage_base);
|
||||
}
|
||||
// When there is no initial map and the prototype is a JSReceiver, the
|
||||
// initial map field is used for the prototype field.
|
||||
return HeapObject::cast(prototype_or_initial_map(cage_base, kAcquireLoad));
|
||||
|
@ -58,15 +58,17 @@ class JSFunction : public JSFunctionOrBoundFunction {
|
||||
// [prototype_or_initial_map]:
|
||||
DECL_RELEASE_ACQUIRE_ACCESSORS(prototype_or_initial_map, HeapObject)
|
||||
|
||||
// [shared]: The information about the function that
|
||||
// can be shared by instances.
|
||||
// [shared]: The information about the function that can be shared by
|
||||
// instances.
|
||||
DECL_ACCESSORS(shared, SharedFunctionInfo)
|
||||
DECL_RELAXED_GETTER(shared, SharedFunctionInfo)
|
||||
|
||||
// Fast binding requires length and name accessors.
|
||||
static const int kMinDescriptorsForFastBind = 2;
|
||||
|
||||
// [context]: The context for this function.
|
||||
inline Context context();
|
||||
DECL_RELAXED_GETTER(context, Context)
|
||||
inline bool has_context() const;
|
||||
inline void set_context(HeapObject context,
|
||||
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
|
||||
|
@ -47,6 +47,7 @@ SerializerTester::SerializerTester(const char* global_source,
|
||||
function =
|
||||
broker_->FindCanonicalPersistentHandleForTesting<JSFunction>(*function);
|
||||
function_ = MakeRef(broker(), function);
|
||||
DCHECK_NOT_NULL(broker_->dependencies());
|
||||
}
|
||||
|
||||
TEST(SerializeEmptyFunction) {
|
||||
@ -54,7 +55,8 @@ TEST(SerializeEmptyFunction) {
|
||||
"", "function f() {}; %EnsureFeedbackVectorForFunction(f); return f;");
|
||||
JSFunctionRef function = tester.function();
|
||||
CHECK(tester.broker()->IsSerializedForCompilation(
|
||||
function.shared(), function.feedback_vector()));
|
||||
function.shared(tester.broker()->dependencies()),
|
||||
function.feedback_vector(tester.broker()->dependencies())));
|
||||
}
|
||||
|
||||
// This helper function allows for testing whether an inlinee candidate
|
||||
@ -65,8 +67,9 @@ void CheckForSerializedInlinee(const char* global_source,
|
||||
Handle<Object> argv[] = {}) {
|
||||
SerializerTester tester(global_source, local_source);
|
||||
JSFunctionRef f = tester.function();
|
||||
CHECK(tester.broker()->IsSerializedForCompilation(f.shared(),
|
||||
f.feedback_vector()));
|
||||
CHECK(tester.broker()->IsSerializedForCompilation(
|
||||
f.shared(tester.broker()->dependencies()),
|
||||
f.feedback_vector(tester.broker()->dependencies())));
|
||||
|
||||
MaybeHandle<Object> g_obj = Execution::Call(
|
||||
tester.isolate(), tester.function().object(),
|
||||
|
@ -187,9 +187,15 @@ TEST(TestConcurrentSharedFunctionInfo) {
|
||||
|
||||
// Finalize job.
|
||||
{
|
||||
// Cannot assert successful completion here since concurrent modifications
|
||||
// may have invalidated compilation dependencies (e.g. since the serialized
|
||||
// JSFunctionRef no longer matches the actual JSFunction state).
|
||||
const CompilationJob::Status status = job->FinalizeJob(isolate);
|
||||
CHECK_EQ(status, CompilationJob::SUCCEEDED);
|
||||
CHECK(job->compilation_info()->has_bytecode_array());
|
||||
if (status == CompilationJob::SUCCEEDED) {
|
||||
CHECK(job->compilation_info()->has_bytecode_array());
|
||||
} else {
|
||||
CHECK_EQ(status, CompilationJob::FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/codegen/tick-counter.h"
|
||||
#include "src/compiler/compilation-dependencies.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/js-heap-broker.h"
|
||||
#include "src/compiler/js-heap-copy-reducer.h"
|
||||
@ -37,7 +38,8 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
|
||||
common(main_zone()),
|
||||
graph(main_zone()),
|
||||
typer(&js_heap_broker, Typer::kNoFlags, &graph, &tick_counter),
|
||||
context_node(nullptr) {
|
||||
context_node(nullptr),
|
||||
deps(&js_heap_broker, main_zone()) {
|
||||
graph.SetStart(graph.NewNode(common.Start(num_parameters)));
|
||||
graph.SetEnd(graph.NewNode(common.End(1), graph.start()));
|
||||
typer.Run();
|
||||
@ -56,6 +58,7 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
|
||||
Graph graph;
|
||||
Typer typer;
|
||||
Node* context_node;
|
||||
CompilationDependencies deps;
|
||||
|
||||
Node* Parameter(Type t, int32_t index = 0) {
|
||||
Node* n = graph.NewNode(common.Parameter(index), graph.start());
|
||||
|
@ -197,6 +197,12 @@
|
||||
'regress/regress-crbug-941743': [PASS, HEAVY],
|
||||
'regress/regress-crbug-1191886': [PASS, HEAVY],
|
||||
'wasm/externref-globals': [PASS, HEAVY],
|
||||
|
||||
# Unpredictable results due to assertOptimized with concurrent optimization.
|
||||
# TODO(v8:12004): Add a runtime function to flush the optimizing compile
|
||||
# dispatcher *without* aborting existing jobs.
|
||||
'interrupt-budget-override': [PASS,FAIL],
|
||||
'never-optimize': [PASS,FAIL],
|
||||
}], # ALWAYS
|
||||
|
||||
##############################################################################
|
||||
|
@ -36,7 +36,7 @@ class JSCallReducerTest : public TypedGraphTest {
|
||||
&machine);
|
||||
GraphReducer graph_reducer(zone(), graph(), tick_counter(), broker());
|
||||
JSCallReducer reducer(&graph_reducer, &jsgraph, broker(), zone(),
|
||||
JSCallReducer::kNoFlags, &deps_);
|
||||
JSCallReducer::kNoFlags);
|
||||
return reducer.Reduce(node);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "src/codegen/code-factory.h"
|
||||
#include "src/compiler/access-builder.h"
|
||||
#include "src/compiler/compilation-dependencies.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/js-heap-copy-reducer.h"
|
||||
#include "src/compiler/js-operator.h"
|
||||
@ -39,7 +40,8 @@ Type const kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
|
||||
|
||||
class JSTypedLoweringTest : public TypedGraphTest {
|
||||
public:
|
||||
JSTypedLoweringTest() : TypedGraphTest(3), javascript_(zone()) {}
|
||||
JSTypedLoweringTest()
|
||||
: TypedGraphTest(3), javascript_(zone()), deps_(broker(), zone()) {}
|
||||
~JSTypedLoweringTest() override = default;
|
||||
|
||||
protected:
|
||||
@ -59,6 +61,7 @@ class JSTypedLoweringTest : public TypedGraphTest {
|
||||
|
||||
private:
|
||||
JSOperatorBuilder javascript_;
|
||||
CompilationDependencies deps_;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user