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,
......
This diff is collapsed.
...@@ -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