| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/megamorphic_cache_table.h" |
| |
| #include <stdlib.h> |
| #include "vm/object.h" |
| #include "vm/object_store.h" |
| #include "vm/stub_code.h" |
| #include "vm/symbols.h" |
| |
| namespace dart { |
| |
| RawMegamorphicCache* MegamorphicCacheTable::Lookup(Isolate* isolate, |
| const String& name, |
| const Array& descriptor) { |
| // Multiple compilation threads could access this lookup. |
| SafepointMutexLocker ml(isolate->megamorphic_lookup_mutex()); |
| ASSERT(name.IsSymbol()); |
| // TODO(rmacnak): ASSERT(descriptor.IsCanonical()); |
| |
| // TODO(rmacnak): Make a proper hashtable a la symbol table. |
| GrowableObjectArray& table = GrowableObjectArray::Handle( |
| isolate->object_store()->megamorphic_cache_table()); |
| MegamorphicCache& cache = MegamorphicCache::Handle(); |
| if (table.IsNull()) { |
| table = GrowableObjectArray::New(Heap::kOld); |
| isolate->object_store()->set_megamorphic_cache_table(table); |
| } else { |
| for (intptr_t i = 0; i < table.Length(); i++) { |
| cache ^= table.At(i); |
| if ((cache.target_name() == name.raw()) && |
| (cache.arguments_descriptor() == descriptor.raw())) { |
| return cache.raw(); |
| } |
| } |
| } |
| |
| cache = MegamorphicCache::New(name, descriptor); |
| table.Add(cache, Heap::kOld); |
| return cache.raw(); |
| } |
| |
| RawFunction* MegamorphicCacheTable::miss_handler(Isolate* isolate) { |
| ASSERT(isolate->object_store()->megamorphic_miss_function() != |
| Function::null()); |
| return isolate->object_store()->megamorphic_miss_function(); |
| } |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| void MegamorphicCacheTable::InitMissHandler(Isolate* isolate) { |
| // The miss handler for a class ID not found in the table is invoked as a |
| // normal Dart function. |
| ObjectPoolWrapper object_pool_wrapper; |
| const Code& code = Code::Handle( |
| StubCode::Generate("_stub_MegamorphicMiss", &object_pool_wrapper, |
| StubCode::GenerateMegamorphicMissStub)); |
| |
| const auto& object_pool = |
| ObjectPool::Handle(object_pool_wrapper.MakeObjectPool()); |
| code.set_object_pool(object_pool.raw()); |
| |
| // When FLAG_lazy_dispatchers=false, this stub can be on the stack during |
| // exceptions, but it has a corresponding function so IsStubCode is false and |
| // it is considered in the search for an exception handler. |
| code.set_exception_handlers(Object::empty_exception_handlers()); |
| const Class& cls = |
| Class::Handle(Type::Handle(Type::DartFunctionType()).type_class()); |
| const Function& function = Function::Handle( |
| Function::New(Symbols::MegamorphicMiss(), RawFunction::kRegularFunction, |
| true, // Static, but called as a method. |
| false, // Not const. |
| false, // Not abstract. |
| false, // Not external. |
| false, // Not native. |
| cls, TokenPosition::kNoSource)); |
| function.set_result_type(Type::Handle(Type::DynamicType())); |
| function.set_is_debuggable(false); |
| function.set_is_visible(false); |
| function.AttachCode(code); // Has a single entry point, as a static function. |
| // For inclusion in Snapshot::kFullJIT. |
| function.set_unoptimized_code(code); |
| |
| ASSERT(isolate->object_store()->megamorphic_miss_function() == |
| Function::null()); |
| isolate->object_store()->SetMegamorphicMissHandler(code, function); |
| } |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| void MegamorphicCacheTable::PrintSizes(Isolate* isolate) { |
| StackZone zone(Thread::Current()); |
| intptr_t size = 0; |
| MegamorphicCache& cache = MegamorphicCache::Handle(); |
| Array& buckets = Array::Handle(); |
| const GrowableObjectArray& table = GrowableObjectArray::Handle( |
| isolate->object_store()->megamorphic_cache_table()); |
| if (table.IsNull()) return; |
| intptr_t max_size = 0; |
| for (intptr_t i = 0; i < table.Length(); i++) { |
| cache ^= table.At(i); |
| buckets = cache.buckets(); |
| size += MegamorphicCache::InstanceSize(); |
| size += Array::InstanceSize(buckets.Length()); |
| if (buckets.Length() > max_size) { |
| max_size = buckets.Length(); |
| } |
| } |
| OS::PrintErr("%" Pd " megamorphic caches using %" Pd "KB.\n", table.Length(), |
| size / 1024); |
| |
| intptr_t* probe_counts = new intptr_t[max_size]; |
| intptr_t entry_count = 0; |
| intptr_t max_probe_count = 0; |
| for (intptr_t i = 0; i < max_size; i++) { |
| probe_counts[i] = 0; |
| } |
| for (intptr_t i = 0; i < table.Length(); i++) { |
| cache ^= table.At(i); |
| buckets = cache.buckets(); |
| intptr_t mask = cache.mask(); |
| intptr_t capacity = mask + 1; |
| for (intptr_t j = 0; j < capacity; j++) { |
| intptr_t class_id = |
| Smi::Value(Smi::RawCast(cache.GetClassId(buckets, j))); |
| if (class_id != kIllegalCid) { |
| intptr_t probe_count = 0; |
| intptr_t probe_index = |
| (class_id * MegamorphicCache::kSpreadFactor) & mask; |
| intptr_t probe_cid; |
| while (true) { |
| probe_count++; |
| probe_cid = |
| Smi::Value(Smi::RawCast(cache.GetClassId(buckets, probe_index))); |
| if (probe_cid == class_id) { |
| break; |
| } |
| probe_index = (probe_index + 1) & mask; |
| } |
| probe_counts[probe_count]++; |
| if (probe_count > max_probe_count) { |
| max_probe_count = probe_count; |
| } |
| entry_count++; |
| } |
| } |
| } |
| intptr_t cumulative_entries = 0; |
| for (intptr_t i = 0; i <= max_probe_count; i++) { |
| cumulative_entries += probe_counts[i]; |
| OS::PrintErr("Megamorphic probe %" Pd ": %" Pd " (%lf)\n", i, |
| probe_counts[i], |
| static_cast<double>(cumulative_entries) / |
| static_cast<double>(entry_count)); |
| } |
| delete[] probe_counts; |
| } |
| |
| } // namespace dart |