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/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index dd96f8d..685ca21 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -470,6 +470,98 @@
}
}
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
+template<bool is_range>
+static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, JValue* result)
+ NO_THREAD_SAFETY_ANALYSIS;
+
+template<bool is_range>
+static void DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, JValue* result) {
+ uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ Object* receiver = shadow_frame.GetVRegReference(vregC);
+ if (UNLIKELY(receiver == NULL)) {
+ // We lost the reference to the method index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return;
+ }
+ uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ AbstractMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx);
+ if (UNLIKELY(method == NULL)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return;
+ }
+ MethodHelper mh(method);
+
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ uint16_t num_regs;
+ uint16_t num_ins;
+ if (code_item != NULL) {
+ num_regs = code_item->registers_size_;
+ num_ins = code_item->ins_size_;
+ } else if (method->IsAbstract()) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->ThrowNewExceptionF(throw_location, "Ljava/lang/AbstractMethodError;",
+ "abstract method \"%s\"", PrettyMethod(method).c_str());
+ return;
+ } else {
+ DCHECK(method->IsNative() || method->IsProxyMethod());
+ num_regs = num_ins = AbstractMethod::NumArgRegisters(mh.GetShorty());
+ if (!method->IsStatic()) {
+ num_regs++;
+ num_ins++;
+ }
+ }
+
+ void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
+ ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame,
+ method, 0, memory));
+ size_t cur_reg = num_regs - num_ins;
+ if (receiver != NULL) {
+ new_shadow_frame->SetVRegReference(cur_reg, receiver);
+ ++cur_reg;
+ }
+
+ size_t arg_offset = (receiver == NULL) ? 0 : 1;
+ const char* shorty = mh.GetShorty();
+ uint32_t arg[5];
+ if (!is_range) {
+ inst->GetArgs(arg);
+ }
+ for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) {
+ DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
+ size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset];
+ switch (shorty[shorty_pos + 1]) {
+ case 'L': {
+ Object* o = shadow_frame.GetVRegReference(arg_pos);
+ new_shadow_frame->SetVRegReference(cur_reg, o);
+ break;
+ }
+ case 'J': case 'D': {
+ uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) |
+ static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos));
+ new_shadow_frame->SetVRegLong(cur_reg, wide_value);
+ cur_reg++;
+ arg_offset++;
+ break;
+ }
+ default:
+ new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos));
+ break;
+ }
+ }
+
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
+ } else {
+ UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins);
+ }
+}
+
// We use template functions to optimize compiler inlining process. Otherwise,
// some parts of the code (like a switch statement) which depend on a constant
// parameter would not be inlined while it should be. These constant parameters
@@ -535,6 +627,41 @@
// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
// specialization.
+template<Primitive::Type field_type>
+static void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst)
+ NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
+
+template<Primitive::Type field_type>
+static inline void DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
+ if (UNLIKELY(obj == NULL)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iget-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c();
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimLong:
+ shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimNot:
+ shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+}
+
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
static void DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
const Instruction* inst)
@@ -591,6 +718,41 @@
}
}
+// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
+// specialization.
+template<Primitive::Type field_type>
+static void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst)
+ NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
+
+template<Primitive::Type field_type>
+static inline void DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
+ if (UNLIKELY(obj == NULL)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iput-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c();
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
+ break;
+ case Primitive::kPrimLong:
+ obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
+ break;
+ case Primitive::kPrimNot:
+ obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+}
+
static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Class* java_lang_string_class = String::GetJavaLangString();
@@ -1770,6 +1932,21 @@
DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
break;
+ case Instruction::IGET_QUICK:
+ PREAMBLE();
+ DoIGetQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IGET_WIDE_QUICK:
+ PREAMBLE();
+ DoIGetQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IGET_OBJECT_QUICK:
+ PREAMBLE();
+ DoIGetQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
case Instruction::SGET_BOOLEAN:
PREAMBLE();
DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
@@ -1840,6 +2017,21 @@
DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
break;
+ case Instruction::IPUT_QUICK:
+ PREAMBLE();
+ DoIPutQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IPUT_WIDE_QUICK:
+ PREAMBLE();
+ DoIPutQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
+ case Instruction::IPUT_OBJECT_QUICK:
+ PREAMBLE();
+ DoIPutQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_2xx);
+ break;
case Instruction::SPUT_BOOLEAN:
PREAMBLE();
DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
@@ -1925,6 +2117,16 @@
DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
break;
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ PREAMBLE();
+ DoInvokeVirtualQuick<false>(self, shadow_frame, inst, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
+ break;
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ PREAMBLE();
+ DoInvokeVirtualQuick<true>(self, shadow_frame, inst, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(Next_3xx);
+ break;
case Instruction::NEG_INT:
PREAMBLE();
shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x()));
@@ -2715,7 +2917,7 @@
inst = inst->Next_2xx();
break;
case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_E3 ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
case Instruction::UNUSED_73:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A: