Index: runtime/vm/object.cc |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
index c51a27cc9ab0cd9391c4a7ec7b5f6434ee2f7ae9..dcd503d5489d596b14d434646fd62b5b3b7d266d 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,122 @@ 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::fill_count() const { |
+ return raw_ptr()->fill_count_; |
+} |
+ |
+ |
+void MegamorphicCache::set_fill_count(intptr_t fill_count) const { |
+ raw_ptr()->fill_count_ = fill_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 size = kInitialCapacity; |
+ const Array& buckets = Array::Handle(Array::New(2 * size)); |
+ 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 < size; ++i) { |
+ buckets.SetAt(2 * i, illegal); |
Vyacheslav Egorov (Google)
2012/12/03 14:55:22
I don't like magical constant spread around
Kevin Millikin (Google)
2012/12/06 14:03:11
I thought this was already pretty localized, but I
|
+ buckets.SetAt((2 * i) + 1, handler); |
+ } |
+ result.set_buckets(buckets); |
+ result.set_mask(size - 1); |
+ result.set_fill_count(0); |
+ return result.raw(); |
+} |
+ |
+ |
+void MegamorphicCache::EnsureCapacity() const { |
+ intptr_t old_size = mask() + 1; |
+ double load_limit = kLoadFactor * static_cast<double>(old_size); |
+ if (static_cast<double>(fill_count() + 1) > load_limit) { |
+ const Array& old_buckets = Array::Handle(buckets()); |
+ intptr_t new_size = old_size * 2; |
+ const Array& new_buckets = Array::Handle(Array::New(2 * new_size)); |
+ |
+ 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_size; ++i) { |
+ new_buckets.SetAt(2 * i, class_id); |
+ new_buckets.SetAt((2 * i) + 1, target); |
+ } |
+ set_buckets(new_buckets); |
+ set_mask(new_size - 1); |
+ set_fill_count(0); |
+ |
+ // Rehash the valid entries. |
+ for (intptr_t i = 0; i < old_size; ++i) { |
+ class_id ^= old_buckets.At(2 * i); |
+ if (class_id.Value() != kIllegalCid) { |
+ target ^= old_buckets.At((2 * i) + 1); |
+ Insert(class_id, target); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void MegamorphicCache::Insert(const Smi& class_id, |
+ const Function& target) const { |
+ ASSERT(static_cast<double>(fill_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 ^= backing_array.At(2 * i); |
+ if (probe.Value() == kIllegalCid) { |
+ backing_array.SetAt(2 * i, class_id); |
+ backing_array.SetAt((2 * i) + 1, target); |
+ set_fill_count(fill_count() + 1); |
Vyacheslav Egorov (Google)
2012/12/03 14:55:22
consider renaming fill_count to size
Kevin Millikin (Google)
2012/12/06 14:03:11
Since variables ending in _size are always in byte
|
+ 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(); |