| // 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/compiler/cha.h" | |
| #include "vm/class_table.h" | |
| #include "vm/compiler/compiler_state.h" | |
| #include "vm/flags.h" | |
| #include "vm/log.h" | |
| #include "vm/object.h" | |
| #include "vm/raw_object.h" | |
| #include "vm/visitor.h" | |
| namespace dart { | |
| void CHA::AddToGuardedClasses(const Class& cls, | |
| intptr_t subclass_count, | |
| intptr_t implementor_cid, | |
| bool track_future) { | |
| for (intptr_t i = 0; i < guarded_classes_.length(); i++) { | |
| if (guarded_classes_[i].cls->ptr() == cls.ptr()) { | |
| if ((subclass_count >= 0) && (guarded_classes_[i].subclass_count == -1)) { | |
| guarded_classes_[i].subclass_count = subclass_count; | |
| } | |
| if ((implementor_cid != kIllegalCid) && | |
| (guarded_classes_[i].implementor_cid == kIllegalCid)) { | |
| guarded_classes_[i].implementor_cid = implementor_cid; | |
| } | |
| if (track_future && !guarded_classes_[i].track_future) { | |
| guarded_classes_[i].track_future = track_future; | |
| } | |
| return; | |
| } | |
| } | |
| GuardedClassInfo info = {&Class::ZoneHandle(thread_->zone(), cls.ptr()), | |
| subclass_count, implementor_cid, track_future}; | |
| guarded_classes_.Add(info); | |
| } | |
| void CHA::AddToGuardedClassesForSubclassCount(const Class& cls, | |
| intptr_t subclass_count) { | |
| ASSERT(subclass_count >= 0); | |
| AddToGuardedClasses(cls, subclass_count, kIllegalCid, false); | |
| } | |
| void CHA::AddToGuardedClassesForImplementorCid(const Class& cls, | |
| intptr_t implementor_cid) { | |
| ASSERT(implementor_cid != kIllegalCid); | |
| ASSERT(implementor_cid != kDynamicCid); | |
| AddToGuardedClasses(cls, -1, implementor_cid, false); | |
| } | |
| void CHA::AddToGuardedClassesToTrackFuture(const Class& cls) { | |
| AddToGuardedClasses(cls, -1, kIllegalCid, true); | |
| } | |
| bool CHA::IsGuardedClass(intptr_t cid) const { | |
| for (intptr_t i = 0; i < guarded_classes_.length(); ++i) { | |
| if (guarded_classes_[i].cls->id() == cid) return true; | |
| } | |
| return false; | |
| } | |
| bool CHA::HasSubclasses(const Class& cls) { | |
| ASSERT(!cls.IsNull()); | |
| ASSERT(!IsInternalOnlyClassId(cls.id())); | |
| if (cls.IsObjectClass()) { | |
| // Class Object has subclasses, although we do not keep track of them. | |
| return true; | |
| } | |
| if (cls.has_dynamically_extendable_subtypes()) return true; | |
| Thread* thread = Thread::Current(); | |
| SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock()); | |
| const GrowableObjectArray& direct_subclasses = | |
| GrowableObjectArray::Handle(cls.direct_subclasses()); | |
| return !direct_subclasses.IsNull() && (direct_subclasses.Length() > 0); | |
| } | |
| bool CHA::HasSubclasses(intptr_t cid) const { | |
| const ClassTable& class_table = *thread_->isolate_group()->class_table(); | |
| Class& cls = Class::Handle(thread_->zone(), class_table.At(cid)); | |
| return HasSubclasses(cls); | |
| } | |
| bool CHA::ConcreteSubclasses(const Class& cls, | |
| GrowableArray<intptr_t>* class_ids) { | |
| if (cls.IsObjectClass()) return false; | |
| if (cls.has_dynamically_extendable_subtypes()) return false; | |
| if (!cls.is_abstract()) { | |
| class_ids->Add(cls.id()); | |
| } | |
| // This is invoked from precompiler only, we can use unsafe version of | |
| // Class::direct_subclasses getter. | |
| ASSERT(FLAG_precompiled_mode); | |
| const GrowableObjectArray& direct_subclasses = | |
| GrowableObjectArray::Handle(cls.direct_subclasses_unsafe()); | |
| if (direct_subclasses.IsNull()) { | |
| return true; | |
| } | |
| Class& subclass = Class::Handle(); | |
| for (intptr_t i = 0; i < direct_subclasses.Length(); i++) { | |
| subclass ^= direct_subclasses.At(i); | |
| if (!ConcreteSubclasses(subclass, class_ids)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool CHA::IsImplemented(const Class& cls) { | |
| if (cls.has_dynamically_extendable_subtypes()) return true; | |
| return cls.is_implemented(); | |
| } | |
| bool CHA::HasSingleConcreteImplementation(const Class& interface, | |
| intptr_t* implementation_cid) { | |
| intptr_t cid = interface.implementor_cid(); | |
| if ((cid == kIllegalCid) || (cid == kDynamicCid) || | |
| interface.has_dynamically_extendable_subtypes()) { | |
| // No implementations / multiple implementations. | |
| *implementation_cid = kDynamicCid; | |
| return false; | |
| } | |
| Thread* thread = Thread::Current(); | |
| if (FLAG_use_cha_deopt || thread->isolate_group()->all_classes_finalized()) { | |
| if (FLAG_trace_cha) { | |
| THR_Print(" **(CHA) Type has one implementation: %s\n", | |
| interface.ToCString()); | |
| } | |
| if (FLAG_use_cha_deopt) { | |
| CHA& cha = thread->compiler_state().cha(); | |
| cha.AddToGuardedClassesForImplementorCid(interface, cid); | |
| } | |
| *implementation_cid = cid; | |
| return true; | |
| } else { | |
| *implementation_cid = kDynamicCid; | |
| return false; | |
| } | |
| } | |
| bool CHA::ClassCanBeFuture(const Class& cls) { | |
| if (cls.can_be_future()) { | |
| return true; | |
| } | |
| // Class cannot be Future with the current set of | |
| // finalized classes. However, as new classes are loaded | |
| // and finalized, there could be a new subtype of [cls] | |
| // which is also a subtype of Future. | |
| // We should deoptimize in such cases. | |
| Thread* thread = Thread::Current(); | |
| if (FLAG_use_cha_deopt || thread->isolate_group()->all_classes_finalized()) { | |
| if (FLAG_use_cha_deopt) { | |
| CHA& cha = thread->compiler_state().cha(); | |
| cha.AddToGuardedClassesToTrackFuture(cls); | |
| } | |
| return false; | |
| } | |
| // Conservatively assume that class can be Future. | |
| return true; | |
| } | |
| static intptr_t CountFinalizedSubclasses(Thread* thread, const Class& cls) { | |
| intptr_t count = 0; | |
| const GrowableObjectArray& cls_direct_subclasses = | |
| GrowableObjectArray::Handle(thread->zone(), cls.direct_subclasses()); | |
| if (cls_direct_subclasses.IsNull()) return count; | |
| Class& direct_subclass = Class::Handle(thread->zone()); | |
| for (intptr_t i = 0; i < cls_direct_subclasses.Length(); i++) { | |
| direct_subclass ^= cls_direct_subclasses.At(i); | |
| // Unfinalized classes are treated as nonexistent for CHA purposes, | |
| // as that means that no instance of that class exists at runtime. | |
| if (!direct_subclass.is_finalized()) { | |
| continue; | |
| } | |
| count += 1 + CountFinalizedSubclasses(thread, direct_subclass); | |
| } | |
| return count; | |
| } | |
| bool CHA::IsConsistentWithCurrentHierarchy() const { | |
| for (intptr_t i = 0; i < guarded_classes_.length(); i++) { | |
| if (guarded_classes_[i].subclass_count != -1) { | |
| intptr_t current_subclass_count = | |
| CountFinalizedSubclasses(thread_, *guarded_classes_[i].cls); | |
| if (guarded_classes_[i].subclass_count != current_subclass_count) { | |
| return false; // New subclass appeared during compilation. | |
| } | |
| } | |
| if (guarded_classes_[i].implementor_cid != kIllegalCid) { | |
| intptr_t current_implementor_cid = | |
| guarded_classes_[i].cls->implementor_cid(); | |
| if (guarded_classes_[i].implementor_cid != current_implementor_cid) { | |
| return false; // New implementor appeared during compilation. | |
| } | |
| } | |
| if (guarded_classes_[i].track_future) { | |
| if (guarded_classes_[i].cls->can_be_future()) { | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool CHA::HasOverride(const Class& cls, | |
| const String& function_name, | |
| intptr_t* subclasses_count) { | |
| // Subclasses of Object are not tracked by CHA. Safely assume that overrides | |
| // exist. | |
| if (cls.IsObjectClass()) { | |
| return true; | |
| } | |
| SafepointReadRwLocker ml(thread_, thread_->isolate_group()->program_lock()); | |
| const GrowableObjectArray& cls_direct_subclasses = | |
| GrowableObjectArray::Handle(thread_->zone(), cls.direct_subclasses()); | |
| if (cls_direct_subclasses.IsNull()) { | |
| return false; | |
| } | |
| Class& direct_subclass = Class::Handle(thread_->zone()); | |
| for (intptr_t i = 0; i < cls_direct_subclasses.Length(); i++) { | |
| direct_subclass ^= cls_direct_subclasses.At(i); | |
| // Unfinalized classes are treated as nonexistent for CHA purposes, | |
| // as that means that no instance of that class exists at runtime. | |
| if (!direct_subclass.is_finalized()) { | |
| continue; | |
| } | |
| if (direct_subclass.LookupDynamicFunctionUnsafe(function_name) != | |
| Function::null()) { | |
| return true; | |
| } | |
| if (HasOverride(direct_subclass, function_name, subclasses_count)) { | |
| return true; | |
| } | |
| (*subclasses_count)++; | |
| } | |
| return false; | |
| } | |
| void CHA::RegisterDependencies(const Code& code) const { | |
| for (intptr_t i = 0; i < guarded_classes_.length(); ++i) { | |
| guarded_classes_[i].cls->RegisterCHACode(code); | |
| } | |
| } | |
| } // namespace dart |