Home | History | Annotate | Download | only in runtime
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef ART_RUNTIME_CHA_H_
     18 #define ART_RUNTIME_CHA_H_
     19 
     20 #include "art_method.h"
     21 #include "base/enums.h"
     22 #include "base/mutex.h"
     23 #include "handle.h"
     24 #include "mirror/class.h"
     25 #include "oat_quick_method_header.h"
     26 #include <unordered_map>
     27 #include <unordered_set>
     28 
     29 namespace art {
     30 
     31 /**
     32  * Class Hierarchy Analysis (CHA) tries to devirtualize virtual calls into
     33  * direct calls based on the info generated by analyzing class hierarchies.
     34  * If a class is not subclassed, or even if it's subclassed but one of its
     35  * virtual methods isn't overridden, a virtual call for that method can be
     36  * changed into a direct call.
     37  *
     38  * Each virtual method carries a single-implementation status. The status is
     39  * incrementally maintained at the end of class linking time when method
     40  * overriding takes effect.
     41  *
     42  * Compiler takes advantage of the single-implementation info of a
     43  * method. If a method A has the single-implementation flag set, the compiler
     44  * devirtualizes the virtual call for method A into a direct call, and
     45  * further try to inline the direct call as a result. The compiler will
     46  * also register a dependency that the compiled code depends on the
     47  * assumption that method A has single-implementation status.
     48  *
     49  * When single-implementation info is updated at the end of class linking,
     50  * and if method A's single-implementation status is invalidated, all compiled
     51  * code that depends on the assumption that method A has single-implementation
     52  * status need to be invalidated. Method entrypoints that have this dependency
     53  * will be updated as a result. Method A can later be recompiled with less
     54  * aggressive assumptions.
     55  *
     56  * For live compiled code that's on stack, deoptmization will be initiated
     57  * to force the invalidated compiled code into interpreter mode to guarantee
     58  * correctness. The deoptimization mechanism used is a hybrid of
     59  * synchronous and asynchronous deoptimization. The synchronous deoptimization
     60  * part checks a hidden local variable flag for the method, and if true,
     61  * initiates deoptimization. The asynchronous deoptimization part issues a
     62  * checkpoint that walks the stack and for any compiled code on the stack
     63  * that should be deoptimized, set the hidden local variable value to be true.
     64  *
     65  * A cha_lock_ needs to be held for updating single-implementation status,
     66  * and registering/unregistering CHA dependencies. Registering CHA dependency
     67  * and making compiled code visible also need to be atomic. Otherwise, we
     68  * may miss invalidating CHA dependents or making compiled code visible even
     69  * after it is invalidated. Care needs to be taken between cha_lock_ and
     70  * JitCodeCache::lock_ to guarantee the atomicity.
     71  *
     72  * We base our CHA on dynamically linked class profiles instead of doing static
     73  * analysis. Static analysis can be too aggressive due to dynamic class loading
     74  * at runtime, and too conservative since some classes may not be really loaded
     75  * at runtime.
     76  */
     77 class ClassHierarchyAnalysis {
     78  public:
     79   // Types for recording CHA dependencies.
     80   // For invalidating CHA dependency, we need to know both the ArtMethod and
     81   // the method header. If the ArtMethod has compiled code with the method header
     82   // as the entrypoint, we update the entrypoint to the interpreter bridge.
     83   // We will also deoptimize frames that are currently executing the code of
     84   // the method header.
     85   typedef std::pair<ArtMethod*, OatQuickMethodHeader*> MethodAndMethodHeaderPair;
     86   typedef std::vector<MethodAndMethodHeaderPair> ListOfDependentPairs;
     87 
     88   ClassHierarchyAnalysis() {}
     89 
     90   // Add a dependency that compiled code with `dependent_header` for `dependent_method`
     91   // assumes that virtual `method` has single-implementation.
     92   void AddDependency(ArtMethod* method,
     93                      ArtMethod* dependent_method,
     94                      OatQuickMethodHeader* dependent_header) REQUIRES(Locks::cha_lock_);
     95 
     96   // Return compiled code that assumes that `method` has single-implementation.
     97   std::vector<MethodAndMethodHeaderPair>* GetDependents(ArtMethod* method)
     98       REQUIRES(Locks::cha_lock_);
     99 
    100   // Remove dependency tracking for compiled code that assumes that
    101   // `method` has single-implementation.
    102   void RemoveDependencyFor(ArtMethod* method) REQUIRES(Locks::cha_lock_);
    103 
    104   // Remove from cha_dependency_map_ all entries that contain OatQuickMethodHeader from
    105   // the given `method_headers` set.
    106   // This is used when some compiled code is freed.
    107   void RemoveDependentsWithMethodHeaders(
    108       const std::unordered_set<OatQuickMethodHeader*>& method_headers)
    109       REQUIRES(Locks::cha_lock_);
    110 
    111   // Update CHA info for methods that `klass` overrides, after loading `klass`.
    112   void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
    113 
    114  private:
    115   void InitSingleImplementationFlag(Handle<mirror::Class> klass,
    116                                     ArtMethod* method,
    117                                     PointerSize pointer_size)
    118       REQUIRES_SHARED(Locks::mutator_lock_);
    119 
    120   // Check/update single-implementation info when one virtual method
    121   // overrides another.
    122   // `virtual_method` in `klass` overrides `method_in_super`.
    123   // This may invalidate some assumptions on single-implementation.
    124   // Append methods that should have their single-implementation flag invalidated
    125   // to `invalidated_single_impl_methods`.
    126   void CheckVirtualMethodSingleImplementationInfo(
    127       Handle<mirror::Class> klass,
    128       ArtMethod* virtual_method,
    129       ArtMethod* method_in_super,
    130       std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
    131       PointerSize pointer_size)
    132       REQUIRES_SHARED(Locks::mutator_lock_);
    133 
    134   // Check/update single-implementation info when one method
    135   // implements an interface method.
    136   // `implementation_method` in `klass` implements `interface_method`.
    137   // Append `interface_method` to `invalidated_single_impl_methods`
    138   // if `interface_method` gets a new implementation.
    139   void CheckInterfaceMethodSingleImplementationInfo(
    140       Handle<mirror::Class> klass,
    141       ArtMethod* interface_method,
    142       ArtMethod* implementation_method,
    143       std::unordered_set<ArtMethod*>& invalidated_single_impl_methods,
    144       PointerSize pointer_size)
    145       REQUIRES_SHARED(Locks::mutator_lock_);
    146 
    147   void InvalidateSingleImplementationMethods(
    148       std::unordered_set<ArtMethod*>& invalidated_single_impl_methods)
    149       REQUIRES_SHARED(Locks::mutator_lock_);
    150 
    151   // For all methods in vtable slot at `verify_index` of `verify_class` and its
    152   // superclasses, single-implementation status should be false, except if the
    153   // method is `excluded_method`.
    154   void VerifyNonSingleImplementation(mirror::Class* verify_class,
    155                                      uint16_t verify_index,
    156                                      ArtMethod* excluded_method)
    157       REQUIRES_SHARED(Locks::mutator_lock_);
    158 
    159   // A map that maps a method to a set of compiled code that assumes that method has a
    160   // single implementation, which is used to do CHA-based devirtualization.
    161   std::unordered_map<ArtMethod*, ListOfDependentPairs*> cha_dependency_map_
    162     GUARDED_BY(Locks::cha_lock_);
    163 
    164   DISALLOW_COPY_AND_ASSIGN(ClassHierarchyAnalysis);
    165 };
    166 
    167 }  // namespace art
    168 
    169 #endif  // ART_RUNTIME_CHA_H_
    170