summaryrefslogtreecommitdiffstats
path: root/runtime/cha.h
blob: d1a1b7cfaedaa9841e25ea4fbea7166e5d26aa63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
 * 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/mutex.h"
#include "handle.h"
#include "mirror/class.h"
#include "oat_quick_method_header.h"

namespace art {

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.
  typedef std::pair<ArtMethod*, OatQuickMethodHeader*> MethodAndMethodHeaderPair;
  typedef std::vector<MethodAndMethodHeaderPair> ListOfDependentPairs;

  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(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_