diff --git a/runtime/vm/canonical_tables.cc b/runtime/vm/canonical_tables.cc index bfdc1401e3c..d2fd4879550 100644 --- a/runtime/vm/canonical_tables.cc +++ b/runtime/vm/canonical_tables.cc @@ -69,4 +69,46 @@ uword MetadataMapTraits::Hash(const Object& key) { UNREACHABLE(); } +CanonicalInstanceKey::CanonicalInstanceKey(const Instance& key) : key_(key) { + ASSERT(!(key.IsString() || key.IsAbstractType())); +} + +bool CanonicalInstanceKey::Matches(const Instance& obj) const { + ASSERT(!(obj.IsString() || obj.IsAbstractType())); + if (key_.CanonicalizeEquals(obj)) { + ASSERT(obj.IsCanonical()); + return true; + } + return false; +} + +uword CanonicalInstanceKey::Hash() const { + return key_.CanonicalizeHash(); +} + +bool CanonicalInstanceTraits::IsMatch(const Object& a, const Object& b) { + ASSERT(!(a.IsString() || a.IsAbstractType())); + ASSERT(!(b.IsString() || b.IsAbstractType())); + return a.ptr() == b.ptr(); +} + +bool CanonicalInstanceTraits::IsMatch(const CanonicalInstanceKey& a, + const Object& b) { + return a.Matches(Instance::Cast(b)); +} + +uword CanonicalInstanceTraits::Hash(const Object& key) { + ASSERT(!(key.IsString() || key.IsAbstractType())); + ASSERT(key.IsInstance()); + return Instance::Cast(key).CanonicalizeHash(); +} + +uword CanonicalInstanceTraits::Hash(const CanonicalInstanceKey& key) { + return key.Hash(); +} + +ObjectPtr CanonicalInstanceTraits::NewKey(const CanonicalInstanceKey& obj) { + return obj.key_.ptr(); +} + } // namespace dart diff --git a/runtime/vm/canonical_tables.h b/runtime/vm/canonical_tables.h index ba9297e16d2..f4f758feac6 100644 --- a/runtime/vm/canonical_tables.h +++ b/runtime/vm/canonical_tables.h @@ -303,6 +303,31 @@ class MetadataMapTraits { }; typedef UnorderedHashMap MetadataMap; +class CanonicalInstanceKey { + public: + explicit CanonicalInstanceKey(const Instance& key); + bool Matches(const Instance& obj) const; + uword Hash() const; + const Instance& key_; + + private: + DISALLOW_ALLOCATION(); +}; + +// Traits for looking up Canonical Instances based on a hash of the fields. +class CanonicalInstanceTraits { + public: + static const char* Name() { return "CanonicalInstanceTraits"; } + static bool ReportStats() { return false; } + + // Called when growing the table. + static bool IsMatch(const Object& a, const Object& b); + static bool IsMatch(const CanonicalInstanceKey& a, const Object& b); + static uword Hash(const Object& key); + static uword Hash(const CanonicalInstanceKey& key); + static ObjectPtr NewKey(const CanonicalInstanceKey& obj); +}; + } // namespace dart #endif // RUNTIME_VM_CANONICAL_TABLES_H_ diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc index b58ff264f7d..4e9be6ffd18 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc @@ -4,6 +4,8 @@ #include "vm/compiler/aot/precompiler.h" +#include + #include "platform/unicode.h" #include "platform/utils.h" #include "vm/canonical_tables.h" @@ -400,6 +402,7 @@ Precompiler::Precompiler(Thread* thread) dropped_functiontype_count_(0), dropped_typeparam_count_(0), dropped_library_count_(0), + dropped_constants_arrays_entries_count_(0), libraries_(GrowableObjectArray::Handle( isolate_->group()->object_store()->libraries())), pending_functions_( @@ -607,6 +610,7 @@ void Precompiler::DoCompileAll() { DropFunctions(); DropFields(); + DropTransitiveUserDefinedConstants(); TraceTypesFromRetainedClasses(); // Clear these before dropping classes as they may hold onto otherwise @@ -688,7 +692,9 @@ void Precompiler::DoCompileAll() { THR_Print(" %" Pd " type parameters,", dropped_typeparam_count_); THR_Print(" %" Pd " type arguments,", dropped_typearg_count_); THR_Print(" %" Pd " classes,", dropped_class_count_); - THR_Print(" %" Pd " libraries.\n", dropped_library_count_); + THR_Print(" %" Pd " libraries,", dropped_library_count_); + THR_Print(" %" Pd " constants arrays entries.\n", + dropped_constants_arrays_entries_count_); } } @@ -2420,6 +2426,190 @@ void Precompiler::AttachOptimizedTypeTestingStub() { StubCode::TopTypeTypeTest().EntryPoint()); } +enum ConstantVisitedValue { kNotVisited = 0, kRetain, kDrop }; + +static bool IsUserDefinedClass(Zone* zone, + ClassPtr cls, + ObjectStore* object_store) { + intptr_t cid = cls.untag()->id(); + if (cid < kNumPredefinedCids) { + return false; + } + + const UntaggedClass* untagged_cls = cls.untag(); + return ((untagged_cls->library() != object_store->core_library()) && + (untagged_cls->library() != object_store->collection_library()) && + (untagged_cls->library() != object_store->typed_data_library())); +} + +/// Updates |visited| weak table with information about whether object +/// (transitevly) references constants of user-defined classes: |kDrop| +/// indicates it does, |kRetain| - does not. +class ConstantInstanceVisitor { + public: + ConstantInstanceVisitor(Zone* zone, + WeakTable* visited, + ObjectStore* object_store) + : zone_(zone), + visited_(visited), + object_store_(object_store), + object_(Object::Handle(zone)), + array_(Array::Handle(zone)) {} + + void Visit(ObjectPtr object_ptr) { + if (!object_ptr->IsHeapObject()) { + return; + } + ConstantVisitedValue value = static_cast( + visited_->GetValueExclusive(object_ptr)); + if (value != kNotVisited) { + return; + } + object_ = object_ptr; + if (IsUserDefinedClass(zone_, object_.clazz(), object_store_)) { + visited_->SetValueExclusive(object_ptr, kDrop); + return; + } + + // Conservatively assume an object will be retained. + visited_->SetValueExclusive(object_ptr, kRetain); + switch (object_ptr.untag()->GetClassId()) { + case kImmutableArrayCid: { + array_ ^= object_ptr; + for (intptr_t i = 0; i < array_.Length(); i++) { + ObjectPtr element = array_.At(i); + Visit(element); + if (static_cast( + visited_->GetValueExclusive(element)) == kDrop) { + visited_->SetValueExclusive(object_ptr, kDrop); + break; + } + } + break; + } + case kImmutableLinkedHashMapCid: { + const LinkedHashMap& map = + LinkedHashMap::Handle(LinkedHashMap::RawCast(object_ptr)); + LinkedHashMap::Iterator iterator(map); + while (iterator.MoveNext()) { + ObjectPtr element = iterator.CurrentKey(); + Visit(element); + if (static_cast( + visited_->GetValueExclusive(element)) == kDrop) { + visited_->SetValueExclusive(object_ptr, kDrop); + break; + } + element = iterator.CurrentValue(); + Visit(element); + if (static_cast( + visited_->GetValueExclusive(element)) == kDrop) { + visited_->SetValueExclusive(object_ptr, kDrop); + break; + } + } + break; + } + case kImmutableLinkedHashSetCid: { + const LinkedHashSet& set = + LinkedHashSet::Handle(LinkedHashSet::RawCast(object_ptr)); + LinkedHashSet::Iterator iterator(set); + while (iterator.MoveNext()) { + ObjectPtr element = iterator.CurrentKey(); + Visit(element); + if (static_cast( + visited_->GetValueExclusive(element)) == kDrop) { + visited_->SetValueExclusive(object_ptr, kDrop); + break; + } + } + break; + } + } + } + + private: + Zone* zone_; + WeakTable* visited_; + ObjectStore* object_store_; + Object& object_; + Array& array_; +}; + +// To reduce snapshot size, we remove from constant tables all constants that +// cannot be sent in messages between isolate groups. Such constants will not +// be canonicalized at runtime. +void Precompiler::DropTransitiveUserDefinedConstants() { + HANDLESCOPE(T); + auto& constants = Array::Handle(Z); + auto& obj = Object::Handle(Z); + auto& lib = Library::Handle(Z); + auto& cls = Class::Handle(Z); + auto& instance = Instance::Handle(Z); + + { + NoSafepointScope no_safepoint(T); + std::unique_ptr visited(new WeakTable()); + ObjectStore* object_store = IG->object_store(); + ConstantInstanceVisitor visitor(Z, visited.get(), object_store); + + for (intptr_t i = 0; i < libraries_.Length(); i++) { + lib ^= libraries_.At(i); + HANDLESCOPE(T); + ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); + while (it.HasNext()) { + cls = it.GetNextClass(); + if (cls.constants() == Array::null()) { + continue; + } + typedef UnorderedHashSet CanonicalInstancesSet; + + CanonicalInstancesSet constants_set(cls.constants()); + CanonicalInstancesSet::Iterator iterator(&constants_set); + + if (IsUserDefinedClass(Z, cls.ptr(), object_store)) { + // All constants for user-defined classes can be dropped. + constants = cls.constants(); + dropped_constants_arrays_entries_count_ += constants.Length(); + if (FLAG_trace_precompiler) { + THR_Print("Dropping %" Pd " entries from constants for class %s\n", + constants.Length(), cls.ToCString()); + } + while (iterator.MoveNext()) { + obj = constants_set.GetKey(iterator.Current()); + instance = Instance::RawCast(obj.ptr()); + consts_to_retain_.Remove(&instance); + visited->SetValueExclusive(obj.ptr(), kDrop); + } + } else { + // Core classes might have constants that refer to user-defined + // classes. Those should be dropped too. + while (iterator.MoveNext()) { + obj = constants_set.GetKey(iterator.Current()); + ConstantVisitedValue value = static_cast( + visited->GetValueExclusive(obj.ptr())); + if (value == kNotVisited) { + visitor.Visit(obj.ptr()); + value = static_cast( + visited->GetValueExclusive(obj.ptr())); + } + ASSERT(value == kDrop || value == kRetain); + if (value == kDrop) { + dropped_constants_arrays_entries_count_++; + if (FLAG_trace_precompiler) { + THR_Print("Dropping constant entry for class %s instance:%s\n", + cls.ToCString(), obj.ToCString()); + } + instance = Instance::RawCast(obj.ptr()); + consts_to_retain_.Remove(&instance); + } + } + } + constants_set.Release(); + } + } + } +} + void Precompiler::TraceTypesFromRetainedClasses() { HANDLESCOPE(T); auto& lib = Library::Handle(Z); diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h index ba2aee79d9f..4e0d2117bb2 100644 --- a/runtime/vm/compiler/aot/precompiler.h +++ b/runtime/vm/compiler/aot/precompiler.h @@ -333,6 +333,10 @@ class Precompiler : public ValueObject { void ReplaceFunctionStaticCallEntries(); void DropFunctions(); void DropFields(); + void VisitConstantInstance(ObjectPtr instance, + WeakTable* visited, + ObjectStore* object_store); + void DropTransitiveUserDefinedConstants(); void TraceTypesFromRetainedClasses(); void DropMetadata(); void DropLibraryEntries(); @@ -370,6 +374,7 @@ class Precompiler : public ValueObject { intptr_t dropped_functiontype_count_; intptr_t dropped_typeparam_count_; intptr_t dropped_library_count_; + intptr_t dropped_constants_arrays_entries_count_; compiler::ObjectPoolBuilder global_object_pool_builder_; GrowableObjectArray& libraries_; diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index da4f7348ab2..e9b0ea28709 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -5949,51 +5949,6 @@ static uword Hash64To32(uint64_t v) { return static_cast(v); } -class CanonicalInstanceKey { - public: - explicit CanonicalInstanceKey(const Instance& key) : key_(key) { - ASSERT(!(key.IsString() || key.IsAbstractType())); - } - bool Matches(const Instance& obj) const { - ASSERT(!(obj.IsString() || obj.IsAbstractType())); - if (key_.CanonicalizeEquals(obj)) { - ASSERT(obj.IsCanonical()); - return true; - } - return false; - } - uword Hash() const { return key_.CanonicalizeHash(); } - const Instance& key_; - - private: - DISALLOW_ALLOCATION(); -}; - -// Traits for looking up Canonical Instances based on a hash of the fields. -class CanonicalInstanceTraits { - public: - static const char* Name() { return "CanonicalInstanceTraits"; } - static bool ReportStats() { return false; } - - // Called when growing the table. - static bool IsMatch(const Object& a, const Object& b) { - ASSERT(!(a.IsString() || a.IsAbstractType())); - ASSERT(!(b.IsString() || b.IsAbstractType())); - return a.ptr() == b.ptr(); - } - static bool IsMatch(const CanonicalInstanceKey& a, const Object& b) { - return a.Matches(Instance::Cast(b)); - } - static uword Hash(const Object& key) { - ASSERT(!(key.IsString() || key.IsAbstractType())); - ASSERT(key.IsInstance()); - return Instance::Cast(key).CanonicalizeHash(); - } - static uword Hash(const CanonicalInstanceKey& key) { return key.Hash(); } - static ObjectPtr NewKey(const CanonicalInstanceKey& obj) { - return obj.key_.ptr(); - } -}; typedef UnorderedHashSet CanonicalInstancesSet; InstancePtr Class::LookupCanonicalInstance(Zone* zone,