Android ART虚拟机执行引擎-Interpreter(八)
2018-02-25 16:08
330 查看
ART虚拟机是一个Interpreter+JIT+AOT的共存环境。
ART虚拟机中解释器的实现源码在art/runtime/interpreter中,其中与平台相关的汇编代码保存在目录art/runtime/interpreter/mterp中,因为interpreter有两个版本,一种是C语言实现的,一种是汇编版本的。
汇编版本的解释器执行效率高,但是要对不同的硬件架构进行适配。
解释器的任务就是解释字节码并执行,它的输入源可以是原始的编程语言,也可以是类似于java中的byteCode的中间表示。解释型的语言并不代表完全不需要编译,事实上很多解释器的内部实现也是先编译源码,然后才去执行,只是这个编译的过程是透明的。
1)Android虚拟机的解释器,输入源特指的是Android字节码。Android虚拟机的解释器有switch类型和goto类型。
其中switch类型对应的源码如下:
art/runtime/interpreter/Interpreter_switch_impl.cc
其中的参数self表示当前线程,shadow_frame类似于java虚拟机中的stack frame,用于存储方法中的本地变量表、操作栈方法出口等信息。
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
bool interpret_one_instruction) {
constexpr bool do_assignability_check = do_access_check;
self->VerifyStack();
uint32_t dex_pc = shadow_frame.GetDexPC();
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
const uint16_t* const insns = code_item->insns_;
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data;
ArtMethod* method = shadow_frame.GetMethod();
jit::Jit* jit = Runtime::Current()->GetJit();
// TODO: collapse capture-variable+create-lambda into one opcode, then we won't need
// to keep this live for the scope of the entire function call.
std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder;
size_t lambda_captured_variable_index = 0;
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();
4000
break;
......
}
} while (!interpret_one_instruction);
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
return result_register;
} // NOLINT(readability/fn_size)
switch类型的解释器,利用case分别处理每一条字节码,内部结构清晰简洁,但是效率较低,因为每条指令的执行都需要很长的判断过程-从case1开始,直到匹配到正确的目标指令,循环往复。
2)goto类型是对switch类型解释器效率缺陷的改进。goto类型解释器将指令和其处理函数建立了一对一的联系。具体实现如下:
art/runtime/interpreter/Interpreter_goto_table_impl.cctemplate<bool do_access_check, bool transaction_active>
JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame,
JValue result_register) {
// Define handler tables:
// - The main handler table contains execution handlers for each instruction.
// - The alternative handler table contains prelude handlers which check for thread suspend and
// manage instrumentation before jumping to the execution handler.
static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = {
{
// Main handler table.
#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_HANDLER
}, {
// Alternative handler table.
#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_HANDLER
}
};
constexpr bool do_assignability_check = do_access_check;
if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
LOG(FATAL) << "Invalid shadow frame for interpreter use";
return JValue();
}
self->VerifyStack();
uint32_t dex_pc = shadow_frame.GetDexPC();
const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
uint16_t inst_data;
const void* const* currentHandlersTable;
UPDATE_HANDLER_TABLE();
std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder;
size_t lambda_captured_variable_index = 0;
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
ArtMethod* method = shadow_frame.GetMethod();
jit::Jit* jit = Runtime::Current()->GetJit();
// Jump to first instruction.
ADVANCE(0);
UNREACHABLE_CODE_CHECK();
HANDLE_INSTRUCTION_START(NOP)
ADVANCE(1);
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(MOVE)
shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
ADVANCE(1);
HANDLE_INSTRUCTION_END();
......
// Create alternative instruction handlers dedicated to instrumentation.
// Return instructions must not call Instrumentation::DexPcMovedEvent since they already call
// Instrumentation::MethodExited. This is to avoid posting debugger events twice for this location.
// Note: we do not use the kReturn instruction flag here (to test the instruction is a return). The
// compiler seems to not evaluate "(Instruction::FlagsOf(Instruction::code) & kReturn) != 0" to
// a constant condition that would remove the "if" statement so the test is free.
#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \
alt_op_##code: { \
if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \
instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc); \
} \
UPDATE_HANDLER_TABLE(); \
goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \
}
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUMENTATION_INSTRUCTION_HANDLER
} // NOLINT(readability/fn_size)首先定义了一个数组handlersTable,这是一个二维数组,第一维是两个Handler Table,其中main Handler table是正常情况下指令处理函数的集合,alternative handler table是在调试情况下其作用,如单步调试,tracer调试等。
#define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels)
#define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK()HANDLE_INSTRUCTION_START这个宏用于定义一个opcode的label,在定义handlerstable时,每一项的取值都是&&op_##code (#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,),这样opcode就和它的处理代码建立了关联。
ADVANCE(1);宏用于取出并跳转到下一条指令去执行。
3)具体使用的解释器是goto,switch,还是assemble,首先由变量kInterpreterImplKind决定,其初始值是kMterpImplKind,汇编解释器。
art/runtime/interpreter/Interpreter.ccstatic constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;既然是由变量来决定,就可以在后期运行过程中根据实际情况做调整。
ART虚拟机中解释器的实现源码在art/runtime/interpreter中,其中与平台相关的汇编代码保存在目录art/runtime/interpreter/mterp中,因为interpreter有两个版本,一种是C语言实现的,一种是汇编版本的。
汇编版本的解释器执行效率高,但是要对不同的硬件架构进行适配。
解释器的任务就是解释字节码并执行,它的输入源可以是原始的编程语言,也可以是类似于java中的byteCode的中间表示。解释型的语言并不代表完全不需要编译,事实上很多解释器的内部实现也是先编译源码,然后才去执行,只是这个编译的过程是透明的。
1)Android虚拟机的解释器,输入源特指的是Android字节码。Android虚拟机的解释器有switch类型和goto类型。
其中switch类型对应的源码如下:
art/runtime/interpreter/Interpreter_switch_impl.cc
其中的参数self表示当前线程,shadow_frame类似于java虚拟机中的stack frame,用于存储方法中的本地变量表、操作栈方法出口等信息。
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
bool interpret_one_instruction) {
constexpr bool do_assignability_check = do_access_check;
self->VerifyStack();
uint32_t dex_pc = shadow_frame.GetDexPC();
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
const uint16_t* const insns = code_item->insns_;
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data;
ArtMethod* method = shadow_frame.GetMethod();
jit::Jit* jit = Runtime::Current()->GetJit();
// TODO: collapse capture-variable+create-lambda into one opcode, then we won't need
// to keep this live for the scope of the entire function call.
std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder;
size_t lambda_captured_variable_index = 0;
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();
4000
break;
......
}
} while (!interpret_one_instruction);
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
return result_register;
} // NOLINT(readability/fn_size)
switch类型的解释器,利用case分别处理每一条字节码,内部结构清晰简洁,但是效率较低,因为每条指令的执行都需要很长的判断过程-从case1开始,直到匹配到正确的目标指令,循环往复。
2)goto类型是对switch类型解释器效率缺陷的改进。goto类型解释器将指令和其处理函数建立了一对一的联系。具体实现如下:
art/runtime/interpreter/Interpreter_goto_table_impl.cctemplate<bool do_access_check, bool transaction_active>
JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowFrame& shadow_frame,
JValue result_register) {
// Define handler tables:
// - The main handler table contains execution handlers for each instruction.
// - The alternative handler table contains prelude handlers which check for thread suspend and
// manage instrumentation before jumping to the execution handler.
static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = {
{
// Main handler table.
#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_HANDLER
}, {
// Alternative handler table.
#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_HANDLER
}
};
constexpr bool do_assignability_check = do_access_check;
if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
LOG(FATAL) << "Invalid shadow frame for interpreter use";
return JValue();
}
self->VerifyStack();
uint32_t dex_pc = shadow_frame.GetDexPC();
const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
uint16_t inst_data;
const void* const* currentHandlersTable;
UPDATE_HANDLER_TABLE();
std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder;
size_t lambda_captured_variable_index = 0;
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
ArtMethod* method = shadow_frame.GetMethod();
jit::Jit* jit = Runtime::Current()->GetJit();
// Jump to first instruction.
ADVANCE(0);
UNREACHABLE_CODE_CHECK();
HANDLE_INSTRUCTION_START(NOP)
ADVANCE(1);
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(MOVE)
shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
ADVANCE(1);
HANDLE_INSTRUCTION_END();
......
// Create alternative instruction handlers dedicated to instrumentation.
// Return instructions must not call Instrumentation::DexPcMovedEvent since they already call
// Instrumentation::MethodExited. This is to avoid posting debugger events twice for this location.
// Note: we do not use the kReturn instruction flag here (to test the instruction is a return). The
// compiler seems to not evaluate "(Instruction::FlagsOf(Instruction::code) & kReturn) != 0" to
// a constant condition that would remove the "if" statement so the test is free.
#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \
alt_op_##code: { \
if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \
instrumentation->DexPcMovedEvent(self, this_object, shadow_frame.GetMethod(), dex_pc); \
} \
UPDATE_HANDLER_TABLE(); \
goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \
}
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUMENTATION_INSTRUCTION_HANDLER
} // NOLINT(readability/fn_size)首先定义了一个数组handlersTable,这是一个二维数组,第一维是两个Handler Table,其中main Handler table是正常情况下指令处理函数的集合,alternative handler table是在调试情况下其作用,如单步调试,tracer调试等。
#define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels)
#define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK()HANDLE_INSTRUCTION_START这个宏用于定义一个opcode的label,在定义handlerstable时,每一项的取值都是&&op_##code (#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,),这样opcode就和它的处理代码建立了关联。
ADVANCE(1);宏用于取出并跳转到下一条指令去执行。
3)具体使用的解释器是goto,switch,还是assemble,首先由变量kInterpreterImplKind决定,其初始值是kMterpImplKind,汇编解释器。
art/runtime/interpreter/Interpreter.ccstatic constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;既然是由变量来决定,就可以在后期运行过程中根据实际情况做调整。
相关文章推荐
- Android ART虚拟机执行引擎-JIT(九)
- Android ART虚拟机执行引擎-本地代码的执行(十)
- 深入理解java虚拟机-读书笔记5-虚拟机字节码执行引擎
- 虚拟机的字节码执行引擎
- 虚拟机字节码执行引擎的方法调用
- JVM读书笔记(五):虚拟机字节码执行引擎
- 虚拟机字节码执行引擎
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- JVM-深入虚拟机字节码执行引擎
- JVM--详解虚拟机字节码执行引擎之静态链接、动态链接与分派
- 第八章 虚拟机字节码执行引擎
- 不同的虚拟机,不同的执行引擎
- Android开发之JAVA虚拟机、Dalvik虚拟机和ART虚拟机简介
- 《深入理解java虚拟机》读书笔记-第八章虚拟机字节码执行引擎
- Android Dex vs ART 虚拟机运行效率提升
- Genymotion 运行Android虚拟机出现错误:Unable to staart the virtual device.To find out the cause of the problem
- Dalvik VM和JVM的比较以及Android新的虚拟机ART
- Android Dalvik虚拟机简述(与Java虚拟机的区别和简要的执行原理)
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- 第8章 虚拟机字节码执行引擎