Commit ea865094 authored by Jaroslav Sevcik's avatar Jaroslav Sevcik Committed by Commit Bot

Constant field tracking for arrays.

This adds constant field tracking for arrays. To prevent changing the
field in some other elements-kind-branch of transition tree, we only
use the const information in the optimizing compiler if the map is not
an array map or if the map is stable (since stable maps cannot
transition to a different elements-kind-branch without deopt).

Some more details:
https://docs.google.com/document/d/1r2GAvdi_wudDS6iRUfdPw0gxWMfV-IX1PqKgwW47FyE

Bug: chromium:912162, v8:8361
Change-Id: Iea1b2f03ddee16205c2141ac5e813a973dd23cf4
Reviewed-on: https://chromium-review.googlesource.com/c/1454606
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59409}
parent 8c30a2cc
...@@ -164,24 +164,17 @@ class FieldTypeDependency final : public CompilationDependencies::Dependency { ...@@ -164,24 +164,17 @@ class FieldTypeDependency final : public CompilationDependencies::Dependency {
// TODO(neis): Once the concurrent compiler frontend is always-on, we no // TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the type. // longer need to explicitly store the type.
FieldTypeDependency(const MapRef& owner, int descriptor, FieldTypeDependency(const MapRef& owner, int descriptor,
const ObjectRef& type, PropertyConstness constness) const ObjectRef& type)
: owner_(owner), : owner_(owner), descriptor_(descriptor), type_(type) {
descriptor_(descriptor),
type_(type),
constness_(constness) {
DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_))); DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
DCHECK(type_.equals(owner_.GetFieldType(descriptor_))); DCHECK(type_.equals(owner_.GetFieldType(descriptor_)));
DCHECK_EQ(constness_, owner_.GetPropertyDetails(descriptor_).constness());
} }
bool IsValid() const override { bool IsValid() const override {
DisallowHeapAllocation no_heap_allocation; DisallowHeapAllocation no_heap_allocation;
Handle<Map> owner = owner_.object(); Handle<Map> owner = owner_.object();
Handle<Object> type = type_.object(); Handle<Object> type = type_.object();
return *type == owner->instance_descriptors()->GetFieldType(descriptor_) && return *type == owner->instance_descriptors()->GetFieldType(descriptor_);
constness_ == owner->instance_descriptors()
->GetDetails(descriptor_)
.constness();
} }
void Install(const MaybeObjectHandle& code) override { void Install(const MaybeObjectHandle& code) override {
...@@ -194,7 +187,34 @@ class FieldTypeDependency final : public CompilationDependencies::Dependency { ...@@ -194,7 +187,34 @@ class FieldTypeDependency final : public CompilationDependencies::Dependency {
MapRef owner_; MapRef owner_;
int descriptor_; int descriptor_;
ObjectRef type_; ObjectRef type_;
PropertyConstness constness_; };
class FieldConstnessDependency final
: public CompilationDependencies::Dependency {
public:
FieldConstnessDependency(const MapRef& owner, int descriptor)
: owner_(owner), descriptor_(descriptor) {
DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_)));
DCHECK_EQ(PropertyConstness::kConst,
owner_.GetPropertyDetails(descriptor_).constness());
}
bool IsValid() const override {
DisallowHeapAllocation no_heap_allocation;
Handle<Map> owner = owner_.object();
return PropertyConstness::kConst ==
owner->instance_descriptors()->GetDetails(descriptor_).constness();
}
void Install(const MaybeObjectHandle& code) override {
SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(owner_.isolate(), code, owner_.object(),
DependentCode::kFieldOwnerGroup);
}
private:
MapRef owner_;
int descriptor_;
}; };
class GlobalPropertyDependency final class GlobalPropertyDependency final
...@@ -361,15 +381,37 @@ PretenureFlag CompilationDependencies::DependOnPretenureMode( ...@@ -361,15 +381,37 @@ PretenureFlag CompilationDependencies::DependOnPretenureMode(
return mode; return mode;
} }
PropertyConstness CompilationDependencies::DependOnFieldConstness(
const MapRef& map, int descriptor) {
MapRef owner = map.FindFieldOwner(descriptor);
PropertyConstness constness =
owner.GetPropertyDetails(descriptor).constness();
if (constness == PropertyConstness::kMutable) return constness;
// If the map can have fast elements transitions, then the field can be only
// considered constant if the map does not transition.
if (Map::CanHaveFastTransitionableElementsKind(map.instance_type())) {
// If the map can already transition away, let us report the field as
// mutable.
if (!map.is_stable()) {
return PropertyConstness::kMutable;
}
DependOnStableMap(map);
}
DCHECK_EQ(constness, PropertyConstness::kConst);
dependencies_.push_front(new (zone_)
FieldConstnessDependency(owner, descriptor));
return PropertyConstness::kConst;
}
void CompilationDependencies::DependOnFieldType(const MapRef& map, void CompilationDependencies::DependOnFieldType(const MapRef& map,
int descriptor) { int descriptor) {
MapRef owner = map.FindFieldOwner(descriptor); MapRef owner = map.FindFieldOwner(descriptor);
ObjectRef type = owner.GetFieldType(descriptor); ObjectRef type = owner.GetFieldType(descriptor);
PropertyConstness constness =
owner.GetPropertyDetails(descriptor).constness();
DCHECK(type.equals(map.GetFieldType(descriptor))); DCHECK(type.equals(map.GetFieldType(descriptor)));
dependencies_.push_front( dependencies_.push_front(new (zone_)
new (zone_) FieldTypeDependency(owner, descriptor, type, constness)); FieldTypeDependency(owner, descriptor, type));
} }
void CompilationDependencies::DependOnGlobalProperty( void CompilationDependencies::DependOnGlobalProperty(
......
...@@ -55,6 +55,15 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject { ...@@ -55,6 +55,15 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
// field is identified by the arguments. // field is identified by the arguments.
void DependOnFieldType(const MapRef& map, int descriptor); void DependOnFieldType(const MapRef& map, int descriptor);
// Return a field's constness and, if kConst, record the assumption that it
// remains kConst. The field is identified by the arguments.
//
// For arrays, arguments objects and value wrappers, only consider the field
// kConst if the map is stable (and register stability dependency in that
// case). This is to ensure that fast elements kind transitions cannot be
// used to mutate fields without deoptimization of the dependent code.
PropertyConstness DependOnFieldConstness(const MapRef& map, int descriptor);
// Record the assumption that neither {cell}'s {CellType} changes, nor the // Record the assumption that neither {cell}'s {CellType} changes, nor the
// {IsReadOnly()} flag of {cell}'s {PropertyDetails}. // {IsReadOnly()} flag of {cell}'s {PropertyDetails}.
void DependOnGlobalProperty(const PropertyCellRef& cell); void DependOnGlobalProperty(const PropertyCellRef& cell);
......
...@@ -473,7 +473,10 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -473,7 +473,10 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
*(factory()->has_instance_symbol()), *(holder_map.object())); *(factory()->has_instance_symbol()), *(holder_map.object()));
CHECK_NE(descriptor_index, DescriptorArray::kNotFound); CHECK_NE(descriptor_index, DescriptorArray::kNotFound);
holder_map.SerializeOwnDescriptors(); holder_map.SerializeOwnDescriptors();
dependencies()->DependOnFieldType(holder_map, descriptor_index); if (dependencies()->DependOnFieldConstness(
holder_map, descriptor_index) != PropertyConstness::kConst) {
return NoChange();
}
} }
if (found_on_proto) { if (found_on_proto) {
......
...@@ -220,7 +220,11 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField( ...@@ -220,7 +220,11 @@ Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
MapRef map(broker(), MapRef map(broker(),
handle(it.GetHolder<HeapObject>()->map(), isolate())); handle(it.GetHolder<HeapObject>()->map(), isolate()));
map.SerializeOwnDescriptors(); // TODO(neis): Remove later. map.SerializeOwnDescriptors(); // TODO(neis): Remove later.
dependencies()->DependOnFieldType(map, it.GetFieldDescriptorIndex()); if (dependencies()->DependOnFieldConstness(
map, it.GetFieldDescriptorIndex()) !=
PropertyConstness::kConst) {
return nullptr;
}
} }
return value; return value;
} }
......
...@@ -644,10 +644,10 @@ Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() { ...@@ -644,10 +644,10 @@ Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() {
// If the |new_elements_kind_| is still transitionable then the old map's // If the |new_elements_kind_| is still transitionable then the old map's
// elements kind is also transitionable and therefore the old descriptors // elements kind is also transitionable and therefore the old descriptors
// array must already have non in-place generalizable fields. // array must already have generalized field type.
CHECK_IMPLIES(is_transitionable_fast_elements_kind_, CHECK_IMPLIES(
!Map::IsInplaceGeneralizableField( is_transitionable_fast_elements_kind_,
next_constness, next_representation, *next_field_type)); Map::IsMostGeneralFieldType(next_representation, *next_field_type));
MaybeObjectHandle wrapped_type( MaybeObjectHandle wrapped_type(
Map::WrapFieldType(isolate_, next_field_type)); Map::WrapFieldType(isolate_, next_field_type));
......
...@@ -605,8 +605,7 @@ void JSObject::JSObjectVerify(Isolate* isolate) { ...@@ -605,8 +605,7 @@ void JSObject::JSObjectVerify(Isolate* isolate) {
CHECK(!field_type->NowStable() || field_type->NowContains(value)); CHECK(!field_type->NowStable() || field_type->NowContains(value));
} }
CHECK_IMPLIES(is_transitionable_fast_elements_kind, CHECK_IMPLIES(is_transitionable_fast_elements_kind,
!Map::IsInplaceGeneralizableField(details.constness(), r, Map::IsMostGeneralFieldType(r, field_type));
field_type));
} }
} }
......
...@@ -102,19 +102,9 @@ InterceptorInfo Map::GetIndexedInterceptor() { ...@@ -102,19 +102,9 @@ InterceptorInfo Map::GetIndexedInterceptor() {
return InterceptorInfo::cast(info->GetIndexedPropertyHandler()); return InterceptorInfo::cast(info->GetIndexedPropertyHandler());
} }
bool Map::IsInplaceGeneralizableField(PropertyConstness constness, bool Map::IsMostGeneralFieldType(Representation representation,
Representation representation, FieldType field_type) {
FieldType field_type) { return !representation.IsHeapObject() || field_type->IsAny();
if (FLAG_track_constant_fields && FLAG_modify_map_inplace &&
(constness == PropertyConstness::kConst)) {
// VariableMode::kConst -> PropertyConstness::kMutable field generalization
// may happen in-place.
return true;
}
if (representation.IsHeapObject() && !field_type->IsAny()) {
return true;
}
return false;
} }
bool Map::CanHaveFastTransitionableElementsKind(InstanceType instance_type) { bool Map::CanHaveFastTransitionableElementsKind(InstanceType instance_type) {
...@@ -135,13 +125,7 @@ void Map::GeneralizeIfCanHaveTransitionableFastElementsKind( ...@@ -135,13 +125,7 @@ void Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
// kind transitions because they are inserted into the transition tree // kind transitions because they are inserted into the transition tree
// before field transitions. In order to avoid complexity of handling // before field transitions. In order to avoid complexity of handling
// such a case we ensure that all maps with transitionable elements kinds // such a case we ensure that all maps with transitionable elements kinds
// do not have fields that can be generalized in-place (without creation // have the most general field type.
// of a new map).
if (FLAG_track_constant_fields && FLAG_modify_map_inplace) {
// The constness is either already PropertyConstness::kMutable or should
// become PropertyConstness::kMutable if it was VariableMode::kConst.
*constness = PropertyConstness::kMutable;
}
if (representation->IsHeapObject()) { if (representation->IsHeapObject()) {
// The field type is either already Any or should become Any if it was // The field type is either already Any or should become Any if it was
// something else. // something else.
......
...@@ -2443,9 +2443,8 @@ bool Map::EquivalentToForElementsKindTransition(const Map other) const { ...@@ -2443,9 +2443,8 @@ bool Map::EquivalentToForElementsKindTransition(const Map other) const {
for (int i = 0; i < nof; i++) { for (int i = 0; i < nof; i++) {
PropertyDetails details = descriptors->GetDetails(i); PropertyDetails details = descriptors->GetDetails(i);
if (details.location() == kField) { if (details.location() == kField) {
DCHECK(!IsInplaceGeneralizableField(details.constness(), DCHECK(IsMostGeneralFieldType(details.representation(),
details.representation(), descriptors->GetFieldType(i)));
descriptors->GetFieldType(i)));
} }
} }
#endif #endif
......
...@@ -504,11 +504,10 @@ class Map : public HeapObject { ...@@ -504,11 +504,10 @@ class Map : public HeapObject {
int modify_index, PropertyConstness new_constness, int modify_index, PropertyConstness new_constness,
Representation new_representation, Representation new_representation,
Handle<FieldType> new_field_type); Handle<FieldType> new_field_type);
// Returns true if |descriptor|'th property is a field that may be generalized // Returns true if the |field_type| is the most general one for
// by just updating current map. // given |representation|.
static inline bool IsInplaceGeneralizableField(PropertyConstness constness, static inline bool IsMostGeneralFieldType(Representation representation,
Representation representation, FieldType field_type);
FieldType field_type);
// Generalizes constness, representation and field_type if objects with given // Generalizes constness, representation and field_type if objects with given
// instance type can have fast elements that can be transitioned by stubs or // instance type can have fast elements that can be transitioned by stubs or
......
...@@ -124,18 +124,10 @@ class Expectations { ...@@ -124,18 +124,10 @@ class Expectations {
kinds_[index] = kind; kinds_[index] = kind;
locations_[index] = location; locations_[index] = location;
if (kind == kData && location == kField && if (kind == kData && location == kField &&
IsTransitionableFastElementsKind(elements_kind_) && IsTransitionableFastElementsKind(elements_kind_)) {
Map::IsInplaceGeneralizableField(constness, representation, // Maps with transitionable elements kinds must have the most general
FieldType::cast(*value))) { // field type.
// Maps with transitionable elements kinds must have non in-place value = FieldType::Any(isolate_);
// generalizable fields.
if (FLAG_track_constant_fields && FLAG_modify_map_inplace &&
constness == PropertyConstness::kConst) {
constness = PropertyConstness::kMutable;
}
if (representation.IsHeapObject() && !FieldType::cast(*value)->IsAny()) {
value = FieldType::Any(isolate_);
}
} }
constnesses_[index] = constness; constnesses_[index] = constness;
attributes_[index] = attributes; attributes_[index] = attributes;
...@@ -1140,6 +1132,7 @@ static void TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial( ...@@ -1140,6 +1132,7 @@ static void TestReconfigureDataFieldAttribute_GeneralizeFieldTrivial(
MapRef map_ref(&broker, map); MapRef map_ref(&broker, map);
map_ref.SerializeOwnDescriptors(); map_ref.SerializeOwnDescriptors();
dependencies.DependOnFieldType(map_ref, kSplitProp); dependencies.DependOnFieldType(map_ref, kSplitProp);
dependencies.DependOnFieldConstness(map_ref, kSplitProp);
// Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which // Reconfigure attributes of property |kSplitProp| of |map2| to NONE, which
// should generalize representations in |map1|. // should generalize representations in |map1|.
...@@ -1921,7 +1914,9 @@ static void TestReconfigureElementsKind_GeneralizeFieldTrivial( ...@@ -1921,7 +1914,9 @@ static void TestReconfigureElementsKind_GeneralizeFieldTrivial(
CompilationDependencies dependencies(isolate, &zone); CompilationDependencies dependencies(isolate, &zone);
MapRef map_ref(&broker, map); MapRef map_ref(&broker, map);
map_ref.SerializeOwnDescriptors(); map_ref.SerializeOwnDescriptors();
dependencies.DependOnFieldType(map_ref, kDiffProp); dependencies.DependOnFieldType(map_ref, kDiffProp);
dependencies.DependOnFieldConstness(map_ref, kDiffProp);
// Reconfigure elements kinds of |map2|, which should generalize // Reconfigure elements kinds of |map2|, which should generalize
// representations in |map|. // representations in |map|.
...@@ -1942,7 +1937,8 @@ static void TestReconfigureElementsKind_GeneralizeFieldTrivial( ...@@ -1942,7 +1937,8 @@ static void TestReconfigureElementsKind_GeneralizeFieldTrivial(
expected.representation, expected.type); expected.representation, expected.type);
CHECK(!map->is_deprecated()); CHECK(!map->is_deprecated());
CHECK_EQ(*map, *new_map); CHECK_EQ(*map, *new_map);
CHECK(dependencies.AreValid()); CHECK_EQ(IsGeneralizableTo(to.constness, from.constness),
dependencies.AreValid());
CHECK(!new_map->is_deprecated()); CHECK(!new_map->is_deprecated());
CHECK(expectations.Check(*new_map)); CHECK(expectations.Check(*new_map));
......
// 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
var a = new Array();
a.prototype = a;
function f() {
a.length = 0x2000001;
a.push();
}
({}).__proto__ = a;
f()
f()
a.length = 1;
a.fill(-255);
%HeapObjectVerify(a);
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