[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:
Jakob Gruber 2021-07-19 15:07:37 +02:00 committed by V8 LUCI CQ
parent 18289533db
commit 0dba97f8dc
27 changed files with 560 additions and 342 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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());
}

View File

@ -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 {

View File

@ -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(); }

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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_; }

View File

@ -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()) {

View File

@ -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);

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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(),

View File

@ -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,

View File

@ -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:

View File

@ -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));

View File

@ -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);

View File

@ -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(),

View File

@ -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);
}
}
}

View File

@ -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());

View File

@ -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
##############################################################################

View File

@ -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);
}

View File

@ -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_;
};