| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_RUNTIME_CHA_H_ |
| #define ART_RUNTIME_CHA_H_ |
| |
| #include <unordered_map> |
| #include <unordered_set> |
| |
| #include "base/enums.h" |
| #include "base/locks.h" |
| #include "base/macros.h" |
| #include "handle.h" |
| #include "mirror/class.h" |
| #include "oat/oat_quick_method_header.h" |
| |
| namespace art HIDDEN { |
| |
| class ArtMethod; |
| class LinearAlloc; |
| |
| /** |
| * Class Hierarchy Analysis (CHA) tries to devirtualize virtual calls into |
| * direct calls based on the info generated by analyzing class hierarchies. |
| * If a class is not subclassed, or even if it's subclassed but one of its |
| * virtual methods isn't overridden, a virtual call for that method can be |
| * changed into a direct call. |
| * |
| * Each virtual method carries a single-implementation status. The status is |
| * incrementally maintained at the end of class linking time when method |
| * overriding takes effect. |
| * |
| * Compiler takes advantage of the single-implementation info of a |
| * method. If a method A has the single-implementation flag set, the compiler |
| * devirtualizes the virtual call for method A into a direct call, and |
| * further try to inline the direct call as a result. The compiler will |
| * also register a dependency that the compiled code depends on the |
| * assumption that method A has single-implementation status. |
| * |
| * When single-implementation info is updated at the end of class linking, |
| * and if method A's single-implementation status is invalidated, all compiled |
| * code that depends on the assumption that method A has single-implementation |
| * status need to be invalidated. Method entrypoints that have this dependency |
| * will be updated as a result. Method A can later be recompiled with less |
| * aggressive assumptions. |
| * |
| * For live compiled code that's on stack, deoptmization will be initiated |
| * to force the invalidated compiled code into interpreter mode to guarantee |
| * correctness. The deoptimization mechanism used is a hybrid of |
| * synchronous and asynchronous deoptimization. The synchronous deoptimization |
| * part checks a hidden local variable flag for the method, and if true, |
| * initiates deoptimization. The asynchronous deoptimization part issues a |
| * checkpoint that walks the stack and for any compiled code on the stack |
| * that should be deoptimized, set the hidden local variable value to be true. |
| * |
| * A cha_lock_ needs to be held for updating single-implementation status, |
| * and registering/unregistering CHA dependencies. Registering CHA dependency |
| * and making compiled code visible also need to be atomic. Otherwise, we |
| * may miss invalidating CHA dependents or making compiled code visible even |
| * after it is invalidated. Care needs to be taken between cha_lock_ and |
| * JitCodeCache::lock_ to guarantee the atomicity. |
| * |
| * We base our CHA on dynamically linked class profiles instead of doing static |
| * analysis. Static analysis can be too aggressive due to dynamic class loading |
| * at runtime, and too conservative since some classes may not be really loaded |
| * at runtime. |
| */ |
| class ClassHierarchyAnalysis { |
| public: |
| // Types for recording CHA dependencies. |
| // For invalidating CHA dependency, we need to know both the ArtMethod and |
| // the method header. If the ArtMethod has compiled code with the method header |
| // as the entrypoint, we update the entrypoint to the interpreter bridge. |
| // We will also deoptimize frames that are currently executing the code of |
| // the method header. |
| using MethodAndMethodHeaderPair = std::pair<ArtMethod*, OatQuickMethodHeader*>; |
| using ListOfDependentPairs = std::vector<MethodAndMethodHeaderPair>; |
| |
| ClassHierarchyAnalysis() {} |
| |
| // Add a dependency that compiled code with `dependent_header` for `dependent_method` |
| // assumes that virtual `method` has single-implementation. |
| void AddDependency(ArtMethod* method, |
| ArtMethod* dependent_method, |
| OatQuickMethodHeader* dependent_header) REQUIRES(Locks::cha_lock_); |
| |
| // Return compiled code that assumes that `method` has single-implementation. |
| const ListOfDependentPairs& GetDependents(ArtMethod* method) REQUIRES(Locks::cha_lock_); |
| |
| // Remove dependency tracking for compiled code that assumes that |
| // `method` has single-implementation. |
| void RemoveAllDependenciesFor(ArtMethod* method) REQUIRES(Locks::cha_lock_); |
| |
| // Remove from cha_dependency_map_ all entries that contain OatQuickMethodHeader from |
| // the given `method_headers` set. |
| // This is used when some compiled code is freed. |
| void RemoveDependentsWithMethodHeaders( |
| const std::unordered_set<OatQuickMethodHeader*>& method_headers) |
| REQUIRES(Locks::cha_lock_); |
| |
| // If a given class belongs to a linear allocation that is about to be deleted, in all its |
| // superclasses and superinterfaces reset SingleImplementation fields of their methods |
| // that might be affected by the deletion. |
| // The method is intended to be called during GC before ReclaimPhase, since it gets info from |
| // Java objects that are going to be collected. |
| // For the same reason it's important to access objects without read barrier to not revive them. |
| void ResetSingleImplementationInHierarchy(ObjPtr<mirror::Class> klass, |
| const LinearAlloc* alloc, |
| PointerSize pointer_size) |
| const REQUIRES_SHARED(Locks::mutator_lock_); |
| |
| // Update CHA info for methods that `klass` overrides, after loading `klass`. |
| void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); |
| |
| // Remove all of the dependencies for a linear allocator. This is called when dex cache unloading |
| // occurs. |
| void RemoveDependenciesForLinearAlloc(Thread* self, const LinearAlloc* linear_alloc) |
| REQUIRES(!Locks::cha_lock_); |
| |
| private: |
| void InitSingleImplementationFlag(Handle<mirror::Class> klass, |
| ArtMethod* method, |
| PointerSize pointer_size) |
| REQUIRES_SHARED(Locks::mutator_lock_); |
| |
| // Check/update single-implementation info when one virtual method |
| // overrides another. |
| // `virtual_method` in `klass` overrides `method_in_super`. |
| // This may invalidate some assumptions on single-implementation. |
| // Append methods that should have their single-implementation flag invalidated |
| // to `invalidated_single_impl_methods`. |
| void CheckVirtualMethodSingleImplementationInfo( |
| Handle<mirror::Class> klass, |
| ArtMethod* virtual_method, |
| ArtMethod* method_in_super, |
| std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, |
| PointerSize pointer_size) |
| REQUIRES_SHARED(Locks::mutator_lock_); |
| |
| // Check/update single-implementation info when one method |
| // implements an interface method. |
| // `implementation_method` in `klass` implements `interface_method`. |
| // Append `interface_method` to `invalidated_single_impl_methods` |
| // if `interface_method` gets a new implementation. |
| void CheckInterfaceMethodSingleImplementationInfo( |
| Handle<mirror::Class> klass, |
| ArtMethod* interface_method, |
| ArtMethod* implementation_method, |
| std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, |
| PointerSize pointer_size) |
| REQUIRES_SHARED(Locks::mutator_lock_); |
| |
| void InvalidateSingleImplementationMethods( |
| std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) |
| REQUIRES_SHARED(Locks::mutator_lock_); |
| |
| // A map that maps a method to a set of compiled code that assumes that method has a |
| // single implementation, which is used to do CHA-based devirtualization. |
| std::unordered_map<ArtMethod*, ListOfDependentPairs> cha_dependency_map_ |
| GUARDED_BY(Locks::cha_lock_); |
| |
| DISALLOW_COPY_AND_ASSIGN(ClassHierarchyAnalysis); |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_RUNTIME_CHA_H_ |