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: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#61506}
This commit is contained in:
parent
05c3f23c6e
commit
376d242fbf
@ -326,11 +326,11 @@ bool AccessInfoFactory::ComputeElementAccessInfos(
|
||||
|
||||
PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
|
||||
Handle<Map> receiver_map, Handle<Map> map, MaybeHandle<JSObject> holder,
|
||||
int number, AccessMode access_mode) const {
|
||||
DCHECK_NE(number, DescriptorArray::kNotFound);
|
||||
int descriptor, AccessMode access_mode) const {
|
||||
DCHECK_NE(descriptor, DescriptorArray::kNotFound);
|
||||
Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate());
|
||||
PropertyDetails const details = descriptors->GetDetails(number);
|
||||
int index = descriptors->GetFieldIndex(number);
|
||||
PropertyDetails const details = descriptors->GetDetails(descriptor);
|
||||
int index = descriptors->GetFieldIndex(descriptor);
|
||||
Representation details_representation = details.representation();
|
||||
if (details_representation.IsNone()) {
|
||||
// The ICs collect feedback in PREMONOMORPHIC state already,
|
||||
@ -347,6 +347,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
|
||||
MachineType::RepCompressedTagged();
|
||||
MaybeHandle<Map> field_map;
|
||||
MapRef map_ref(broker(), map);
|
||||
map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
|
||||
ZoneVector<CompilationDependencies::Dependency const*>
|
||||
unrecorded_dependencies(zone());
|
||||
if (details_representation.IsSmi()) {
|
||||
@ -355,7 +356,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
|
||||
map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
|
||||
unrecorded_dependencies.push_back(
|
||||
dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
|
||||
number));
|
||||
descriptor));
|
||||
} else if (details_representation.IsDouble()) {
|
||||
field_type = type_cache_->kFloat64;
|
||||
field_representation = MachineRepresentation::kFloat64;
|
||||
@ -363,8 +364,8 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
|
||||
// Extract the field type from the property details (make sure its
|
||||
// representation is TaggedPointer to reflect the heap object case).
|
||||
field_representation = MachineType::RepCompressedTaggedPointer();
|
||||
Handle<FieldType> descriptors_field_type(descriptors->GetFieldType(number),
|
||||
isolate());
|
||||
Handle<FieldType> descriptors_field_type(
|
||||
descriptors->GetFieldType(descriptor), isolate());
|
||||
if (descriptors_field_type->IsNone()) {
|
||||
// Store is not safe if the field type was cleared.
|
||||
if (access_mode == AccessMode::kStore) {
|
||||
@ -377,17 +378,20 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
|
||||
map_ref.SerializeOwnDescriptors(); // TODO(neis): Remove later.
|
||||
unrecorded_dependencies.push_back(
|
||||
dependencies()->FieldRepresentationDependencyOffTheRecord(map_ref,
|
||||
number));
|
||||
descriptor));
|
||||
if (descriptors_field_type->IsClass()) {
|
||||
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.
|
||||
Handle<Map> map(descriptors_field_type->AsClass(), isolate());
|
||||
field_type = Type::For(MapRef(broker(), 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:
|
||||
return PropertyAccessInfo::DataField(
|
||||
zone(), receiver_map, std::move(unrecorded_dependencies), field_index,
|
||||
@ -402,10 +406,11 @@ PropertyAccessInfo AccessInfoFactory::ComputeDataFieldAccessInfo(
|
||||
|
||||
PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
|
||||
Handle<Map> receiver_map, Handle<Name> name, Handle<Map> map,
|
||||
MaybeHandle<JSObject> holder, int number, AccessMode access_mode) const {
|
||||
DCHECK_NE(number, DescriptorArray::kNotFound);
|
||||
MaybeHandle<JSObject> holder, int descriptor,
|
||||
AccessMode access_mode) const {
|
||||
DCHECK_NE(descriptor, DescriptorArray::kNotFound);
|
||||
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) {
|
||||
DCHECK(map->is_prototype_map());
|
||||
Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(map->prototype_info()),
|
||||
@ -427,7 +432,7 @@ PropertyAccessInfo AccessInfoFactory::ComputeAccessorDescriptorAccessInfo(
|
||||
return PropertyAccessInfo::AccessorConstant(zone(), receiver_map,
|
||||
Handle<Object>(), holder);
|
||||
}
|
||||
Handle<Object> accessors(descriptors->GetStrongValue(number), isolate());
|
||||
Handle<Object> accessors(descriptors->GetStrongValue(descriptor), isolate());
|
||||
if (!accessors->IsAccessorPair()) {
|
||||
return PropertyAccessInfo::Invalid(zone());
|
||||
}
|
||||
|
@ -204,11 +204,12 @@ class AccessInfoFactory final {
|
||||
PropertyAccessInfo ComputeDataFieldAccessInfo(Handle<Map> receiver_map,
|
||||
Handle<Map> map,
|
||||
MaybeHandle<JSObject> holder,
|
||||
int number,
|
||||
int descriptor,
|
||||
AccessMode access_mode) const;
|
||||
PropertyAccessInfo ComputeAccessorDescriptorAccessInfo(
|
||||
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,
|
||||
AccessMode access_mode,
|
||||
|
@ -459,20 +459,6 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
|
||||
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) {
|
||||
dependencies()->DependOnStablePrototypeChains(
|
||||
access_info.receiver_maps(), kStartAtPrototype,
|
||||
|
@ -145,6 +145,9 @@ bool IsCompatible(MachineRepresentation r1, MachineRepresentation r2) {
|
||||
|
||||
} // namespace
|
||||
|
||||
LoadElimination::AbstractState const
|
||||
LoadElimination::AbstractState::empty_state_;
|
||||
|
||||
Node* LoadElimination::AbstractElements::Lookup(
|
||||
Node* object, Node* index, MachineRepresentation representation) const {
|
||||
for (Element const element : elements_) {
|
||||
@ -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 {
|
||||
if (this->elements_) {
|
||||
if (!that->elements_ || !that->elements_->Equals(this->elements_)) {
|
||||
@ -384,14 +402,9 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
|
||||
} else if (that->elements_) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0u; i < arraysize(fields_); ++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;
|
||||
}
|
||||
if (!FieldsEquals(this->fields_, that->fields_) ||
|
||||
!FieldsEquals(this->const_fields_, that->const_fields_)) {
|
||||
return false;
|
||||
}
|
||||
if (this->maps_) {
|
||||
if (!that->maps_ || !that->maps_->Equals(this->maps_)) {
|
||||
@ -403,6 +416,20 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
|
||||
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,
|
||||
Zone* zone) {
|
||||
// Merge the information we have about the elements.
|
||||
@ -413,15 +440,8 @@ void LoadElimination::AbstractState::Merge(AbstractState const* that,
|
||||
}
|
||||
|
||||
// Merge the information we have about the fields.
|
||||
for (size_t i = 0; i < arraysize(fields_); ++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;
|
||||
}
|
||||
}
|
||||
}
|
||||
FieldsMerge(this->fields_, that->fields_, zone);
|
||||
FieldsMerge(this->const_fields_, that->const_fields_, zone);
|
||||
|
||||
// Merge the information we have about the maps.
|
||||
if (this->maps_) {
|
||||
@ -505,13 +525,15 @@ LoadElimination::AbstractState::KillElement(Node* object, Node* index,
|
||||
|
||||
LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField(
|
||||
Node* object, size_t index, Node* value, MaybeHandle<Name> name,
|
||||
Zone* zone) const {
|
||||
PropertyConstness constness, Zone* zone) const {
|
||||
AbstractState* that = new (zone) AbstractState(*this);
|
||||
if (that->fields_[index]) {
|
||||
that->fields_[index] =
|
||||
that->fields_[index]->Extend(object, value, name, zone);
|
||||
AbstractFields& fields = constness == PropertyConstness::kConst
|
||||
? that->const_fields_
|
||||
: that->fields_;
|
||||
if (fields[index]) {
|
||||
fields[index] = fields[index]->Extend(object, value, name, zone);
|
||||
} else {
|
||||
that->fields_[index] = new (zone) AbstractField(object, value, name, zone);
|
||||
fields[index] = new (zone) AbstractField(object, value, name, zone);
|
||||
}
|
||||
return that;
|
||||
}
|
||||
@ -541,14 +563,14 @@ LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name,
|
||||
Zone* zone) const {
|
||||
AliasStateInfo alias_info(this, object);
|
||||
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]) {
|
||||
AbstractField const* that_field =
|
||||
this_field->Kill(alias_info, name, zone);
|
||||
if (that_field != this_field) {
|
||||
AbstractState* that = new (zone) AbstractState(*this);
|
||||
that->fields_[i] = that_field;
|
||||
while (++i < arraysize(fields_)) {
|
||||
while (++i < fields_.size()) {
|
||||
if (this->fields_[i] != nullptr) {
|
||||
that->fields_[i] = this->fields_[i]->Kill(alias_info, name, zone);
|
||||
}
|
||||
@ -559,11 +581,29 @@ LoadElimination::AbstractState::KillFields(Node* object, MaybeHandle<Name> name,
|
||||
}
|
||||
}
|
||||
|
||||
Node* LoadElimination::AbstractState::LookupField(Node* object,
|
||||
size_t index) const {
|
||||
if (AbstractField const* this_field = this->fields_[index]) {
|
||||
LoadElimination::AbstractState const* LoadElimination::AbstractState::KillAll(
|
||||
Zone* zone) const {
|
||||
// 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);
|
||||
}
|
||||
if (constness == PropertyConstness::kConst) {
|
||||
return LookupField(object, index, PropertyConstness::kMutable);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -600,12 +640,18 @@ void LoadElimination::AbstractState::Print() const {
|
||||
PrintF(" elements:\n");
|
||||
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]) {
|
||||
PrintF(" field %zu:\n", i);
|
||||
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*
|
||||
@ -690,8 +736,9 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
|
||||
state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
|
||||
MaybeHandle<Name>(), zone());
|
||||
// Add the new elements on {object}.
|
||||
state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
|
||||
MaybeHandle<Name>(), zone());
|
||||
state =
|
||||
state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
|
||||
MaybeHandle<Name>(), PropertyConstness::kMutable, zone());
|
||||
return UpdateState(node, state);
|
||||
}
|
||||
|
||||
@ -716,8 +763,9 @@ Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
|
||||
state = state->KillField(object, FieldIndexOf(JSObject::kElementsOffset),
|
||||
MaybeHandle<Name>(), zone());
|
||||
// Add the new elements on {object}.
|
||||
state = state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
|
||||
MaybeHandle<Name>(), zone());
|
||||
state =
|
||||
state->AddField(object, FieldIndexOf(JSObject::kElementsOffset), node,
|
||||
MaybeHandle<Name>(), PropertyConstness::kMutable, zone());
|
||||
return UpdateState(node, state);
|
||||
}
|
||||
|
||||
@ -805,7 +853,8 @@ Reduction LoadElimination::ReduceLoadField(Node* node,
|
||||
} else {
|
||||
int field_index = FieldIndexOf(access);
|
||||
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.
|
||||
if (!replacement->IsDead()) {
|
||||
// Introduce a TypeGuard if the type of the {replacement} node is not
|
||||
@ -824,7 +873,8 @@ Reduction LoadElimination::ReduceLoadField(Node* node,
|
||||
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;
|
||||
@ -857,15 +907,16 @@ Reduction LoadElimination::ReduceStoreField(Node* node,
|
||||
} else {
|
||||
int field_index = FieldIndexOf(access);
|
||||
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) {
|
||||
// This store is fully redundant.
|
||||
return Replace(effect);
|
||||
}
|
||||
// Kill all potentially aliasing fields and record the new value.
|
||||
state = state->KillField(object, field_index, access.name, zone());
|
||||
state =
|
||||
state->AddField(object, field_index, new_value, access.name, zone());
|
||||
state = state->AddField(object, field_index, new_value, access.name,
|
||||
access.constness, zone());
|
||||
} else {
|
||||
// Unsupported StoreField operator.
|
||||
state = state->KillFields(object, access.name, zone());
|
||||
@ -1047,7 +1098,7 @@ Reduction LoadElimination::ReduceOtherNode(Node* node) {
|
||||
if (state == nullptr) return NoChange();
|
||||
// Check if this {node} has some uncontrolled side effects.
|
||||
if (!node->op()->HasProperty(Operator::kNoWrite)) {
|
||||
state = empty_state();
|
||||
state = state->KillAll(zone());
|
||||
}
|
||||
return UpdateState(node, state);
|
||||
} else {
|
||||
@ -1163,7 +1214,7 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return empty_state();
|
||||
return state->KillAll(zone());
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
|
||||
|
@ -182,11 +182,7 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
|
||||
class AbstractState final : public ZoneObject {
|
||||
public:
|
||||
AbstractState() {
|
||||
for (size_t i = 0; i < arraysize(fields_); ++i) {
|
||||
fields_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
AbstractState() {}
|
||||
|
||||
bool Equals(AbstractState const* that) const;
|
||||
void Merge(AbstractState const* that, Zone* zone);
|
||||
@ -199,7 +195,9 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
bool LookupMaps(Node* object, ZoneHandleSet<Map>* object_maps) const;
|
||||
|
||||
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,
|
||||
size_t index, MaybeHandle<Name> name,
|
||||
Zone* zone) const;
|
||||
@ -207,7 +205,9 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
MaybeHandle<Name> name, Zone* zone) const;
|
||||
AbstractState const* KillFields(Node* object, MaybeHandle<Name> name,
|
||||
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,
|
||||
MachineRepresentation representation,
|
||||
@ -219,9 +219,21 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
|
||||
void Print() const;
|
||||
|
||||
static AbstractState const* empty_state() { return &empty_state_; }
|
||||
|
||||
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;
|
||||
AbstractField const* fields_[kMaxTrackedFields];
|
||||
AbstractFields fields_{};
|
||||
AbstractFields const_fields_{};
|
||||
AbstractMaps const* maps_ = nullptr;
|
||||
};
|
||||
|
||||
@ -266,15 +278,17 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
static int FieldIndexOf(int offset);
|
||||
static int FieldIndexOf(FieldAccess const& access);
|
||||
|
||||
static AbstractState const* empty_state() {
|
||||
return AbstractState::empty_state();
|
||||
}
|
||||
|
||||
CommonOperatorBuilder* common() const;
|
||||
AbstractState const* empty_state() const { return &empty_state_; }
|
||||
Isolate* isolate() const;
|
||||
Factory* factory() const;
|
||||
Graph* graph() const;
|
||||
JSGraph* jsgraph() const { return jsgraph_; }
|
||||
Zone* zone() const { return node_states_.zone(); }
|
||||
|
||||
AbstractState const empty_state_;
|
||||
AbstractStateForEffectNodes node_states_;
|
||||
JSGraph* const jsgraph_;
|
||||
|
||||
|
@ -228,17 +228,7 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
|
||||
if (it.IsReadOnly() && !it.IsConfigurable()) {
|
||||
return jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
|
||||
} 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());
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -264,6 +254,9 @@ Node* PropertyAccessBuilder::BuildLoadDataField(
|
||||
simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
|
||||
storage, *effect, *control);
|
||||
}
|
||||
PropertyConstness constness = access_info.IsDataConstant()
|
||||
? PropertyConstness::kConst
|
||||
: PropertyConstness::kMutable;
|
||||
FieldAccess field_access = {
|
||||
kTaggedBase,
|
||||
field_index.offset(),
|
||||
@ -272,15 +265,21 @@ Node* PropertyAccessBuilder::BuildLoadDataField(
|
||||
field_type,
|
||||
MachineType::TypeForRepresentation(field_representation),
|
||||
kFullWriteBarrier,
|
||||
LoadSensitivity::kCritical};
|
||||
LoadSensitivity::kCritical,
|
||||
constness};
|
||||
if (field_representation == MachineRepresentation::kFloat64) {
|
||||
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
|
||||
!FLAG_unbox_double_fields) {
|
||||
FieldAccess const storage_access = {
|
||||
kTaggedBase, field_index.offset(),
|
||||
name.object(), MaybeHandle<Map>(),
|
||||
Type::OtherInternal(), MachineType::TypeCompressedTaggedPointer(),
|
||||
kPointerWriteBarrier, LoadSensitivity::kCritical};
|
||||
kTaggedBase,
|
||||
field_index.offset(),
|
||||
name.object(),
|
||||
MaybeHandle<Map>(),
|
||||
Type::OtherInternal(),
|
||||
MachineType::TypeCompressedTaggedPointer(),
|
||||
kPointerWriteBarrier,
|
||||
LoadSensitivity::kCritical,
|
||||
constness};
|
||||
storage = *effect = graph()->NewNode(
|
||||
simplified()->LoadField(storage_access), storage, *effect, *control);
|
||||
field_access.offset = HeapNumber::kValueOffset;
|
||||
|
@ -56,6 +56,7 @@ struct FieldAccess {
|
||||
MachineType machine_type; // machine type of the field.
|
||||
WriteBarrierKind write_barrier_kind; // write barrier hint.
|
||||
LoadSensitivity load_sensitivity; // load safety for poisoning.
|
||||
PropertyConstness constness; // whether the field is assigned only once
|
||||
|
||||
FieldAccess()
|
||||
: base_is_tagged(kTaggedBase),
|
||||
@ -63,12 +64,14 @@ struct FieldAccess {
|
||||
type(Type::None()),
|
||||
machine_type(MachineType::None()),
|
||||
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,
|
||||
MaybeHandle<Map> map, Type type, MachineType machine_type,
|
||||
WriteBarrierKind write_barrier_kind,
|
||||
LoadSensitivity load_sensitivity = LoadSensitivity::kUnsafe)
|
||||
LoadSensitivity load_sensitivity = LoadSensitivity::kUnsafe,
|
||||
PropertyConstness constness = PropertyConstness::kMutable)
|
||||
: base_is_tagged(base_is_tagged),
|
||||
offset(offset),
|
||||
name(name),
|
||||
@ -76,7 +79,8 @@ struct FieldAccess {
|
||||
type(type),
|
||||
machine_type(machine_type),
|
||||
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; }
|
||||
};
|
||||
|
28
test/mjsunit/compiler/load-elimination-const-field.js
Normal file
28
test/mjsunit/compiler/load-elimination-const-field.js
Normal file
@ -0,0 +1,28 @@
|
||||
// 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);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user