Art 虚拟机是如何执行java指令的?严格来说, Art 虚拟机所执行的应该是dex 指令或对应的机器码,我们暂且将这些指令统称为java指令。
笼统而言, ART 虚拟机执行java指令的有两种方式:1. 解释执行,2.执行编译后得到的机器码;普遍情况下,这两种执行方式并不是泾渭分明、独立行动的,而是以一种穿插的方式运行
- 一个方法以解释方式执行,但其内部调用的某个方法却存在机器码。所以执行到这个内部调用指令时,原来的解释运行将切换到机器码方式运行;反之同理
- 方法没有在dex2oat的阶段被编译,但是随着多次运行,满足JIT的条件而编译得到了对应的机器码,那么,这个方法在JIT之前应该以解释方式执行,编译成机器码后又以机器码方式执行
- ART 虚拟机在将dex转为机器码的过程中,会进行一些优化【optimizing compiler阶段】过程中添加一些HDeoptimize IR指令,这些 HDeoptimize IR 指令最终将生成一些机器码,ART虚拟机执行的时候遇到这些HDeoptimize IR机器码将从机器码执行模式跳转到解释执行模式。
- 如果虚拟机进入了调试状态,则必须使用解释模式执行。
在介绍解释执行之前,我们先介绍下ART中的一些基础知识
-
java 源码中定义的一个方法将转换成 ART 虚拟机中的一个 ArtMethod 对象,ArtMethod 中有两个和机器码入口地址相关的成员变量
-
linkcode 阶段,如果java方法已经被oat过了,则ArtMethod 中还会通过一个变量存储 OatMethod的相关地址
-
oat 文件里的 Trampoline code 为间接跳转代码,目标是跳转到直接到直接跳转代码
-
classLinker阶段的getEntrypointFromQuickCompiledCode 可以获取到机器码入口地址
-
如果一个方法采取解释执行,其 artMethod 对象的机器码入口地址将指向一处跳转代码 art_quick_to_interpreter_bridge
art_quick_to_interpreter_bridge
art_quick_to_interpreter_bridge 是一段汇编代码函数,它的跳转目标函数是 artQuickToInterpreterBridge
/*
* Called to bridge from the quick to interpreter ABI. On entry the arguments match those
* of a quick call:
* x0 = method being called/to bridge to.
* x1..x7, d0..d7 = arguments to that method.
*/
ENTRY art_quick_to_interpreter_bridge
SETUP_SAVE_REFS_AND_ARGS_FRAME // Set up frame and save arguments.
// x0 will contain mirror::ArtMethod* method.
mov x1, xSELF // How to get Thread::Current() ???
mov x2, sp // 将栈指针的值存储到寄存器 x2 中 ,sp 对应的是 ArtMethod
// uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Thread* self,
// mirror::ArtMethod** sp)
bl artQuickToInterpreterBridge // bl 是函数调用指令
RESTORE_SAVE_REFS_AND_ARGS_FRAME // TODO: no need to restore arguments in this case.
REFRESH_MARKING_REGISTER
fmov d0, x0 // 将浮点寄存器 x0 中的值移动到浮点寄存器 d0 中
RETURN_OR_DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
artQuickToInterpreterBridge
extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Ensure we don't get thread suspension until the object arguments are safely in the shadow
// frame. 确保在执行线程暂停时,对象参数已经安全地保存在了shadow frame中
ScopedQuickEntrypointChecks sqec(self);
if (UNLIKELY(!method->IsInvokable())) {
method->ThrowInvocationTimeError();
return 0;
}
JValue tmp_value;
// PopStackedShadowFrame 和 thread 对堆栈的管理有关,假设是从机器码跳到解释执行,且不是HDeoptimize情况,deopt_frame 返回 nullptr
ShadowFrame* deopt_frame = self->PopStackedShadowFrame(
StackedShadowFrameType::kDeoptimizationShadowFrame, false);
// 构造一个 ManagedStack 对象,顾名思义,用作栈管理,很重要
ManagedStack fragment;
DCHECK(!method->IsNative()) << method->PrettyMethod();
uint32_t shorty_len = 0;
// 如果不是代理对象,non_proxy_method 就是 ArtMethod 本身
ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod();
CodeItemDataAccessor accessor(non_proxy_method->DexInstructionData());
// shorty 对应方法的简短描述
const char* shorty = non_proxy_method->GetShorty(&shorty_len);
// result 存储方法调用返回值
JValue result;
bool force_frame_pop = false;
if (UNLIKELY(deopt_frame != nullptr)) {
// 和 HDeoptimize 有关,暂不关注;HDeoptimize 是dex2oat阶段进行CFG优化时候可能插入的指令
HandleDeoptimization(&result, method, deopt_frame, &fragment);
} else {
const char* old_cause = self->StartAssertNoThreadSuspension(
"Building interpreter shadow frame");
uint16_t num_regs = accessor.RegistersSize();
// No last shadow coming from quick.
// 创建代表ArtMethod 的栈帧对象ShadowFrame,用于在方法调用期间跟踪和保存当前方法的状态。
// 它通常用于表示当前方法的局部变量、操作数栈和返回值,以及与方法调用相关的其他信息
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
CREATE_SHADOW_FRAME(num_regs, /* link= */ nullptr, method, /* dex_pc= */ 0);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
size_t first_arg_reg = accessor.RegistersSize() - accessor.InsSize();
// 借助 BuildQuickShadowFrameVisitor 将调用参数放到 shadow_frame 对象中去
BuildQuickShadowFrameVisitor shadow_frame_builder(sp, method->IsStatic(), shorty, shorty_len,
shadow_frame, first_arg_reg);
shadow_frame_builder.VisitArguments();
// 判断 ArtMethod 所属的类是否已经初始化
const bool needs_initialization =
method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
// Push a transition back into managed code onto the linked list in thread.
// 将 fragment 和 shadow_frame 放到 Thread 类对应的成员变量中去处理
self->PushManagedStackFragment(&fragment);
self->PushShadowFrame(shadow_frame);
self->EndAssertNoThreadSuspension(old_cause);
// 如果 ArtMethod 类没有初始化,则先初始化,初始化是通过 ClassLinker 的 EnsureInitialized 进行的
if (needs_initialization) {
// Ensure static method's class is initialized.
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(shadow_frame->GetMethod()->GetDeclaringClass()));
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
DCHECK(Thread::Current()->IsExceptionPending())
<< shadow_frame->GetMethod()->PrettyMethod();
self->PopManagedStackFragment(fragment);
return 0;
}
}
// 解释执行的入口函数
result = interpreter::EnterInterpreterFromEntryPoint(self, accessor, shadow_frame);
force_frame_pop = shadow_frame->GetForcePopFrame();
}
// Pop transition. 和 Thread 对栈的管理有关
self->PopManagedStackFragment(fragment);
// Request a stack deoptimization if needed 根据sp的位置找方法的调用者
ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
uintptr_t caller_pc = QuickArgumentVisitor::GetCallingPc(sp);
// If caller_pc is the instrumentation exit stub, the stub will check to see if deoptimization
// should be done and it knows the real return pc. NB If the upcall is null we don't need to do
// anything. This can happen during shutdown or early startup.
// 下面是与机器码优化有关的一些逻辑处理,暂不用考虑
if (UNLIKELY(
caller != nullptr &&
caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) &&
(self->IsForceInterpreter() || Dbg::IsForcedInterpreterNeededForUpcall(self, caller)))) {
if (!Runtime::Current()->IsAsyncDeoptimizeable(caller_pc)) {
LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
<< caller->PrettyMethod();
} else {
VLOG(deopt) << "Forcing deoptimization on return from method " << method->PrettyMethod()
<< " to " << caller->PrettyMethod()
<< (force_frame_pop ? " for frame-pop" : "");
DCHECK(!force_frame_pop || result.GetJ() == 0) << "Force frame pop should have no result.";
if (force_frame_pop && self->GetException() != nullptr) {
LOG(WARNING) << "Suppressing exception for instruction-retry: "
<< self->GetException()->Dump();
}
// Push the context of the deoptimization stack so we can restore the return value and the
// exception before executing the deoptimized frames.
// 和 HDeoptimize 有关
self->PushDeoptimizationContext(
result,
shorty[0] == 'L' || shorty[0] == '[', /* class or array */
force_frame_pop ? nullptr : self->GetException(),
/* from_code= */ false,
DeoptimizationMethodType::kDefault);
// Set special exception to cause deoptimization.
self->SetException(Thread::GetDeoptimizationException());
}
}
// No need to restore the args since the method has already been run by the interpreter.
return result.GetJ();
}
artQuickToInterpreterBridge 主要做了两件事
- 构造 shadowframe 对象,在 ART 虚拟机中,ShadowFrame 通常与解释器(interpreter)和即时编译器(just-in-time compiler)等组件一起使用,用于在运行时管理方法调用的状态。它有助于在方法执行期间对当前执行状态进行跟踪和管理,以确保正确的方法调用和执行。
- 进入 EnterInterpreterFromEntrypoint,这是解析执行模式的核心函数
EnterInterpreterFromEntrypoint
JValue EnterInterpreterFromEntryPoint(Thread* self, const CodeItemDataAccessor& accessor,
ShadowFrame* shadow_frame) {
DCHECK_EQ(self, Thread::Current());
bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return JValue();
}
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
// 和 JIT 有关,暂不关注
jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod());
}
return Execute(self, accessor, *shadow_frame, JValue());
}
最终执行到了 Execute 函数上,Execute 函数不同版本实现有所差异,但是整体功能保持一致
Android 12
static inline JValue Execute(
Thread* self,
const CodeItemDataAccessor& accessor,
ShadowFrame& shadow_frame,
JValue result_register,
bool stay_in_interpreter = false, // 是否强制使用解释执行模式
bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
// Check that we are using the right interpreter.
if (kIsDebugBuild && self->UseMterp() != CanUseMterp()) {
// The flag might be currently being updated on all threads. Retry with lock.
MutexLock tll_mu(self, *Locks::thread_list_lock_);
DCHECK_EQ(self->UseMterp(), CanUseMterp());
}
// from_deoptimize 默认为false
if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization.
// 如果是 调试模式,必然是以解释模式执行,shadow_frame 的dex_pc 必然要为0
if (kIsDebugBuild) {
CHECK_EQ(shadow_frame.GetDexPC(), 0u);
self->AssertNoPendingException();
}
// 如果是HDeoptimizion 的情况,shadow_frame 的dex_pc 不是0,如果 dex_pc 为0,则表示该方法一开始就是以解释方式执行,这种情况称为纯解释执行,此时就需要检测是否存在JIT的情况
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
ArtMethod *method = shadow_frame.GetMethod();
if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
instrumentation->MethodEnterEvent(self,
shadow_frame.GetThisObject(accessor.InsSize()),
method,
0);
if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
// The caller will retry this invoke. Just return immediately without any value.
DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
DCHECK(PrevFrameWillRetry(self, shadow_frame));
return JValue();
}
if (UNLIKELY(self->IsExceptionPending())) {
instrumentation->MethodUnwindEvent(self,
shadow_frame.GetThisObject(accessor.InsSize()),
method,
0);
return JValue();
}
}
// 这里判断这个需要纯解释执行的方法是否经过 JIT 编译了,对已经JIT过的代码通过 ArtInterpreterToCompiledCodeBridge 桥接到 jit 编译的机器码去执行
if (!stay_in_interpreter && !self->IsForceInterpreter()) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
jit->MethodEntered(self, shadow_frame.GetMethod());
if (jit->CanInvokeCompiledCode(method)) {
JValue result;
// Pop the shadow frame before calling into compiled code.
self->PopShadowFrame();
// Calculate the offset of the first input reg. The input registers are in the high regs.
// It's ok to access the code item here since JIT code will have been touched by the
// interpreter and compiler already.
uint16_t arg_offset = accessor.RegistersSize() - accessor.InsSize();
ArtInterpreterToCompiledCodeBridge(self, nullptr, &shadow_frame, arg_offset, &result);
// Push the shadow frame back as the caller will expect it.
self->PushShadowFrame(&shadow_frame);
return result;
}
}
}
}
// 下面的是解释执行的逻辑
ArtMethod* method = shadow_frame.GetMethod();
DCheckStaticState(self, method);
// Lock counting is a special version of accessibility checks, and for simplicity and
// reduction of template parameters, we gate it behind access-checks mode.
DCHECK(!method->SkipAccessChecks() || !method->MustCountLocks());
// transaction_active 和 dex2oat的编译逻辑有关,完整虚拟机运行的时候返回false
bool transaction_active = Runtime::Current()->IsActiveTransaction();
// SkipAccessChecks 判断是否有权限执行本方法,大部分情况下都是 true
if (LIKELY(method->SkipAccessChecks())) {
// Enter the "without access check" interpreter.
// 在 art 虚拟机中,解释执行的方式由 kInterpreterImplKind 的取值来控制
// 低版本的有3种实现,分别是 kMterpImplKind、kSwitchImplKind 和 kComputedGotoImplKind;android 8之后删除了 kComputedGotoImplKind
if (kInterpreterImplKind == kMterpImplKind) {
// kMterpImplKind 的实现在 android 13之前是通过 MterpImpl 实现的,android 13 废弃了Mterp 改由 Nterp 实现
if (transaction_active) {
// No Mterp variant - just use the switch interpreter.
// 大部分情况下走不到,没有 Mterp ,降级到 Switch
return ExecuteSwitchImpl<false, true>(self, accessor, shadow_frame, result_register,
false);
} else if (UNLIKELY(!Runtime::Current()->IsStarted())) {
// 针对 dex2oat的情况
return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
false);
} else {
while (true) {
// Mterp does not support all instrumentation/debugging.
// 禁用掉了 Mterp ,降级到 Switch
if (!self->UseMterp()) {
return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
false);
}
// ExecuteMterpImpl 函数的定义由汇编代码实现
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.
// Mterp 无法处理的指令, 降级到 Switch 解释器
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.
//
// The boot classpath should really not have to run access checks.
DCHECK(method->GetDeclaringClass()->GetClassLoader() != nullptr
|| Runtime::Current()->IsVerificationSoftFail()
|| Runtime::Current()->IsAotCompiler())
<< method->PrettyMethod();
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);
}
} 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);
}
}
}
}
在 art 虚拟机中,解释执行的方式由 kInterpreterImplKind 的取值来控制低版本的有3种实现,分别是 kMterpImplKind、kSwitchImplKind 和 kComputedGotoImplKind;android 8之后删除了 kComputedGotoImplKind
- ExecuteMterpImpl 基于汇编代码编写,基于goto逻辑实现,android13之前的实现是Mterp,android 11 引入了Nterp,android 13删除了Mterp,Nterp全面转正;Nterp省去了ManagedStack的栈维护,采用了和Native方法一样的栈帧结构,并且译码和翻译执行全程都由汇编代码实现,进一步拉进解释器和compiled code的性能差距
- ExecuteSwitchImpl c 编写,基于switch/case逻辑实现
- ExecutGotoImpl C实现,基于 goto 逻辑实现,不支持clang编译,android 8.0 废弃
Android 14
NO_STACK_PROTECTOR
static inline JValue Execute(
Thread* self,
const CodeItemDataAccessor& accessor,
ShadowFrame& shadow_frame,
JValue result_register,
bool stay_in_interpreter = false,
bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
// We cache the result of NeedsDexPcEvents in the shadow frame so we don't need to call
// NeedsDexPcEvents on every instruction for better performance. NeedsDexPcEvents only gets
// updated asynchronoulsy in a SuspendAll scope and any existing shadow frames are updated with
// new value. So it is safe to cache it here.
shadow_frame.SetNotifyDexPcMoveEvents(
Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(shadow_frame.GetMethod(), self));
if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization.
if (kIsDebugBuild) {
CHECK_EQ(shadow_frame.GetDexPC(), 0u);
self->AssertNoPendingException();
}
ArtMethod *method = shadow_frame.GetMethod();
// If we can continue in JIT and have JITed code available execute JITed code.
if (!stay_in_interpreter && !self->IsForceInterpreter() && !shadow_frame.GetForcePopFrame()) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
jit->MethodEntered(self, shadow_frame.GetMethod());
if (jit->CanInvokeCompiledCode(method)) {
JValue result;
// Pop the shadow frame before calling into compiled code.
self->PopShadowFrame();
// Calculate the offset of the first input reg. The input registers are in the high regs.
// It's ok to access the code item here since JIT code will have been touched by the
// interpreter and compiler already.
uint16_t arg_offset = accessor.RegistersSize() - accessor.InsSize();
ArtInterpreterToCompiledCodeBridge(self, nullptr, &shadow_frame, arg_offset, &result);
// Push the shadow frame back as the caller will expect it.
self->PushShadowFrame(&shadow_frame);
return result;
}
}
}
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (UNLIKELY(instrumentation->HasMethodEntryListeners() || shadow_frame.GetForcePopFrame())) {
instrumentation->MethodEnterEvent(self, method);
if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
// The caller will retry this invoke or ignore the result. Just return immediately without
// any value.
DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
JValue ret = JValue();
PerformNonStandardReturn(self,
shadow_frame,
ret,
instrumentation,
accessor.InsSize(),
/* unlock_monitors= */ false);
return ret;
}
if (UNLIKELY(self->IsExceptionPending())) {
instrumentation->MethodUnwindEvent(self,
method,
0);
JValue ret = JValue();
if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
PerformNonStandardReturn(self,
shadow_frame,
ret,
instrumentation,
accessor.InsSize(),
/* unlock_monitors= */ false);
}
return ret;
}
}
}
ArtMethod* method = shadow_frame.GetMethod();
DCheckStaticState(self, method);
// Lock counting is a special version of accessibility checks, and for simplicity and
// reduction of template parameters, we gate it behind access-checks mode.
DCHECK_IMPLIES(method->SkipAccessChecks(), !method->MustCountLocks());
VLOG(interpreter) << "Interpreting " << method->PrettyMethod();
return ExecuteSwitch(
self, accessor, shadow_frame, result_register, /*interpret_one_instruction=*/ false);
}
ExecuteSwitchImpl
template<bool transaction_active>
ALWAYS_INLINE JValue ExecuteSwitchImpl(Thread* self,
const CodeItemDataAccessor& accessor,
ShadowFrame& shadow_frame,
JValue result_register,
bool interpret_one_instruction)
REQUIRES_SHARED(Locks::mutator_lock_) {
SwitchImplContext ctx {
.self = self,
.accessor = accessor,
.shadow_frame = shadow_frame,
.result_register = result_register,
.interpret_one_instruction = interpret_one_instruction,
.result = JValue(),
};
void* impl = reinterpret_cast<void*>(&ExecuteSwitchImplCpp<transaction_active>);
const uint16_t* dex_pc = ctx.accessor.Insns();
ExecuteSwitchImplAsm(&ctx, impl, dex_pc);
return ctx.result;
}
ExecuteSwitchImplAsm 函数的定义由汇编代码实现,逐个调用 ExecuteSwitchImplCpp 实现
// Wrap ExecuteSwitchImpl in assembly method which specifies DEX PC for unwinding.
// Argument 0: x0: The context pointer for ExecuteSwitchImpl.
// Argument 1: x1: Pointer to the templated ExecuteSwitchImpl to call.
// Argument 2: x2: The value of DEX PC (memory address of the methods bytecode).
ENTRY ExecuteSwitchImplAsm
SAVE_TWO_REGS_INCREASE_FRAME x19, xLR, 16
mov x19, x2 // x19 = DEX PC
CFI_DEFINE_DEX_PC_WITH_OFFSET(0 /* x0 */, 19 /* x19 */, 0)
blr x1 // Call the wrapped method.
RESTORE_TWO_REGS_DECREASE_FRAME x19, xLR, 16
ret
END ExecuteSwitchImplAsm
ExecuteSwitchImpl 获取了下 dex_pc 的指令数组,调用了ExecuteSwitchImplAsm 汇编去执行解释,解释的功能实现主要是在ExecuteSwitchImplCpp中的,先看下ExecuteSwitchImplCpp 这个方法的实现
ExecuteSwitchImplCpp
template<bool transaction_active>
NO_STACK_PROTECTOR
void ExecuteSwitchImplCpp(SwitchImplContext* ctx) {
Thread* self = ctx->self;
const CodeItemDataAccessor& accessor = ctx->accessor;
// 构建 ShadowFrame
ShadowFrame& shadow_frame = ctx->shadow_frame;
self->VerifyStack();
// dex_pc 指向要执行的 dex 指令
uint32_t dex_pc = shadow_frame.GetDexPC();
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
// insns 代表方法的指令数组
const uint16_t* const insns = accessor.Insns();
const Instruction* next = Instruction::At(insns + dex_pc);
DCHECK(!shadow_frame.GetForceRetryInstruction())
<< "Entered interpreter from invoke without retry instruction being handled!";
bool const interpret_one_instruction = ctx->interpret_one_instruction;
while (true) {
const Instruction* const inst = next;
dex_pc = inst->GetDexPc(insns);
shadow_frame.SetDexPC(dex_pc);
TraceExecution(shadow_frame, inst, dex_pc);
// 遍历方法的 dex 指令数组
uint16_t inst_data = inst->Fetch16(0);
bool exit = false;
bool success; // Moved outside to keep frames small under asan.
// 这离调用 InstructionHandler 的 Preamble 方法不断执行 OPCODE,这里的 OPCODE 定义在 dex_instruction_list.h
if (InstructionHandler<transaction_active, Instruction::kInvalidFormat>(
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit).
Preamble()) { // invoke
DCHECK_EQ(self->IsExceptionPending(), inst->Opcode(inst_data) == Instruction::MOVE_EXCEPTION);
// 借助 switch/case 针对每一种dex指令进行处理
switch (inst->Opcode(inst_data)) {
# define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v) \
case OPCODE: {
// next 是 Instruction 类的成员函数,用于跳过本指令参数,使之指向下个指令开头 \
next = inst->RelativeAt(Instruction::SizeInCodeUnits(Instruction::FORMAT)); \
success = OP_##OPCODE_NAME<transaction_active>( \
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit); \
if (success && LIKELY(!interpret_one_instruction)) { \
continue; \
} \
break; \
}
DEX_INSTRUCTION_LIST(OPCODE_CASE)
# undef OPCODE_CASE
}
}
// 记录dex指令执行的位置并且更新到 shadow_frame 中
if (exit) {
shadow_frame.SetDexPC(dex::kDexNoIndex);
return; // Return statement or debugger forced exit.
}
if (self->IsExceptionPending()) {
if (!InstructionHandler<transaction_active, Instruction::kInvalidFormat>(
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit).
HandlePendingException()) {
shadow_frame.SetDexPC(dex::kDexNoIndex);
return; // Locally unhandled exception - return to caller.
}
// Continue execution in the catch block.
}
if (interpret_one_instruction) {
shadow_frame.SetDexPC(next->GetDexPc(insns)); // Record where we stopped.
ctx->result = ctx->result_register;
return;
}
}
} // NOLINT(readability/fn_size)
} // namespace interpreter
} // namespace art
ExecuteSwitchImplCpp 处理中,一种dex 指令对应switch的一种case,记录dex指令执行的位置并且更新到 shadow_frame 中,核心函数是 OP_##OPCODE_NAME,它会构造一个 InstructionHandler 对象,并且调用它的OPCODE_NAME, 对于每个dex指令,InstructionHandler 都定义了对应的实现,这样就代理到了真实的实现方法上去了。
#define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v) \
template<bool transaction_active> \
ASAN_NO_INLINE NO_STACK_PROTECTOR static bool OP_##OPCODE_NAME( \
SwitchImplContext* ctx, \
const instrumentation::Instrumentation* instrumentation, \
Thread* self, \
ShadowFrame& shadow_frame, \
uint16_t dex_pc, \
const Instruction* inst, \
uint16_t inst_data, \
const Instruction*& next, \
bool& exit) REQUIRES_SHARED(Locks::mutator_lock_) { \
InstructionHandler<transaction_active, Instruction::FORMAT> handler( \
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, next, exit); \
return LIKELY(handler.OPCODE_NAME()); \
}
InstructionHandler中对dex 的 273个指令都一一对应来一个方法实现,以 invoke 指令对应的实现来看
HANDLER_ATTRIBUTES bool INVOKE_VIRTUAL() {
return HandleInvoke<kVirtual, /*is_range=*/ false>();
}
template<InvokeType type, bool is_range>
HANDLER_ATTRIBUTES bool HandleInvoke() {
bool success = DoInvoke<type, is_range>(
Self(), shadow_frame_, inst_, inst_data_, ResultRegister());
return PossiblyHandlePendingExceptionOnInvoke(!success);
}
简而言之,ExecuteSwitchImpl 的处理逻辑就是对 dex 中的每一种指令对应一种逻辑中的case。高版本和低版本只是实现上的代码写法有些许差异,思路都是一样。我们用android 9.0的源码可以更清晰的理解这一点。
template<bool do_access_check, bool transaction_active>
void ExecuteSwitchImplCpp(SwitchImplContext* ctx) {
Thread* self = ctx->self;
const CodeItemDataAccessor& accessor = ctx->accessor;
ShadowFrame& shadow_frame = ctx->shadow_frame;
JValue result_register = ctx->result_register;
bool interpret_one_instruction = ctx->interpret_one_instruction;
constexpr bool do_assignability_check = do_access_check;
if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
LOG(FATAL) << "Invalid shadow frame for interpreter use";
ctx->result = JValue();
return;
}
self->VerifyStack();
uint32_t dex_pc = shadow_frame.GetDexPC();
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
const uint16_t* const insns = accessor.Insns();
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data;
jit::Jit* jit = Runtime::Current()->GetJit();
do {
dex_pc = inst->GetDexPc(insns);
shadow_frame.SetDexPC(dex_pc);
TraceExecution(shadow_frame, inst, dex_pc);
inst_data = inst->Fetch16(0);
switch (inst->Opcode(inst_data)) {
case Instruction::NOP:
PREAMBLE();
inst = inst->Next_1xx();
break;
case Instruction::MOVE:
PREAMBLE();
shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
inst = inst->Next_1xx();
break;
case Instruction::MOVE_FROM16:
PREAMBLE();
shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
shadow_frame.GetVReg(inst->VRegB_22x()));
inst = inst->Next_2xx();
break;
case Instruction::MOVE_16:
PREAMBLE();
shadow_frame.SetVReg(inst->VRegA_32x(),
shadow_frame.GetVReg(inst->VRegB_32x()));
inst = inst->Next_3xx();
break;
case Instruction::MOVE_WIDE:
PREAMBLE();
shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
inst = inst->Next_1xx();
break;
case Instruction::MOVE_WIDE_FROM16:
PREAMBLE();
shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
......
}
} while (!interpret_one_instruction);
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
ctx->result = result_register;
return;
} // NOLINT(readability/fn_size)
函数调用处理函数 DoInvoke
// Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
// Returns true on success, otherwise throws an exception and returns false.
template<InvokeType type, bool is_range>
static ALWAYS_INLINE bool DoInvoke(Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
uint16_t inst_data,
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Make sure to check for async exceptions before anything else.
if (UNLIKELY(self->ObserveAsyncException())) {
return false;
}
const uint32_t vregC = is_range ? inst->VRegC_3rc() : inst->VRegC_35c();
ObjPtr<mirror::Object> obj = type == kStatic ? nullptr : shadow_frame.GetVRegReference(vregC);
ArtMethod* sf_method = shadow_frame.GetMethod();
bool string_init = false;
// FindMethodToCall 是根据不同调用类型(kStatic、kDirect、kVirtual、kSupper、kinterface)以找到对应 Artmethod 的关键代码
ArtMethod* called_method = FindMethodToCall<type>(
self, sf_method, &obj, *inst, /* only_lookup_tls_cache= */ false, &string_init);
if (called_method == nullptr) {
DCHECK(self->IsExceptionPending());
result->SetJ(0);
return false;
}
return DoCall<is_range>(
called_method, self, shadow_frame, inst, inst_data, string_init, result);
}
先通过 FindMethodToCall 找到对应 ArtMethod 对象,然后调用 DoCall 方法
template<bool is_range>
NO_STACK_PROTECTOR
bool DoCall(ArtMethod* called_method,
Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
uint16_t inst_data,
bool is_string_init,
JValue* result) {
// Argument word count.
const uint16_t number_of_inputs =
(is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
// TODO: find a cleaner way to separate non-range and range information without duplicating
// code. kMaxVarArgRegs 为编译常量
uint32_t arg[Instruction::kMaxVarArgRegs] = {}; // only used in invoke-XXX.
uint32_t vregC = 0;
if (is_range) {
vregC = inst->VRegC_3rc();
} else {
vregC = inst->VRegC_35c();
inst->GetVarArgs(arg, inst_data);
}
return DoCallCommon<is_range>(
called_method,
self,
shadow_frame,
result,
number_of_inputs,
arg,
vregC,
is_string_init);
}
DoCall 接着调用 DoCallCommon 这个函数,继续看这个DoCallCommon函数
template <bool is_range>
static inline bool DoCallCommon(ArtMethod* called_method,
Thread* self,
ShadowFrame& shadow_frame,
JValue* result,
uint16_t number_of_inputs,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
uint32_t vregC,
bool string_init) {
// Compute method information.
CodeItemDataAccessor accessor(called_method->DexInstructionData());
// Number of registers for the callee's call frame.
uint16_t num_regs;
// Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
// PerformCall. A deoptimization could occur at any time, and we shouldn't change which
// entrypoint to use once we start building the shadow frame.
// use_interpreter_entrypoint 判断是否需要保存使用 Switch 解释器模式执行
const bool use_interpreter_entrypoint = ShouldStayInSwitchInterpreter(called_method);
if (LIKELY(accessor.HasCodeItem())) {
// When transitioning to compiled code, space only needs to be reserved for the input registers.
// The rest of the frame gets discarded. This also prevents accessing the called method's code
// item, saving memory by keeping code items of compiled code untouched.
if (!use_interpreter_entrypoint) {
DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint";
num_regs = number_of_inputs;
} else {
num_regs = accessor.RegistersSize();
DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, accessor.InsSize());
}
} else {
DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
num_regs = number_of_inputs;
}
// Hack for String init:
//
// Rewrite invoke-x java.lang.String.<init>(this, a, b, c, ...) into:
// invoke-x StringFactory(a, b, c, ...)
// by effectively dropping the first virtual register from the invoke.
//
// (at this point the ArtMethod has already been replaced,
// so we just need to fix-up the arguments)
//
// Note that FindMethodFromCode in entrypoint_utils-inl.h was also special-cased
// to handle the compiler optimization of replacing `this` with null without
// throwing NullPointerException.
uint32_t string_init_vreg_this = is_range ? vregC : arg[0];
if (UNLIKELY(string_init)) {
DCHECK_GT(num_regs, 0u); // As the method is an instance method, there should be at least 1.
// The new StringFactory call is static and has one fewer argument.
if (!accessor.HasCodeItem()) {
DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
num_regs--;
} // else ... don't need to change num_regs since it comes up from the string_init's code item
number_of_inputs--;
// Rewrite the var-args, dropping the 0th argument ("this")
for (uint32_t i = 1; i < arraysize(arg); ++i) {
arg[i - 1] = arg[i];
}
arg[arraysize(arg) - 1] = 0;
// Rewrite the non-var-arg case
vregC++; // Skips the 0th vreg in the range ("this").
}
// Parameter registers go at the end of the shadow frame.
DCHECK_GE(num_regs, number_of_inputs);
size_t first_dest_reg = num_regs - number_of_inputs;
DCHECK_NE(first_dest_reg, (size_t)-1);
// Allocate shadow frame on the stack.
// 这里开始创建 ShadowFrame 对象,
const char* old_cause = self->StartAssertNoThreadSuspension("DoCallCommon");
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
CREATE_SHADOW_FRAME(num_regs, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
// Initialize new shadow frame by copying the registers from the callee shadow frame.
if (!shadow_frame.GetMethod()->SkipAccessChecks()) {
// Slow path.
// We might need to do class loading, which incurs a thread state change to kNative. So
// register the shadow frame as under construction and allow suspension again.
ScopedStackedShadowFramePusher pusher(self, new_shadow_frame);
self->EndAssertNoThreadSuspension(old_cause);
// ArtMethod here is needed to check type information of the call site against the callee.
// Type information is retrieved from a DexFile/DexCache for that respective declared method.
//
// As a special case for proxy methods, which are not dex-backed,
// we have to retrieve type information from the proxy's method
// interface method instead (which is dex backed since proxies are never interfaces).
ArtMethod* method =
new_shadow_frame->GetMethod()->GetInterfaceMethodIfProxy(kRuntimePointerSize);
// We need to do runtime check on reference assignment. We need to load the shorty
// to get the exact type of each reference argument.
const dex::TypeList* params = method->GetParameterTypeList();
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
// Handle receiver apart since it's not part of the shorty.
size_t dest_reg = first_dest_reg;
size_t arg_offset = 0;
if (!method->IsStatic()) {
size_t receiver_reg = is_range ? vregC : arg[0];
new_shadow_frame->SetVRegReference(dest_reg, shadow_frame.GetVRegReference(receiver_reg));
++dest_reg;
++arg_offset;
DCHECK(!string_init); // All StringFactory methods are static.
}
// 从调用方法B的 ShadowFrame 对象中拷贝被调用方法C所需的参数到C的 ShadowFrame 对象里
// Copy the caller's invoke-* arguments into the callee's parameter registers.
for (uint32_t shorty_pos = 0; dest_reg < num_regs; ++shorty_pos, ++dest_reg, ++arg_offset) {
// Skip the 0th 'shorty' type since it represents the return type.
DCHECK_LT(shorty_pos + 1, shorty_len) << "for shorty '" << shorty << "'";
const size_t src_reg = (is_range) ? vregC + arg_offset : arg[arg_offset];
switch (shorty[shorty_pos + 1]) {
// Handle Object references. 1 virtual register slot.
case 'L': {
ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
if (o != nullptr) {
const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_;
ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx);
if (arg_type == nullptr) {
StackHandleScope<1> hs(self);
// Preserve o since it is used below and GetClassFromTypeIndex may cause thread
// suspension.
HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
arg_type = method->ResolveClassFromTypeIndex(type_idx);
if (arg_type == nullptr) {
CHECK(self->IsExceptionPending());
return false;
}
}
if (!o->VerifierInstanceOf(arg_type)) {
// This should never happen.
std::string temp1, temp2;
self->ThrowNewExceptionF("Ljava/lang/InternalError;",
"Invoking %s with bad arg %d, type '%s' not instance of '%s'",
new_shadow_frame->GetMethod()->GetName(), shorty_pos,
o->GetClass()->GetDescriptor(&temp1),
arg_type->GetDescriptor(&temp2));
return false;
}
}
new_shadow_frame->SetVRegReference(dest_reg, o);
break;
}
// Handle doubles and longs. 2 consecutive virtual register slots.
case 'J': case 'D': {
uint64_t wide_value =
(static_cast<uint64_t>(shadow_frame.GetVReg(src_reg + 1)) << BitSizeOf<uint32_t>()) |
static_cast<uint32_t>(shadow_frame.GetVReg(src_reg));
new_shadow_frame->SetVRegLong(dest_reg, wide_value);
// Skip the next virtual register slot since we already used it.
++dest_reg;
++arg_offset;
break;
}
// Handle all other primitives that are always 1 virtual register slot.
default:
new_shadow_frame->SetVReg(dest_reg, shadow_frame.GetVReg(src_reg));
break;
}
}
} else {
if (is_range) {
DCHECK_EQ(num_regs, first_dest_reg + number_of_inputs);
}
CopyRegisters<is_range>(shadow_frame,
new_shadow_frame,
arg,
vregC,
first_dest_reg,
number_of_inputs);
self->EndAssertNoThreadSuspension(old_cause);
}
// 准备好 ShadowFrame 对象后,通过PerformCall 跳转到目标方法
PerformCall(self,
accessor,
shadow_frame.GetMethod(),
first_dest_reg,
new_shadow_frame,
result,
use_interpreter_entrypoint);
if (string_init && !self->IsExceptionPending()) {
SetStringInitValueToAllAliases(&shadow_frame, string_init_vreg_this, *result);
}
return !self->IsExceptionPending();
}
inline void PerformCall(Thread* self,
const CodeItemDataAccessor& accessor,
ArtMethod* caller_method,
const size_t first_dest_reg,
ShadowFrame* callee_frame,
JValue* result,
bool use_interpreter_entrypoint)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (UNLIKELY(!Runtime::Current()->IsStarted())) {
interpreter::UnstartedRuntime::Invoke(self, accessor, callee_frame, result, first_dest_reg);
return;
}
if (!EnsureInitialized(self, callee_frame)) {
return;
}
if (use_interpreter_entrypoint) {
interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result);
} else {
interpreter::ArtInterpreterToCompiledCodeBridge(
self, caller_method, callee_frame, first_dest_reg, result);
}
}
如果处于调试模式或者方法机器码不存在,则调用 ArtInterpreterToInterpreterBridge 函数;如果可以用机器码方式执行,则调用 ArtInterpreterToCompiledCodeBridge ,它将从解释模式切换到机器码执行模式。
ArtInterpreterToInterpreterBridge
NO_STACK_PROTECTOR
void ArtInterpreterToInterpreterBridge(Thread* self,
const CodeItemDataAccessor& accessor,
ShadowFrame* shadow_frame,
JValue* result) {
bool implicit_check = Runtime::Current()->GetImplicitStackOverflowChecks();
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return;
}
// 方法对应的 shadow_frame 入栈
self->PushShadowFrame(shadow_frame);
if (LIKELY(!shadow_frame->GetMethod()->IsNative())) {
// 如果不是 JNI 方法,则调用 Execute 执行该方法
result->SetJ(Execute(self, accessor, *shadow_frame, JValue()).GetJ());
} else {
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub) except during testing and image writing.
CHECK(!Runtime::Current()->IsStarted());
bool is_static = shadow_frame->GetMethod()->IsStatic();
ObjPtr<mirror::Object> receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);
uint32_t* args = shadow_frame->GetVRegArgs(is_static ? 0 : 1);
UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver.Ptr(), args, result);
}
self->PopShadowFrame();
}
ArtInterpreterToCompiledCodeBridge
NO_STACK_PROTECTOR
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
ShadowFrame* shadow_frame,
uint16_t arg_offset,
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = shadow_frame->GetMethod();
// Basic checks for the arg_offset. If there's no code item, the arg_offset must be 0. Otherwise,
// check that the arg_offset isn't greater than the number of registers. A stronger check is
// difficult since the frame may contain space for all the registers in the method, or only enough
// space for the arguments.
if (kIsDebugBuild) {
if (method->GetCodeItem() == nullptr) {
DCHECK_EQ(0u, arg_offset) << method->PrettyMethod();
} else {
DCHECK_LE(arg_offset, shadow_frame->NumberOfVRegs());
}
}
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr && caller != nullptr) {
// jit 相关
jit->NotifyInterpreterToCompiledCodeTransition(self, caller);
}
// 调用 ArtMethod 的 Invoke 方法
method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
(shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
result, method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty());
}
ArtInterpreterToCompiledCodeBridge 先执行了对 JIT 相关的处理,然后调用 ArtMethod 的 Invoke 方法,这个 Invoke 方法中的参数 args 表示方法所需的参数数组,result 用来存储方法调用的结果,shorty 则对应方法的简短描述
NO_STACK_PROTECTOR
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
ThrowStackOverflowError(self);
return;
}
if (kIsDebugBuild) {
self->AssertThreadSuspensionIsAllowable();
CHECK_EQ(ThreadState::kRunnable, self->GetState());
CHECK_STREQ(GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(), shorty);
}
// Push a transition back into managed code onto the linked list in thread.
// 前面介绍过,ManagedStack 与栈操作有关
ManagedStack fragment;
self->PushManagedStackFragment(&fragment);
Runtime* runtime = Runtime::Current();
// Call the invoke stub, passing everything as arguments.
// If the runtime is not yet started or it is required by the debugger, then perform the
// Invocation by the interpreter, explicitly forcing interpretation over JIT to prevent
// cycling around the various JIT/Interpreter methods that handle method invocation.
if (UNLIKELY(!runtime->IsStarted() ||
(self->IsForceInterpreter() && !IsNative() && !IsProxyMethod() && IsInvokable()))) {
if (IsStatic()) {
art::interpreter::EnterInterpreterFromInvoke(
self, this, nullptr, args, result, /*stay_in_interpreter=*/ true);
} else {
mirror::Object* receiver =
reinterpret_cast<StackReference<mirror::Object>*>(&args[0])->AsMirrorPtr();
art::interpreter::EnterInterpreterFromInvoke(
self, this, receiver, args + 1, result, /*stay_in_interpreter=*/ true);
}
} else {
DCHECK_EQ(runtime->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
constexpr bool kLogInvocationStartAndReturn = false;
// 再次判断方法是否存在机器码
bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
if (LIKELY(have_quick_code)) {
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf(
"Invoking '%s' quick code=%p static=%d", PrettyMethod().c_str(),
GetEntryPointFromQuickCompiledCode(), static_cast<int>(IsStatic() ? 1 : 0));
}
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
CHECK(!runtime->UseJitCompilation());
const void* oat_quick_code =
(IsNative() || !IsInvokable() || IsProxyMethod() || IsObsolete())
? nullptr
: GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize());
CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
<< "Don't call compiled code when -Xint " << PrettyMethod();
}
// 如果是非静态函数则调用 art_quick_invoke_stub 函数,否则 调用 art_quick_invoke_static_stub 函数
if (!IsStatic()) {
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
} else {
(*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
}
if (UNLIKELY(self->GetException() == Thread::GetDeoptimizationException())) {
// Unusual case where we were running generated code and an
// exception was thrown to force the activations to be removed from the
// stack. Continue execution in the interpreter.
self->DeoptimizeWithDeoptimizationException(result);
}
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf("Returned '%s' quick code=%p", PrettyMethod().c_str(),
GetEntryPointFromQuickCompiledCode());
}
} else {
LOG(INFO) << "Not invoking '" << PrettyMethod() << "' code=null";
if (result != nullptr) {
result->SetJ(0);
}
}
}
// Pop transition.
self->PopManagedStackFragment(fragment);
}
art_quick_invoke_stub 和 art_quick_invoke_static_stub 都由汇编实现
/*
* extern"C" void art_quick_invoke_stub(ArtMethod *method, x0
* uint32_t *args, x1
* uint32_t argsize, w2
* Thread *self, x3
* JValue *result, x4
* char *shorty); x5
* +----------------------+
* | |
* | C/C++ frame |
* | LR'' |
* | FP'' | <- SP'
* +----------------------+
* +----------------------+
* | x28 | <- TODO: Remove callee-saves.
* | : |
* | x19 |
* | SP' |
* | X5 |
* | X4 | Saved registers
* | LR' |
* | FP' | <- FP
* +----------------------+
* | uint32_t out[n-1] |
* | : : | Outs
* | uint32_t out[0] |
* | ArtMethod* | <- SP value=null
* +----------------------+
*
* Outgoing registers:
* x0 - Method*
* x1-x7 - integer parameters.
* d0-d7 - Floating point parameters.
* xSELF = self
* SP = & of ArtMethod*
* x1 = "this" pointer.
*
*/
ENTRY art_quick_invoke_stub
// Spill registers as per AACPS64 calling convention.
INVOKE_STUB_CREATE_FRAME
// Load args into registers.
INVOKE_STUB_LOAD_ALL_ARGS _instance
// Call the method and return.
INVOKE_STUB_CALL_AND_RETURN
END art_quick_invoke_stub
/* extern"C"
* void art_quick_invoke_static_stub(ArtMethod *method, x0
* uint32_t *args, x1
* uint32_t argsize, w2
* Thread *self, x3
* JValue *result, x4
* char *shorty); x5
*/
ENTRY art_quick_invoke_static_stub
// Spill registers as per AACPS64 calling convention.
INVOKE_STUB_CREATE_FRAME
// Load args into registers.
INVOKE_STUB_LOAD_ALL_ARGS _static
// Call the method and return.
INVOKE_STUB_CALL_AND_RETURN
END art_quick_invoke_static_stub
总结
Interpreter 模式的运行流程如下所示