Commit 9a23caf0 authored by Georg Neis's avatar Georg Neis Committed by V8 LUCI CQ

[compiler] Make AllocationSite never-serialized

To get there, also:

- Refactor AllocationSite serialization as necessary.

- Make some accessors on AllocationSite atomic.

- Add JSObjectRef::raw_properties_or_hash().

- Eliminate use of IsFastLiteral in JSCallReducer. It isn't really
  needed there and we want to have only a single piece of code
  traversing boilerplates. (We still have a separate traversal in the
  serializer but that will be removed soon.)

- Merge IsFastLiteral checks into JSCreateLowering's
  TryAllocateFastLiteral.
  Note: TryAllocateFastLiteral doesn't explicitly look at the
  boilerplate's elements kind beyond bailing out for
  DICTIONARY_ELEMENTS in the beginning. After that it looks only at
  the backing store instance type. There is no room for confusion
  because, while elements kind transitions can generally happen
  concurrently to TryAllocateFastLiteral, boilerplates can never
  transition to DICTIONARY_ELEMENTS (added a CHECK for that).

- Slightly adapt CompilationDependencies and remove obsolete comments.

- Fix JSHeapBroker::ClearReconstructibleData (clearing of Refs in
  stress mode) to exclude JSObjectRefs with extra data.

Bug: v8:7790
Change-Id: Iee1232d01e04bcd00db04d48f6e82064fce6ff62
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3008894
Commit-Queue: Georg Neis <neis@chromium.org>
Auto-Submit: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarSantiago Aboy Solanes <solanes@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75656}
parent 433ff6b9
......@@ -25,8 +25,6 @@ CompilationDependencies::CompilationDependencies(JSHeapBroker* broker,
class InitialMapDependency final : public CompilationDependency {
public:
// TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the initial map.
InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map)
: function_(function), initial_map_(initial_map) {
DCHECK(function_.has_initial_map());
......@@ -53,8 +51,6 @@ class InitialMapDependency final : public CompilationDependency {
class PrototypePropertyDependency final : public CompilationDependency {
public:
// TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the prototype.
PrototypePropertyDependency(const JSFunctionRef& function,
const ObjectRef& prototype)
: function_(function), prototype_(prototype) {
......@@ -362,13 +358,9 @@ class TransitionDependency final : public CompilationDependency {
class PretenureModeDependency final : public CompilationDependency {
public:
// TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the mode.
PretenureModeDependency(const AllocationSiteRef& site,
AllocationType allocation)
: site_(site), allocation_(allocation) {
DCHECK_EQ(allocation, site_.GetAllocationType());
}
: site_(site), allocation_(allocation) {}
bool IsValid() const override {
return allocation_ == site_.object()->GetAllocationType();
......@@ -392,8 +384,6 @@ class PretenureModeDependency final : public CompilationDependency {
class FieldRepresentationDependency final : public CompilationDependency {
public:
// TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the representation.
FieldRepresentationDependency(const MapRef& owner, InternalIndex descriptor,
Representation representation)
: owner_(owner),
......@@ -437,8 +427,6 @@ class FieldRepresentationDependency final : public CompilationDependency {
class FieldTypeDependency final : public CompilationDependency {
public:
// TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the type.
FieldTypeDependency(const MapRef& owner, InternalIndex descriptor,
const ObjectRef& type)
: owner_(owner), descriptor_(descriptor), type_(type) {}
......@@ -556,14 +544,9 @@ class ProtectorDependency final : public CompilationDependency {
class ElementsKindDependency final : public CompilationDependency {
public:
// TODO(neis): Once the concurrent compiler frontend is always-on, we no
// longer need to explicitly store the elements kind.
ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind)
: site_(site), kind_(kind) {
DCHECK(AllocationSite::ShouldTrack(kind_));
DCHECK_EQ(kind_, site_.PointsToLiteral()
? site_.boilerplate().value().map().elements_kind()
: site_.GetElementsKind());
}
bool IsValid() const override {
......@@ -686,7 +669,7 @@ void CompilationDependencies::DependOnConstantInDictionaryPrototypeChain(
AllocationType CompilationDependencies::DependOnPretenureMode(
const AllocationSiteRef& site) {
DCHECK(!site.IsNeverSerializedHeapObject());
if (!FLAG_allocation_site_pretenuring) return AllocationType::kYoung;
AllocationType allocation = site.GetAllocationType();
RecordDependency(zone_->New<PretenureModeDependency>(site, allocation));
return allocation;
......@@ -769,8 +752,6 @@ bool CompilationDependencies::DependOnPromiseThenProtector() {
void CompilationDependencies::DependOnElementsKind(
const AllocationSiteRef& site) {
DCHECK(!site.IsNeverSerializedHeapObject());
// Do nothing if the object doesn't have any useful element transitions left.
ElementsKind kind = site.PointsToLiteral()
? site.boilerplate().value().map().elements_kind()
: site.GetElementsKind();
......
......@@ -7,6 +7,7 @@
#include "src/common/globals.h"
#include "src/flags/flags.h"
#include "src/objects/js-objects.h"
namespace v8 {
namespace internal {
......@@ -74,6 +75,14 @@ inline std::ostream& operator<<(std::ostream& os,
return os;
}
// Maximum depth and total number of elements and properties for literal
// graphs to be considered for fast deep-copying. The limit is chosen to
// match the maximum number of inobject properties, to ensure that the
// performance of using object literals is not worse than using constructor
// functions, see crbug.com/v8/6211 for details.
const int kMaxFastLiteralDepth = 3;
const int kMaxFastLiteralProperties = JSObject::kMaxInObjectProperties;
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -435,7 +435,8 @@ class JSObjectData : public JSReceiverData {
ObjectDataKind kind = kSerializedHeapObject);
// Recursive serialization of all reachable JSObjects.
void SerializeAsBoilerplate(JSHeapBroker* broker);
bool SerializeAsBoilerplateRecursive(JSHeapBroker* broker,
int max_depth = kMaxFastLiteralDepth);
ObjectData* GetInobjectField(int property_index) const;
// Shallow serialization of {elements}.
......@@ -443,6 +444,8 @@ class JSObjectData : public JSReceiverData {
bool serialized_elements() const { return serialized_elements_; }
ObjectData* elements() const;
ObjectData* raw_properties_or_hash() const { return raw_properties_or_hash_; }
void SerializeObjectCreateMap(JSHeapBroker* broker);
// Can be nullptr.
......@@ -468,10 +471,14 @@ class JSObjectData : public JSReceiverData {
// This method is only used to assert our invariants.
bool cow_or_empty_elements_tenured() const;
private:
void SerializeRecursiveAsBoilerplate(JSHeapBroker* broker, int max_depths);
bool has_extra_serialized_data() const {
return serialized_as_boilerplate_ || serialized_elements_ ||
serialized_object_create_map_;
}
private:
ObjectData* elements_ = nullptr;
ObjectData* raw_properties_or_hash_ = nullptr;
bool cow_or_empty_elements_tenured_ = false;
// The {serialized_as_boilerplate} flag is set when all recursively
// reachable JSObjects are serialized.
......@@ -953,94 +960,6 @@ class InternalizedStringData : public StringData {
}
};
namespace {
bool IsFastLiteralHelper(Handle<JSObject> boilerplate, int max_depth,
int* max_properties) {
DCHECK_GE(max_depth, 0);
DCHECK_GE(*max_properties, 0);
// Check for too deep nesting.
if (max_depth == 0) return false;
Isolate* const isolate = boilerplate->GetIsolate();
// If the boilerplate map has been deprecated, bailout of fast literal
// optimization. The map could be deprecated at some point after the line
// below, but it's not a correctness issue -- it only means the literal isn't
// created with the most up to date map(s).
if (boilerplate->map().is_deprecated()) return false;
// Check the elements.
Handle<FixedArrayBase> elements(boilerplate->elements(), isolate);
if (elements->length() > 0 &&
elements->map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) {
if (boilerplate->HasSmiOrObjectElements()) {
Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
int length = elements->length();
for (int i = 0; i < length; i++) {
if ((*max_properties)-- == 0) return false;
Handle<Object> value(fast_elements->get(i), isolate);
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
if (!IsFastLiteralHelper(value_object, max_depth - 1,
max_properties)) {
return false;
}
}
}
} else if (boilerplate->HasDoubleElements()) {
if (elements->Size() > kMaxRegularHeapObjectSize) return false;
} else {
return false;
}
}
// TODO(turbofan): Do we want to support out-of-object properties?
if (!(boilerplate->HasFastProperties() &&
boilerplate->property_array().length() == 0)) {
return false;
}
// Check the in-object properties.
Handle<DescriptorArray> descriptors(
boilerplate->map().instance_descriptors(isolate, kRelaxedLoad), isolate);
for (InternalIndex i : boilerplate->map().IterateOwnDescriptors()) {
PropertyDetails details = descriptors->GetDetails(i);
if (details.location() != kField) continue;
DCHECK_EQ(kData, details.kind());
if ((*max_properties)-- == 0) return false;
FieldIndex field_index = FieldIndex::ForDescriptor(boilerplate->map(), i);
Handle<Object> value(boilerplate->RawFastPropertyAt(field_index), isolate);
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
if (!IsFastLiteralHelper(value_object, max_depth - 1, max_properties)) {
return false;
}
}
}
return true;
}
// Maximum depth and total number of elements and properties for literal
// graphs to be considered for fast deep-copying. The limit is chosen to
// match the maximum number of inobject properties, to ensure that the
// performance of using object literals is not worse than using constructor
// functions, see crbug.com/v8/6211 for details.
const int kMaxFastLiteralDepth = 3;
const int kMaxFastLiteralProperties = JSObject::kMaxInObjectProperties;
// Determines whether the given array or object literal boilerplate satisfies
// all limits to be considered for fast deep-copying and computes the total
// size of all objects that are part of the graph.
bool IsInlinableFastLiteral(Handle<JSObject> boilerplate) {
int max_properties = kMaxFastLiteralProperties;
return IsFastLiteralHelper(boilerplate, kMaxFastLiteralDepth,
&max_properties);
}
} // namespace
class AccessorInfoData : public HeapObjectData {
public:
AccessorInfoData(JSHeapBroker* broker, ObjectData** storage,
......@@ -1051,12 +970,11 @@ class AllocationSiteData : public HeapObjectData {
public:
AllocationSiteData(JSHeapBroker* broker, ObjectData** storage,
Handle<AllocationSite> object);
void SerializeBoilerplate(JSHeapBroker* broker);
void Serialize(JSHeapBroker* broker);
bool PointsToLiteral() const { return PointsToLiteral_; }
AllocationType GetAllocationType() const { return GetAllocationType_; }
ObjectData* nested_site() const { return nested_site_; }
bool IsFastLiteral() const { return IsFastLiteral_; }
ObjectData* boilerplate() const { return boilerplate_; }
// These are only valid if PointsToLiteral is false.
......@@ -1067,11 +985,10 @@ class AllocationSiteData : public HeapObjectData {
bool const PointsToLiteral_;
AllocationType const GetAllocationType_;
ObjectData* nested_site_ = nullptr;
bool IsFastLiteral_ = false;
ObjectData* boilerplate_ = nullptr;
ElementsKind GetElementsKind_ = NO_ELEMENTS;
bool CanInlineCall_ = false;
bool serialized_boilerplate_ = false;
bool serialized_ = false;
};
class BigIntData : public HeapObjectData {
......@@ -1244,34 +1161,27 @@ AllocationSiteData::AllocationSiteData(JSHeapBroker* broker,
: HeapObjectData(broker, storage, object),
PointsToLiteral_(object->PointsToLiteral()),
GetAllocationType_(object->GetAllocationType()) {
if (PointsToLiteral_) {
IsFastLiteral_ = IsInlinableFastLiteral(
handle(object->boilerplate(kAcquireLoad), broker->isolate()));
} else {
DCHECK(!broker->is_concurrent_inlining());
if (!PointsToLiteral_) {
GetElementsKind_ = object->GetElementsKind();
CanInlineCall_ = object->CanInlineCall();
}
}
void AllocationSiteData::SerializeBoilerplate(JSHeapBroker* broker) {
if (serialized_boilerplate_) return;
serialized_boilerplate_ = true;
void AllocationSiteData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "AllocationSiteData::SerializeBoilerplate");
TraceScope tracer(broker, this, "AllocationSiteData::Serialize");
Handle<AllocationSite> site = Handle<AllocationSite>::cast(object());
CHECK(IsFastLiteral_);
if (PointsToLiteral_) {
DCHECK_NULL(boilerplate_);
boilerplate_ = broker->GetOrCreateData(site->boilerplate(kAcquireLoad));
if (!boilerplate_->should_access_heap()) {
boilerplate_->AsJSObject()->SerializeAsBoilerplate(broker);
}
DCHECK_NULL(nested_site_);
nested_site_ = broker->GetOrCreateData(site->nested_site());
if (nested_site_->IsAllocationSite() && !nested_site_->should_access_heap()) {
nested_site_->AsAllocationSite()->SerializeBoilerplate(broker);
}
}
HeapObjectData::HeapObjectData(JSHeapBroker* broker, ObjectData** storage,
......@@ -2125,10 +2035,6 @@ ObjectData* JSObjectData::elements() const {
return elements_;
}
void JSObjectData::SerializeAsBoilerplate(JSHeapBroker* broker) {
SerializeRecursiveAsBoilerplate(broker, kMaxFastLiteralDepth);
}
void JSObjectData::SerializeElements(JSHeapBroker* broker) {
if (serialized_elements_) return;
serialized_elements_ = true;
......@@ -2237,18 +2143,17 @@ void MapData::SerializeRootMap(JSHeapBroker* broker) {
ObjectData* MapData::FindRootMap() const { return root_map_; }
void JSObjectData::SerializeRecursiveAsBoilerplate(JSHeapBroker* broker,
int depth) {
if (serialized_as_boilerplate_) return;
serialized_as_boilerplate_ = true;
bool JSObjectData::SerializeAsBoilerplateRecursive(JSHeapBroker* broker,
int max_depth) {
if (serialized_as_boilerplate_) return true;
// If serialization succeeds, we set this to true at the end.
TraceScope tracer(broker, this,
"JSObjectData::SerializeRecursiveAsBoilerplate");
"JSObjectData::SerializeAsBoilerplateRecursive");
Handle<JSObject> boilerplate = Handle<JSObject>::cast(object());
// We only serialize boilerplates that pass the IsInlinableFastLiteral
// check, so we only do a check on the depth here.
CHECK_GT(depth, 0);
DCHECK_GE(max_depth, 0);
if (max_depth == 0) return false;
// Serialize the elements.
Isolate* const isolate = broker->isolate();
......@@ -2264,41 +2169,24 @@ void JSObjectData::SerializeRecursiveAsBoilerplate(JSHeapBroker* broker,
cow_or_empty_elements_tenured_ = !ObjectInYoungGeneration(*elements_object);
}
DCHECK_NULL(elements_);
DCHECK(!serialized_elements_);
raw_properties_or_hash_ =
broker->GetOrCreateData(boilerplate->raw_properties_or_hash());
serialized_elements_ = true;
elements_ = broker->GetOrCreateData(elements_object);
DCHECK(elements_->IsFixedArrayBase());
if (empty_or_cow || elements_->should_access_heap()) {
// No need to do anything here. Empty or copy-on-write elements
// do not need to be serialized because we only need to store the elements
// reference to the allocated object.
} else if (boilerplate->HasSmiOrObjectElements()) {
Handle<FixedArray> fast_elements =
Handle<FixedArray>::cast(elements_object);
int length = elements_object->length();
for (int i = 0; i < length; i++) {
Handle<Object> value(fast_elements->get(i), isolate);
if (value->IsJSObject()) {
ObjectData* value_data = broker->GetOrCreateData(value);
if (!value_data->should_access_heap()) {
value_data->AsJSObject()->SerializeRecursiveAsBoilerplate(broker,
depth - 1);
}
}
}
} else {
CHECK(boilerplate->HasDoubleElements());
CHECK_LE(elements_object->Size(), kMaxRegularHeapObjectSize);
if (!boilerplate->HasFastProperties() ||
boilerplate->property_array().length() != 0) {
return false;
}
// TODO(turbofan): Do we want to support out-of-object properties?
CHECK(boilerplate->HasFastProperties() &&
boilerplate->property_array().length() == 0);
CHECK_EQ(inobject_fields_.size(), 0u);
if (!map()->should_access_heap()) {
map()->AsMap()->SerializeOwnDescriptors(broker);
}
// Check the in-object properties.
inobject_fields_.clear();
Handle<DescriptorArray> descriptors(
boilerplate->map().instance_descriptors(isolate), isolate);
for (InternalIndex i : boilerplate->map().IterateOwnDescriptors()) {
......@@ -2313,21 +2201,47 @@ void JSObjectData::SerializeRecursiveAsBoilerplate(JSHeapBroker* broker,
static_cast<int>(inobject_fields_.size()));
Handle<Object> value(boilerplate->RawFastPropertyAt(field_index), isolate);
ObjectData* value_data = broker->GetOrCreateData(value);
inobject_fields_.push_back(value_data);
if (value_data->IsJSObject() && !value_data->should_access_heap()) {
value_data->AsJSObject()->SerializeRecursiveAsBoilerplate(broker,
depth - 1);
if (!value_data->AsJSObject()->SerializeAsBoilerplateRecursive(
broker, max_depth - 1))
return false;
}
inobject_fields_.push_back(value_data);
}
TRACE(broker, "Copied " << inobject_fields_.size() << " in-object fields");
if (!map()->should_access_heap()) {
map()->AsMap()->SerializeOwnDescriptors(broker);
if (empty_or_cow || elements_->should_access_heap()) {
// No need to do anything here. Empty or copy-on-write elements
// do not need to be serialized because we only need to store the elements
// reference to the allocated object.
} else if (boilerplate->HasSmiOrObjectElements()) {
Handle<FixedArray> fast_elements =
Handle<FixedArray>::cast(elements_object);
int length = elements_object->length();
for (int i = 0; i < length; i++) {
Handle<Object> value(fast_elements->get(i), isolate);
if (value->IsJSObject()) {
ObjectData* value_data = broker->GetOrCreateData(value);
if (!value_data->should_access_heap()) {
if (!value_data->AsJSObject()->SerializeAsBoilerplateRecursive(
broker, max_depth - 1)) {
return false;
}
}
}
}
} else {
if (!boilerplate->HasDoubleElements()) return false;
int const size = FixedDoubleArray::SizeFor(elements_object->length());
if (size > kMaxRegularHeapObjectSize) return false;
}
if (IsJSArray() && !broker->is_concurrent_inlining()) {
AsJSArray()->Serialize(broker);
}
serialized_as_boilerplate_ = true;
return true;
}
#ifdef DEBUG
......@@ -2632,6 +2546,11 @@ void JSHeapBroker::ClearReconstructibleData() {
value->AsMap()->has_extra_serialized_data()) {
continue;
}
if (value->IsJSObject() &&
value->kind() == ObjectDataKind::kBackgroundSerializedHeapObject &&
value->AsJSObject()->has_extra_serialized_data()) {
continue;
}
// Can be reconstructed from the background thread.
CHECK_NOT_NULL(refs_->Remove(key));
}
......@@ -2836,6 +2755,13 @@ FeedbackCellRef FeedbackVectorRef::GetClosureFeedbackCell(int index) const {
data()->AsFeedbackVector()->GetClosureFeedbackCell(broker(), index));
}
base::Optional<ObjectRef> JSObjectRef::raw_properties_or_hash() const {
if (data_->should_access_heap()) {
return TryMakeRef(broker(), object()->raw_properties_or_hash());
}
return ObjectRef(broker(), data()->AsJSObject()->raw_properties_or_hash());
}
base::Optional<ObjectRef> JSObjectRef::RawInobjectPropertyAt(
FieldIndex index) const {
CHECK(index.is_inobject());
......@@ -2864,19 +2790,23 @@ base::Optional<ObjectRef> JSObjectRef::RawInobjectPropertyAt(
object_data->GetInobjectField(index.property_index()));
}
bool AllocationSiteRef::IsFastLiteral() const {
if (data_->should_access_heap()) {
CHECK_NE(data_->kind(), ObjectDataKind::kNeverSerializedHeapObject);
return IsInlinableFastLiteral(
handle(object()->boilerplate(kAcquireLoad), broker()->isolate()));
}
return data()->AsAllocationSite()->IsFastLiteral();
}
void AllocationSiteRef::SerializeBoilerplate() {
void JSObjectRef::SerializeAsBoilerplateRecursive() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsAllocationSite()->SerializeBoilerplate(broker());
data()->AsJSObject()->SerializeAsBoilerplateRecursive(broker());
}
void AllocationSiteRef::SerializeRecursive() {
if (!data_->should_access_heap()) {
data()->AsAllocationSite()->Serialize(broker());
}
if (boilerplate().has_value()) {
boilerplate()->SerializeAsBoilerplateRecursive();
}
if (nested_site().IsAllocationSite()) {
nested_site().AsAllocationSite().SerializeRecursive();
}
}
void JSObjectRef::SerializeElements() {
......@@ -3020,6 +2950,7 @@ base::Optional<ObjectRef> FixedArrayRef::TryGet(int i) const {
Float64 FixedDoubleArrayRef::GetFromImmutableFixedDoubleArray(int i) const {
STATIC_ASSERT(ref_traits<FixedDoubleArray>::ref_serialization_kind ==
RefSerializationKind::kNeverSerialized);
CHECK(data_->should_access_heap());
return Float64::FromBits(object()->get_representation(i));
}
......@@ -3938,15 +3869,13 @@ HeapObjectType HeapObjectRef::GetHeapObjectType() const {
}
base::Optional<JSObjectRef> AllocationSiteRef::boilerplate() const {
if (!PointsToLiteral()) return {};
if (data_->should_access_heap()) {
return TryMakeRef(broker(), object()->boilerplate(kAcquireLoad));
}
ObjectData* boilerplate = data()->AsAllocationSite()->boilerplate();
if (boilerplate) {
if (boilerplate == nullptr) return {};
return JSObjectRef(broker(), boilerplate);
} else {
return base::nullopt;
}
}
base::Optional<FixedArrayBaseRef> JSObjectRef::elements(
......
......@@ -105,7 +105,7 @@ enum class RefSerializationKind {
V(JSObject, RefSerializationKind::kBackgroundSerialized) \
/* Subtypes of HeapObject */ \
V(AccessorInfo, RefSerializationKind::kNeverSerialized) \
V(AllocationSite, RefSerializationKind::kSerialized) \
V(AllocationSite, RefSerializationKind::kNeverSerialized) \
V(ArrayBoilerplateDescription, RefSerializationKind::kNeverSerialized) \
V(BigInt, RefSerializationKind::kBackgroundSerialized) \
V(CallHandlerInfo, RefSerializationKind::kNeverSerialized) \
......@@ -319,6 +319,8 @@ class JSObjectRef : public JSReceiverRef {
Handle<JSObject> object() const;
base::Optional<ObjectRef> raw_properties_or_hash() const;
// Usable only for in-object properties. Only use this if the underlying
// value can be an uninitialized-sentinel, or if HeapNumber construction must
// be avoided for some reason. Otherwise, use the higher-level
......@@ -368,6 +370,8 @@ class JSObjectRef : public JSReceiverRef {
void SerializeObjectCreateMap();
base::Optional<MapRef> GetObjectCreateMap() const;
void SerializeAsBoilerplateRecursive();
};
class JSDataViewRef : public JSObjectRef {
......@@ -617,18 +621,9 @@ class AllocationSiteRef : public HeapObjectRef {
AllocationType GetAllocationType() const;
ObjectRef nested_site() const;
// {IsFastLiteral} determines whether the given array or object literal
// boilerplate satisfies all limits to be considered for fast deep-copying
// and computes the total size of all objects that are part of the graph.
//
// If PointsToLiteral() is false, then IsFastLiteral() is also false.
bool IsFastLiteral() const;
void SerializeRecursive();
void SerializeBoilerplate();
// We only serialize boilerplate if IsFastLiteral is true.
base::Optional<JSObjectRef> boilerplate() const;
ElementsKind GetElementsKind() const;
bool CanInlineCall() const;
};
......
......@@ -4198,7 +4198,7 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
int new_argument_count;
// Find array length and elements' kind from the feedback's allocation
// site's boilerplate JSArray..
// site's boilerplate JSArray.
JSCreateLiteralOpNode args_node(arguments_list);
CreateLiteralParameters const& args_params = args_node.Parameters();
const FeedbackSource& array_feedback = args_params.feedback();
......@@ -4207,8 +4207,6 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
if (feedback.IsInsufficient()) return NoChange();
AllocationSiteRef site = feedback.AsLiteral().value();
if (!site.IsFastLiteral()) return NoChange();
base::Optional<JSArrayRef> boilerplate_array =
site.boilerplate()->AsJSArray();
int const array_length = boilerplate_array->GetBoilerplateLength().AsSmi();
......
......@@ -1095,27 +1095,18 @@ Reduction JSCreateLowering::ReduceJSCreateLiteralArrayOrObject(Node* node) {
broker()->GetFeedbackForArrayOrObjectLiteral(p.feedback());
if (!feedback.IsInsufficient()) {
AllocationSiteRef site = feedback.AsLiteral().value();
if (site.IsFastLiteral()) {
AllocationType allocation = FLAG_allocation_site_pretenuring
? site.GetAllocationType()
: AllocationType::kYoung;
JSObjectRef boilerplate = site.boilerplate().value();
if (!site.boilerplate().has_value()) return NoChange();
AllocationType allocation = dependencies()->DependOnPretenureMode(site);
int max_properties = kMaxFastLiteralProperties;
base::Optional<Node*> maybe_value =
TryAllocateFastLiteral(effect, control, boilerplate, allocation);
if (!maybe_value.has_value()) {
TRACE_BROKER_MISSING(broker(), "bound argument");
return NoChange();
}
if (FLAG_allocation_site_pretenuring) {
CHECK_EQ(dependencies()->DependOnPretenureMode(site), allocation);
}
TryAllocateFastLiteral(effect, control, *site.boilerplate(), allocation,
kMaxFastLiteralDepth, &max_properties);
if (!maybe_value.has_value()) return NoChange();
dependencies()->DependOnElementsKinds(site);
Node* value = effect = maybe_value.value();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
return NoChange();
}
......@@ -1660,20 +1651,48 @@ Node* JSCreateLowering::AllocateElements(Node* effect, Node* control,
base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteral(
Node* effect, Node* control, JSObjectRef boilerplate,
AllocationType allocation) {
AllocationType allocation, int max_depth, int* max_properties) {
DCHECK_GE(max_depth, 0);
DCHECK_GE(*max_properties, 0);
if (max_depth == 0) return {};
// Prevent concurrent migrations of boilerplate objects.
JSHeapBroker::BoilerplateMigrationGuardIfNeeded boilerplate_access_guard(
broker());
// Now that we hold the migration lock, get the current map.
MapRef boilerplate_map = boilerplate.map();
{
base::Optional<MapRef> current_boilerplate_map =
boilerplate.map_direct_read();
if (!current_boilerplate_map.has_value() ||
!current_boilerplate_map.value().equals(boilerplate_map)) {
!current_boilerplate_map->equals(boilerplate_map)) {
return {};
}
}
// Bail out if the boilerplate map has been deprecated. The map could of
// course be deprecated at some point after the line below, but it's not a
// correctness issue -- it only means the literal won't be created with the
// most up to date map(s).
if (boilerplate_map.is_deprecated()) return {};
// We currently only support in-object properties.
if (boilerplate.map().elements_kind() == DICTIONARY_ELEMENTS ||
boilerplate.map().is_dictionary_map() ||
!boilerplate.raw_properties_or_hash().has_value()) {
return {};
}
{
ObjectRef properties = *boilerplate.raw_properties_or_hash();
bool const empty = properties.IsSmi() ||
properties.equals(MakeRef<Object>(
broker(), factory()->empty_fixed_array())) ||
properties.equals(MakeRef<Object>(
broker(), factory()->empty_property_array()));
if (!empty) return {};
}
// Compute the in-object properties to store first (might have effects).
ZoneVector<std::pair<FieldAccess, Node*>> inobject_fields(zone());
......@@ -1684,6 +1703,8 @@ base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteral(
boilerplate_map.GetPropertyDetails(i);
if (property_details.location() != kField) continue;
DCHECK_EQ(kData, property_details.kind());
if ((*max_properties)-- == 0) return {};
NameRef property_name = boilerplate_map.GetPropertyKey(i);
FieldIndex index = boilerplate_map.GetFieldIndexFor(i);
ConstFieldInfo const_field_info(boilerplate_map.object());
......@@ -1730,8 +1751,9 @@ base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteral(
Node* value;
if (boilerplate_value.IsJSObject()) {
JSObjectRef boilerplate_object = boilerplate_value.AsJSObject();
base::Optional<Node*> maybe_value = TryAllocateFastLiteral(
effect, control, boilerplate_object, allocation);
base::Optional<Node*> maybe_value =
TryAllocateFastLiteral(effect, control, boilerplate_object,
allocation, max_depth - 1, max_properties);
if (!maybe_value.has_value()) return {};
value = effect = maybe_value.value();
} else if (property_details.representation().IsDouble()) {
......@@ -1770,8 +1792,8 @@ base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteral(
}
// Setup the elements backing store.
base::Optional<Node*> maybe_elements =
TryAllocateFastLiteralElements(effect, control, boilerplate, allocation);
base::Optional<Node*> maybe_elements = TryAllocateFastLiteralElements(
effect, control, boilerplate, allocation, max_depth, max_properties);
if (!maybe_elements.has_value()) return {};
Node* elements = maybe_elements.value();
if (elements->op()->EffectOutputCount() > 0) effect = elements;
......@@ -1798,7 +1820,10 @@ base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteral(
base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteralElements(
Node* effect, Node* control, JSObjectRef boilerplate,
AllocationType allocation) {
AllocationType allocation, int max_depth, int* max_properties) {
DCHECK_GT(max_depth, 0);
DCHECK_GE(*max_properties, 0);
base::Optional<FixedArrayBaseRef> maybe_boilerplate_elements =
boilerplate.elements(kRelaxedLoad);
if (!maybe_boilerplate_elements.has_value()) return {};
......@@ -1812,34 +1837,36 @@ base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteralElements(
!boilerplate.IsElementsTenured(boilerplate_elements)) {
return {};
}
return jsgraph()->HeapConstant(boilerplate_elements.object());
return jsgraph()->Constant(boilerplate_elements);
}
// Compute the elements to store first (might have effects).
ZoneVector<Node*> elements_values(elements_length, zone());
if (elements_map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE) {
if (boilerplate_elements.IsFixedDoubleArray()) {
int const size = FixedDoubleArray::SizeFor(boilerplate_elements.length());
if (size > kMaxRegularHeapObjectSize) return {};
FixedDoubleArrayRef elements = boilerplate_elements.AsFixedDoubleArray();
for (int i = 0; i < elements_length; ++i) {
Float64 value = elements.GetFromImmutableFixedDoubleArray(i);
if (value.is_hole_nan()) {
elements_values[i] = jsgraph()->TheHoleConstant();
} else {
elements_values[i] = jsgraph()->Constant(value.get_scalar());
}
elements_values[i] = value.is_hole_nan()
? jsgraph()->TheHoleConstant()
: jsgraph()->Constant(value.get_scalar());
}
} else {
FixedArrayRef elements = boilerplate_elements.AsFixedArray();
for (int i = 0; i < elements_length; ++i) {
base::Optional<ObjectRef> maybe_element_value = elements.TryGet(i);
if (!maybe_element_value.has_value()) return {};
ObjectRef element_value = maybe_element_value.value();
if (element_value.IsJSObject()) {
base::Optional<Node*> maybe_value = TryAllocateFastLiteral(
effect, control, element_value.AsJSObject(), allocation);
if (!maybe_value.has_value()) return {};
elements_values[i] = effect = maybe_value.value();
if ((*max_properties)-- == 0) return {};
base::Optional<ObjectRef> element_value = elements.TryGet(i);
if (!element_value.has_value()) return {};
if (element_value->IsJSObject()) {
base::Optional<Node*> object =
TryAllocateFastLiteral(effect, control, element_value->AsJSObject(),
allocation, max_depth - 1, max_properties);
if (!object.has_value()) return {};
elements_values[i] = effect = *object;
} else {
elements_values[i] = jsgraph()->Constant(element_value);
elements_values[i] = jsgraph()->Constant(*element_value);
}
}
}
......@@ -1848,8 +1875,7 @@ base::Optional<Node*> JSCreateLowering::TryAllocateFastLiteralElements(
AllocationBuilder ab(jsgraph(), effect, control);
CHECK(ab.CanAllocateArray(elements_length, elements_map, allocation));
ab.AllocateArray(elements_length, elements_map, allocation);
ElementAccess const access =
(elements_map.instance_type() == FIXED_DOUBLE_ARRAY_TYPE)
ElementAccess const access = boilerplate_elements.IsFixedDoubleArray()
? AccessBuilder::ForFixedDoubleArrayElement()
: AccessBuilder::ForFixedArrayElement();
for (int i = 0; i < elements_length; ++i) {
......
......@@ -99,10 +99,12 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
bool* has_aliased_arguments);
base::Optional<Node*> TryAllocateFastLiteral(Node* effect, Node* control,
JSObjectRef boilerplate,
AllocationType allocation);
AllocationType allocation,
int max_depth,
int* max_properties);
base::Optional<Node*> TryAllocateFastLiteralElements(
Node* effect, Node* control, JSObjectRef boilerplate,
AllocationType allocation);
AllocationType allocation, int max_depth, int* max_properties);
Node* AllocateElements(Node* effect, Node* control,
ElementsKind elements_kind, int capacity,
......
......@@ -762,12 +762,8 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForArrayOrObjectLiteral(
return NewInsufficientFeedback(nexus.kind());
}
AllocationSiteRef site =
MakeRef(this, handle(AllocationSite::cast(object), isolate()));
if (site.IsFastLiteral()) {
site.SerializeBoilerplate();
}
AllocationSiteRef site = MakeRef(this, AllocationSite::cast(object));
if (site.PointsToLiteral()) site.SerializeRecursive();
return *zone()->New<LiteralFeedback>(site, nexus.kind());
}
......
......@@ -30,7 +30,7 @@ ACCESSORS(AllocationSite, transition_info_or_boilerplate, Object,
RELEASE_ACQUIRE_ACCESSORS(AllocationSite, transition_info_or_boilerplate,
Object, kTransitionInfoOrBoilerplateOffset)
ACCESSORS(AllocationSite, nested_site, Object, kNestedSiteOffset)
INT32_ACCESSORS(AllocationSite, pretenure_data, kPretenureDataOffset)
RELAXED_INT32_ACCESSORS(AllocationSite, pretenure_data, kPretenureDataOffset)
INT32_ACCESSORS(AllocationSite, pretenure_create_count,
kPretenureCreateCountOffset)
ACCESSORS(AllocationSite, dependent_code, DependentCode, kDependentCodeOffset)
......@@ -55,12 +55,13 @@ void AllocationSite::set_boilerplate(JSObject value, ReleaseStoreTag tag,
int AllocationSite::transition_info() const {
DCHECK(!PointsToLiteral());
return Smi::cast(transition_info_or_boilerplate()).value();
return Smi::cast(transition_info_or_boilerplate(kAcquireLoad)).value();
}
void AllocationSite::set_transition_info(int value) {
DCHECK(!PointsToLiteral());
set_transition_info_or_boilerplate(Smi::FromInt(value), SKIP_WRITE_BARRIER);
set_transition_info_or_boilerplate(Smi::FromInt(value), kReleaseStore,
SKIP_WRITE_BARRIER);
}
bool AllocationSite::HasWeakNext() const {
......@@ -113,7 +114,7 @@ void AllocationSite::SetDoNotInlineCall() {
}
bool AllocationSite::PointsToLiteral() const {
Object raw_value = transition_info_or_boilerplate();
Object raw_value = transition_info_or_boilerplate(kAcquireLoad);
DCHECK_EQ(!raw_value.IsSmi(),
raw_value.IsJSArray() || raw_value.IsJSObject());
return !raw_value.IsSmi();
......@@ -234,6 +235,7 @@ bool AllocationSite::DigestTransitionFeedback(Handle<AllocationSite> site,
is_nested ? "(nested)" : " ", ElementsKindToString(kind),
ElementsKindToString(to_kind));
}
CHECK_NE(to_kind, DICTIONARY_ELEMENTS);
JSObject::TransitionElementsKind(boilerplate, to_kind);
site->dependent_code().DeoptimizeDependentCodeGroup(
DependentCode::kAllocationSiteTransitionChangedGroup);
......
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