summaryrefslogtreecommitdiff
path: root/runtime/interpreter
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/interpreter')
-rw-r--r--runtime/interpreter/interpreter.cc117
-rw-r--r--runtime/interpreter/mterp/mterp.cc214
2 files changed, 129 insertions, 202 deletions
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 67644d66cd..0586ad9e76 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -244,6 +244,30 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind;
static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;
#endif
+static JValue ExecuteSwitch(Thread* self,
+ const CodeItemDataAccessor& accessor,
+ ShadowFrame& shadow_frame,
+ JValue result_register,
+ bool interpret_one_instruction) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ if (shadow_frame.GetMethod()->SkipAccessChecks()) {
+ return ExecuteSwitchImpl<false, true>(
+ self, accessor, shadow_frame, result_register, interpret_one_instruction);
+ } else {
+ return ExecuteSwitchImpl<true, true>(
+ self, accessor, shadow_frame, result_register, interpret_one_instruction);
+ }
+ } else {
+ if (shadow_frame.GetMethod()->SkipAccessChecks()) {
+ return ExecuteSwitchImpl<false, false>(
+ self, accessor, shadow_frame, result_register, interpret_one_instruction);
+ } else {
+ return ExecuteSwitchImpl<true, false>(
+ self, accessor, shadow_frame, result_register, interpret_one_instruction);
+ }
+ }
+}
+
static inline JValue Execute(
Thread* self,
const CodeItemDataAccessor& accessor,
@@ -341,72 +365,39 @@ static inline JValue Execute(
// reduction of template parameters, we gate it behind access-checks mode.
DCHECK(!method->SkipAccessChecks() || !method->MustCountLocks());
- bool transaction_active = Runtime::Current()->IsActiveTransaction();
VLOG(interpreter) << "Interpreting " << method->PrettyMethod();
- if (LIKELY(method->SkipAccessChecks())) {
- // Enter the "without access check" interpreter.
- if (kInterpreterImplKind == kMterpImplKind) {
- if (transaction_active) {
- // No Mterp variant - just use the switch interpreter.
- return ExecuteSwitchImpl<false, true>(self, accessor, shadow_frame, result_register,
- false);
- } else if (UNLIKELY(!Runtime::Current()->IsStarted())) {
- return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
- false);
- } else {
- while (true) {
- // Mterp does not support all instrumentation/debugging.
- if (!self->UseMterp()) {
- return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
- false);
- }
- bool returned = ExecuteMterpImpl(self,
- accessor.Insns(),
- &shadow_frame,
- &result_register);
- if (returned) {
- return result_register;
- } else {
- // Mterp didn't like that instruction. Single-step it with the reference interpreter.
- result_register = ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame,
- result_register, true);
- if (shadow_frame.GetDexPC() == dex::kDexNoIndex) {
- // Single-stepped a return or an exception not handled locally. Return to caller.
- return result_register;
- }
- }
- }
- }
- } else {
- DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
- if (transaction_active) {
- return ExecuteSwitchImpl<false, true>(self, accessor, shadow_frame, result_register,
- false);
- } else {
- return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
- false);
- }
- }
- } else {
- // Enter the "with access check" interpreter.
- if (kInterpreterImplKind == kMterpImplKind) {
- // No access check variants for Mterp. Just use the switch version.
- if (transaction_active) {
- return ExecuteSwitchImpl<true, true>(self, accessor, shadow_frame, result_register,
- false);
- } else {
- return ExecuteSwitchImpl<true, false>(self, accessor, shadow_frame, result_register,
- false);
- }
+ // Note that mterp doesn't support non-compilable methods, nor methods on
+ // which we must count locks.
+ if (kInterpreterImplKind == kSwitchImplKind ||
+ UNLIKELY(!Runtime::Current()->IsStarted()) ||
+ !method->IsCompilable() ||
+ method->MustCountLocks() ||
+ Runtime::Current()->IsActiveTransaction()) {
+ return ExecuteSwitch(
+ self, accessor, shadow_frame, result_register, /*interpret_one_instruction=*/ false);
+ }
+
+ CHECK_EQ(kInterpreterImplKind, kMterpImplKind);
+ while (true) {
+ // Mterp does not support all instrumentation/debugging.
+ if (!self->UseMterp()) {
+ return ExecuteSwitch(
+ self, accessor, shadow_frame, result_register, /*interpret_one_instruction=*/ false);
+ }
+ bool returned = ExecuteMterpImpl(self,
+ accessor.Insns(),
+ &shadow_frame,
+ &result_register);
+ if (returned) {
+ return result_register;
} else {
- DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
- if (transaction_active) {
- return ExecuteSwitchImpl<true, true>(self, accessor, shadow_frame, result_register,
- false);
- } else {
- return ExecuteSwitchImpl<true, false>(self, accessor, shadow_frame, result_register,
- false);
+ // Mterp didn't like that instruction. Single-step it with the reference interpreter.
+ result_register = ExecuteSwitch(
+ self, accessor, shadow_frame, result_register, /*interpret_one_instruction=*/ true);
+ if (shadow_frame.GetDexPC() == dex::kDexNoIndex) {
+ // Single-stepped a return or an exception not handled locally. Return to caller.
+ return result_register;
}
}
}
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 93d9506b86..7a47282295 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -159,61 +159,45 @@ bool CanUseMterp()
(runtime->GetJit() == nullptr || !runtime->GetJit()->JitAtFirstUse());
}
-
-extern "C" size_t MterpInvokeVirtual(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kVirtual, /*is_range=*/ false, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeSuper(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kSuper, /*is_range=*/ false, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeInterface(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kInterface, /*is_range=*/ false, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeDirect(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kDirect, /*is_range=*/ false, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeStatic(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kStatic, /*is_range=*/ false, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
+#define MTERP_INVOKE(Name) \
+extern "C" size_t MterpInvoke##Name(Thread* self, \
+ ShadowFrame* shadow_frame, \
+ uint16_t* dex_pc_ptr, \
+ uint16_t inst_data) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ JValue* result_register = shadow_frame->GetResultRegister(); \
+ const Instruction* inst = Instruction::At(dex_pc_ptr); \
+ if (shadow_frame->GetMethod()->SkipAccessChecks()) { \
+ return DoInvoke<k##Name, /*is_range=*/ false, /*do_access_check=*/ false, /*is_mterp=*/ true>( \
+ self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u; \
+ } else { \
+ return DoInvoke<k##Name, /*is_range=*/ false, /*do_access_check=*/ true, /*is_mterp=*/ true>( \
+ self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u; \
+ } \
+} \
+extern "C" size_t MterpInvoke##Name##Range(Thread* self, \
+ ShadowFrame* shadow_frame, \
+ uint16_t* dex_pc_ptr, \
+ uint16_t inst_data) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ JValue* result_register = shadow_frame->GetResultRegister(); \
+ const Instruction* inst = Instruction::At(dex_pc_ptr); \
+ if (shadow_frame->GetMethod()->SkipAccessChecks()) { \
+ return DoInvoke<k##Name, /*is_range=*/ true, /*do_access_check=*/ false, /*is_mterp=*/ true>( \
+ self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u; \
+ } else { \
+ return DoInvoke<k##Name, /*is_range=*/ true, /*do_access_check=*/ true, /*is_mterp=*/ true>( \
+ self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u; \
+ } \
+}
+
+MTERP_INVOKE(Virtual)
+MTERP_INVOKE(Super)
+MTERP_INVOKE(Interface)
+MTERP_INVOKE(Direct)
+MTERP_INVOKE(Static)
+
+#undef MTERP_INVOKE
extern "C" size_t MterpInvokeCustom(Thread* self,
ShadowFrame* shadow_frame,
@@ -237,61 +221,6 @@ extern "C" size_t MterpInvokePolymorphic(Thread* self,
self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
}
-extern "C" size_t MterpInvokeVirtualRange(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kVirtual, /*is_range=*/ true, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeSuperRange(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kSuper, /*is_range=*/ true, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeInterfaceRange(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kInterface, /*is_range=*/ true, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeDirectRange(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kDirect, /*is_range=*/ true, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
-extern "C" size_t MterpInvokeStaticRange(Thread* self,
- ShadowFrame* shadow_frame,
- uint16_t* dex_pc_ptr,
- uint16_t inst_data)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- JValue* result_register = shadow_frame->GetResultRegister();
- const Instruction* inst = Instruction::At(dex_pc_ptr);
- return DoInvoke<kStatic, /*is_range=*/ true, /*do_access_check=*/ false, /*is_mterp=*/ true>(
- self, *shadow_frame, inst, inst_data, result_register) ? 1u : 0u;
-}
-
extern "C" size_t MterpInvokeCustomRange(Thread* self,
ShadowFrame* shadow_frame,
uint16_t* dex_pc_ptr,
@@ -358,11 +287,12 @@ extern "C" size_t MterpConstClass(uint32_t index,
ShadowFrame* shadow_frame,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(index),
- shadow_frame->GetMethod(),
- self,
- /* can_run_clinit= */ false,
- /* verify_access= */ false);
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(dex::TypeIndex(index),
+ shadow_frame->GetMethod(),
+ self,
+ /* can_run_clinit= */ false,
+ !shadow_frame->GetMethod()->SkipAccessChecks());
if (UNLIKELY(c == nullptr)) {
return 1u;
}
@@ -402,11 +332,12 @@ extern "C" size_t MterpCheckCast(uint32_t index,
art::ArtMethod* method,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(index),
- method,
- self,
- false,
- false);
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(dex::TypeIndex(index),
+ method,
+ self,
+ /* can_run_clinit= */ false,
+ !method->SkipAccessChecks());
if (UNLIKELY(c == nullptr)) {
return 1u;
}
@@ -427,8 +358,8 @@ extern "C" size_t MterpInstanceOf(uint32_t index,
ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(index),
method,
self,
- false,
- false);
+ /* can_run_clinit= */ false,
+ !method->SkipAccessChecks());
if (UNLIKELY(c == nullptr)) {
return 0u; // Caller will check for pending exception. Return value unimportant.
}
@@ -447,11 +378,12 @@ extern "C" size_t MterpNewInstance(ShadowFrame* shadow_frame, Thread* self, uint
REQUIRES_SHARED(Locks::mutator_lock_) {
const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr());
ObjPtr<mirror::Object> obj = nullptr;
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
- shadow_frame->GetMethod(),
- self,
- /* can_run_clinit= */ false,
- /* verify_access= */ false);
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
+ shadow_frame->GetMethod(),
+ self,
+ /* can_run_clinit= */ false,
+ !shadow_frame->GetMethod()->SkipAccessChecks());
if (LIKELY(c != nullptr)) {
if (UNLIKELY(c->IsStringClass())) {
gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
@@ -710,7 +642,7 @@ ALWAYS_INLINE void MterpFieldAccess(Instruction* inst,
}
}
-template<typename PrimType, FindFieldType kAccessType>
+template<typename PrimType, FindFieldType kAccessType, bool do_access_checks>
NO_INLINE bool MterpFieldAccessSlow(Instruction* inst,
uint16_t inst_data,
ShadowFrame* shadow_frame,
@@ -723,7 +655,7 @@ NO_INLINE bool MterpFieldAccessSlow(Instruction* inst,
shadow_frame->SetDexPCPtr(reinterpret_cast<uint16_t*>(inst));
ArtMethod* referrer = shadow_frame->GetMethod();
uint32_t field_idx = kIsStatic ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* field = FindFieldFromCode<kAccessType, /* access_checks= */ false>(
+ ArtField* field = FindFieldFromCode<kAccessType, do_access_checks>(
field_idx, referrer, self, sizeof(PrimType));
if (UNLIKELY(field == nullptr)) {
DCHECK(self->IsExceptionPending());
@@ -742,10 +674,7 @@ NO_INLINE bool MterpFieldAccessSlow(Instruction* inst,
}
// This methods is called from assembly to handle field access instructions.
-//
-// This method is fairly hot. It is long, but it has been carefully optimized.
-// It contains only fully inlined methods -> no spills -> no prologue/epilogue.
-template<typename PrimType, FindFieldType kAccessType>
+template<typename PrimType, FindFieldType kAccessType, bool do_access_checks>
ALWAYS_INLINE bool MterpFieldAccessFast(Instruction* inst,
uint16_t inst_data,
ShadowFrame* shadow_frame,
@@ -764,7 +693,7 @@ ALWAYS_INLINE bool MterpFieldAccessFast(Instruction* inst,
: tls_value;
if (kIsDebugBuild) {
uint32_t field_idx = kIsStatic ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* field = FindFieldFromCode<kAccessType, /* access_checks= */ false>(
+ ArtField* field = FindFieldFromCode<kAccessType, do_access_checks>(
field_idx, shadow_frame->GetMethod(), self, sizeof(PrimType));
DCHECK_EQ(offset, field->GetOffset().SizeValue());
}
@@ -780,7 +709,7 @@ ALWAYS_INLINE bool MterpFieldAccessFast(Instruction* inst,
// This effectively inlines the fast path from ArtMethod::GetDexCache.
ArtMethod* referrer = shadow_frame->GetMethod();
- if (LIKELY(!referrer->IsObsolete())) {
+ if (LIKELY(!referrer->IsObsolete() && !do_access_checks)) {
// Avoid read barriers, since we need only the pointer to the native (non-movable)
// DexCache field array which we can get even through from-space objects.
ObjPtr<mirror::Class> klass = referrer->GetDeclaringClass<kWithoutReadBarrier>();
@@ -793,12 +722,14 @@ ALWAYS_INLINE bool MterpFieldAccessFast(Instruction* inst,
if (LIKELY(field != nullptr)) {
bool visibly_initialized = !kIsStatic || field->GetDeclaringClass()->IsVisiblyInitialized();
if (LIKELY(visibly_initialized)) {
- DCHECK_EQ(field, (FindFieldFromCode<kAccessType, /* access_checks= */ false>(
+ DCHECK_EQ(field, (FindFieldFromCode<kAccessType, do_access_checks>(
field_idx, referrer, self, sizeof(PrimType))));
ObjPtr<mirror::Object> obj = kIsStatic
? field->GetDeclaringClass().Ptr()
: shadow_frame->GetVRegReference(inst->VRegB_22c(inst_data));
- if (LIKELY(kIsStatic || obj != nullptr)) {
+ // We check if nterp is supported as nterp and mterp use the cache in an
+ // incompatible way.
+ if (!IsNterpSupported() && LIKELY(kIsStatic || obj != nullptr)) {
// Only non-volatile fields are allowed in the thread-local cache.
if (LIKELY(!field->IsVolatile())) {
if (kIsStatic) {
@@ -816,13 +747,18 @@ ALWAYS_INLINE bool MterpFieldAccessFast(Instruction* inst,
}
// Slow path. Last and with identical arguments so that it becomes single instruction tail call.
- return MterpFieldAccessSlow<PrimType, kAccessType>(inst, inst_data, shadow_frame, self);
+ return MterpFieldAccessSlow<PrimType, kAccessType, do_access_checks>(
+ inst, inst_data, shadow_frame, self);
}
#define MTERP_FIELD_ACCESSOR(Name, PrimType, AccessType) \
extern "C" bool Name(Instruction* inst, uint16_t inst_data, ShadowFrame* sf, Thread* self) \
REQUIRES_SHARED(Locks::mutator_lock_) { \
- return MterpFieldAccessFast<PrimType, AccessType>(inst, inst_data, sf, self); \
+ if (sf->GetMethod()->SkipAccessChecks()) { \
+ return MterpFieldAccessFast<PrimType, AccessType, false>(inst, inst_data, sf, self); \
+ } else { \
+ return MterpFieldAccessFast<PrimType, AccessType, true>(inst, inst_data, sf, self); \
+ } \
}
#define MTERP_FIELD_ACCESSORS_FOR_TYPE(Sufix, PrimType, Kind) \