[讨论] [Dalvik VM] 什么情况下JIT不能编译整个方法?

pzz2011 2014-11-06
在Dalvik VM的 vm/compiler/Compiler.h 中有这么一个结构
/* Description of a compiled trace. */
typedef struct JitTranslationInfo {
    void *codeAddress;
    JitInstructionSetType instructionSet;
    int profileCodeSize;
    bool discardResult;         // Used for debugging divergence and IC patching
    bool methodCompilationAborted;  // Cannot compile the whole method 这里
    Thread *requestingThread;   // For debugging purpose
    int cacheVersion;           // Used to identify stale trace requests
} JitTranslationInfo;


同时还有一个疑问:
/* Work order for inline cache patching */
typedef struct ICPatchWorkOrder {
    PredictedChainingCell *cellAddr;    /* Address to be patched */
    PredictedChainingCell cellContent;  /* content of the new cell */
    const char *classDescriptor;        /* Descriptor of the class object */
    Object *classLoader;                /* Class loader */
    u4 serialNumber;                    /* Serial # (for verification only) */
} ICPatchWorkOrder;

这个结构体到底是干啥的?一点都不清楚呢! 
@R大 and 各位知道的朋友~~~ 谢谢啦~
RednaxelaFX 2014-11-07
1. 至少在Kitkat里没见到什么地方能把methodCompilationAborted设为true。

Dalvik VM的JIT(至少Google原厂版)默认用trace而不用method作为编译单元。就是说本来默认就不是编译整个方法的。
而实际在代码里的method编译模式其实也是基于trace机制来实现的,好像到Kitkat也还不能保证trace完整个方法,所以…请忽略Dalvik现有的method模式JIT。

至于Intel啊啥的别厂版的Dalvik JIT那就另当别论了。反正不开源看不到代码也没啥好说的。

2. 这个ICPatchWorkOrder结构体,顾名思义,是描述一个Inline Cache Patch任务的数据结构。在x86上的话当宏 PREDICTED_CHAINING 有定义的时候才适用;其它平台没留意。
由JIT编译出来的代码里的调用检查代码调用:
/*
 * This method is called from the invoke templates for virtual and interface
 * methods to speculatively setup a chain to the callee. The templates are
 * written in assembly and have setup method, cell, and clazz at r0, r2, and
 * r3 respectively, so there is a unused argument in the list. Upon return one
 * of the following three results may happen:
 *   1) Chain is not setup because the callee is native. Reset the rechain
 *      count to a big number so that it will take a long time before the next
 *      rechain attempt to happen.
 *   2) Chain is not setup because the callee has not been created yet. Reset
 *      the rechain count to a small number and retry in the near future.
 *   3) Ask all other threads to stop before patching this chaining cell.
 *      This is required because another thread may have passed the class check
 *      but hasn't reached the chaining cell yet to follow the chain. If we
 *      patch the content before halting the other thread, there could be a
 *      small window for race conditions to happen that it may follow the new
 *      but wrong chain to invoke a different method.
 */
const Method *dvmJitToPatchPredictedChain(const Method *method,
                                          Thread *self,
                                          PredictedChainingCell *cell,
                                          const ClassObject *clazz)


在这个函数插入请求队列:
/*
 * Attempt to enqueue a work order to patch an inline cache for a predicted
 * chaining cell for virtual/interface calls.
 */
static bool inlineCachePatchEnqueue(PredictedChainingCell *cellAddr,
                                    PredictedChainingCell *newContent)

然后在safepoint里由:
/*
 * Perform actions that are only safe when all threads are suspended. Currently
 * we do:
 * 1) Check if the code cache is full. If so reset it and restart populating it
 *    from scratch.
 * 2) Patch predicted chaining cells by consuming recorded work orders.
 */
void dvmCompilerPerformSafePointChecks(void)
{
    if (gDvmJit.codeCacheFull) {
        resetCodeCache();
    }
    dvmCompilerPatchInlineCache();
}

调用 dvmCompilerPatchInlineCache() 来执行IC patching。

楼主知道inline cache是啥不?
Dalvik VM用的inline cache是monomorphic inline cache(MIC),单态內联缓存。
在这个上下文里,它的基本概念就是:对虚方法调用的调用点(call site),记住一个最主要的类型及其方法信息,如果这次被调用的对象完全符合该类型那么就直接跳到该方法的入口,否则根据一定策略更新MIC记住的信息。

“最重要”有许多不同策略,最简单的就是记住“最近一次传进来的对象的类型”。复杂一点的可以记住若干个后备类型然后选择被调用频率比较高的那个。

在Dalvik里,invoke-virtual / invoke-interface的调用点都会用这种MIC机制来调用。伪代码类似这样:
// this: reference to the receiver object
// predictedChainCell: the MIC of this call site
if (this == NULL) {
  throw NullPointerException();
}
if (this->class == predictedChainCell->clazz) { // predicate valid
  goto predictedChainCell->method; // follow predicted chain (PredictedChainingCell::branch)
} else {
  // ... 省略检查rechain count的逻辑
  
  // patch inline cache ("rechain")
  dvmJitToPatchPredictedChain(this->class->vtable[methodIdx], // method
                              thread,
                              predictedChainCell,
                              this->class);
}


这个PredictedChainingCell结构是直接生成在代码里的。它的branch字段有两种状态:
1、标记未初始化状态的值:PREDICTED_CHAIN_BX_PAIR_INIT
2、初始化好的话它是一条机器码:跳转到目标方法的入口的指令
Global site tag (gtag.js) - Google Analytics