1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18/*! \file LowerInvoke.cpp
19    \brief This file lowers the following bytecodes: INVOKE_XXX
20*/
21#include "libdex/DexOpcodes.h"
22#include "libdex/DexFile.h"
23#include "mterp/Mterp.h"
24#include "Lower.h"
25#include "NcgAot.h"
26#include "enc_wrapper.h"
27
28char* streamMisPred = NULL;
29
30/* according to callee, decide the ArgsDoneType*/
31ArgsDoneType convertCalleeToType(const Method* calleeMethod) {
32    if(calleeMethod == NULL)
33        return ArgsDone_Full;
34    if(dvmIsNativeMethod(calleeMethod))
35        return ArgsDone_Native;
36    return ArgsDone_Normal;
37}
38int common_invokeMethodRange(ArgsDoneType);
39int common_invokeMethodNoRange(ArgsDoneType);
40void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg);
41
42//inputs to common_invokeMethodRange: %ecx
43//          common_errNoSuchMethod: %edx
44#define P_GPR_1 PhysicalReg_ESI
45#define P_GPR_2 PhysicalReg_EBX
46#define P_GPR_3 PhysicalReg_ECX
47#define P_SCRATCH_1 PhysicalReg_EDX
48#define PP_GPR_1 PhysicalReg_EBX
49#define PP_GPR_2 PhysicalReg_ESI
50#define PP_GPR_3 PhysicalReg_EAX
51#define PP_GPR_4 PhysicalReg_EDX
52
53#ifdef WITH_JIT_INLINING
54/*
55 * The function here takes care the
56 * branch over if prediction is correct and the misprediction target for misPredBranchOver.
57 */
58static void genLandingPadForMispredictedCallee(MIR* mir) {
59    BasicBlock *fallThrough = traceCurrentBB->fallThrough;
60    /* Bypass the move-result block if there is one */
61    if (fallThrough->firstMIRInsn) {
62        assert(fallThrough->firstMIRInsn->OptimizationFlags & MIR_INLINED_PRED);
63        fallThrough = fallThrough->fallThrough;
64    }
65    /* Generate a branch over if the predicted inlining is correct */
66    jumpToBasicBlock(stream, fallThrough->id);
67    /* Hook up the target to the verification branch */
68    int relativeNCG = stream - streamMisPred;
69    unsigned instSize = encoder_get_inst_size(streamMisPred);
70    relativeNCG -= instSize; //size of the instruction
71    updateJumpInst(streamMisPred, OpndSize_8, relativeNCG);
72}
73#endif
74
75//! LOWER bytecode INVOKE_VIRTUAL without usage of helper function
76
77//!
78int common_invoke_virtual_nohelper(bool isRange, u2 tmp, u2 vD) {
79#ifdef WITH_JIT_INLINING
80    /*
81     * If the invoke has non-null misPredBranchOver, we need to generate
82     * the non-inlined version of the invoke here to handle the
83     * mispredicted case.
84     */
85    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
86        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
87    }
88#endif
89    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
90    export_pc();
91    constVREndOfBB();
92    beforeCall("exception"); //dump GG, GL VRs
93
94    get_virtual_reg(vD, OpndSize_32, 5, false);
95    simpleNullCheck(5, false, vD);
96#ifndef PREDICTED_CHAINING
97    move_mem_to_reg(OpndSize_32, offObject_clazz, 5, false, 6, false); //clazz of "this"
98    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 6, false, 7, false); //vtable
99    /* method is already resolved in trace-based JIT */
100    int methodIndex =
101                currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
102    move_mem_to_reg(OpndSize_32, methodIndex*4, 7, false, PhysicalReg_ECX, true);
103    if(isRange) {
104        common_invokeMethodRange(ArgsDone_Full);
105    }
106    else {
107        common_invokeMethodNoRange(ArgsDone_Full);
108    }
109#else
110    int methodIndex =
111                currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
112    gen_predicted_chain(isRange, tmp, methodIndex*4, false, 5/*tmp5*/);
113#endif
114    ///////////////////////////////////
115    return 0;
116}
117//! wrapper to call either common_invoke_virtual_helper or common_invoke_virtual_nohelper
118
119//!
120int common_invoke_virtual(bool isRange, u2 tmp, u2 vD) {
121    return common_invoke_virtual_nohelper(isRange, tmp, vD);
122}
123#undef P_GPR_1
124#undef P_GPR_2
125#undef P_GPR_3
126#undef P_SCRATCH_1
127#undef PP_GPR_1
128#undef PP_GPR_2
129#undef PP_GPR_3
130#undef PP_GPR_4
131
132#define P_GPR_1 PhysicalReg_ESI
133#define P_GPR_2 PhysicalReg_EBX
134#define P_GPR_3 PhysicalReg_EDX
135#define PP_GPR_1 PhysicalReg_EBX
136#define PP_GPR_2 PhysicalReg_ESI
137#define PP_GPR_3 PhysicalReg_EAX
138#define PP_GPR_4 PhysicalReg_EDX
139//! common section to lower INVOKE_SUPER
140
141//! It will use helper function if the switch is on
142int common_invoke_super(bool isRange, u2 tmp) {
143    export_pc();
144    constVREndOfBB();
145    beforeCall("exception"); //dump GG, GL VRs
146    ///////////////////////
147    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
148    /* method is already resolved in trace-based JIT */
149    int mIndex = currentMethod->clazz->pDvmDex->pResMethods[tmp]->methodIndex;
150    const Method *calleeMethod =
151        currentMethod->clazz->super->vtable[mIndex];
152    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
153    if(isRange) {
154        common_invokeMethodRange(convertCalleeToType(calleeMethod));
155    }
156    else {
157        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
158    }
159    ///////////////////////////////
160    return 0;
161}
162#undef PP_GPR_1
163#undef PP_GPR_2
164#undef PP_GPR_3
165#undef PP_GPR_4
166
167//! helper function to handle no such method error
168
169//!
170int invoke_super_nsm() {
171    insertLabel(".invoke_super_nsm", false);
172    //NOTE: it seems that the name in %edx is not used in common_errNoSuchMethod
173    move_mem_to_reg(OpndSize_32, offMethod_name, PhysicalReg_EAX, true, PhysicalReg_EDX, true); //method name
174    unconditional_jump("common_errNoSuchMethod", false);
175    return 0;
176}
177#undef P_GPR_1
178#undef P_GPR_2
179#undef P_GPR_3
180
181#define P_GPR_1 PhysicalReg_EBX
182#define P_GPR_2 PhysicalReg_ESI
183#define P_GPR_3 PhysicalReg_ECX
184#define PP_GPR_1 PhysicalReg_EBX
185#define PP_GPR_2 PhysicalReg_ESI
186#define PP_GPR_3 PhysicalReg_EAX
187#define PP_GPR_4 PhysicalReg_EDX
188//! common section to lower INVOKE_DIRECT
189
190//! It will use helper function if the switch is on
191int common_invoke_direct(bool isRange, u2 tmp, u2 vD) {
192    //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
193    export_pc();
194    constVREndOfBB();
195    beforeCall("exception"); //dump GG, GL VRs
196    ////////////////////////////////////
197    get_virtual_reg(vD, OpndSize_32, 5, false);
198    simpleNullCheck(5, false, vD);
199    /* method is already resolved in trace-based JIT */
200    const Method *calleeMethod =
201        currentMethod->clazz->pDvmDex->pResMethods[tmp];
202    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
203    //%ecx passed to common_invokeMethod...
204    if(isRange) {
205        common_invokeMethodRange(convertCalleeToType(calleeMethod));
206    }
207    else {
208        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
209    }
210    ////////////////////////////
211    return 0;
212}
213#undef P_GPR_1
214#undef P_GPR_2
215#undef P_GPR_3
216#undef PP_GPR_1
217#undef PP_GPR_2
218#undef PP_GPR_3
219#undef PP_GPR_4
220
221#define P_GPR_1  PhysicalReg_EBX
222#define P_GPR_3  PhysicalReg_ECX
223#define PP_GPR_1 PhysicalReg_EBX
224#define PP_GPR_2 PhysicalReg_ESI
225#define PP_GPR_3 PhysicalReg_EAX
226#define PP_GPR_4 PhysicalReg_EDX
227//! common section to lower INVOKE_STATIC
228
229//! It will use helper function if the switch is on
230int common_invoke_static(bool isRange, u2 tmp) {
231    //%ecx can be used as scratch when calling export_pc, get_res_methods and resolve_method
232    export_pc();
233    constVREndOfBB();
234    beforeCall("exception"); //dump GG, GL VRs
235    ////////////////////////////
236    /* method is already resolved in trace-based JIT */
237    const Method *calleeMethod =
238        currentMethod->clazz->pDvmDex->pResMethods[tmp];
239    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
240    //%ecx passed to common_invokeMethod...
241    if(isRange) {
242        common_invokeMethodRange(convertCalleeToType(calleeMethod));
243    }
244    else {
245        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
246    }
247    ////////////////////////
248    return 0;
249}
250#undef P_GPR_1
251#undef PP_GPR_1
252#undef PP_GPR_2
253#undef PP_GPR_3
254#undef PP_GPR_4
255
256#define P_GPR_1 PhysicalReg_EBX
257#define P_GPR_2 PhysicalReg_EAX //scratch
258#define P_GPR_3 PhysicalReg_ECX
259#define P_SCRATCH_1 PhysicalReg_ESI //clazz of object
260#define PP_GPR_1 PhysicalReg_EBX
261#define PP_GPR_2 PhysicalReg_ESI
262#define PP_GPR_3 PhysicalReg_EAX
263#define PP_GPR_4 PhysicalReg_EDX
264//! common section to lower INVOKE_INTERFACE
265
266//! It will use helper function if the switch is on
267int common_invoke_interface(bool isRange, u2 tmp, u2 vD) {
268#ifdef WITH_JIT_INLINING
269    /*
270     * If the invoke has non-null misPredBranchOver, we need to generate
271     * the non-inlined version of the invoke here to handle the
272     * mispredicted case.
273     */
274    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
275        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
276    }
277#endif
278    export_pc(); //use %edx
279    constVREndOfBB();
280    beforeCall("exception"); //dump GG, GL VRs
281    ///////////////////////
282    scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
283    get_virtual_reg(vD, OpndSize_32, 1, false);
284    simpleNullCheck(1, false, vD);
285
286#ifndef PREDICTED_CHAINING
287    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
288    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
289    /* for trace-based JIT, pDvmDex is a constant at JIT time
290       4th argument to dvmFindInterfaceMethodInCache at -4(%esp) */
291    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
292    move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 5, false);
293    /* for trace-based JIT, method is a constant at JIT time
294       3rd argument to dvmFindInterfaceMethodInCache at 8(%esp) */
295    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
296    move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true);
297    scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null;
298    call_dvmFindInterfaceMethodInCache();
299    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
300    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
301
302    conditional_jump_global_API(Condition_E, "common_exceptionThrown", false);
303    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
304    if(isRange) {
305        common_invokeMethodRange(ArgsDone_Full);
306    }
307    else {
308        common_invokeMethodNoRange(ArgsDone_Full);
309    }
310#else
311    gen_predicted_chain(isRange, tmp, -1, true /*interface*/, 1/*tmp1*/);
312#endif
313    ///////////////////////
314    return 0;
315}
316#undef PP_GPR_1
317#undef PP_GPR_2
318#undef PP_GPR_3
319#undef PP_GPR_4
320#undef P_GPR_1
321#undef P_GPR_2
322#undef P_GPR_3
323#undef P_SCRATCH_1
324//! lower bytecode INVOKE_VIRTUAL by calling common_invoke_virtual
325
326//!
327int op_invoke_virtual() {
328#ifdef WITH_JIT_INLINING
329    /* An invoke with the MIR_INLINED is effectively a no-op */
330    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
331        return false;
332#endif
333    //B|A|op CCCC G|F|E|D
334    //D: the first argument, which is the "this" pointer
335    //B: argument count
336    //D,E,F,G,A: arguments
337    u2 vD = FETCH(2) & 0xf;
338    u2 tmp = FETCH(1); //method index
339    int retval = common_invoke_virtual(false/*not range*/, tmp, vD);
340#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
341    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
342#endif
343    rPC += 3;
344    return retval;
345}
346//! lower bytecode INVOKE_SUPER by calling common_invoke_super
347
348//!
349int op_invoke_super() {
350#ifdef WITH_JIT_INLINING
351    /* An invoke with the MIR_INLINED is effectively a no-op */
352    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
353        return false;
354#endif
355    //B|A|op CCCC G|F|E|D
356    //D: the first argument
357    //B: argument count
358    //D,E,F,G,A: arguments
359    u2 tmp = FETCH(1); //method index
360    int retval = common_invoke_super(false/*not range*/, tmp);
361#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
362    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
363#endif
364    rPC += 3;
365    return retval;
366}
367//! lower bytecode INVOKE_DIRECT by calling common_invoke_direct
368
369//!
370int op_invoke_direct() {
371#ifdef WITH_JIT_INLINING
372    /* An invoke with the MIR_INLINED is effectively a no-op */
373    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
374        return false;
375#endif
376    //B|A|op CCCC G|F|E|D
377    //D: the first argument, which is the "this" pointer
378    //B: argument count
379    //D,E,F,G,A: arguments
380    u2 vD = FETCH(2) & 0xf;
381    u2 tmp = FETCH(1); //method index
382    int retval = common_invoke_direct(false/*not range*/, tmp, vD);
383#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
384    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
385#endif
386    rPC += 3;
387    return retval;
388}
389//! lower bytecode INVOKE_STATIC by calling common_invoke_static
390
391//!
392int op_invoke_static() {
393#ifdef WITH_JIT_INLINING
394    /* An invoke with the MIR_INLINED is effectively a no-op */
395    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
396        return false;
397#endif
398    //B|A|op CCCC G|F|E|D
399    //D: the first argument
400    //B: argument count
401    //D,E,F,G,A: arguments
402    u2 tmp = FETCH(1); //method index
403    int retval = common_invoke_static(false/*not range*/, tmp);
404#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
405    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
406#endif
407    rPC += 3;
408    return retval;
409}
410//! lower bytecode INVOKE_INTERFACE by calling common_invoke_interface
411
412//!
413int op_invoke_interface() {
414#ifdef WITH_JIT_INLINING
415    /* An invoke with the MIR_INLINED is effectively a no-op */
416    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
417        return false;
418#endif
419    //B|A|op CCCC G|F|E|D
420    //D: the first argument, which is the "this" pointer
421    //B: argument count
422    //D,E,F,G,A: arguments
423    u2 vD = FETCH(2) & 0xf;
424    u2 tmp = FETCH(1); //method index
425    int retval = common_invoke_interface(false/*not range*/, tmp, vD);
426#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
427    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
428#endif
429    rPC += 3;
430    return retval;
431}
432//! lower bytecode INVOKE_VIRTUAL_RANGE by calling common_invoke_virtual
433
434//!
435int op_invoke_virtual_range() {
436#ifdef WITH_JIT_INLINING
437    /* An invoke with the MIR_INLINED is effectively a no-op */
438    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
439        return false;
440#endif
441    //AA|op BBBB CCCC
442    //CCCC: the first argument, which is the "this" pointer
443    //AA: argument count
444    u2 tmp = FETCH(1); //BBBB, method index
445    u2 vD = FETCH(2); //the first argument
446    int retval = common_invoke_virtual(true/*range*/, tmp, vD);
447#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
448    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
449#endif
450    rPC += 3;
451    return retval;
452}
453//! lower bytecode INVOKE_SUPER_RANGE by calling common_invoke_super
454
455//!
456int op_invoke_super_range() {
457#ifdef WITH_JIT_INLINING
458    /* An invoke with the MIR_INLINED is effectively a no-op */
459    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
460        return false;
461#endif
462    u2 tmp = FETCH(1); //BBBB, method index
463    int retval = common_invoke_super(true/*range*/, tmp);
464#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
465    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
466#endif
467    rPC += 3;
468    return retval;
469}
470//! lower bytecode INVOKE_DIRECT_RANGE by calling common_invoke_direct
471
472//!
473int op_invoke_direct_range() {
474#ifdef WITH_JIT_INLINING
475    /* An invoke with the MIR_INLINED is effectively a no-op */
476    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
477        return false;
478#endif
479    u2 tmp = FETCH(1); //BBBB, method index
480    u2 vD = FETCH(2); //the first argument
481    int retval = common_invoke_direct(true/*range*/, tmp, vD);
482#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
483    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
484#endif
485    rPC += 3;
486    return retval;
487}
488//! lower bytecode INVOKE_STATIC_RANGE by calling common_invoke_static
489
490//!
491int op_invoke_static_range() {
492#ifdef WITH_JIT_INLINING
493    /* An invoke with the MIR_INLINED is effectively a no-op */
494    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
495        return false;
496#endif
497    u2 tmp = FETCH(1); //BBBB, method index
498    int retval = common_invoke_static(true/*range*/, tmp);
499#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
500    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
501#endif
502    rPC += 3;
503    return retval;
504}
505//! lower bytecode INVOKE_INTERFACE_RANGE by calling common_invoke_interface
506
507//!
508int op_invoke_interface_range() {
509#ifdef WITH_JIT_INLINING
510    /* An invoke with the MIR_INLINED is effectively a no-op */
511    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
512        return false;
513#endif
514    u2 tmp = FETCH(1); //BBBB, method index
515    u2 vD = FETCH(2); //the first argument
516    int retval = common_invoke_interface(true/*range*/, tmp, vD);
517#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
518    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
519#endif
520    rPC += 3;
521    return retval;
522}
523
524//used %ecx, %edi, %esp %ebp
525#define P_GPR_1 PhysicalReg_EBX
526#define P_SCRATCH_1 PhysicalReg_ESI
527#define P_SCRATCH_2 PhysicalReg_EAX
528#define P_SCRATCH_3 PhysicalReg_EDX
529#define P_SCRATCH_4 PhysicalReg_ESI
530#define P_SCRATCH_5 PhysicalReg_EAX
531//! pass the arguments for invoking method without range
532
533//!
534int common_invokeMethodNoRange_noJmp() {
535    u2 count = INST_B(inst);
536    u2 vD = FETCH(2) & 0xf;
537    u2 vE = (FETCH(2) >> 4) & 0xf;
538    u2 vF = (FETCH(2) >> 8) & 0xf;
539    u2 vG = (FETCH(2) >> 12) & 0xf;
540    u2 vA = INST_A(inst); //5th argument
541    int offsetFromSaveArea = -4;
542    if(count == 5) {
543        get_virtual_reg(vA, OpndSize_32, 22, false);
544        move_reg_to_mem(OpndSize_32, 22, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
545        offsetFromSaveArea -= 4;
546    }
547    if(count >= 4) {
548        get_virtual_reg(vG, OpndSize_32, 23, false);
549        move_reg_to_mem(OpndSize_32, 23, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
550        offsetFromSaveArea -= 4;
551    }
552    if(count >= 3) {
553        get_virtual_reg(vF, OpndSize_32, 24, false);
554        move_reg_to_mem(OpndSize_32, 24, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
555        offsetFromSaveArea -= 4;
556    }
557    if(count >= 2) {
558        get_virtual_reg(vE, OpndSize_32, 25, false);
559        move_reg_to_mem(OpndSize_32, 25, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
560        offsetFromSaveArea -= 4;
561    }
562    if(count >= 1) {
563        get_virtual_reg(vD, OpndSize_32, 26, false);
564        move_reg_to_mem(OpndSize_32, 26, false, offsetFromSaveArea-sizeofStackSaveArea, PhysicalReg_FP, true);
565    }
566    return 0;
567}
568
569int common_invokeMethod_Jmp(ArgsDoneType form) {
570    nextVersionOfHardReg(PhysicalReg_EDX, 1);
571    move_imm_to_reg(OpndSize_32, (int)rPC, PhysicalReg_EDX, true);
572    //arguments needed in ArgsDone:
573    //    start of HotChainingCell for next bytecode: -4(%esp)
574    //    start of InvokeSingletonChainingCell for callee: -8(%esp)
575    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
576    insertChainingWorklist(traceCurrentBB->fallThrough->id, stream);
577    move_chain_to_mem(OpndSize_32, traceCurrentBB->fallThrough->id, 4, PhysicalReg_ESP, true);
578    // for honeycomb: JNI call doesn't need a chaining cell, so the taken branch is null
579    if(traceCurrentBB->taken)
580        insertChainingWorklist(traceCurrentBB->taken->id, stream);
581    int takenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
582    move_chain_to_mem(OpndSize_32, takenId, 0, PhysicalReg_ESP, true);
583    if(form == ArgsDone_Full)
584        unconditional_jump_global_API(".invokeArgsDone_jit", false);
585    else if(form == ArgsDone_Native)
586        unconditional_jump_global_API(".invokeArgsDone_native", false);
587    else
588        unconditional_jump_global_API(".invokeArgsDone_normal", false);
589    return 0;
590}
591
592int common_invokeMethodNoRange(ArgsDoneType form) {
593    common_invokeMethodNoRange_noJmp();
594    common_invokeMethod_Jmp(form);
595    return 0;
596}
597
598#undef P_GPR_1
599#undef P_SCRATCH_1
600#undef P_SCRATCH_2
601#undef P_SCRATCH_3
602#undef P_SCRATCH_4
603#undef P_SCRATCH_5
604
605//input: %ecx (method to call)
606#define P_GPR_1 PhysicalReg_EBX
607#define P_GPR_2 PhysicalReg_ESI
608#define P_GPR_3 PhysicalReg_EDX //not used with P_SCRATCH_2
609#define P_SCRATCH_1 PhysicalReg_EAX
610#define P_SCRATCH_2 PhysicalReg_EDX
611#define P_SCRATCH_3 PhysicalReg_EAX
612#define P_SCRATCH_4 PhysicalReg_EDX
613#define P_SCRATCH_5 PhysicalReg_EAX
614#define P_SCRATCH_6 PhysicalReg_EDX
615#define P_SCRATCH_7 PhysicalReg_EAX
616#define P_SCRATCH_8 PhysicalReg_EDX
617#define P_SCRATCH_9 PhysicalReg_EAX
618#define P_SCRATCH_10 PhysicalReg_EDX
619//! pass the arguments for invoking method with range
620
621//! loop is unrolled when count <= 10
622int common_invokeMethodRange_noJmp() {
623    u2 count = INST_AA(inst);
624    u2 vD = FETCH(2); //the first argument
625    savearea_from_fp(21, false);
626    //vD to rFP-4*count-20
627    //vD+1 to rFP-4*count-20+4 = rFP-20-4*(count-1)
628    if(count >= 1 && count <= 10) {
629        get_virtual_reg(vD, OpndSize_32, 22, false);
630        move_reg_to_mem(OpndSize_32, 22, false, -4*count, 21, false);
631    }
632    if(count >= 2 && count <= 10) {
633        get_virtual_reg(vD+1, OpndSize_32, 23, false);
634        move_reg_to_mem(OpndSize_32, 23, false, -4*(count-1), 21, false);
635    }
636    if(count >= 3 && count <= 10) {
637        get_virtual_reg(vD+2, OpndSize_32, 24, false);
638        move_reg_to_mem(OpndSize_32, 24, false, -4*(count-2), 21, false);
639    }
640    if(count >= 4 && count <= 10) {
641        get_virtual_reg(vD+3, OpndSize_32, 25, false);
642        move_reg_to_mem(OpndSize_32, 25, false, -4*(count-3), 21, false);
643    }
644    if(count >= 5 && count <= 10) {
645        get_virtual_reg(vD+4, OpndSize_32, 26, false);
646        move_reg_to_mem(OpndSize_32, 26, false, -4*(count-4), 21, false);
647    }
648    if(count >= 6 && count <= 10) {
649        get_virtual_reg(vD+5, OpndSize_32, 27, false);
650        move_reg_to_mem(OpndSize_32, 27, false, -4*(count-5), 21, false);
651    }
652    if(count >= 7 && count <= 10) {
653        get_virtual_reg(vD+6, OpndSize_32, 28, false);
654        move_reg_to_mem(OpndSize_32, 28, false, -4*(count-6), 21, false);
655    }
656    if(count >= 8 && count <= 10) {
657        get_virtual_reg(vD+7, OpndSize_32, 29, false);
658        move_reg_to_mem(OpndSize_32, 29, false, -4*(count-7), 21, false);
659    }
660    if(count >= 9 && count <= 10) {
661        get_virtual_reg(vD+8, OpndSize_32, 30, false);
662        move_reg_to_mem(OpndSize_32, 30, false, -4*(count-8), 21, false);
663    }
664    if(count == 10) {
665        get_virtual_reg(vD+9, OpndSize_32, 31, false);
666        move_reg_to_mem(OpndSize_32, 31, false, -4*(count-9), 21, false);
667    }
668    if(count > 10) {
669        //dump to memory first, should we set physicalReg to Null?
670        //this bytecodes uses a set of virtual registers (update getVirtualInfo)
671        //this is necessary to correctly insert transfer points
672        int k;
673        for(k = 0; k < count; k++) {
674            spillVirtualReg(vD+k, LowOpndRegType_gp, true); //will update refCount
675        }
676        load_effective_addr(4*vD, PhysicalReg_FP, true, 12, false);
677        alu_binary_imm_reg(OpndSize_32, sub_opc, 4*count, 21, false);
678        move_imm_to_reg(OpndSize_32, count, 13, false);
679        insertLabel(".invokeMethod_1", true); //if checkDup: will perform work from ShortWorklist
680        rememberState(1);
681        move_mem_to_reg(OpndSize_32, 0, 12, false, 14, false);
682        move_reg_to_mem(OpndSize_32, 14, false, 0, 21, false);
683        load_effective_addr(4, 12, false, 12, false);
684        alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 13, false);
685        load_effective_addr(4, 21, false, 21, false);
686        transferToState(1);
687        conditional_jump(Condition_NE, ".invokeMethod_1", true); //backward branch
688    }
689    return 0;
690}
691
692int common_invokeMethodRange(ArgsDoneType form) {
693    common_invokeMethodRange_noJmp();
694    common_invokeMethod_Jmp(form);
695    return 0;
696}
697#undef P_GPR_1
698#undef P_GPR_2
699#undef P_GPR_3
700#undef P_SCRATCH_1
701#undef P_SCRATCH_2
702#undef P_SCRATCH_3
703#undef P_SCRATCH_4
704#undef P_SCRATCH_5
705#undef P_SCRATCH_6
706#undef P_SCRATCH_7
707#undef P_SCRATCH_8
708#undef P_SCRATCH_9
709#undef P_SCRATCH_10
710
711#define P_GPR_1 PhysicalReg_EBX
712#define P_GPR_3 PhysicalReg_ESI
713#define P_SCRATCH_1 PhysicalReg_EAX
714#define P_SCRATCH_2 PhysicalReg_EDX
715#define P_SCRATCH_3 PhysicalReg_EAX
716#define P_SCRATCH_4 PhysicalReg_EDX
717#define P_SCRATCH_5 PhysicalReg_EAX
718#define P_SCRATCH_6 PhysicalReg_EDX
719
720//! spill a register to native stack
721
722//! decrease %esp by 4, then store a register at 0(%esp)
723int spill_reg(int reg, bool isPhysical) {
724    load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
725    move_reg_to_mem(OpndSize_32, reg, isPhysical, 0, PhysicalReg_ESP, true);
726    return 0;
727}
728//! get a register from native stack
729
730//! load a register from 0(%esp), then increase %esp by 4
731int unspill_reg(int reg, bool isPhysical) {
732    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, reg, isPhysical);
733    load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
734    return 0;
735}
736
737void generate_invokeNative(bool generateForNcg); //forward declaration
738void generate_stackOverflow(); //forward declaration
739
740//! common code to invoke a method after all arguments are handled
741
742//!
743//takes one argument to generate code
744//  for invokeNativeSingle (form == ArgsDone_Native)
745//   or invokeNonNativeSingle (form == ArgsDone_Normal) when WITH_JIT is true
746//   to dynamically determine which one to choose (form == ArgsDone_Full)
747/* common_invokeArgsDone is called at NCG time and
748     at execution time during relocation
749   generate invokeArgsDone for NCG if isJitFull is false && form == Full */
750int common_invokeArgsDone(ArgsDoneType form, bool isJitFull) {
751    bool generateForNcg = false;
752    if(form == ArgsDone_Full) {
753        if(isJitFull)
754            insertLabel(".invokeArgsDone_jit", false);
755        else {
756            insertLabel(".invokeArgsDone", false);
757            generateForNcg = true;
758        }
759    }
760    else if(form == ArgsDone_Normal)
761        insertLabel(".invokeArgsDone_normal", false);
762    else if(form == ArgsDone_Native)
763        insertLabel(".invokeArgsDone_native", false);
764    //%ecx: methodToCall
765    movez_mem_to_reg(OpndSize_16, offMethod_registersSize, PhysicalReg_ECX, true, P_SCRATCH_1, true); //regSize
766    scratchRegs[0] = PhysicalReg_EBX; scratchRegs[1] = PhysicalReg_ESI;
767    scratchRegs[2] = PhysicalReg_EDX; scratchRegs[3] = PhysicalReg_Null;
768    savearea_from_fp(P_GPR_3, true);
769    alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_1, true);
770    alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_1, true, P_GPR_3, true);
771    //update newSaveArea->savedPc, here P_GPR_3 is new FP
772    move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true);
773    movez_mem_to_reg(OpndSize_16, offMethod_outsSize, PhysicalReg_ECX, true, P_SCRATCH_2, true); //outsSize
774    move_reg_to_reg(OpndSize_32, P_GPR_3, true, P_GPR_1, true); //new FP
775    alu_binary_imm_reg(OpndSize_32, sub_opc, sizeofStackSaveArea, P_GPR_3, true);
776
777    alu_binary_imm_reg(OpndSize_32, shl_opc, 2, P_SCRATCH_2, true);
778    alu_binary_reg_reg(OpndSize_32, sub_opc, P_SCRATCH_2, true, P_GPR_3, true);
779    get_self_pointer(P_SCRATCH_3, true);
780    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offStackSaveArea_prevFrame-sizeofStackSaveArea, P_GPR_1, true); //set stack->prevFrame
781    compare_mem_reg(OpndSize_32, offsetof(Thread, interpStackEnd), P_SCRATCH_3, true, P_GPR_3, true);
782    conditional_jump(Condition_L, ".stackOverflow", true);
783
784    if(form == ArgsDone_Full) {
785        test_imm_mem(OpndSize_32, ACC_NATIVE, offMethod_accessFlags, PhysicalReg_ECX, true);
786    }
787    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offStackSaveArea_method-sizeofStackSaveArea, P_GPR_1, true); //set stack->method
788
789    if(form == ArgsDone_Native || form == ArgsDone_Full) {
790        /* to correctly handle code cache reset:
791           update returnAddr and check returnAddr after done with the native method
792           if returnAddr is set to NULL during code cache reset,
793           the execution will correctly continue with interpreter */
794        //get returnAddr from 4(%esp) and update stack
795        move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
796                        PhysicalReg_EDX, true);
797        move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
798                        offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_1, true);
799    }
800    if(form == ArgsDone_Native) {
801        generate_invokeNative(generateForNcg);
802        return 0;
803    }
804    if(form == ArgsDone_Full) {
805        conditional_jump(Condition_NE, ".invokeNative", true);
806    }
807    move_mem_to_reg(OpndSize_32, offMethod_clazz, PhysicalReg_ECX, true, P_SCRATCH_4, true); //get method->claz
808    move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, P_SCRATCH_4, true, P_SCRATCH_4, true); //get method->clazz->pDvmDex
809    move_reg_to_reg(OpndSize_32, P_GPR_1, true, PhysicalReg_FP, true); //update rFP
810    get_self_pointer(P_GPR_1, true);
811    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, offsetof(Thread, interpSave.method), P_GPR_1, true); //glue->method
812    move_reg_to_mem(OpndSize_32, P_SCRATCH_4, true, offsetof(Thread, interpSave.methodClassDex), P_GPR_1, true); //set_glue_dvmdex
813    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set glue->self->frame
814    if(!generateForNcg) {
815        /* returnAddr updated already for Full */
816        //get returnAddr from 4(%esp) and update stack
817        if(form == ArgsDone_Normal)
818            move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true,
819                            PhysicalReg_EDX, true);
820        //for JIT: starting bytecode in %ebx to invoke JitToInterp
821        move_mem_to_reg(OpndSize_32, offMethod_insns, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
822        if(form == ArgsDone_Normal)
823            move_reg_to_mem(OpndSize_32, PhysicalReg_EDX, true,
824                            offStackSaveArea_returnAddr-sizeofStackSaveArea, PhysicalReg_FP, true);
825    }
826
827    insertLabel(".invokeInterp", true);
828    if(!generateForNcg) {
829        bool callNoChain = false;
830#ifdef PREDICTED_CHAINING
831        if(form == ArgsDone_Full) callNoChain = true;
832#endif
833        if(callNoChain) {
834            scratchRegs[0] = PhysicalReg_EAX;
835            load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
836#if defined(WITH_JIT_TUNING)
837            /* Predicted chaining failed. Fall back to interpreter and indicate
838             * inline cache miss.
839             */
840            move_imm_to_reg(OpndSize_32, kInlineCacheMiss, PhysicalReg_EDX, true);
841#endif
842            call_dvmJitToInterpTraceSelectNoChain(); //input: rPC in %ebx
843        } else {
844            //jump to the stub at (%esp)
845            move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true,
846                            PhysicalReg_EDX, true);
847            load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
848            unconditional_jump_reg(PhysicalReg_EDX, true);
849        }
850    }
851
852    if(form == ArgsDone_Full) generate_invokeNative(generateForNcg);
853    generate_stackOverflow();
854    return 0;
855}
856
857/* when WITH_JIT is true,
858     JIT'ed code invokes native method, after invoke, execution will continue
859     with the interpreter or with JIT'ed code if chained
860*/
861void generate_invokeNative(bool generateForNcg) {
862    insertLabel(".invokeNative", true);
863    //if(!generateForNcg)
864    //    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
865    load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
866    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
867    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 20, PhysicalReg_ESP, true);
868    scratchRegs[0] = PhysicalReg_EDX;
869    get_self_pointer(P_SCRATCH_1, true); //glue->self
870    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 8, PhysicalReg_ESP, true);
871    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 12, PhysicalReg_ESP, true);
872    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 24, PhysicalReg_ESP, true);
873    move_mem_to_reg(OpndSize_32, offThread_jniLocal_nextEntry, P_SCRATCH_1, true, P_SCRATCH_2, true); //get self->local_next
874    scratchRegs[1] = PhysicalReg_EAX;
875    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_1, true); //update jniLocalRef of stack
876    move_reg_to_mem(OpndSize_32, P_GPR_1, true, offThread_curFrame, P_SCRATCH_1, true); //set self->curFrame
877    move_imm_to_mem(OpndSize_32, 0, offThread_inJitCodeCache, P_SCRATCH_1, true); //clear self->inJitCodeCache
878    load_effective_addr(offsetof(Thread, interpSave.retval), P_SCRATCH_1, true, P_SCRATCH_3, true); //self->retval
879    move_reg_to_mem(OpndSize_32, P_SCRATCH_3, true, 4, PhysicalReg_ESP, true);
880    //NOTE: native method checks the interpreted stack for arguments
881    //      The immediate arguments on native stack: address of return value, new FP, self
882    call_mem(40, PhysicalReg_ECX, true);//*40(%ecx)
883    //we can't assume the argument stack is unmodified after the function call
884    //duplicate newFP & glue->self on stack: newFP (-28 & -8) glue->self (-16 & -4)
885    move_mem_to_reg(OpndSize_32, 20, PhysicalReg_ESP, true, P_GPR_3, true); //new FP
886    move_mem_to_reg(OpndSize_32, 24, PhysicalReg_ESP, true, P_GPR_1, true); //glue->self
887    load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
888    move_mem_to_reg(OpndSize_32, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_1, true); //newSaveArea->jniLocal
889    compare_imm_mem(OpndSize_32, 0, offThread_exception, P_GPR_1, true); //self->exception
890    if(!generateForNcg)
891        load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
892    //NOTE: PhysicalReg_FP should be callee-saved register
893    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set self->curFrame
894    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, offThread_jniLocal_nextEntry, P_GPR_1, true); //set self->jniLocal
895    conditional_jump(Condition_NE, "common_exceptionThrown", false);
896    if(!generateForNcg) {
897        //get returnAddr, if it is not NULL,
898        //    return to JIT'ed returnAddr after executing the native method
899        /* to correctly handle code cache reset:
900           update returnAddr and check returnAddr after done with the native method
901           if returnAddr is set to NULL during code cache reset,
902           the execution will correctly continue with interpreter */
903        move_mem_to_reg(OpndSize_32, offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_2, true);
904        //set self->inJitCodeCache to returnAddr (P_GPR_1 is in %ebx)
905        move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offThread_inJitCodeCache, P_GPR_1, true);
906        move_mem_to_reg(OpndSize_32, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true, PhysicalReg_EBX, true); //savedPc
907        compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
908        conditional_jump(Condition_E, ".nativeToInterp", true);
909        unconditional_jump_reg(P_SCRATCH_2, true);
910        //if returnAddr is NULL, return to interpreter after executing the native method
911        insertLabel(".nativeToInterp", true);
912        //move rPC by 6 (3 bytecode units for INVOKE)
913        alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EBX, true);
914        scratchRegs[0] = PhysicalReg_EAX;
915#if defined(WITH_JIT_TUNING)
916        /* Return address not in code cache. Indicate that continuing with interpreter
917         */
918        move_imm_to_reg(OpndSize_32, kCallsiteInterpreted, PhysicalReg_EDX, true);
919#endif
920        call_dvmJitToInterpTraceSelectNoChain(); //rPC in %ebx
921    }
922    return;
923}
924void generate_stackOverflow() {
925    insertLabel(".stackOverflow", true);
926    //load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
927    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 4, PhysicalReg_ESP, true);
928    get_self_pointer(P_GPR_1, true); //glue->self
929    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
930    call_dvmHandleStackOverflow();
931    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
932    unconditional_jump("common_exceptionThrown", false);
933}
934#undef P_GPR_1
935#undef P_GPR_2
936#undef P_GPR_3
937#undef P_SCRATCH_1
938#undef P_SCRATCH_2
939#undef P_SCRATCH_3
940#undef P_SCRATCH_4
941#undef P_SCRATCH_5
942#undef P_SCRATCH_6
943
944/////////////////////////////////////////////
945#define P_GPR_1 PhysicalReg_EBX
946#define P_GPR_2 PhysicalReg_ECX
947#define P_SCRATCH_1 PhysicalReg_ESI
948#define P_SCRATCH_2 PhysicalReg_EDX
949#define P_SCRATCH_3 PhysicalReg_ESI
950#define P_SCRATCH_4 PhysicalReg_EDX
951//! lower bytecode EXECUTE_INLINE
952
953//!
954int op_execute_inline(bool isRange) {
955    //tmp, vC, vD, vE, vF
956    int num;
957    if(!isRange) num = INST_B(inst);
958    else num = INST_AA(inst);
959    u2 tmp = FETCH(1);
960    u2 vC, vD, vE, vF;
961    if(!isRange) {
962        vC = FETCH(2) & 0xf;
963        vD = (FETCH(2) >> 4) & 0xf;
964        vE = (FETCH(2) >> 8) & 0xf;
965        vF = FETCH(2) >> 12;
966    } else {
967        vC = FETCH(2);
968        vD = vC + 1;
969        vE = vC + 2;
970        vF = vC + 3;
971    }
972    export_pc();
973    switch (tmp) {
974        case INLINE_EMPTYINLINEMETHOD:
975            return 0;  /* Nop */
976        case INLINE_STRING_LENGTH:
977            get_virtual_reg(vC, OpndSize_32, 1, false);
978            compare_imm_reg(OpndSize_32, 0, 1, false);
979            conditional_jump(Condition_NE, ".do_inlined_string_length", true);
980            scratchRegs[0] = PhysicalReg_SCRATCH_1;
981            jumpToExceptionThrown(1/*exception number*/);
982            insertLabel(".do_inlined_string_length", true);
983            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 2, false);
984            get_self_pointer(3, false);
985            move_reg_to_mem(OpndSize_32, 2, false, offsetof(Thread, interpSave.retval), 3, false);
986            return 0;
987        case INLINE_STRING_IS_EMPTY:
988            get_virtual_reg(vC, OpndSize_32, 1, false);
989            compare_imm_reg(OpndSize_32, 0, 1, false);
990            conditional_jump(Condition_NE, ".do_inlined_string_length", true);
991            scratchRegs[0] = PhysicalReg_SCRATCH_1;
992            jumpToExceptionThrown(1/*exception number*/);
993            insertLabel(".do_inlined_string_length", true);
994            compare_imm_mem(OpndSize_32, 0, 0x14, 1, false);
995            conditional_jump(Condition_E, ".inlined_string_length_return_true",
996                             true);
997            get_self_pointer(2, false);
998            move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, interpSave.retval), 2, false);
999            unconditional_jump(".inlined_string_length_done", true);
1000            insertLabel(".inlined_string_length_return_true", true);
1001            get_self_pointer(2, false);
1002            move_imm_to_mem(OpndSize_32, 1, offsetof(Thread, interpSave.retval), 2, false);
1003            insertLabel(".inlined_string_length_done", true);
1004            return 0;
1005        case INLINE_MATH_ABS_INT:
1006            get_virtual_reg(vC, OpndSize_32, 1, false);
1007            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
1008            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 2, false);
1009            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 1, false);
1010            alu_binary_reg_reg(OpndSize_32, sub_opc, 2, false, 1, false);
1011            get_self_pointer(3, false);
1012            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1013            return 0;
1014        case INLINE_MATH_ABS_LONG:
1015            get_virtual_reg(vD, OpndSize_32, 1, false);
1016            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
1017            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 1, false);
1018            move_reg_to_reg(OpndSize_32, 1, false, 3, false);
1019            move_reg_to_reg(OpndSize_32, 1, false, 4, false);
1020            get_virtual_reg(vC, OpndSize_32, 5, false);
1021            alu_binary_reg_reg(OpndSize_32, xor_opc, 5, false, 1, false);
1022            get_self_pointer(6, false);
1023            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 6, false);
1024            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 3, false);
1025            move_reg_to_mem(OpndSize_32, 3, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
1026            alu_binary_reg_mem(OpndSize_32, sub_opc, 4, false, offsetof(Thread, interpSave.retval), 6, false);
1027            alu_binary_reg_mem(OpndSize_32, sbb_opc, 4, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
1028            return 0;
1029        case INLINE_MATH_MAX_INT:
1030            get_virtual_reg(vC, OpndSize_32, 1, false);
1031            get_virtual_reg(vD, OpndSize_32, 2, false);
1032            compare_reg_reg(1, false, 2, false);
1033            conditional_move_reg_to_reg(OpndSize_32, Condition_GE, 2,
1034                                        false/*src*/, 1, false/*dst*/);
1035            get_self_pointer(3, false);
1036            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1037            return 0;
1038        case INLINE_MATH_ABS_FLOAT:
1039            get_virtual_reg(vC, OpndSize_32, 1, false);
1040            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 1, false);
1041            get_self_pointer(2, false);
1042            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
1043            return 0;
1044        case INLINE_MATH_ABS_DOUBLE:
1045            get_virtual_reg(vC, OpndSize_32, 1, false);
1046            get_virtual_reg(vD, OpndSize_32, 2, false);
1047            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 2, false);
1048            get_self_pointer(3, false);
1049            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1050            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
1051            return 0;
1052        case INLINE_STRING_FASTINDEXOF_II:
1053#if defined(USE_GLOBAL_STRING_DEFS)
1054            break;
1055#else
1056            get_virtual_reg(vC, OpndSize_32, 1, false);
1057            compare_imm_reg(OpndSize_32, 0, 1, false);
1058            get_virtual_reg(vD, OpndSize_32, 2, false);
1059            get_virtual_reg(vE, OpndSize_32, 3, false);
1060            conditional_jump(Condition_NE, ".do_inlined_string_fastIndexof",
1061                             true);
1062            scratchRegs[0] = PhysicalReg_SCRATCH_1;
1063            jumpToExceptionThrown(1/*exception number*/);
1064            insertLabel(".do_inlined_string_fastIndexof", true);
1065            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 4, false);
1066            move_mem_to_reg(OpndSize_32, 0x8, 1, false, 5, false);
1067            move_mem_to_reg(OpndSize_32, 0x10, 1, false, 6, false);
1068            alu_binary_reg_reg(OpndSize_32, xor_opc, 1, false, 1, false);
1069            compare_imm_reg(OpndSize_32, 0, 3, false);
1070            conditional_move_reg_to_reg(OpndSize_32, Condition_NS, 3, false, 1,
1071                                        false);
1072            compare_reg_reg(4, false, 1, false);
1073            conditional_jump(Condition_GE,
1074                             ".do_inlined_string_fastIndexof_exitfalse", true);
1075            dump_mem_scale_reg(Mnemonic_LEA, OpndSize_32, 5, false, 0xc/*disp*/,
1076                               6, false, 2, 5, false, LowOpndRegType_gp);
1077            movez_mem_disp_scale_to_reg(OpndSize_16, 5, false, 0, 1, false, 2,
1078                                        3, false);
1079            compare_reg_reg(3, false, 2, false);
1080            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
1081                             true);
1082            load_effective_addr(0x1, 1, false, 3, false);
1083            load_effective_addr_scale(5, false, 3, false, 2, 5, false);
1084            unconditional_jump(".do_inlined_string_fastIndexof_iter", true);
1085            insertLabel(".do_inlined_string_fastIndexof_ch_cmp", true);
1086            if(gDvm.executionMode == kExecutionModeNcgO1) {
1087                rememberState(1);
1088            }
1089            movez_mem_to_reg(OpndSize_16, 0, 5, false, 6, false);
1090            load_effective_addr(0x2, 5, false, 5, false);
1091            compare_reg_reg(6, false, 2, false);
1092            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
1093                             true);
1094            load_effective_addr(0x1, 3, false, 3, false);
1095            insertLabel(".do_inlined_string_fastIndexof_iter", true);
1096            compare_reg_reg(4, false, 3, false);
1097            move_reg_to_reg(OpndSize_32, 3, false, 1, false);
1098            if(gDvm.executionMode == kExecutionModeNcgO1) {
1099                transferToState(1);
1100            }
1101            conditional_jump(Condition_NE,
1102                             ".do_inlined_string_fastIndexof_ch_cmp", true);
1103            insertLabel(".do_inlined_string_fastIndexof_exitfalse", true);
1104            move_imm_to_reg(OpndSize_32, 0xffffffff, 1,  false);
1105            insertLabel(".do_inlined_string_fastIndexof_exit", true);
1106            get_self_pointer(7, false);
1107            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 7, false);
1108            return 0;
1109        case INLINE_FLOAT_TO_RAW_INT_BITS:
1110            get_virtual_reg(vC, OpndSize_32, 1, false);
1111            get_self_pointer(2, false);
1112            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
1113            return 0;
1114        case INLINE_INT_BITS_TO_FLOAT:
1115            get_virtual_reg(vC, OpndSize_32, 1, false);
1116            get_self_pointer(2, false);
1117            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
1118            return 0;
1119        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
1120            get_virtual_reg(vC, OpndSize_32, 1, false);
1121            get_self_pointer(3, false);
1122            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1123            get_virtual_reg(vD, OpndSize_32, 2, false);
1124            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
1125            return 0;
1126        case INLINE_LONG_BITS_TO_DOUBLE:
1127            get_virtual_reg(vC, OpndSize_32, 1, false);
1128            get_virtual_reg(vD, OpndSize_32, 2, false);
1129            get_self_pointer(3, false);
1130            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
1131            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1132            return 0;
1133        default:
1134                break;
1135    }
1136#endif
1137    get_self_pointer(PhysicalReg_SCRATCH_1, false);
1138    load_effective_addr(offsetof(Thread, interpSave.retval), PhysicalReg_SCRATCH_1, false, 1, false);
1139    load_effective_addr(-24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1140    move_reg_to_mem(OpndSize_32, 1, false, 16, PhysicalReg_ESP, true);
1141    if(num >= 1) {
1142        get_virtual_reg(vC, OpndSize_32, 2, false);
1143        move_reg_to_mem(OpndSize_32, 2, false, 0, PhysicalReg_ESP, true);
1144    }
1145    if(num >= 2) {
1146        get_virtual_reg(vD, OpndSize_32, 3, false);
1147        move_reg_to_mem(OpndSize_32, 3, false, 4, PhysicalReg_ESP, true);
1148    }
1149    if(num >= 3) {
1150        get_virtual_reg(vE, OpndSize_32, 4, false);
1151        move_reg_to_mem(OpndSize_32, 4, false, 8, PhysicalReg_ESP, true);
1152    }
1153    if(num >= 4) {
1154        get_virtual_reg(vF, OpndSize_32, 5, false);
1155        move_reg_to_mem(OpndSize_32, 5, false, 12, PhysicalReg_ESP, true);
1156    }
1157    beforeCall("execute_inline");
1158    load_imm_global_data_API("gDvmInlineOpsTable", OpndSize_32, 6, false);
1159    call_mem(16*tmp, 6, false);//
1160    afterCall("execute_inline");
1161    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
1162
1163    load_effective_addr(24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1164    conditional_jump(Condition_NE, ".execute_inline_done", true);
1165    //jump to dvmJitToExceptionThrown
1166    scratchRegs[0] = PhysicalReg_SCRATCH_1;
1167    jumpToExceptionThrown(1/*exception number*/);
1168    insertLabel(".execute_inline_done", true);
1169    rPC += 3;
1170    return 0;
1171}
1172#undef P_GPR_1
1173#undef P_GPR_2
1174#undef P_SCRATCH_1
1175#undef P_SCRATCH_2
1176#undef P_SCRATCH_3
1177#undef P_SCRATCH_4
1178
1179//! lower bytecode INVOKE_OBJECT_INIT_RANGE
1180
1181//!
1182int op_invoke_object_init_range() {
1183    return -1;
1184}
1185
1186#define P_GPR_1 PhysicalReg_EBX
1187#define P_SCRATCH_1 PhysicalReg_ESI
1188#define P_SCRATCH_2 PhysicalReg_EDX
1189#define PP_GPR_1 PhysicalReg_EBX
1190#define PP_GPR_2 PhysicalReg_ESI
1191#define PP_GPR_3 PhysicalReg_EAX
1192#define PP_GPR_4 PhysicalReg_EDX
1193//! common code for INVOKE_VIRTUAL_QUICK
1194
1195//! It uses helper function if the switch is on
1196int common_invoke_virtual_quick(bool hasRange, u2 vD, u2 IMMC) {
1197#ifdef WITH_JIT_INLINING
1198    /* An invoke with the MIR_INLINED is effectively a no-op */
1199    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
1200        return false;
1201    /*
1202     * If the invoke has non-null misPredBranchOver, we need to generate
1203     * the non-inlined version of the invoke here to handle the
1204     * mispredicted case.
1205     */
1206    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
1207        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
1208    }
1209#endif
1210    export_pc();
1211    constVREndOfBB();
1212    beforeCall("exception"); //dump GG, GL VRs
1213    /////////////////////////////////////////////////
1214    get_virtual_reg(vD, OpndSize_32, 1, false);
1215    simpleNullCheck(1, false, vD);
1216#ifndef PREDICTED_CHAINING
1217    move_mem_to_reg(OpndSize_32, 0, 1, false, 2, false);
1218    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 2, false, 3, false);
1219    move_mem_to_reg(OpndSize_32, IMMC, 3, false, PhysicalReg_ECX, true);
1220
1221    if(hasRange) {
1222        common_invokeMethodRange(ArgsDone_Full);
1223    }
1224    else {
1225        common_invokeMethodNoRange(ArgsDone_Full);
1226    }
1227#else
1228    gen_predicted_chain(hasRange, -1, IMMC, false, 1/*tmp1*/);
1229#endif
1230    ////////////////////////
1231    return 0;
1232}
1233#undef P_GPR_1
1234#undef P_SCRATCH_1
1235#undef P_SCRATCH_2
1236#undef PP_GPR_1
1237#undef PP_GPR_2
1238#undef PP_GPR_3
1239#undef PP_GPR_4
1240//! lower bytecode INVOKE_VIRTUAL_QUICK by calling common_invoke_virtual_quick
1241
1242//!
1243int op_invoke_virtual_quick() {
1244    u2 vD = FETCH(2) & 0xf;
1245    u2 IMMC = 4*FETCH(1);
1246    int retval = common_invoke_virtual_quick(false, vD, IMMC);
1247#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1248    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1249#endif
1250    rPC += 3;
1251    return retval;
1252}
1253//! lower bytecode INVOKE_VIRTUAL_QUICK_RANGE by calling common_invoke_virtual_quick
1254
1255//!
1256int op_invoke_virtual_quick_range() {
1257    u2 vD = FETCH(2);
1258    u2 IMMC = 4*FETCH(1);
1259    int retval = common_invoke_virtual_quick(true, vD, IMMC);
1260#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1261    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1262#endif
1263    rPC += 3;
1264    return retval;
1265}
1266#define P_GPR_1 PhysicalReg_EBX
1267#define P_GPR_2 PhysicalReg_ESI
1268#define P_SCRATCH_1 PhysicalReg_EDX
1269//! common code to lower INVOKE_SUPER_QUICK
1270
1271//!
1272int common_invoke_super_quick(bool hasRange, u2 vD, u2 IMMC) {
1273    export_pc();
1274    constVREndOfBB();
1275    beforeCall("exception"); //dump GG, GL VRs
1276    compare_imm_VR(OpndSize_32, 0, vD);
1277
1278    conditional_jump_global_API(Condition_E, "common_errNullObject", false);
1279    /* for trace-based JIT, callee is already resolved */
1280    int mIndex = IMMC/4;
1281    const Method *calleeMethod = currentMethod->clazz->super->vtable[mIndex];
1282    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
1283    if(hasRange) {
1284        common_invokeMethodRange(convertCalleeToType(calleeMethod));
1285    }
1286    else {
1287        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
1288    }
1289    return 0;
1290}
1291#undef P_GPR_1
1292#undef P_GPR_2
1293#undef P_SCRATCH_1
1294//! lower bytecode INVOKE_SUPER_QUICK by calling common_invoke_super_quick
1295
1296//!
1297int op_invoke_super_quick() {
1298    u2 vD = FETCH(2) & 0xf;
1299    u2 IMMC = 4*FETCH(1);
1300    int retval = common_invoke_super_quick(false, vD, IMMC);
1301#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1302    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1303#endif
1304    rPC += 3;
1305    return retval;
1306}
1307//! lower bytecode INVOKE_SUPER_QUICK_RANGE by calling common_invoke_super_quick
1308
1309//!
1310int op_invoke_super_quick_range() {
1311    u2 vD = FETCH(2);
1312    u2 IMMC = 4*FETCH(1);
1313    int retval = common_invoke_super_quick(true, vD, IMMC);
1314#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1315    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1316#endif
1317    rPC += 3;
1318    return retval;
1319}
1320/////// code to predict the callee method for invoke_virtual & invoke_interface
1321#define offChainingCell_clazz 8
1322#define offChainingCell_method 12
1323#define offChainingCell_counter 16
1324#define P_GPR_1 PhysicalReg_EBX
1325#define P_GPR_2 PhysicalReg_EAX
1326#define P_GPR_3 PhysicalReg_ESI
1327#define P_SCRATCH_2 PhysicalReg_EDX
1328/* TODO gingerbread: implemented for O1, but not for O0:
1329   valid input to JitToPatch & use icRechainCount */
1330/* update predicted method for invoke interface */
1331// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
1332void predicted_chain_interface_O0(u2 tmp) {
1333    ALOGI("TODO chain_interface_O0");
1334
1335    /* set up arguments to dvmFindInterfaceMethodInCache */
1336    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1337    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
1338    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
1339    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
1340    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
1341    scratchRegs[0] = PhysicalReg_EDX;
1342    call_dvmFindInterfaceMethodInCache();
1343    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1344
1345    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
1346       otherwise, jump to .find_interface_done */
1347    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
1348    conditional_jump(Condition_NE, ".find_interface_done", true);
1349    scratchRegs[0] = PhysicalReg_EAX;
1350    jumpToExceptionThrown(1/*exception number*/);
1351
1352    /* the interface method is found */
1353    insertLabel(".find_interface_done", true);
1354    /* reduce counter in chaining cell by 1 */
1355    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_SCRATCH_2, true); //counter
1356    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_SCRATCH_2, true);
1357    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offChainingCell_counter, P_GPR_1, true);
1358
1359    /* if counter is still greater than zero, skip prediction
1360       if it is zero, update predicted method */
1361    compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
1362    conditional_jump(Condition_G, ".skipPrediction", true);
1363
1364    /* call dvmJitToPatchPredictedChain to update predicted method */
1365    //%ecx has callee method for virtual, %eax has callee for interface
1366    /* set up arguments for dvmJitToPatchPredictedChain */
1367    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1368    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
1369    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1370    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
1371    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
1372    scratchRegs[0] = PhysicalReg_EAX;
1373    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1374    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1375    insertLabel(".skipPrediction", true);
1376    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1377}
1378
1379// 2 inputs: ChainingCell in temp 41, current class object in temp 40
1380void predicted_chain_interface_O1(u2 tmp) {
1381
1382    /* set up arguments to dvmFindInterfaceMethodInCache */
1383    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1384    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
1385    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
1386    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
1387    move_reg_to_mem(OpndSize_32, 40, false, 0, PhysicalReg_ESP, true);
1388    scratchRegs[0] = PhysicalReg_SCRATCH_10;
1389    call_dvmFindInterfaceMethodInCache();
1390    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1391
1392    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
1393       otherwise, jump to .find_interface_done */
1394    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
1395    conditional_jump(Condition_NE, ".find_interface_done", true);
1396    rememberState(3);
1397    scratchRegs[0] = PhysicalReg_SCRATCH_9;
1398    jumpToExceptionThrown(1/*exception number*/);
1399
1400    goToState(3);
1401    /* the interface method is found */
1402    insertLabel(".find_interface_done", true);
1403#if 1 //
1404    /* for gingerbread, counter is stored in glue structure
1405       if clazz is not initialized, set icRechainCount to 0, otherwise, reduce it by 1 */
1406    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
1407    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 45, false);
1408    move_imm_to_reg(OpndSize_32, 0, 43, false);
1409    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
1410    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
1411    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
1412    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
1413    /* sub_opc will update control flags, so compare_imm_reg must happen after */
1414    compare_imm_reg(OpndSize_32, 0, 45, false);
1415    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
1416    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
1417    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
1418#else
1419    /* reduce counter in chaining cell by 1 */
1420    move_mem_to_reg(OpndSize_32, offChainingCell_counter, 41, false, 33, false); //counter
1421    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
1422    move_reg_to_mem(OpndSize_32, 33, false, offChainingCell_counter, 41, false);
1423#endif
1424
1425    /* if counter is still greater than zero, skip prediction
1426       if it is zero, update predicted method */
1427    compare_imm_reg(OpndSize_32, 0, 43, false);
1428    conditional_jump(Condition_G, ".skipPrediction", true);
1429
1430    rememberState(4);
1431    /* call dvmJitToPatchPredictedChain to update predicted method */
1432    //%ecx has callee method for virtual, %eax has callee for interface
1433    /* set up arguments for dvmJitToPatchPredictedChain */
1434    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1435    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
1436    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
1437    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1438    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
1439    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
1440    scratchRegs[0] = PhysicalReg_SCRATCH_8;
1441    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1442    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1443    transferToState(4);
1444
1445    insertLabel(".skipPrediction", true);
1446    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1447}
1448
1449/* update predicted method for invoke virtual */
1450// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
1451void predicted_chain_virtual_O0(u2 IMMC) {
1452    ALOGI("TODO chain_virtual_O0");
1453
1454    /* reduce counter in chaining cell by 1 */
1455    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_GPR_2, true); //counter
1456    move_mem_to_reg(OpndSize_32, offClassObject_vtable, P_GPR_3, true, P_SCRATCH_2, true);
1457    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_GPR_2, true);
1458    move_mem_to_reg(OpndSize_32, IMMC, P_SCRATCH_2, true, PhysicalReg_ECX, true);
1459    move_reg_to_mem(OpndSize_32, P_GPR_2, true, offChainingCell_counter, P_GPR_1, true);
1460
1461    /* if counter is still greater than zero, skip prediction
1462       if it is zero, update predicted method */
1463    compare_imm_reg(OpndSize_32, 0, P_GPR_2, true);
1464    conditional_jump(Condition_G, ".skipPrediction", true);
1465
1466    /* call dvmJitToPatchPredictedChain to update predicted method */
1467    //%ecx has callee method for virtual, %eax has callee for interface
1468    /* set up arguments for dvmJitToPatchPredictedChain */
1469    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1470    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0,  PhysicalReg_ESP, true);
1471    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1472    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
1473    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
1474    scratchRegs[0] = PhysicalReg_EAX;
1475    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1476    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1477
1478    //callee method in %ecx for invoke virtual
1479    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1480    insertLabel(".skipPrediction", true);
1481}
1482
1483// 2 inputs: ChainingCell in temp 41, current class object in temp 40
1484// extra input: predicted clazz in temp 32
1485void predicted_chain_virtual_O1(u2 IMMC) {
1486
1487    /* reduce counter in chaining cell by 1 */
1488    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
1489    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
1490    move_imm_to_reg(OpndSize_32, 0, 43, false);
1491    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
1492    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 40, false, 34, false);
1493    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
1494    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
1495    compare_imm_reg(OpndSize_32, 0, 32, false); // after sub_opc
1496    move_mem_to_reg(OpndSize_32, IMMC, 34, false, PhysicalReg_ECX, true);
1497    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
1498    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
1499    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
1500
1501    /* if counter is still greater than zero, skip prediction
1502       if it is zero, update predicted method */
1503    compare_imm_reg(OpndSize_32, 0, 43, false);
1504    conditional_jump(Condition_G, ".skipPrediction", true);
1505
1506    rememberState(2);
1507    /* call dvmJitToPatchPredictedChain to update predicted method */
1508    //%ecx has callee method for virtual, %eax has callee for interface
1509    /* set up arguments for dvmJitToPatchPredictedChain */
1510    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1511    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0, PhysicalReg_ESP, true);
1512    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
1513    if(traceCurrentBB->taken)
1514        insertChainingWorklist(traceCurrentBB->taken->id, stream);
1515    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
1516    move_chain_to_mem(OpndSize_32, traceTakenId, 8, PhysicalReg_ESP, true); //predictedChainCell
1517    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
1518    scratchRegs[0] = PhysicalReg_SCRATCH_10;
1519    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1520    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1521
1522    //callee method in %ecx for invoke virtual
1523    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1524    transferToState(2);
1525
1526    insertLabel(".skipPrediction", true);
1527}
1528
1529static int invokeChain_inst = 0;
1530/* object "this" is in %ebx */
1531void gen_predicted_chain_O0(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
1532    ALOGI("TODO predicted_chain_O0");
1533
1534    /* get current class object */
1535    move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true,
1536             P_GPR_3, true);
1537#ifdef DEBUG_CALL_STACK3
1538    scratchRegs[0] = PhysicalReg_EAX;
1539    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1540    move_imm_to_reg(OpndSize_32, 0xdd11, PhysicalReg_EBX, true);
1541    call_debug_dumpSwitch();
1542#endif
1543
1544    /* get predicted clazz
1545       get predicted method
1546    */
1547    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1548    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, P_GPR_1, true); //predictedChainCell
1549    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, P_GPR_1, true, P_SCRATCH_2, true);//predicted clazz
1550    move_mem_to_reg(OpndSize_32, offChainingCell_method, P_GPR_1, true, PhysicalReg_ECX, true);//predicted method
1551
1552#ifdef DEBUG_CALL_STACK3
1553    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1554    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 8, PhysicalReg_ESP, true);
1555    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, 4, PhysicalReg_ESP, true);
1556    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
1557
1558    move_reg_to_reg(OpndSize_32, P_SCRATCH_2, true, PhysicalReg_EBX, true);
1559    call_debug_dumpSwitch();
1560    move_imm_to_reg(OpndSize_32, 0xdd22, PhysicalReg_EBX, true);
1561    scratchRegs[0] = PhysicalReg_EAX;
1562    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1563    move_reg_to_reg(OpndSize_32, P_GPR_3, true, PhysicalReg_EBX, true);
1564    call_debug_dumpSwitch();
1565    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
1566    call_debug_dumpSwitch();
1567
1568    move_mem_to_reg(OpndSize_32, 8, PhysicalReg_ESP, true, P_GPR_1, true);
1569    move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, P_SCRATCH_2, true);
1570    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, P_GPR_3, true);
1571    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1572#endif
1573
1574    /* compare current class object against predicted clazz
1575       if equal, prediction is still valid, jump to .invokeChain */
1576    //live registers: P_GPR_1, P_GPR_3, P_SCRATCH_2
1577    compare_reg_reg(P_GPR_3, true, P_SCRATCH_2, true);
1578    conditional_jump(Condition_E, ".invokeChain", true);
1579    invokeChain_inst++;
1580
1581    //get callee method and update predicted method if necessary
1582    if(isInterface) {
1583        predicted_chain_interface_O0(tmp);
1584    } else {
1585        predicted_chain_virtual_O0(IMMC);
1586    }
1587
1588#ifdef DEBUG_CALL_STACK3
1589    move_imm_to_reg(OpndSize_32, 0xeeee, PhysicalReg_EBX, true);
1590    scratchRegs[0] = PhysicalReg_EAX;
1591    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1592    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1593    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
1594    call_debug_dumpSwitch();
1595#endif
1596
1597    if(isRange) {
1598        common_invokeMethodRange(ArgsDone_Full);
1599    }
1600    else {
1601        common_invokeMethodNoRange(ArgsDone_Full);
1602    }
1603
1604    insertLabel(".invokeChain", true);
1605#ifdef DEBUG_CALL_STACK3
1606    move_imm_to_reg(OpndSize_32, 0xdddd, PhysicalReg_EBX, true);
1607    scratchRegs[0] = PhysicalReg_EAX;
1608    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1609    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1610    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
1611    call_debug_dumpSwitch();
1612    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
1613    call_debug_dumpSwitch();
1614#endif
1615
1616    if(isRange) {
1617        common_invokeMethodRange(ArgsDone_Normal);
1618    }
1619    else {
1620        common_invokeMethodNoRange(ArgsDone_Normal);
1621    }
1622}
1623
1624/* object "this" is in inputReg: 5 for virtual, 1 for interface, 1 for virtual_quick */
1625void gen_predicted_chain_O1(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
1626
1627    /* get current class object */
1628    move_mem_to_reg(OpndSize_32, offObject_clazz, inputReg, false,
1629             40, false);
1630
1631    /* get predicted clazz
1632       get predicted method
1633    */
1634    if(traceCurrentBB->taken)
1635        insertChainingWorklist(traceCurrentBB->taken->id, stream);
1636    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
1637    move_chain_to_reg(OpndSize_32, traceTakenId, 41, false); //predictedChainCell
1638    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 32, false);//predicted clazz
1639    move_mem_to_reg(OpndSize_32, offChainingCell_method, 41, false, PhysicalReg_ECX, true);//predicted method
1640
1641    /* update stack with parameters first, then decide the callee */
1642    if(isRange) common_invokeMethodRange_noJmp();
1643    else common_invokeMethodNoRange_noJmp();
1644
1645    /* compare current class object against predicted clazz
1646       if equal, prediction is still valid, jump to .invokeChain */
1647    compare_reg_reg(40, false, 32, false);
1648    conditional_jump(Condition_E, ".invokeChain", true);
1649    rememberState(1);
1650    invokeChain_inst++;
1651
1652    //get callee method and update predicted method if necessary
1653    if(isInterface) {
1654        predicted_chain_interface_O1(tmp);
1655    } else {
1656        predicted_chain_virtual_O1(IMMC);
1657    }
1658
1659    common_invokeMethod_Jmp(ArgsDone_Full); //will touch %ecx
1660
1661    insertLabel(".invokeChain", true);
1662    goToState(1);
1663    common_invokeMethod_Jmp(ArgsDone_Normal);
1664}
1665
1666void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
1667    return gen_predicted_chain_O1(isRange, tmp, IMMC, isInterface, inputReg);
1668}
1669#undef P_GPR_1
1670#undef P_GPR_2
1671#undef P_GPR_3
1672#undef P_SCRATCH_2
1673