You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
8.2 KiB
180 lines
8.2 KiB
/*
|
|
* 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 "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_
|