Commit 376d242f authored by Georg Schmid's avatar Georg Schmid Committed by Commit Bot

Make LoadElimination aware of const fields

Change-Id: I28f2c87ffae32d16bcfb7cb17ec6e607e7fa2285
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1599172
Commit-Queue: Georg Schmid <gsps@google.com>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61506}
parent 05c3f23c
...@@ -326,11 +326,11 @@ bool AccessInfoFactory::ComputeElementAccessInfos( ...@@ -326,11 +326,11 @@ bool AccessInfoFactory::ComputeElementAccessInfos(
PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder, Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
int number, AccessMode access_mode) const { int descriptor, AccessMode access_mode) const {
DCHECK_NE(number, DescriptorArray::kNotFound); DCHECK_NE(descriptor, DescriptorArray::kNotFound);
Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
PropertyDetails const details = descriptors->GetDetails(number); PropertyDetails const details = descriptors->GetDetails(descriptor);
int index = descriptors->GetFieldIndex(number); int index = descriptors->GetFieldIndex(descriptor);
Representation details_representation = details.representation(); Representation details_representation = details.representation();
if (details_representation.IsNone()) { if (details_representation.IsNone()) {
// The ICs collect feedback in PREMONOMORPHIC state already, // The ICs collect feedback in PREMONOMORPHIC state already,
...@@ -347,6 +347,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( ...@@ -347,6 +347,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
MachineType::RepCompressedTagged(); MachineType::RepCompressedTagged();
MaybeHandle<Map> field_map; MaybeHandle<Map> field_map;
MapRef map_ref(broker(), map); MapRef map_ref(broker(), map);
map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
ZoneVector<CompilationDependencies::Dependency const*> ZoneVector<CompilationDependencies::Dependency const*>
unrecorded_dependencies(zone()); unrecorded_dependencies(zone());
if (details_representation.IsSmi()) { if (details_representation.IsSmi()) {
...@@ -355,7 +356,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( ...@@ -355,7 +356,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later. map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
unrecorded_dependencies.push_back( unrecorded_dependencies.push_back(
dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref, dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
number)); descriptor));
} else if (details_representation.IsDouble()) { } else if (details_representation.IsDouble()) {
field_type = type_cache_->kFloat64; field_type = type_cache_->kFloat64;
field_representation = MachineRepresentation::kFloat64; field_representation = MachineRepresentation::kFloat64;
...@@ -363,8 +364,8 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( ...@@ -363,8 +364,8 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
// Extract the field type from the property details (make sure its // Extract the field type from the property details (make sure its
// representation is TaggedPointer to reflect the heap object case). // representation is TaggedPointer to reflect the heap object case).
field_representation = MachineType::RepCompressedTaggedPointer(); field_representation = MachineType::RepCompressedTaggedPointer();
Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number), Handle<FieldType> descriptors_field_type(
isolate()); descriptors->GetFieldType(descriptor), isolate());
if (descriptors_field_type->IsNone()) { if (descriptors_field_type->IsNone()) {
// Store is not safe if the field type was cleared. // Store is not safe if the field type was cleared.
if (access_mode == AccessMode::kStore) { if (access_mode == AccessMode::kStore) {
...@@ -377,17 +378,20 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( ...@@ -377,17 +378,20 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later. map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
unrecorded_dependencies.push_back( unrecorded_dependencies.push_back(
dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref, dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
number)); descriptor));
if (descriptors_field_type->IsClass()) { if (descriptors_field_type->IsClass()) {
unrecorded_dependencies.push_back( unrecorded_dependencies.push_back(
dependencies()->FieldTypeDependencyOffTheRecord(map_ref, number)); dependencies()->FieldTypeDependencyOffTheRecord(map_ref, descriptor));
// Remember the field map, and try to infer a useful type. // Remember the field map, and try to infer a useful type.
Handle<Map> map(descriptors_field_type->AsClass(), isolate()); Handle<Map> map(descriptors_field_type->AsClass(), isolate());
field_type = Type::For(MapRef(broker(), map)); field_type = Type::For(MapRef(broker(), map));
field_map = MaybeHandle<Map>(map); field_map = MaybeHandle<Map>(map);
} }
} }
switch (details.constness()) { map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
PropertyConstness constness =
dependencies()->DependOnFieldConstness(map_ref, descriptor);
switch (constness) {
case PropertyConstness::kMutable: case PropertyConstness::kMutable:
return PropertyAccessInfo::DataField( return PropertyAccessInfo::DataField(
zone(), receiver_map, std::move(unrecorded_dependencies), field_index, zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
...@@ -402,10 +406,11 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo( ...@@ -402,10 +406,11 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo( PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map, Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const { MaybeHandle<JSObject> holder, int descriptor,
DCHECK_NE(number, DescriptorArray::kNotFound); AccessMode access_mode) const {
DCHECK_NE(descriptor, DescriptorArray::kNotFound);
Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
SLOW_DCHECK(number == descriptors->Search(*name, *map)); SLOW_DCHECK(descriptor == descriptors->Search(*name, *map));
if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) { if (map->instance_type() == JS_MODULE_NAMESPACE_TYPE) {
DCHECK(map->is_prototype_map()); DCHECK(map->is_prototype_map());
Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()), Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
...@@ -427,7 +432,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo( ...@@ -427,7 +432,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
return PropertyAccessInfo::AccessorConstant(zone(), receiver_map, return PropertyAccessInfo::AccessorConstant(zone(), receiver_map,
Handle<Object>(), holder); Handle<Object>(), holder);
} }
Handle<Object> accessors(descriptors->GetStrongValue(number), isolate()); Handle<Object> accessors(descriptors->GetStrongValue(descriptor), isolate());
if (!accessors->IsAccessorPair()) { if (!accessors->IsAccessorPair()) {
return PropertyAccessInfo::Invalid(zone()); return PropertyAccessInfo::Invalid(zone());
} }
......
...@@ -204,11 +204,12 @@ class AccessInfoFactory final { ...@@ -204,11 +204,12 @@ class AccessInfoFactory final {
PropertyAccessInfo ComputeDataFieldAccessInfo(Handle<Map> receiver_map, PropertyAccessInfo ComputeDataFieldAccessInfo(Handle<Map> receiver_map,
Handle<Map> map, Handle<Map> map,
MaybeHandle<JSObject> holder, MaybeHandle<JSObject> holder,
int number, int descriptor,
AccessMode access_mode) const; AccessMode access_mode) const;
PropertyAccessInfo ComputeAccessorDescriptorAccessInfo( PropertyAccessInfo ComputeAccessorDescriptorAccessInfo(
Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map, Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const; MaybeHandle<JSObject> holder, int descriptor,
AccessMode access_mode) const;
void MergePropertyAccessInfos(ZoneVector<PropertyAccessInfo> infos, void MergePropertyAccessInfos(ZoneVector<PropertyAccessInfo> infos,
AccessMode access_mode, AccessMode access_mode,
......
...@@ -459,20 +459,6 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -459,20 +459,6 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
return NoChange(); return NoChange();
} }
// Install dependency on constness. Unfortunately, access_info does not
// track descriptor index, so we have to search for it.
MapRef holder_map(broker(), handle(holder->map(), isolate()));
Handle<DescriptorArray> descriptors(
holder_map.object()->instance_descriptors(), isolate());
int descriptor_index = descriptors->Search(
*(factory()->has_instance_symbol()), *(holder_map.object()));
CHECK_NE(descriptor_index, DescriptorArray::kNotFound);
holder_map.SerializeOwnDescriptors();
if (dependencies()->DependOnFieldConstness(holder_map, descriptor_index) !=
PropertyConstness::kConst) {
return NoChange();
}
if (found_on_proto) { if (found_on_proto) {
dependencies()->DependOnStablePrototypeChains( dependencies()->DependOnStablePrototypeChains(
access_info.receiver_maps(), kStartAtPrototype, access_info.receiver_maps(), kStartAtPrototype,
......
...@@ -145,6 +145,9 @@ bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) { ...@@ -145,6 +145,9 @@ bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) {
} // namespace } // namespace
LoadElimination::AbstractState const
LoadElimination::AbstractState::empty_state_;
Node* LoadElimination::AbstractElements::Lookup( Node* LoadElimination::AbstractElements::Lookup(
Node* object, Node* index, MachineRepresentation representation) const { Node* object, Node* index, MachineRepresentation representation) const {
for (Element const element : elements_) { for (Element const element : elements_) {
...@@ -376,6 +379,21 @@ void LoadElimination::AbstractMaps::Print() const { ...@@ -376,6 +379,21 @@ void LoadElimination::AbstractMaps::Print() const {
} }
} }
bool LoadElimination::AbstractState::FieldsEquals(
AbstractFields const& this_fields,
AbstractFields const& that_fields) const {
for (size_t i = 0u; i < this_fields.size(); ++i) {
AbstractField const* this_field = this_fields[i];
AbstractField const* that_field = that_fields[i];
if (this_field) {
if (!that_field || !that_field->Equals(this_field)) return false;
} else if (that_field) {
return false;
}
}
return true;
}
bool LoadElimination::AbstractState::Equals(AbstractState const* that) const { bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
if (this->elements_) { if (this->elements_) {
if (!that->elements_ || !that->elements_->Equals(this->elements_)) { if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
...@@ -384,14 +402,9 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const { ...@@ -384,14 +402,9 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
} else if (that->elements_) { } else if (that->elements_) {
return false; return false;
} }
for (size_t i = 0u; i < arraysize(fields_); ++i) { if (!FieldsEquals(this->fields_, that->fields_) ||
AbstractField const* this_field = this->fields_[i]; !FieldsEquals(this->const_fields_, that->const_fields_)) {
AbstractField const* that_field = that->fields_[i]; return false;
if (this_field) {
if (!that_field || !that_field->Equals(this_field)) return false;
} else if (that_field) {
return false;
}
} }
if (this->maps_) { if (this->maps_) {
if (!that->maps_ || !that->maps_->Equals(this->maps_)) { if (!that->maps_ || !that->maps_->Equals(this->maps_)) {
...@@ -403,6 +416,20 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const { ...@@ -403,6 +416,20 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
return true; return true;
} }
void LoadElimination::AbstractState::FieldsMerge(
AbstractFields& this_fields, AbstractFields const& that_fields,
Zone* zone) {
for (size_t i = 0; i < this_fields.size(); ++i) {
if (this_fields[i]) {
if (that_fields[i]) {
this_fields[i] = this_fields[i]->Merge(that_fields[i], zone);
} else {
this_fields[i] = nullptr;
}
}
}
}
void LoadElimination::AbstractState::Merge(AbstractState const* that, void LoadElimination::AbstractState::Merge(AbstractState const* that,
Zone* zone) { Zone* zone) {
// Merge the information we have about the elements. // Merge the information we have about the elements.
...@@ -413,15 +440,8 @@ void LoadElimination::AbstractState::Merge(AbstractState const* that, ...@@ -413,15 +440,8 @@ void LoadElimination::AbstractState::Merge(AbstractState const* that,
} }
// Merge the information we have about the fields. // Merge the information we have about the fields.
for (size_t i = 0; i < arraysize(fields_); ++i) { FieldsMerge(this->fields_, that->fields_, zone);
if (this->fields_[i]) { FieldsMerge(this->const_fields_, that->const_fields_, zone);
if (that->fields_[i]) {
this->fields_[i] = this->fields_[i]->Merge(that->fields_[i], zone);
} else {
this->fields_[i] = nullptr;
}
}
}
// Merge the information we have about the maps. // Merge the information we have about the maps.
if (this->maps_) { if (this->maps_) {
...@@ -505,13 +525,15 @@ LoadElimination::AbstractState::KillElement(Node* object, Node* index, ...@@ -505,13 +525,15 @@ LoadElimination::AbstractState::KillElement(Node* object, Node* index,
LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField( LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
Node* object, size_t index, Node* value, MaybeHandle<Name> name, Node* object, size_t index, Node* value, MaybeHandle<Name> name,
Zone* zone) const { PropertyConstness constness, Zone* zone) const {
AbstractState* that = new (zone) AbstractState(*this); AbstractState* that = new (zone) AbstractState(*this);
if (that->fields_[index]) { AbstractFields& fields = constness == PropertyConstness::kConst
that->fields_[index] = ? that->const_fields_
that->fields_[index]->Extend(object, value, name, zone); : that->fields_;
if (fields[index]) {
fields[index] = fields[index]->Extend(object, value, name, zone);
} else { } else {
that->fields_[index] = new (zone) AbstractField(object, value, name, zone); fields[index] = new (zone) AbstractField(object, value, name, zone);
} }
return that; return that;
} }
...@@ -541,14 +563,14 @@ LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name, ...@@ -541,14 +563,14 @@ LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name,
Zone* zone) const { Zone* zone) const {
AliasStateInfo alias_info(this, object); AliasStateInfo alias_info(this, object);
for (size_t i = 0;; ++i) { for (size_t i = 0;; ++i) {
if (i == arraysize(fields_)) return this; if (i == fields_.size()) return this;
if (AbstractField const* this_field = this->fields_[i]) { if (AbstractField const* this_field = this->fields_[i]) {
AbstractField const* that_field = AbstractField const* that_field =
this_field->Kill(alias_info, name, zone); this_field->Kill(alias_info, name, zone);
if (that_field != this_field) { if (that_field != this_field) {
AbstractState* that = new (zone) AbstractState(*this); AbstractState* that = new (zone) AbstractState(*this);
that->fields_[i] = that_field; that->fields_[i] = that_field;
while (++i < arraysize(fields_)) { while (++i < fields_.size()) {
if (this->fields_[i] != nullptr) { if (this->fields_[i] != nullptr) {
that->fields_[i] = this->fields_[i]->Kill(alias_info, name, zone); that->fields_[i] = this->fields_[i]->Kill(alias_info, name, zone);
} }
...@@ -559,11 +581,29 @@ LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name, ...@@ -559,11 +581,29 @@ LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name,
} }
} }
Node* LoadElimination::AbstractState::LookupField(Node* object, LoadElimination::AbstractState const* LoadElimination::AbstractState::KillAll(
size_t index) const { Zone* zone) const {
if (AbstractField const* this_field = this->fields_[index]) { // Kill everything except for const fields
for (size_t i = 0; i < const_fields_.size(); ++i) {
if (const_fields_[i]) {
AbstractState* that = new (zone) AbstractState();
that->const_fields_ = const_fields_;
return that;
}
}
return LoadElimination::empty_state();
}
Node* LoadElimination::AbstractState::LookupField(
Node* object, size_t index, PropertyConstness constness) const {
AbstractFields const& fields =
constness == PropertyConstness::kConst ? const_fields_ : fields_;
if (AbstractField const* this_field = fields[index]) {
return this_field->Lookup(object); return this_field->Lookup(object);
} }
if (constness == PropertyConstness::kConst) {
return LookupField(object, index, PropertyConstness::kMutable);
}
return nullptr; return nullptr;
} }
...@@ -600,12 +640,18 @@ void LoadElimination::AbstractState::Print() const { ...@@ -600,12 +640,18 @@ void LoadElimination::AbstractState::Print() const {
PrintF(" elements:\n"); PrintF(" elements:\n");
elements_->Print(); elements_->Print();
} }
for (size_t i = 0; i < arraysize(fields_); ++i) { for (size_t i = 0; i < fields_.size(); ++i) {
if (AbstractField const* const field = fields_[i]) { if (AbstractField const* const field = fields_[i]) {
PrintF(" field %zu:\n", i); PrintF(" field %zu:\n", i);
field->Print(); field->Print();
} }
} }
for (size_t i = 0; i < const_fields_.size(); ++i) {
if (AbstractField const* const const_field = const_fields_[i]) {
PrintF(" const field %zu:\n", i);
const_field->Print();
}
}
} }
LoadElimination::AbstractState const* LoadElimination::AbstractState const*
...@@ -690,8 +736,9 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) { ...@@ -690,8 +736,9 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
MaybeHandle<Name>(), zone()); MaybeHandle<Name>(), zone());
// Add the new elements on {object}. // Add the new elements on {object}.
state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node, state =
MaybeHandle<Name>(), zone()); state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
MaybeHandle<Name>(), PropertyConstness::kMutable, zone());
return UpdateState(node, state); return UpdateState(node, state);
} }
...@@ -716,8 +763,9 @@ Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) { ...@@ -716,8 +763,9 @@ Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
MaybeHandle<Name>(), zone()); MaybeHandle<Name>(), zone());
// Add the new elements on {object}. // Add the new elements on {object}.
state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node, state =
MaybeHandle<Name>(), zone()); state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
MaybeHandle<Name>(), PropertyConstness::kMutable, zone());
return UpdateState(node, state); return UpdateState(node, state);
} }
...@@ -805,7 +853,8 @@ Reduction LoadElimination::ReduceLoadField(Node* node, ...@@ -805,7 +853,8 @@ Reduction LoadElimination::ReduceLoadField(Node* node,
} else { } else {
int field_index = FieldIndexOf(access); int field_index = FieldIndexOf(access);
if (field_index >= 0) { if (field_index >= 0) {
if (Node* replacement = state->LookupField(object, field_index)) { if (Node* replacement =
state->LookupField(object, field_index, access.constness)) {
// Make sure we don't resurrect dead {replacement} nodes. // Make sure we don't resurrect dead {replacement} nodes.
if (!replacement->IsDead()) { if (!replacement->IsDead()) {
// Introduce a TypeGuard if the type of the {replacement} node is not // Introduce a TypeGuard if the type of the {replacement} node is not
...@@ -824,7 +873,8 @@ Reduction LoadElimination::ReduceLoadField(Node* node, ...@@ -824,7 +873,8 @@ Reduction LoadElimination::ReduceLoadField(Node* node,
return Replace(replacement); return Replace(replacement);
} }
} }
state = state->AddField(object, field_index, node, access.name, zone()); state = state->AddField(object, field_index, node, access.name,
access.constness, zone());
} }
} }
Handle<Map> field_map; Handle<Map> field_map;
...@@ -857,15 +907,16 @@ Reduction LoadElimination::ReduceStoreField(Node* node, ...@@ -857,15 +907,16 @@ Reduction LoadElimination::ReduceStoreField(Node* node,
} else { } else {
int field_index = FieldIndexOf(access); int field_index = FieldIndexOf(access);
if (field_index >= 0) { if (field_index >= 0) {
Node* const old_value = state->LookupField(object, field_index); Node* const old_value =
state->LookupField(object, field_index, access.constness);
if (old_value == new_value) { if (old_value == new_value) {
// This store is fully redundant. // This store is fully redundant.
return Replace(effect); return Replace(effect);
} }
// Kill all potentially aliasing fields and record the new value. // Kill all potentially aliasing fields and record the new value.
state = state->KillField(object, field_index, access.name, zone()); state = state->KillField(object, field_index, access.name, zone());
state = state = state->AddField(object, field_index, new_value, access.name,
state->AddField(object, field_index, new_value, access.name, zone()); access.constness, zone());
} else { } else {
// Unsupported StoreField operator. // Unsupported StoreField operator.
state = state->KillFields(object, access.name, zone()); state = state->KillFields(object, access.name, zone());
...@@ -1047,7 +1098,7 @@ Reduction LoadElimination::ReduceOtherNode(Node* node) { ...@@ -1047,7 +1098,7 @@ Reduction LoadElimination::ReduceOtherNode(Node* node) {
if (state == nullptr) return NoChange(); if (state == nullptr) return NoChange();
// Check if this {node} has some uncontrolled side effects. // Check if this {node} has some uncontrolled side effects.
if (!node->op()->HasProperty(Operator::kNoWrite)) { if (!node->op()->HasProperty(Operator::kNoWrite)) {
state = empty_state(); state = state->KillAll(zone());
} }
return UpdateState(node, state); return UpdateState(node, state);
} else { } else {
...@@ -1163,7 +1214,7 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState( ...@@ -1163,7 +1214,7 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
break; break;
} }
default: default:
return empty_state(); return state->KillAll(zone());
} }
} }
for (int i = 0; i < current->op()->EffectInputCount(); ++i) { for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
......
...@@ -182,11 +182,7 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -182,11 +182,7 @@ class V8_EXPORT_PRIVATE LoadElimination final
class AbstractState final : public ZoneObject { class AbstractState final : public ZoneObject {
public: public:
AbstractState() { AbstractState() {}
for (size_t i = 0; i < arraysize(fields_); ++i) {
fields_[i] = nullptr;
}
}
bool Equals(AbstractState const* that) const; bool Equals(AbstractState const* that) const;
void Merge(AbstractState const* that, Zone* zone); void Merge(AbstractState const* that, Zone* zone);
...@@ -199,7 +195,9 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -199,7 +195,9 @@ class V8_EXPORT_PRIVATE LoadElimination final
bool LookupMaps(Node* object, ZoneHandleSet<Map>* object_maps) const; bool LookupMaps(Node* object, ZoneHandleSet<Map>* object_maps) const;
AbstractState const* AddField(Node* object, size_t index, Node* value, AbstractState const* AddField(Node* object, size_t index, Node* value,
MaybeHandle<Name> name, Zone* zone) const; MaybeHandle<Name> name,
PropertyConstness constness,
Zone* zone) const;
AbstractState const* KillField(const AliasStateInfo& alias_info, AbstractState const* KillField(const AliasStateInfo& alias_info,
size_t index, MaybeHandle<Name> name, size_t index, MaybeHandle<Name> name,
Zone* zone) const; Zone* zone) const;
...@@ -207,7 +205,9 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -207,7 +205,9 @@ class V8_EXPORT_PRIVATE LoadElimination final
MaybeHandle<Name> name, Zone* zone) const; MaybeHandle<Name> name, Zone* zone) const;
AbstractState const* KillFields(Node* object, MaybeHandle<Name> name, AbstractState const* KillFields(Node* object, MaybeHandle<Name> name,
Zone* zone) const; Zone* zone) const;
Node* LookupField(Node* object, size_t index) const; AbstractState const* KillAll(Zone* zone) const;
Node* LookupField(Node* object, size_t index,
PropertyConstness constness) const;
AbstractState const* AddElement(Node* object, Node* index, Node* value, AbstractState const* AddElement(Node* object, Node* index, Node* value,
MachineRepresentation representation, MachineRepresentation representation,
...@@ -219,9 +219,21 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -219,9 +219,21 @@ class V8_EXPORT_PRIVATE LoadElimination final
void Print() const; void Print() const;
static AbstractState const* empty_state() { return &empty_state_; }
private: private:
static AbstractState const empty_state_;
using AbstractFields = std::array<AbstractField const*, kMaxTrackedFields>;
bool FieldsEquals(AbstractFields const& this_fields,
AbstractFields const& that_fields) const;
void FieldsMerge(AbstractFields& this_fields,
AbstractFields const& that_fields, Zone* zone);
AbstractElements const* elements_ = nullptr; AbstractElements const* elements_ = nullptr;
AbstractField const* fields_[kMaxTrackedFields]; AbstractFields fields_{};
AbstractFields const_fields_{};
AbstractMaps const* maps_ = nullptr; AbstractMaps const* maps_ = nullptr;
}; };
...@@ -266,15 +278,17 @@ class V8_EXPORT_PRIVATE LoadElimination final ...@@ -266,15 +278,17 @@ class V8_EXPORT_PRIVATE LoadElimination final
static int FieldIndexOf(int offset); static int FieldIndexOf(int offset);
static int FieldIndexOf(FieldAccess const& access); static int FieldIndexOf(FieldAccess const& access);
static AbstractState const* empty_state() {
return AbstractState::empty_state();
}
CommonOperatorBuilder* common() const; CommonOperatorBuilder* common() const;
AbstractState const* empty_state() const { return &empty_state_; }
Isolate* isolate() const; Isolate* isolate() const;
Factory* factory() const; Factory* factory() const;
Graph* graph() const; Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; } JSGraph* jsgraph() const { return jsgraph_; }
Zone* zone() const { return node_states_.zone(); } Zone* zone() const { return node_states_.zone(); }
AbstractState const empty_state_;
AbstractStateForEffectNodes node_states_; AbstractStateForEffectNodes node_states_;
JSGraph* const jsgraph_; JSGraph* const jsgraph_;
......
...@@ -228,17 +228,7 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField( ...@@ -228,17 +228,7 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
if (it.IsReadOnly() && !it.IsConfigurable()) { if (it.IsReadOnly() && !it.IsConfigurable()) {
return jsgraph()->Constant(JSReceiver::GetDataProperty(&it)); return jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
} else if (access_info.IsDataConstant()) { } else if (access_info.IsDataConstant()) {
// It's necessary to add dependency on the map that introduced
// the field.
DCHECK(access_info.IsDataConstant());
DCHECK(!it.is_dictionary_holder()); DCHECK(!it.is_dictionary_holder());
MapRef map(broker(),
handle(it.GetHolder<HeapObject>()->map(), isolate()));
map.SerializeOwnDescriptors(); // TODO(neis): Remove later.
if (dependencies()->DependOnFieldConstness(
map, it.GetFieldDescriptorIndex()) != PropertyConstness::kConst) {
return nullptr;
}
return jsgraph()->Constant(JSReceiver::GetDataProperty(&it)); return jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
} }
} }
...@@ -264,6 +254,9 @@ Node* PropertyAccessBuilder::BuildLoadDataField( ...@@ -264,6 +254,9 @@ Node* PropertyAccessBuilder::BuildLoadDataField(
simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()), simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
storage, *effect, *control); storage, *effect, *control);
} }
PropertyConstness constness = access_info.IsDataConstant()
? PropertyConstness::kConst
: PropertyConstness::kMutable;
FieldAccess field_access = { FieldAccess field_access = {
kTaggedBase, kTaggedBase,
field_index.offset(), field_index.offset(),
...@@ -272,15 +265,21 @@ Node* PropertyAccessBuilder::BuildLoadDataField( ...@@ -272,15 +265,21 @@ Node* PropertyAccessBuilder::BuildLoadDataField(
field_type, field_type,
MachineType::TypeForRepresentation(field_representation), MachineType::TypeForRepresentation(field_representation),
kFullWriteBarrier, kFullWriteBarrier,
LoadSensitivity::kCritical}; LoadSensitivity::kCritical,
constness};
if (field_representation == MachineRepresentation::kFloat64) { if (field_representation == MachineRepresentation::kFloat64) {
if (!field_index.is_inobject() || field_index.is_hidden_field() || if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) { !FLAG_unbox_double_fields) {
FieldAccess const storage_access = { FieldAccess const storage_access = {
kTaggedBase, field_index.offset(), kTaggedBase,
name.object(), MaybeHandle<Map>(), field_index.offset(),
Type::OtherInternal(), MachineType::TypeCompressedTaggedPointer(), name.object(),
kPointerWriteBarrier, LoadSensitivity::kCritical}; MaybeHandle<Map>(),
Type::OtherInternal(),
MachineType::TypeCompressedTaggedPointer(),
kPointerWriteBarrier,
LoadSensitivity::kCritical,
constness};
storage = *effect = graph()->NewNode( storage = *effect = graph()->NewNode(
simplified()->LoadField(storage_access), storage, *effect, *control); simplified()->LoadField(storage_access), storage, *effect, *control);
field_access.offset = HeapNumber::kValueOffset; field_access.offset = HeapNumber::kValueOffset;
......
...@@ -56,6 +56,7 @@ struct FieldAccess { ...@@ -56,6 +56,7 @@ struct FieldAccess {
MachineType machine_type; // machine type of the field. MachineType machine_type; // machine type of the field.
WriteBarrierKind write_barrier_kind; // write barrier hint. WriteBarrierKind write_barrier_kind; // write barrier hint.
LoadSensitivity load_sensitivity; // load safety for poisoning. LoadSensitivity load_sensitivity; // load safety for poisoning.
PropertyConstness constness; // whether the field is assigned only once
FieldAccess() FieldAccess()
: base_is_tagged(kTaggedBase), : base_is_tagged(kTaggedBase),
...@@ -63,12 +64,14 @@ struct FieldAccess { ...@@ -63,12 +64,14 @@ struct FieldAccess {
type(Type::None()), type(Type::None()),
machine_type(MachineType::None()), machine_type(MachineType::None()),
write_barrier_kind(kFullWriteBarrier), write_barrier_kind(kFullWriteBarrier),
load_sensitivity(LoadSensitivity::kUnsafe) {} load_sensitivity(LoadSensitivity::kUnsafe),
constness(PropertyConstness::kMutable) {}
FieldAccess(BaseTaggedness base_is_tagged, int offset, MaybeHandle<Name> name, FieldAccess(BaseTaggedness base_is_tagged, int offset, MaybeHandle<Name> name,
MaybeHandle<Map> map, Type type, MachineType machine_type, MaybeHandle<Map> map, Type type, MachineType machine_type,
WriteBarrierKind write_barrier_kind, WriteBarrierKind write_barrier_kind,
LoadSensitivity load_sensitivity = LoadSensitivity::kUnsafe) LoadSensitivity load_sensitivity = LoadSensitivity::kUnsafe,
PropertyConstness constness = PropertyConstness::kMutable)
: base_is_tagged(base_is_tagged), : base_is_tagged(base_is_tagged),
offset(offset), offset(offset),
name(name), name(name),
...@@ -76,7 +79,8 @@ struct FieldAccess { ...@@ -76,7 +79,8 @@ struct FieldAccess {
type(type), type(type),
machine_type(machine_type), machine_type(machine_type),
write_barrier_kind(write_barrier_kind), write_barrier_kind(write_barrier_kind),
load_sensitivity(load_sensitivity) {} load_sensitivity(load_sensitivity),
constness(constness) {}
int tag() const { return base_is_tagged == kTaggedBase ? kHeapObjectTag : 0; } int tag() const { return base_is_tagged == kTaggedBase ? kHeapObjectTag : 0; }
}; };
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// Check that load elimination on const-marked fields works
(function() {
function maybe_sideeffect(b) { return 42; }
function f(k) {
let b = { value: k };
maybe_sideeffect(b);
let v1 = b.value;
maybe_sideeffect(b);
let v2 = b.value;
%TurbofanStaticAssert(v1 == v2);
// TODO(gsps): Improve analysis to also propagate stored value
// Eventually, this should also work:
// %TurbofanStaticAssert(v2 == k);
}
%NeverOptimizeFunction(maybe_sideeffect);
f(1);
f(2);
%OptimizeFunctionOnNextCall(f);
f(3);
})();
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