Commit d70ee61c authored by Jakob Gruber's avatar Jakob Gruber Committed by V8 LUCI CQ

[compiler] Various refactors

A mix of readability refactors, additional DCHECKs, and
addressed/updated TODOs.

Bug: v8:7790
Change-Id: I87ff996abd40b0ed081586e2c0da1a4c0942fed4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3041665
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75858}
parent 869e8f47
...@@ -271,16 +271,22 @@ bool OptionalRefEquals(base::Optional<RefT> lhs, base::Optional<RefT> rhs) { ...@@ -271,16 +271,22 @@ bool OptionalRefEquals(base::Optional<RefT> lhs, base::Optional<RefT> rhs) {
return lhs->equals(rhs.value()); return lhs->equals(rhs.value());
} }
template <class T>
void AppendVector(ZoneVector<T>* dst, const ZoneVector<T>& src) {
dst->insert(dst->end(), src.begin(), src.end());
}
} // namespace } // namespace
bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
AccessMode access_mode, Zone* zone) { AccessMode access_mode, Zone* zone) {
if (this->kind_ != that->kind_) return false; if (kind_ != that->kind_) return false;
if (!OptionalRefEquals(holder_, that->holder_)) return false; if (!OptionalRefEquals(holder_, that->holder_)) return false;
switch (this->kind_) { switch (kind_) {
case kInvalid: case kInvalid:
return that->kind_ == kInvalid; DCHECK_EQ(that->kind_, kInvalid);
return true;
case kDataField: case kDataField:
case kFastDataConstant: { case kFastDataConstant: {
...@@ -288,89 +294,70 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that, ...@@ -288,89 +294,70 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
// GetFieldAccessStubKey method here just like the ICs do // GetFieldAccessStubKey method here just like the ICs do
// since that way we only compare the relevant bits of the // since that way we only compare the relevant bits of the
// field indices). // field indices).
if (this->field_index_.GetFieldAccessStubKey() == if (field_index_.GetFieldAccessStubKey() !=
that->field_index_.GetFieldAccessStubKey()) { that->field_index_.GetFieldAccessStubKey()) {
switch (access_mode) { return false;
case AccessMode::kHas: }
case AccessMode::kLoad: {
if (!this->field_representation_.Equals( switch (access_mode) {
that->field_representation_)) { case AccessMode::kHas:
if (this->field_representation_.IsDouble() || case AccessMode::kLoad: {
that->field_representation_.IsDouble()) { if (!field_representation_.Equals(that->field_representation_)) {
return false; if (field_representation_.IsDouble() ||
} that->field_representation_.IsDouble()) {
this->field_representation_ = Representation::Tagged();
}
if (!OptionalRefEquals(field_map_, that->field_map_)) {
field_map_ = {};
}
break;
}
case AccessMode::kStore:
case AccessMode::kStoreInLiteral: {
// For stores, the field map and field representation information
// must match exactly, otherwise we cannot merge the stores. We
// also need to make sure that in case of transitioning stores,
// the transition targets match.
if (!OptionalRefEquals(field_map_, that->field_map_) ||
!this->field_representation_.Equals(
that->field_representation_) ||
!OptionalRefEquals(transition_map_, that->transition_map_)) {
return false; return false;
} }
break; field_representation_ = Representation::Tagged();
}
if (!OptionalRefEquals(field_map_, that->field_map_)) {
field_map_ = {};
}
break;
}
case AccessMode::kStore:
case AccessMode::kStoreInLiteral: {
// For stores, the field map and field representation information
// must match exactly, otherwise we cannot merge the stores. We
// also need to make sure that in case of transitioning stores,
// the transition targets match.
if (!OptionalRefEquals(field_map_, that->field_map_) ||
!field_representation_.Equals(that->field_representation_) ||
!OptionalRefEquals(transition_map_, that->transition_map_)) {
return false;
} }
break;
} }
this->field_type_ =
Type::Union(this->field_type_, that->field_type_, zone);
this->lookup_start_object_maps_.insert(
this->lookup_start_object_maps_.end(),
that->lookup_start_object_maps_.begin(),
that->lookup_start_object_maps_.end());
this->unrecorded_dependencies_.insert(
this->unrecorded_dependencies_.end(),
that->unrecorded_dependencies_.begin(),
that->unrecorded_dependencies_.end());
return true;
} }
return false;
field_type_ = Type::Union(field_type_, that->field_type_, zone);
AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
AppendVector(&unrecorded_dependencies_, that->unrecorded_dependencies_);
return true;
} }
case kDictionaryProtoAccessorConstant: case kDictionaryProtoAccessorConstant:
case kFastAccessorConstant: { case kFastAccessorConstant: {
// Check if we actually access the same constant. // Check if we actually access the same constant.
if (OptionalRefEquals(constant_, that->constant_)) { if (!OptionalRefEquals(constant_, that->constant_)) return false;
DCHECK(this->unrecorded_dependencies_.empty());
DCHECK(that->unrecorded_dependencies_.empty()); DCHECK(unrecorded_dependencies_.empty());
this->lookup_start_object_maps_.insert( DCHECK(that->unrecorded_dependencies_.empty());
this->lookup_start_object_maps_.end(), AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
that->lookup_start_object_maps_.begin(), return true;
that->lookup_start_object_maps_.end());
return true;
}
return false;
} }
case kDictionaryProtoDataConstant: { case kDictionaryProtoDataConstant: {
DCHECK_EQ(AccessMode::kLoad, access_mode); DCHECK_EQ(AccessMode::kLoad, access_mode);
if (this->dictionary_index_ == that->dictionary_index_) { if (dictionary_index_ != that->dictionary_index_) return false;
this->lookup_start_object_maps_.insert( AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
this->lookup_start_object_maps_.end(), return true;
that->lookup_start_object_maps_.begin(),
that->lookup_start_object_maps_.end());
return true;
}
return false;
} }
case kNotFound: case kNotFound:
case kStringLength: { case kStringLength: {
DCHECK(this->unrecorded_dependencies_.empty()); DCHECK(unrecorded_dependencies_.empty());
DCHECK(that->unrecorded_dependencies_.empty()); DCHECK(that->unrecorded_dependencies_.empty());
this->lookup_start_object_maps_.insert( AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
this->lookup_start_object_maps_.end(),
that->lookup_start_object_maps_.begin(),
that->lookup_start_object_maps_.end());
return true; return true;
} }
case kModuleExport: case kModuleExport:
...@@ -512,7 +499,9 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( ...@@ -512,7 +499,9 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
constness = dependencies()->DependOnFieldConstness(map, descriptor); constness = dependencies()->DependOnFieldConstness(map, descriptor);
} }
// TODO(v8:11670): Make FindFieldOwner and friends robust wrt concurrency. // Note: FindFieldOwner may be called multiple times throughout one
// compilation. This is safe since its result is fixed for a given map and
// descriptor.
MapRef field_owner_map = map.FindFieldOwner(descriptor); MapRef field_owner_map = map.FindFieldOwner(descriptor);
switch (constness) { switch (constness) {
...@@ -742,6 +731,11 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -742,6 +731,11 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
MapRef map, NameRef name, AccessMode access_mode) const { MapRef map, NameRef name, AccessMode access_mode) const {
CHECK(name.IsUniqueName()); CHECK(name.IsUniqueName());
// Dictionary property const tracking is unsupported when concurrent inlining
// is enabled.
CHECK_IMPLIES(V8_DICT_PROPERTY_CONST_TRACKING_BOOL,
!broker()->is_concurrent_inlining());
JSHeapBroker::MapUpdaterGuardIfNeeded mumd_scope(broker()); JSHeapBroker::MapUpdaterGuardIfNeeded mumd_scope(broker());
if (access_mode == AccessMode::kHas && !map.object()->IsJSReceiverMap()) { if (access_mode == AccessMode::kHas && !map.object()->IsJSReceiverMap()) {
...@@ -779,9 +773,8 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -779,9 +773,8 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
DCHECK(!map.is_dictionary_map()); DCHECK(!map.is_dictionary_map());
// Don't bother optimizing stores to read-only properties. // Don't bother optimizing stores to read-only properties.
if (details.IsReadOnly()) { if (details.IsReadOnly()) return Invalid();
return Invalid();
}
if (details.kind() == kData && holder.has_value()) { if (details.kind() == kData && holder.has_value()) {
// This is a store to a property not found on the receiver but on a // This is a store to a property not found on the receiver but on a
// prototype. According to ES6 section 9.1.9 [[Set]], we need to // prototype. According to ES6 section 9.1.9 [[Set]], we need to
...@@ -790,6 +783,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -790,6 +783,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
return LookupTransition(receiver_map, name, holder); return LookupTransition(receiver_map, name, holder);
} }
} }
if (map.is_dictionary_map()) { if (map.is_dictionary_map()) {
DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
...@@ -806,6 +800,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -806,6 +800,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
return ComputeDictionaryProtoAccessInfo( return ComputeDictionaryProtoAccessInfo(
receiver_map, name, holder.value(), index, access_mode, details); receiver_map, name, holder.value(), index, access_mode, details);
} }
if (dictionary_prototype_on_chain) { if (dictionary_prototype_on_chain) {
// If V8_DICT_PROPERTY_CONST_TRACKING_BOOL was disabled, then a // If V8_DICT_PROPERTY_CONST_TRACKING_BOOL was disabled, then a
// dictionary prototype would have caused a bailout earlier. // dictionary prototype would have caused a bailout earlier.
...@@ -843,6 +838,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -843,6 +838,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
} }
// The property wasn't found on {map}. Look on the prototype if appropriate. // The property wasn't found on {map}. Look on the prototype if appropriate.
DCHECK(!index.is_found());
// Don't search on the prototype chain for special indices in case of // Don't search on the prototype chain for special indices in case of
// integer indexed exotic objects (see ES6 section 9.4.5). // integer indexed exotic objects (see ES6 section 9.4.5).
......
...@@ -138,7 +138,12 @@ class PropertyAccessInfo final { ...@@ -138,7 +138,12 @@ class PropertyAccessInfo final {
DCHECK(!HasDictionaryHolder()); DCHECK(!HasDictionaryHolder());
return transition_map_; return transition_map_;
} }
base::Optional<ObjectRef> constant() const { return constant_; } base::Optional<ObjectRef> constant() const {
DCHECK_IMPLIES(constant_.has_value(),
IsModuleExport() || IsFastAccessorConstant() ||
IsDictionaryProtoAccessorConstant());
return constant_;
}
FieldIndex field_index() const { FieldIndex field_index() const {
DCHECK(!HasDictionaryHolder()); DCHECK(!HasDictionaryHolder());
return field_index_; return field_index_;
......
...@@ -413,19 +413,15 @@ class FieldRepresentationDependency final : public CompilationDependency { ...@@ -413,19 +413,15 @@ class FieldRepresentationDependency final : public CompilationDependency {
Representation representation) Representation representation)
: owner_(owner), : owner_(owner),
descriptor_(descriptor), descriptor_(descriptor),
representation_(representation) {} representation_(representation) {
DCHECK_EQ(owner.object()->FindFieldOwner(owner.isolate(), descriptor),
*owner.object());
}
bool IsValid() const override { bool IsValid() const override {
DisallowGarbageCollection no_heap_allocation; DisallowGarbageCollection no_heap_allocation;
Handle<Map> owner = owner_.object(); return representation_.Equals(owner_.object()
Isolate* isolate = owner_.isolate(); ->instance_descriptors(owner_.isolate())
// TODO(v8:11670): Consider turn this back into a CHECK inside the
// constructor, if possible in light of concurrent heap state
// modifications.
if (owner->FindFieldOwner(isolate, descriptor_) != *owner) return false;
return representation_.Equals(owner->instance_descriptors(isolate)
.GetDetails(descriptor_) .GetDetails(descriptor_)
.representation()); .representation());
} }
...@@ -453,21 +449,16 @@ class FieldTypeDependency final : public CompilationDependency { ...@@ -453,21 +449,16 @@ class FieldTypeDependency final : public CompilationDependency {
public: public:
FieldTypeDependency(const MapRef& owner, InternalIndex descriptor, FieldTypeDependency(const MapRef& owner, InternalIndex descriptor,
const ObjectRef& type) const ObjectRef& type)
: owner_(owner), descriptor_(descriptor), type_(type) {} : owner_(owner), descriptor_(descriptor), type_(type) {
DCHECK_EQ(owner.object()->FindFieldOwner(owner.isolate(), descriptor),
*owner.object());
}
bool IsValid() const override { bool IsValid() const override {
DisallowGarbageCollection no_heap_allocation; DisallowGarbageCollection no_heap_allocation;
Handle<Map> owner = owner_.object(); return *type_.object() == owner_.object()
Isolate* isolate = owner_.isolate(); ->instance_descriptors(owner_.isolate())
.GetFieldType(descriptor_);
// TODO(v8:11670): Consider turn this back into a CHECK inside the
// constructor, if possible in light of concurrent heap state
// modifications.
if (owner->FindFieldOwner(isolate, descriptor_) != *owner) return false;
Handle<Object> type = type_.object();
return *type ==
owner->instance_descriptors(isolate).GetFieldType(descriptor_);
} }
void Install(Handle<Code> code) const override { void Install(Handle<Code> code) const override {
...@@ -485,21 +476,18 @@ class FieldTypeDependency final : public CompilationDependency { ...@@ -485,21 +476,18 @@ class FieldTypeDependency final : public CompilationDependency {
class FieldConstnessDependency final : public CompilationDependency { class FieldConstnessDependency final : public CompilationDependency {
public: public:
FieldConstnessDependency(const MapRef& owner, InternalIndex descriptor) FieldConstnessDependency(const MapRef& owner, InternalIndex descriptor)
: owner_(owner), descriptor_(descriptor) {} : owner_(owner), descriptor_(descriptor) {
DCHECK_EQ(owner.object()->FindFieldOwner(owner.isolate(), descriptor),
*owner.object());
}
bool IsValid() const override { bool IsValid() const override {
DisallowGarbageCollection no_heap_allocation; DisallowGarbageCollection no_heap_allocation;
Handle<Map> owner = owner_.object(); return PropertyConstness::kConst ==
Isolate* isolate = owner_.isolate(); owner_.object()
->instance_descriptors(owner_.isolate())
// TODO(v8:11670): Consider turn this back into a CHECK inside the .GetDetails(descriptor_)
// constructor, if possible in light of concurrent heap state .constness();
// modifications.
if (owner->FindFieldOwner(isolate, descriptor_) != *owner) return false;
return PropertyConstness::kConst == owner->instance_descriptors(isolate)
.GetDetails(descriptor_)
.constness();
} }
void Install(Handle<Code> code) const override { void Install(Handle<Code> code) const override {
......
...@@ -7993,29 +7993,24 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { ...@@ -7993,29 +7993,24 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) {
access_info_factory.FinalizePropertyAccessInfosAsOne(access_infos, access_info_factory.FinalizePropertyAccessInfosAsOne(access_infos,
AccessMode::kLoad); AccessMode::kLoad);
if (ai_exec.IsInvalid()) return inference.NoChange(); if (ai_exec.IsInvalid()) return inference.NoChange();
if (!ai_exec.IsFastDataConstant()) return inference.NoChange();
// If "exec" has been modified on {regexp}, we can't do anything. // Do not reduce if the exec method is not on the prototype chain.
if (ai_exec.IsFastDataConstant()) { base::Optional<JSObjectRef> holder = ai_exec.holder();
base::Optional<JSObjectRef> holder = ai_exec.holder(); if (!holder.has_value()) return inference.NoChange();
// Do not reduce if the exec method is not on the prototype chain.
if (!holder.has_value()) return inference.NoChange();
// Bail out if the exec method is not the original one.
base::Optional<ObjectRef> constant = holder->GetOwnFastDataProperty(
ai_exec.field_representation(), ai_exec.field_index(), dependencies());
if (!constant.has_value() ||
!constant->equals(native_context().regexp_exec_function())) {
return inference.NoChange();
}
// Add proper dependencies on the {regexp}s [[Prototype]]s. // Bail out if the exec method is not the original one.
dependencies()->DependOnStablePrototypeChains( base::Optional<ObjectRef> constant = holder->GetOwnFastDataProperty(
ai_exec.lookup_start_object_maps(), kStartAtPrototype, holder.value()); ai_exec.field_representation(), ai_exec.field_index(), dependencies());
} else { if (!constant.has_value() ||
// TODO(v8:11457) Support dictionary mode protoypes here. !constant->equals(native_context().regexp_exec_function())) {
return inference.NoChange(); return inference.NoChange();
} }
// Add proper dependencies on the {regexp}s [[Prototype]]s.
dependencies()->DependOnStablePrototypeChains(
ai_exec.lookup_start_object_maps(), kStartAtPrototype, holder.value());
inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect,
control, p.feedback()); control, p.feedback());
......
...@@ -453,8 +453,9 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -453,8 +453,9 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
access_info.field_representation(), access_info.field_index(), access_info.field_representation(), access_info.field_index(),
dependencies()); dependencies());
if (!constant.has_value() || !constant->IsHeapObject() || if (!constant.has_value() || !constant->IsHeapObject() ||
!constant->AsHeapObject().map().is_callable()) !constant->AsHeapObject().map().is_callable()) {
return NoChange(); return NoChange();
}
if (found_on_proto) { if (found_on_proto) {
dependencies()->DependOnStablePrototypeChains( dependencies()->DependOnStablePrototypeChains(
...@@ -731,8 +732,9 @@ Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) { ...@@ -731,8 +732,9 @@ Reduction JSNativeContextSpecialization::ReduceJSResolvePromise(Node* node) {
AccessMode::kLoad); AccessMode::kLoad);
// TODO(v8:11457) Support dictionary mode prototypes here. // TODO(v8:11457) Support dictionary mode prototypes here.
if (access_info.IsInvalid() || access_info.HasDictionaryHolder()) if (access_info.IsInvalid() || access_info.HasDictionaryHolder()) {
return inference.NoChange(); return inference.NoChange();
}
// Only optimize when {resolution} definitely doesn't have a "then" property. // Only optimize when {resolution} definitely doesn't have a "then" property.
if (!access_info.IsNotFound()) return inference.NoChange(); if (!access_info.IsNotFound()) return inference.NoChange();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment