Quickening support.
This CL adds quickening support for methods which are interpreted at runtime.
This CL introduces a DEX-to-DEX compiler. A method is now compiled in one of
the two following modes:
- Native compilation: the method is compiled by the Quick or Portable backends.
At runtime, the generated native target-dependent code is executed.
- DEX-to-DEX compilation: the method is executed by the interpreter at runtime.
Its DEX code is compiled so some instructions can be replaced by special
instructions only valid at runtime. No native code is generated.
The quickening adds special instructions to improve runtime performance. They
are "-quick" versions of the following instructions:
- iget/iput
- iget-wide/iput-wide
- iget-object/iput-object
- invoke-virtual/range.
These special instructions cannot be treated by the verifier since they lose
the field/method index referencing the field/method being accessed/invoked.
To prevent this, the DEX-to-DEX compiler is run only on methods of preverified
classes (without verification error at compilation time).
The DEX-to-DEX compiler implements quickening support using the CompilerDriver
interface like the native compiler does (Quick or Portable backends).
To replace instructions, the DEX-to-DEX compiler must be able to modify the
mmapped DEX file. Since it can be read-only protected, the DEX-to-DEX compiler
must be able to temporarily change its protection to read-write mmapped file.
To achieve this, this CL adds support for changing DEX file protection with
DexFile::EnableWrite and DexFile::DisableWrite methods. Besides, it also adds
a dedicated lock (DexFile::modification_lock) to ensure thread-safety and avoid
concurrent DEX file protection change (from a parallel DEX-to-DEX compiler on
the same DEX file).
Change-Id: Iaafd103b9766810d7fc94a2c424a8fafba66e26a
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index 198d8cb..da64979 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -197,11 +197,23 @@
LOCKS_EXCLUDED(safecast_map_lock_);
// Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
- // to the locks held at 'dex_pc' in 'm'.
+ // to the locks held at 'dex_pc' in method 'm'.
static void FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc,
std::vector<uint32_t>& monitor_enter_dex_pcs)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Returns the accessed field corresponding to the quick instruction's field
+ // offset at 'dex_pc' in method 'm'.
+ static mirror::Field* FindAccessedFieldAtDexPc(mirror::AbstractMethod* m,
+ uint32_t dex_pc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Returns the invoked method corresponding to the quick instruction's vtable
+ // index at 'dex_pc' in method 'm'.
+ static mirror::AbstractMethod* FindInvokedMethodAtDexPc(mirror::AbstractMethod* m,
+ uint32_t dex_pc)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void Shutdown();
@@ -254,6 +266,10 @@
void FindLocksAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void FindAccessedFieldAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void FindInvokedMethodAtDexPc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/*
* Compute the width of the instruction at each address in the instruction stream, and store it in
* insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch
@@ -484,6 +500,16 @@
bool is_primitive, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Perform verification of an iget-quick instruction.
+ void VerifyIGetQuick(const Instruction* inst, const RegType& insn_type,
+ bool is_primitive)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Perform verification of an iput-quick instruction.
+ void VerifyIPutQuick(const Instruction* inst, const RegType& insn_type,
+ bool is_primitive)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Resolves a class based on an index and performs access checks to ensure the referrer can
// access the resolved class.
const RegType& ResolveClassAndCheckAccess(uint32_t class_idx)
@@ -532,6 +558,10 @@
bool is_range, bool is_super)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::AbstractMethod* VerifyInvokeVirtualQuickArgs(const Instruction* inst,
+ bool is_range)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/*
* Verify that the target instruction is not "move-exception". It's important that the only way
* to execute a move-exception is as the first instruction of an exception handler.
@@ -646,11 +676,18 @@
const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class.
// Instruction widths and flags, one entry per code unit.
UniquePtr<InstructionFlags[]> insn_flags_;
- // The dex PC of a FindLocksAtDexPc request, -1 otherwise.
+ // The dex PC of a FindLocksAtDexPc, FindAccessedFieldAtDexPc or
+ // FindInvokedMethodAtDexPc request, -1 otherwise.
uint32_t interesting_dex_pc_;
// The container into which FindLocksAtDexPc should write the registers containing held locks,
// NULL if we're not doing FindLocksAtDexPc.
std::vector<uint32_t>* monitor_enter_dex_pcs_;
+ // The pointer into which FindAccessedFieldAtDexPc should write the accessed field,
+ // NULL if we're not doing FindAccessedFieldAtDexPc.
+ mirror::Field** accessed_field;
+ // The pointer into which FindInvokedMethodAtDexPc should write the invoked method,
+ // NULL if we're not doing FindInvokedMethodAtDexPc.
+ mirror::AbstractMethod** invoked_method;
// The types of any error that occurs.
std::vector<VerifyError> failures_;