diff options
Diffstat (limited to 'runtime/cha.cc')
| -rw-r--r-- | runtime/cha.cc | 197 |
1 files changed, 171 insertions, 26 deletions
diff --git a/runtime/cha.cc b/runtime/cha.cc index d94b091ebd..d11b12f700 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -16,6 +16,7 @@ #include "cha.h" +#include "art_method-inl.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "runtime.h" @@ -185,7 +186,8 @@ class CHACheckpoint FINAL : public Closure { }; void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify_class, - uint16_t verify_index) { + uint16_t verify_index, + ArtMethod* excluded_method) { // Grab cha_lock_ to make sure all single-implementation updates are seen. PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); @@ -195,9 +197,14 @@ void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify return; } ArtMethod* verify_method = verify_class->GetVTableEntry(verify_index, image_pointer_size); - DCHECK(!verify_method->HasSingleImplementation()) - << "class: " << verify_class->PrettyClass() - << " verify_method: " << verify_method->PrettyMethod(true); + if (verify_method != excluded_method) { + DCHECK(!verify_method->HasSingleImplementation()) + << "class: " << verify_class->PrettyClass() + << " verify_method: " << verify_method->PrettyMethod(true); + if (verify_method->IsAbstract()) { + DCHECK(verify_method->GetSingleImplementation(image_pointer_size) == nullptr); + } + } verify_class = verify_class->GetSuperClass(); } } @@ -206,41 +213,160 @@ void ClassHierarchyAnalysis::CheckSingleImplementationInfo( Handle<mirror::Class> klass, ArtMethod* virtual_method, ArtMethod* method_in_super, - std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) { + std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, + PointerSize pointer_size) { // TODO: if klass is not instantiable, virtual_method isn't invocable yet so // even if it overrides, it doesn't invalidate single-implementation // assumption. - DCHECK_NE(virtual_method, method_in_super); + DCHECK((virtual_method != method_in_super) || virtual_method->IsAbstract()); DCHECK(method_in_super->GetDeclaringClass()->IsResolved()) << "class isn't resolved"; // If virtual_method doesn't come from a default interface method, it should // be supplied by klass. - DCHECK(virtual_method->IsCopied() || + DCHECK(virtual_method == method_in_super || + virtual_method->IsCopied() || virtual_method->GetDeclaringClass() == klass.Get()); - // A new virtual_method should set method_in_super to - // non-single-implementation (if not set already). - // We don't grab cha_lock_. Single-implementation flag won't be set to true - // again once it's set to false. + // To make updating single-implementation flags simple, we always maintain the following + // invariant: + // Say all virtual methods in the same vtable slot, starting from the bottom child class + // to super classes, is a sequence of unique methods m3, m2, m1, ... (after removing duplicate + // methods for inherited methods). + // For example for the following class hierarchy, + // class A { void m() { ... } } + // class B extends A { void m() { ... } } + // class C extends B {} + // class D extends C { void m() { ... } } + // the sequence is D.m(), B.m(), A.m(). + // The single-implementation status for that sequence of methods begin with one or two true's, + // then become all falses. The only case where two true's are possible is for one abstract + // method m and one non-abstract method mImpl that overrides method m. + // With the invariant, when linking in a new class, we only need to at most update one or + // two methods in the sequence for their single-implementation status, in order to maintain + // the invariant. + if (!method_in_super->HasSingleImplementation()) { // method_in_super already has multiple implementations. All methods in the // same vtable slots in its super classes should have // non-single-implementation already. if (kIsDebugBuild) { VerifyNonSingleImplementation(klass->GetSuperClass()->GetSuperClass(), - method_in_super->GetMethodIndex()); + method_in_super->GetMethodIndex(), + nullptr /* excluded_method */); } return; } // Native methods don't have single-implementation flag set. DCHECK(!method_in_super->IsNative()); - // Invalidate method_in_super's single-implementation status. - invalidated_single_impl_methods.insert(method_in_super); + + uint16_t method_index = method_in_super->GetMethodIndex(); + if (method_in_super->IsAbstract()) { + if (kIsDebugBuild) { + // An abstract method should have made all methods in the same vtable + // slot above it in the class hierarchy having non-single-implementation. + mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass(); + VerifyNonSingleImplementation(super_super, + method_index, + method_in_super); + } + + if (virtual_method->IsAbstract()) { + // SUPER: abstract, VIRTUAL: abstract. + if (method_in_super == virtual_method) { + DCHECK(klass->IsInstantiable()); + // An instantiable subclass hasn't provided a concrete implementation of + // the abstract method. Invoking method_in_super may throw AbstractMethodError. + // This is an uncommon case, so we simply treat method_in_super as not + // having single-implementation. + invalidated_single_impl_methods.insert(method_in_super); + return; + } else { + // One abstract method overrides another abstract method. This is an uncommon + // case. We simply treat method_in_super as not having single-implementation. + invalidated_single_impl_methods.insert(method_in_super); + return; + } + } else { + // SUPER: abstract, VIRTUAL: non-abstract. + // A non-abstract method overrides an abstract method. + if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { + // Abstract method_in_super has no implementation yet. + // We need to grab cha_lock_ for further checking/updating due to possible + // races. + MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); + if (!method_in_super->HasSingleImplementation()) { + return; + } + if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { + // virtual_method becomes the first implementation for method_in_super. + method_in_super->SetSingleImplementation(virtual_method, pointer_size); + // Keep method_in_super's single-implementation status. + return; + } + // Fall through to invalidate method_in_super's single-implementation status. + } + // Abstract method_in_super already got one implementation. + // Invalidate method_in_super's single-implementation status. + invalidated_single_impl_methods.insert(method_in_super); + return; + } + } else { + if (virtual_method->IsAbstract()) { + // SUPER: non-abstract, VIRTUAL: abstract. + // An abstract method overrides a non-abstract method. This is an uncommon + // case, we simply treat both methods as not having single-implementation. + invalidated_single_impl_methods.insert(virtual_method); + // Fall-through to handle invalidating method_in_super of its + // single-implementation status. + } + + // SUPER: non-abstract, VIRTUAL: non-abstract/abstract(fall-through from previous if). + // Invalidate method_in_super's single-implementation status. + invalidated_single_impl_methods.insert(method_in_super); + + // method_in_super might be the single-implementation of another abstract method, + // which should be also invalidated of its single-implementation status. + mirror::Class* super_super = klass->GetSuperClass()->GetSuperClass(); + while (super_super != nullptr && + method_index < super_super->GetVTableLength()) { + ArtMethod* method_in_super_super = super_super->GetVTableEntry(method_index, pointer_size); + if (method_in_super_super != method_in_super) { + if (method_in_super_super->IsAbstract()) { + if (method_in_super_super->HasSingleImplementation()) { + // Invalidate method_in_super's single-implementation status. + invalidated_single_impl_methods.insert(method_in_super_super); + // No need to further traverse up the class hierarchy since if there + // are cases that one abstract method overrides another method, we + // should have made that method having non-single-implementation already. + } else { + // method_in_super_super is already non-single-implementation. + // No need to further traverse up the class hierarchy. + } + } else { + DCHECK(!method_in_super_super->HasSingleImplementation()); + // No need to further traverse up the class hierarchy since two non-abstract + // methods (method_in_super and method_in_super_super) should have set all + // other methods (abstract or not) in the vtable slot to be non-single-implementation. + } + + if (kIsDebugBuild) { + VerifyNonSingleImplementation(super_super->GetSuperClass(), + method_index, + method_in_super_super); + } + // No need to go any further. + return; + } else { + super_super = super_super->GetSuperClass(); + } + } + } } void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> klass, - ArtMethod* method) { + ArtMethod* method, + PointerSize pointer_size) { DCHECK(method->IsCopied() || method->GetDeclaringClass() == klass.Get()); if (klass->IsFinal() || method->IsFinal()) { // Final classes or methods do not need CHA for devirtualization. @@ -253,16 +379,21 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> // cannot be inlined. It's not worthwhile to devirtualize the // call which can add a deoptimization point. DCHECK(!method->HasSingleImplementation()); - } else { - method->SetHasSingleImplementation(true); - if (method->IsAbstract()) { - // There is no real implementation yet. - // TODO: implement single-implementation logic for abstract methods. - DCHECK(method->GetSingleImplementation() == nullptr); + } else if (method->IsAbstract()) { + if (method->GetDeclaringClass()->IsInstantiable()) { + // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali). + // Do not attempt to devirtualize it. + method->SetHasSingleImplementation(false); } else { - // Single implementation of non-abstract method is itself. - DCHECK_EQ(method->GetSingleImplementation(), method); + // Abstract method starts with single-implementation flag set and null + // implementation method. + method->SetHasSingleImplementation(true); + DCHECK(method->GetSingleImplementation(pointer_size) == nullptr); } + } else { + method->SetHasSingleImplementation(true); + // Single implementation of non-abstract method is itself. + DCHECK_EQ(method->GetSingleImplementation(pointer_size), method); } } @@ -286,19 +417,29 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { ArtMethod* method_in_super = super_class->GetVTableEntry(i, image_pointer_size); if (method == method_in_super) { // vtable slot entry is inherited from super class. + if (method->IsAbstract() && klass->IsInstantiable()) { + // An instantiable class that inherits an abstract method is treated as + // supplying an implementation that throws AbstractMethodError. + CheckSingleImplementationInfo(klass, + method, + method_in_super, + invalidated_single_impl_methods, + image_pointer_size); + } continue; } - InitSingleImplementationFlag(klass, method); + InitSingleImplementationFlag(klass, method, image_pointer_size); CheckSingleImplementationInfo(klass, method, method_in_super, - invalidated_single_impl_methods); + invalidated_single_impl_methods, + image_pointer_size); } // For new virtual methods that don't override. for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) { ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); - InitSingleImplementationFlag(klass, method); + InitSingleImplementationFlag(klass, method, image_pointer_size); } Runtime* const runtime = Runtime::Current(); @@ -321,6 +462,10 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { continue; } invalidated->SetHasSingleImplementation(false); + if (invalidated->IsAbstract()) { + // Clear the single implementation method. + invalidated->SetSingleImplementation(nullptr, image_pointer_size); + } if (runtime->IsAotCompiler()) { // No need to invalidate any compiled code as the AotCompiler doesn't |