Index: runtime/vm/object.cc |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
index 15b3d58683c3eb1e671bb1d96f01a2b4f9aa8274..bf4b5fc87b85e962d8219c0e0903532a6c90b240 100644 |
--- a/runtime/vm/object.cc |
+++ b/runtime/vm/object.cc |
@@ -97,6 +97,8 @@ RawClass* Object::deopt_info_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
RawClass* Object::context_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
RawClass* Object::context_scope_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
RawClass* Object::icdata_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
+RawClass* Object::megamorphic_cache_class_ = |
+ reinterpret_cast<RawClass*>(RAW_NULL); |
RawClass* Object::subtypetestcache_class_ = |
reinterpret_cast<RawClass*>(RAW_NULL); |
RawClass* Object::api_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL); |
@@ -382,6 +384,9 @@ void Object::InitOnce() { |
cls = Class::New<ICData>(); |
icdata_class_ = cls.raw(); |
+ cls = Class::New<MegamorphicCache>(); |
+ megamorphic_cache_class_ = cls.raw(); |
+ |
cls = Class::New<SubtypeTestCache>(); |
subtypetestcache_class_ = cls.raw(); |
@@ -457,6 +462,7 @@ void Object::RegisterSingletonClassNames() { |
SET_CLASS_NAME(context, Context); |
SET_CLASS_NAME(context_scope, ContextScope); |
SET_CLASS_NAME(icdata, ICData); |
+ SET_CLASS_NAME(megamorphic_cache, MegamorphicCache); |
SET_CLASS_NAME(subtypetestcache, SubtypeTestCache); |
SET_CLASS_NAME(api_error, ApiError); |
SET_CLASS_NAME(language_error, LanguageError); |
@@ -7758,6 +7764,120 @@ RawICData* ICData::New(const Function& function, |
} |
+RawArray* MegamorphicCache::buckets() const { |
+ return raw_ptr()->buckets_; |
+} |
+ |
+ |
+void MegamorphicCache::set_buckets(const Array& buckets) const { |
+ StorePointer(&raw_ptr()->buckets_, buckets.raw()); |
+} |
+ |
+ |
+// Class IDs in the table are smi-tagged, so we use a smi-tagged mask |
+// and target class ID to avoid untagging (on each iteration of the |
+// test loop) in generated code. |
+intptr_t MegamorphicCache::mask() const { |
+ return Smi::Value(raw_ptr()->mask_); |
+} |
+ |
+ |
+void MegamorphicCache::set_mask(intptr_t mask) const { |
+ raw_ptr()->mask_ = Smi::New(mask); |
+} |
+ |
+ |
+intptr_t MegamorphicCache::filled_entry_count() const { |
+ return raw_ptr()->filled_entry_count_; |
+} |
+ |
+ |
+void MegamorphicCache::set_filled_entry_count(intptr_t count) const { |
+ raw_ptr()->filled_entry_count_ = count; |
+} |
+ |
+ |
+RawMegamorphicCache* MegamorphicCache::New() { |
+ MegamorphicCache& result = MegamorphicCache::Handle(); |
+ { RawObject* raw = Object::Allocate(MegamorphicCache::kClassId, |
+ MegamorphicCache::InstanceSize(), |
+ Heap::kOld); |
+ NoGCScope no_gc; |
+ result ^= raw; |
+ } |
+ const intptr_t capacity = kInitialCapacity; |
+ const Array& buckets = Array::Handle(Array::New(kEntryLength * capacity)); |
+ const Smi& illegal = Smi::Handle(Smi::New(kIllegalCid)); |
+ const Function& handler = Function::Handle( |
+ Isolate::Current()->megamorphic_cache_table()->miss_handler()); |
+ for (intptr_t i = 0; i < capacity; ++i) { |
+ SetEntry(buckets, i, illegal, handler); |
+ } |
+ result.set_buckets(buckets); |
+ result.set_mask(capacity - 1); |
+ result.set_filled_entry_count(0); |
+ return result.raw(); |
+} |
+ |
+ |
+void MegamorphicCache::EnsureCapacity() const { |
+ intptr_t old_capacity = mask() + 1; |
+ double load_limit = kLoadFactor * static_cast<double>(old_capacity); |
+ if (static_cast<double>(filled_entry_count() + 1) > load_limit) { |
+ const Array& old_buckets = Array::Handle(buckets()); |
+ intptr_t new_capacity = old_capacity * 2; |
+ const Array& new_buckets = |
+ Array::Handle(Array::New(kEntryLength * new_capacity)); |
+ |
+ Smi& class_id = Smi::Handle(Smi::New(kIllegalCid)); |
+ Function& target = Function::Handle( |
+ Isolate::Current()->megamorphic_cache_table()->miss_handler()); |
+ for (intptr_t i = 0; i < new_capacity; ++i) { |
+ SetEntry(new_buckets, i, class_id, target); |
+ } |
+ set_buckets(new_buckets); |
+ set_mask(new_capacity - 1); |
+ set_filled_entry_count(0); |
+ |
+ // Rehash the valid entries. |
+ for (intptr_t i = 0; i < old_capacity; ++i) { |
+ class_id ^= GetClassId(old_buckets, i); |
+ if (class_id.Value() != kIllegalCid) { |
+ target ^= GetTargetFunction(old_buckets, i); |
+ Insert(class_id, target); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void MegamorphicCache::Insert(const Smi& class_id, |
+ const Function& target) const { |
+ ASSERT(static_cast<double>(filled_entry_count() + 1) <= |
+ (kLoadFactor * static_cast<double>(mask() + 1))); |
+ const Array& backing_array = Array::Handle(buckets()); |
+ intptr_t id_mask = mask(); |
+ intptr_t index = class_id.Value() & id_mask; |
+ Smi& probe = Smi::Handle(); |
+ intptr_t i = index; |
+ do { |
+ probe ^= GetClassId(backing_array, i); |
+ if (probe.Value() == kIllegalCid) { |
+ SetEntry(backing_array, i, class_id, target); |
+ set_filled_entry_count(filled_entry_count() + 1); |
+ return; |
+ } |
+ i = (i + 1) & id_mask; |
+ } while (i != index); |
+ UNREACHABLE(); |
+} |
+ |
+ |
+const char* MegamorphicCache::ToCString() const { |
+ return ""; |
+} |
+ |
+ |
RawSubtypeTestCache* SubtypeTestCache::New() { |
ASSERT(Object::subtypetestcache_class() != Class::null()); |
SubtypeTestCache& result = SubtypeTestCache::Handle(); |