-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
JIT简介
JIT分类
- Method-based JIT 优化整个方法,优化比较彻底,但需要前期预热。
- Trace-based JIT 优化一小段代码,优化粒度细,消耗的内存页比较小。
Dalvik是采用Trace-based JIT.下面的文章文成2部分,第一部分是dalvik虚拟机主线程在执行指令时都发生了什么事情 第二部分是:JIT线程如何启动,并且如何处理JIT请求。
Dalvik取指令执行
记得在 #9 中,我们忽略了JIT部分,在这里补上。
#if defined(WITH_JIT)
.LentryInstr:
/* Entry is always a possible trace start */
ldr r0, [rSELF, #offThread_pJitProfTable]
FETCH_INST()
mov r1, #0 @ prepare the value for the new state
str r1, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
cmp r0,#0 @ is profiling disabled?
#if !defined(WITH_SELF_VERIFICATION)
bne common_updateProfile @ profiling is enabled
#else
ldr r2, [rSELF, #offThread_shadowSpace] @ to find out the jit exit state
beq 1f @ profiling is disabled
ldr r3, [r2, #offShadowSpace_jitExitState] @ jit exit state
cmp r3, #kSVSTraceSelect @ hot trace following?
moveq r2,#kJitTSelectRequestHot @ ask for trace selection
beq common_selectTrace @ go build the trace
cmp r3, #kSVSNoProfile @ don't profile the next instruction?
beq 1f @ intrepret the next instruction
b common_updateProfile @ collect profiles
#endif
1:
GET_INST_OPCODE(ip)
GOTO_OPCODE(ip)在上面这段代码中,我们看到 有创建trace的步骤,也有
b common_updateProfile
common_updateProfile中会去检测有没有到达阈值,如果检测到已经过了阈值了,那么会交给JIT去优化,否则回到解释器继续执行。
common_updateProfile:
eor r3,rPC,rPC,lsr #12 @ cheap, but fast hash function
lsl r3,r3,#(32 - JIT_PROF_SIZE_LOG_2) @ shift out excess bits
ldrb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ get counter
GET_INST_OPCODE(ip)
subs r1,r1,#1 @ decrement counter
strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ and store it
GOTO_OPCODE_IFNE(ip) @ if not threshold, fallthrough otherwise */要是过了阈值了,那么不会去走GOTO_OPCODE_IFNE(ip) ,而是继续下面的代码:
/* Looks good, reset the counter */
ldr r1, [rSELF, #offThread_jitThreshold]
strb r1,[r0,r3,lsr #(32 - JIT_PROF_SIZE_LOG_2)] @ reset counter
//这里,某一代码片段的计数已经过了阈值了,那么重置该计数。
EXPORT_PC()
mov r0,rPC
mov r1,rSELF
bl dvmJitGetTraceAddrThread @ (pc, self)
//获取二进制代码所在内存。
str r0, [rSELF, #offThread_inJitCodeCache] @ set the inJitCodeCache flag
mov r1, rPC @ arg1 of translation may need this
mov lr, #0 @ in case target is HANDLER_INTERPRET
cmp r0,#0
//判断内存地址是否是0
#if !defined(WITH_SELF_VERIFICATION)
bxne r0 @ jump to the translation
mov r2,#kJitTSelectRequest @ ask for trace selection
@ fall-through to common_selectTrace
#else
//如果不是0,则发送构造路径请求,并跳转执行
moveq r2,#kJitTSelectRequest @ ask for trace selection
beq common_selectTrace
/*
* At this point, we have a target translation. However, if
* that translation is actually the interpret-only pseudo-translation
* we want to treat it the same as no translation.
*/
mov r10, r0 @ save target
bl dvmCompilerGetInterpretTemplate
cmp r0, r10 @ special case?
bne jitSVShadowRunStart @ set up self verification shadow space
@ Need to clear the inJitCodeCache flag
mov r3, #0 @ 0 means not in the JIT code cache
str r3, [rSELF, #offThread_inJitCodeCache] @ back to the interp land
GET_INST_OPCODE(ip)
GOTO_OPCODE(ip)
/* no return */
#endif在common_selectTrace
中,会调用c代码去初始化环境,之后每次运行都会在去这里检查了。
common_selectTrace:
...
bl dvmJitCheckTraceRequest
//关于JIt的相关信息,都在全全局变量struct DvmJitGlobals gDvmJit;中。在dalvik真正开始执行的时候,JIT模式下和FAST模式下,跳转的指令处理代码不同。在JIT模式下,比如一个MOV指令,会跳转到这里
dvmAsmAltInstructionStart = .L_ALT_OP_NOP
/* ------------------------------ */
.balign 64
.L_ALT_OP_NOP: /* 0x00 */
/* File: armv5te/alt_stub.S */
/*
* Inter-instruction transfer stub. Call out to dvmCheckBefore to handle
* any interesting requests and then jump to the real instruction
* handler. Note that the call to dvmCheckBefore is done as a tail call.
* rIBASE updates won't be seen until a refresh, and we can tell we have a
* stale rIBASE if breakFlags==0. Always refresh rIBASE here, and then
* bail to the real handler if breakFlags==0.
*/
ldrb r3, [rSELF, #offThread_breakFlags]
adrl lr, dvmAsmInstructionStart + (0 * 64)
ldr rIBASE, [rSELF, #offThread_curHandlerTable]
cmp r3, #0
bxeq lr @ nothing to do - jump to real handler
EXPORT_PC()
mov r0, rPC @ arg0
mov r1, rFP @ arg1
mov r2, rSELF @ arg2
b dvmCheckBefore @ (dPC,dFP,self) tail call从代码中可以看到,会调用dvmCheckBefore,这个函数就是会处理JIT路径的相关问题,所以每一个指令都会进入到JIT去扩充Trace,当满足一定条件时,Trace建立结束:
- 如果Trace中指令数不为0,且当前指令时SWITCH,则建立结束。
- throw抛出异常,则结束建立。
- Trace中指令数目最大为100,超出则建立结束。
- 遇到return,建立结束
- 如果不是GOTO指令,且是Branch,Switch,Return,Invoke指令,则建立结束。
这些Trace会加入Trace队列,等待JIT线程去编译处理。
JIT线程的建立与执行
dvmStartup() -> dvmInitAfterZygote()->dvmCompilerStartup()->dvmCreateInternalThread
在dvmCreateInternalThread中,会调用linux的pthread_create方法,创建一个线程,并传入需要线程执行的函数compilerThreadStart,所以,现在JIT线程启动了,并去执行compilerThreadStart函数。
compilerThreadStart中:
- 初始化了存放函数指针的table并进行其他初始化操作
- 只要!gDvmJit.haltCompilerThread则不停的从workDequeue中取Trace,如果队列为空,则等待,否则取出Trace,调用dvmCompilerDoWork(&work);进行处理。dvmCompilerDoWork->dvmCompileTrace编译部分暂不学习,待以后回头看
Reactions are currently unavailable