DalvikVM

昨日の高速化云々な記事にて DalvikVM の中身が云々という記述があったので中身を見てやれ、と言いつつ em1 な dalvik ディレクトリ配下を gtags -v したりしてたんですが、どうもとっかかりが無くって困ってました。
dalvik/dalvikvm/Main.c とか果てし無いカンジですし。

色々探していたらようやくエントリポイントを発見。dalvik/vm/interp 配下な模様。README.txt には以下な記述があります。

Dalvik interpreter entry point.

The "mterp" directory now holds the interpreter implementation.

dalvik/vm/interp/interp.c あたりから掘っていけば良いのかな。

中身確認ちう

なんですが、存在しない手続きを呼び出しているのかどうなのか。定義を見つけきれない。
と思ったら dvmMterpStdRun な手続きはアセンブラな手続きとして定義されているのを発見。でも dvmInterpretStd というソレは無いッス。
とりあえず dvmMterpStd が _"standard" mterp entry point._ って事なんで

しばらく

中でさまよっておりましたが、ざっくりベースでまとめます。
dalvik/vm/interp/interp.c の dvmInterpret という手続きから。手続きの最初らへんをざっくり以下に引用。

void dvmInterpret(Thread* self, const Method* method, JValue* pResult)
{
    InterpState interpState;
    bool change;

/* snip */

    /*
     * Initialize working state.
     *
     * No need to initialize "retval".
     */
    interpState.method = method;
    interpState.fp = (u4*) self->curFrame;
    interpState.pc = method->insns;
    interpState.entryPoint = kInterpEntryInstr;

interpState という変数は行ってらっしゃい、な手続きに渡しています。

            change = (*stdInterp)(self, &interpState);

データ構造をちょっとだけ見ておくと、Method 型は dalvik/vm/oo/Object.h で定義されてて insns という属性の定義だけ控えておくと以下。

    const u2*       insns;          /* instructions, in memory-mapped .dex */

u2 は unsigned short (2 byte) って事な模様。unsigned short な命令を格納した配列がどこかにあるはず。
あるいは InterpState 型については dalvik/vm/interp/InterpDefs.h で定義されてます。定義のアタマの部分のみ以下に引用。

typedef struct InterpState {
    /*
     * To make some mterp state updates easier, "pc" and "fp" MUST come
     * first and MUST appear in this order.
     */
    const u2*   pc;                     // program counter
    u4*         fp;                     // frame pointer

    JValue      retval;                 // return value -- "out" only
    const Method* method;               // method being executed

適度に初期化をして関数ポインタの初期化をして手続き呼び出ししてるんですが、

    typedef bool (*Interpreter)(Thread*, InterpState*);
    Interpreter stdInterp;
    if (gDvm.executionMode == kExecutionModeInterpFast)
        stdInterp = dvmMterpStd;
    else
        stdInterp = dvmInterpretStd;

上記の通り dvmInterpretStd が解決できてません。とりあえず dvmMterpStd という手続きを掘っております。詳細は略しますが、以下が核心。

    changeInterp = dvmMterpStdRun(glue);

で、この svmMterpStdRun というソレがアセンブラになります。gtags では見つかりませんでしたので、必殺 find|xargs grep で発見。
とりあえず、x86アセンブラな記述がしてあるソレ (dalvik/vm/mterp/x86/entry.S) を確認。とその前に vm の中で使ってるレジスタの定義が以下かな。。

#define rPC      %edx
#define rFP      %esi
#define rIBASE   %edi
#define rINST_FULL %ebx
#define rINST    %bx
#define rINST_HI %bh
#define rINST_LO %bl
#define rOPCODE  %bl

で、手続きの中としてはレジスタの初期設定をして

    movl    IN_ARG0(%ebp),%ecx
    movl    %ecx,rGLUE_SPILL(%ebp)
    LOAD_PC_FROM_GLUE(%ecx)
    LOAD_FP_FROM_GLUE(%ecx)
    movl    $$dvmAsmInstructionStart,rIBASE

詳細は略しますが、以下が eval な部分かな。。。

    FETCH_INST()
    GOTO_NEXT

マクロ定義がそれぞれ以下。

#define FETCH_INST()            movzwl    (rPC),rINST_FULL

movzwl って何だったか。。。
最近 z80 とか 65c02 とかのアセンブラをナニしてたので、x86 云々って微妙。あと、GOTO_NEXT の定義が以下かな。

    /* For computed next version */
     movzx    rOPCODE,%eax
     sall     $6,%eax
     addl     rIBASE,%eax
     jmp      *%eax

FETCH_NEXT でプログラムカウンタの指してる命令が rINST_FULL レジスタ (ebx レジスタ) に格納されるんですが、次の GOTO_NEXT なマクロの中で bl レジスタからという形でオペコードを取り出しています。
オペコードはドキュメントによれば 5bit 表現になってます。が 6bit 左シフトさせて rIBASE レジスタと加算したソレに jmp している模様。
そりゃいいんですが、行ってらっしゃい、というだけでポインタ++ はしてませんな。あらら? と言いつつ実際の命令の定義を見てみたらその末端でやっておりました。以下は dalvik/vm/mterp/x86/OP_AGET.S の中身です。

%default { "load":"movl", "shift":"4" }
%verify "executed"
    /*
     * Array get, 32 bits or less.  vAA <- vBB[vCC].
     *
     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short
     */
    /* op vAA, vBB, vCC */
    movzbl    2(rPC),%eax               # eax<- BB
    movzbl    3(rPC),%ecx               # ecx<- CC
    movzbl    rINST_HI,rINST_FULL       # rINST_FULL<- AA
    GET_VREG(%eax,%eax)                 # eax<- vBB (array object)
    GET_VREG(%ecx,%ecx)                 # ecs<- vCC (requested index)
    testl     %eax,%eax                 # null array object?
    je        common_errNullObject      # bail if so
    cmpl      offArrayObject_length(%eax),%ecx
    jae       common_errArrayIndex      # index >= length, bail
    $load     offArrayObject_contents(%eax,%ecx,$shift),%eax
    movl      rINST_FULL,%ecx
    FETCH_INST_WORD(2)
    SET_VREG(%eax,%ecx)
    ADVANCE_PC(2)
    GOTO_NEXT

ただ、ADVANCE_PC なソレの定義が若干微妙。

#define ADVANCE_PC(_count)    leal  2*_count(rPC),rPC

とりあえず微妙なカンジではありますがエントリ投入。

追記

げげ。opcode ですが 8bit 使っておりますな。6bit シフトさせてる根拠は何だろ。