LowerInvoke.cpp revision 0c2dc522d0e120f346cf0a40c8cf0c93346131c2
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            call_dvmJitToInterpTraceSelectNoChain(); //input: rPC in %ebx
837        } else {
838            //jump to the stub at (%esp)
839            move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true,
840                            PhysicalReg_EDX, true);
841            load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
842            unconditional_jump_reg(PhysicalReg_EDX, true);
843        }
844    }
845
846    if(form == ArgsDone_Full) generate_invokeNative(generateForNcg);
847    generate_stackOverflow();
848    return 0;
849}
850
851/* when WITH_JIT is true,
852     JIT'ed code invokes native method, after invoke, execution will continue
853     with the interpreter or with JIT'ed code if chained
854*/
855void generate_invokeNative(bool generateForNcg) {
856    insertLabel(".invokeNative", true);
857    //if(!generateForNcg)
858    //    load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
859    load_effective_addr(-28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
860    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
861    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 20, PhysicalReg_ESP, true);
862    scratchRegs[0] = PhysicalReg_EDX;
863    get_self_pointer(P_SCRATCH_1, true); //glue->self
864    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 8, PhysicalReg_ESP, true);
865    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 12, PhysicalReg_ESP, true);
866    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, 24, PhysicalReg_ESP, true);
867    move_mem_to_reg(OpndSize_32, offThread_jniLocal_nextEntry, P_SCRATCH_1, true, P_SCRATCH_2, true); //get self->local_next
868    scratchRegs[1] = PhysicalReg_EAX;
869    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_1, true); //update jniLocalRef of stack
870    move_reg_to_mem(OpndSize_32, P_GPR_1, true, offThread_curFrame, P_SCRATCH_1, true); //set self->curFrame
871    move_imm_to_mem(OpndSize_32, 0, offThread_inJitCodeCache, P_SCRATCH_1, true); //clear self->inJitCodeCache
872    load_effective_addr(offsetof(Thread, interpSave.retval), P_SCRATCH_1, true, P_SCRATCH_3, true); //self->retval
873    move_reg_to_mem(OpndSize_32, P_SCRATCH_3, true, 4, PhysicalReg_ESP, true);
874    //NOTE: native method checks the interpreted stack for arguments
875    //      The immediate arguments on native stack: address of return value, new FP, self
876    call_mem(40, PhysicalReg_ECX, true);//*40(%ecx)
877    //we can't assume the argument stack is unmodified after the function call
878    //duplicate newFP & glue->self on stack: newFP (-28 & -8) glue->self (-16 & -4)
879    move_mem_to_reg(OpndSize_32, 20, PhysicalReg_ESP, true, P_GPR_3, true); //new FP
880    move_mem_to_reg(OpndSize_32, 24, PhysicalReg_ESP, true, P_GPR_1, true); //glue->self
881    load_effective_addr(28, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
882    move_mem_to_reg(OpndSize_32, offStackSaveArea_localRefTop-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_1, true); //newSaveArea->jniLocal
883    compare_imm_mem(OpndSize_32, 0, offThread_exception, P_GPR_1, true); //self->exception
884    if(!generateForNcg)
885        load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
886    //NOTE: PhysicalReg_FP should be callee-saved register
887    move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, P_GPR_1, true); //set self->curFrame
888    move_reg_to_mem(OpndSize_32, P_SCRATCH_1, true, offThread_jniLocal_nextEntry, P_GPR_1, true); //set self->jniLocal
889    conditional_jump(Condition_NE, "common_exceptionThrown", false);
890    if(!generateForNcg) {
891        //get returnAddr, if it is not NULL,
892        //    return to JIT'ed returnAddr after executing the native method
893        /* to correctly handle code cache reset:
894           update returnAddr and check returnAddr after done with the native method
895           if returnAddr is set to NULL during code cache reset,
896           the execution will correctly continue with interpreter */
897        move_mem_to_reg(OpndSize_32, offStackSaveArea_returnAddr-sizeofStackSaveArea, P_GPR_3, true, P_SCRATCH_2, true);
898        //set self->inJitCodeCache to returnAddr (P_GPR_1 is in %ebx)
899        move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offThread_inJitCodeCache, P_GPR_1, true);
900        move_mem_to_reg(OpndSize_32, offStackSaveArea_savedPc-sizeofStackSaveArea, P_GPR_3, true, PhysicalReg_EBX, true); //savedPc
901        compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
902        conditional_jump(Condition_E, ".nativeToInterp", true);
903        unconditional_jump_reg(P_SCRATCH_2, true);
904        //if returnAddr is NULL, return to interpreter after executing the native method
905        insertLabel(".nativeToInterp", true);
906        //move rPC by 6 (3 bytecode units for INVOKE)
907        alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EBX, true);
908        scratchRegs[0] = PhysicalReg_EAX;
909        call_dvmJitToInterpTraceSelectNoChain(); //rPC in %ebx
910    }
911    return;
912}
913void generate_stackOverflow() {
914    insertLabel(".stackOverflow", true);
915    //load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
916    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 4, PhysicalReg_ESP, true);
917    get_self_pointer(P_GPR_1, true); //glue->self
918    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 0, PhysicalReg_ESP, true);
919    call_dvmHandleStackOverflow();
920    load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
921    unconditional_jump("common_exceptionThrown", false);
922}
923#undef P_GPR_1
924#undef P_GPR_2
925#undef P_GPR_3
926#undef P_SCRATCH_1
927#undef P_SCRATCH_2
928#undef P_SCRATCH_3
929#undef P_SCRATCH_4
930#undef P_SCRATCH_5
931#undef P_SCRATCH_6
932
933/////////////////////////////////////////////
934#define P_GPR_1 PhysicalReg_EBX
935#define P_GPR_2 PhysicalReg_ECX
936#define P_SCRATCH_1 PhysicalReg_ESI
937#define P_SCRATCH_2 PhysicalReg_EDX
938#define P_SCRATCH_3 PhysicalReg_ESI
939#define P_SCRATCH_4 PhysicalReg_EDX
940//! lower bytecode EXECUTE_INLINE
941
942//!
943int op_execute_inline(bool isRange) {
944    //tmp, vC, vD, vE, vF
945    int num;
946    if(!isRange) num = INST_B(inst);
947    else num = INST_AA(inst);
948    u2 tmp = FETCH(1);
949    u2 vC, vD, vE, vF;
950    if(!isRange) {
951        vC = FETCH(2) & 0xf;
952        vD = (FETCH(2) >> 4) & 0xf;
953        vE = (FETCH(2) >> 8) & 0xf;
954        vF = FETCH(2) >> 12;
955    } else {
956        vC = FETCH(2);
957        vD = vC + 1;
958        vE = vC + 2;
959        vF = vC + 3;
960    }
961    export_pc();
962    switch (tmp) {
963        case INLINE_EMPTYINLINEMETHOD:
964            return 0;  /* Nop */
965        case INLINE_STRING_LENGTH:
966            get_virtual_reg(vC, OpndSize_32, 1, false);
967            compare_imm_reg(OpndSize_32, 0, 1, false);
968            conditional_jump(Condition_NE, ".do_inlined_string_length", true);
969            scratchRegs[0] = PhysicalReg_SCRATCH_1;
970            jumpToExceptionThrown(1/*exception number*/);
971            insertLabel(".do_inlined_string_length", true);
972            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 2, false);
973            get_self_pointer(3, false);
974            move_reg_to_mem(OpndSize_32, 2, false, offsetof(Thread, interpSave.retval), 3, false);
975            return 0;
976        case INLINE_STRING_IS_EMPTY:
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            compare_imm_mem(OpndSize_32, 0, 0x14, 1, false);
984            conditional_jump(Condition_E, ".inlined_string_length_return_true",
985                             true);
986            get_self_pointer(2, false);
987            move_imm_to_mem(OpndSize_32, 0, offsetof(Thread, interpSave.retval), 2, false);
988            unconditional_jump(".inlined_string_length_done", true);
989            insertLabel(".inlined_string_length_return_true", true);
990            get_self_pointer(2, false);
991            move_imm_to_mem(OpndSize_32, 1, offsetof(Thread, interpSave.retval), 2, false);
992            insertLabel(".inlined_string_length_done", true);
993            return 0;
994        case INLINE_MATH_ABS_INT:
995            get_virtual_reg(vC, OpndSize_32, 1, false);
996            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
997            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 2, false);
998            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 1, false);
999            alu_binary_reg_reg(OpndSize_32, sub_opc, 2, false, 1, false);
1000            get_self_pointer(3, false);
1001            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1002            return 0;
1003        case INLINE_MATH_ABS_LONG:
1004            get_virtual_reg(vD, OpndSize_32, 1, false);
1005            move_reg_to_reg(OpndSize_32, 1, false, 2, false);
1006            alu_binary_imm_reg(OpndSize_32, sar_opc, 0x1f, 1, false);
1007            move_reg_to_reg(OpndSize_32, 1, false, 3, false);
1008            move_reg_to_reg(OpndSize_32, 1, false, 4, false);
1009            get_virtual_reg(vC, OpndSize_32, 5, false);
1010            alu_binary_reg_reg(OpndSize_32, xor_opc, 5, false, 1, false);
1011            get_self_pointer(6, false);
1012            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 6, false);
1013            alu_binary_reg_reg(OpndSize_32, xor_opc, 2, false, 3, false);
1014            move_reg_to_mem(OpndSize_32, 3, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
1015            alu_binary_reg_mem(OpndSize_32, sub_opc, 4, false, offsetof(Thread, interpSave.retval), 6, false);
1016            alu_binary_reg_mem(OpndSize_32, sbb_opc, 4, false, 4 + offsetof(Thread, interpSave.retval), 6, false);
1017            return 0;
1018        case INLINE_MATH_MAX_INT:
1019            get_virtual_reg(vC, OpndSize_32, 1, false);
1020            get_virtual_reg(vD, OpndSize_32, 2, false);
1021            compare_reg_reg(1, false, 2, false);
1022            conditional_move_reg_to_reg(OpndSize_32, Condition_GE, 2,
1023                                        false/*src*/, 1, false/*dst*/);
1024            get_self_pointer(3, false);
1025            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1026            return 0;
1027        case INLINE_MATH_ABS_FLOAT:
1028            get_virtual_reg(vC, OpndSize_32, 1, false);
1029            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 1, false);
1030            get_self_pointer(2, false);
1031            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
1032            return 0;
1033        case INLINE_MATH_ABS_DOUBLE:
1034            get_virtual_reg(vC, OpndSize_32, 1, false);
1035            get_virtual_reg(vD, OpndSize_32, 2, false);
1036            alu_binary_imm_reg(OpndSize_32, and_opc, 0x7fffffff, 2, false);
1037            get_self_pointer(3, false);
1038            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1039            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
1040            return 0;
1041        case INLINE_STRING_FASTINDEXOF_II:
1042#if defined(USE_GLOBAL_STRING_DEFS)
1043            break;
1044#else
1045            get_virtual_reg(vC, OpndSize_32, 1, false);
1046            compare_imm_reg(OpndSize_32, 0, 1, false);
1047            get_virtual_reg(vD, OpndSize_32, 2, false);
1048            get_virtual_reg(vE, OpndSize_32, 3, false);
1049            conditional_jump(Condition_NE, ".do_inlined_string_fastIndexof",
1050                             true);
1051            scratchRegs[0] = PhysicalReg_SCRATCH_1;
1052            jumpToExceptionThrown(1/*exception number*/);
1053            insertLabel(".do_inlined_string_fastIndexof", true);
1054            move_mem_to_reg(OpndSize_32, 0x14, 1, false, 4, false);
1055            move_mem_to_reg(OpndSize_32, 0x8, 1, false, 5, false);
1056            move_mem_to_reg(OpndSize_32, 0x10, 1, false, 6, false);
1057            alu_binary_reg_reg(OpndSize_32, xor_opc, 1, false, 1, false);
1058            compare_imm_reg(OpndSize_32, 0, 3, false);
1059            conditional_move_reg_to_reg(OpndSize_32, Condition_NS, 3, false, 1,
1060                                        false);
1061            compare_reg_reg(4, false, 1, false);
1062            conditional_jump(Condition_GE,
1063                             ".do_inlined_string_fastIndexof_exitfalse", true);
1064            dump_mem_scale_reg(Mnemonic_LEA, OpndSize_32, 5, false, 0xc/*disp*/,
1065                               6, false, 2, 5, false, LowOpndRegType_gp);
1066            movez_mem_disp_scale_to_reg(OpndSize_16, 5, false, 0, 1, false, 2,
1067                                        3, false);
1068            compare_reg_reg(3, false, 2, false);
1069            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
1070                             true);
1071            load_effective_addr(0x1, 1, false, 3, false);
1072            load_effective_addr_scale(5, false, 3, false, 2, 5, false);
1073            unconditional_jump(".do_inlined_string_fastIndexof_iter", true);
1074            insertLabel(".do_inlined_string_fastIndexof_ch_cmp", true);
1075            if(gDvm.executionMode == kExecutionModeNcgO1) {
1076                rememberState(1);
1077            }
1078            movez_mem_to_reg(OpndSize_16, 0, 5, false, 6, false);
1079            load_effective_addr(0x2, 5, false, 5, false);
1080            compare_reg_reg(6, false, 2, false);
1081            conditional_jump(Condition_E, ".do_inlined_string_fastIndexof_exit",
1082                             true);
1083            load_effective_addr(0x1, 3, false, 3, false);
1084            insertLabel(".do_inlined_string_fastIndexof_iter", true);
1085            compare_reg_reg(4, false, 3, false);
1086            move_reg_to_reg(OpndSize_32, 3, false, 1, false);
1087            if(gDvm.executionMode == kExecutionModeNcgO1) {
1088                transferToState(1);
1089            }
1090            conditional_jump(Condition_NE,
1091                             ".do_inlined_string_fastIndexof_ch_cmp", true);
1092            insertLabel(".do_inlined_string_fastIndexof_exitfalse", true);
1093            move_imm_to_reg(OpndSize_32, 0xffffffff, 1,  false);
1094            insertLabel(".do_inlined_string_fastIndexof_exit", true);
1095            get_self_pointer(7, false);
1096            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 7, false);
1097            return 0;
1098        case INLINE_FLOAT_TO_RAW_INT_BITS:
1099            get_virtual_reg(vC, OpndSize_32, 1, false);
1100            get_self_pointer(2, false);
1101            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
1102            return 0;
1103        case INLINE_INT_BITS_TO_FLOAT:
1104            get_virtual_reg(vC, OpndSize_32, 1, false);
1105            get_self_pointer(2, false);
1106            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 2, false);
1107            return 0;
1108        case INLINE_DOUBLE_TO_RAW_LONG_BITS:
1109            get_virtual_reg(vC, OpndSize_32, 1, false);
1110            get_self_pointer(3, false);
1111            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1112            get_virtual_reg(vD, OpndSize_32, 2, false);
1113            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
1114            return 0;
1115        case INLINE_LONG_BITS_TO_DOUBLE:
1116            get_virtual_reg(vC, OpndSize_32, 1, false);
1117            get_virtual_reg(vD, OpndSize_32, 2, false);
1118            get_self_pointer(3, false);
1119            move_reg_to_mem(OpndSize_32, 2, false, 4 + offsetof(Thread, interpSave.retval), 3, false);
1120            move_reg_to_mem(OpndSize_32, 1, false, offsetof(Thread, interpSave.retval), 3, false);
1121            return 0;
1122        default:
1123                break;
1124    }
1125#endif
1126    get_self_pointer(PhysicalReg_SCRATCH_1, false);
1127    load_effective_addr(offsetof(Thread, interpSave.retval), PhysicalReg_SCRATCH_1, false, 1, false);
1128    load_effective_addr(-24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1129    move_reg_to_mem(OpndSize_32, 1, false, 16, PhysicalReg_ESP, true);
1130    if(num >= 1) {
1131        get_virtual_reg(vC, OpndSize_32, 2, false);
1132        move_reg_to_mem(OpndSize_32, 2, false, 0, PhysicalReg_ESP, true);
1133    }
1134    if(num >= 2) {
1135        get_virtual_reg(vD, OpndSize_32, 3, false);
1136        move_reg_to_mem(OpndSize_32, 3, false, 4, PhysicalReg_ESP, true);
1137    }
1138    if(num >= 3) {
1139        get_virtual_reg(vE, OpndSize_32, 4, false);
1140        move_reg_to_mem(OpndSize_32, 4, false, 8, PhysicalReg_ESP, true);
1141    }
1142    if(num >= 4) {
1143        get_virtual_reg(vF, OpndSize_32, 5, false);
1144        move_reg_to_mem(OpndSize_32, 5, false, 12, PhysicalReg_ESP, true);
1145    }
1146    beforeCall("execute_inline");
1147    load_imm_global_data_API("gDvmInlineOpsTable", OpndSize_32, 6, false);
1148    call_mem(16*tmp, 6, false);//
1149    afterCall("execute_inline");
1150    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
1151
1152    load_effective_addr(24, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1153    conditional_jump(Condition_NE, ".execute_inline_done", true);
1154    //jump to dvmJitToExceptionThrown
1155    scratchRegs[0] = PhysicalReg_SCRATCH_1;
1156    jumpToExceptionThrown(1/*exception number*/);
1157    insertLabel(".execute_inline_done", true);
1158    rPC += 3;
1159    return 0;
1160}
1161#undef P_GPR_1
1162#undef P_GPR_2
1163#undef P_SCRATCH_1
1164#undef P_SCRATCH_2
1165#undef P_SCRATCH_3
1166#undef P_SCRATCH_4
1167
1168//! lower bytecode INVOKE_OBJECT_INIT_RANGE
1169
1170//!
1171int op_invoke_object_init_range() {
1172    return -1;
1173}
1174
1175#define P_GPR_1 PhysicalReg_EBX
1176#define P_SCRATCH_1 PhysicalReg_ESI
1177#define P_SCRATCH_2 PhysicalReg_EDX
1178#define PP_GPR_1 PhysicalReg_EBX
1179#define PP_GPR_2 PhysicalReg_ESI
1180#define PP_GPR_3 PhysicalReg_EAX
1181#define PP_GPR_4 PhysicalReg_EDX
1182//! common code for INVOKE_VIRTUAL_QUICK
1183
1184//! It uses helper function if the switch is on
1185int common_invoke_virtual_quick(bool hasRange, u2 vD, u2 IMMC) {
1186#ifdef WITH_JIT_INLINING
1187    /* An invoke with the MIR_INLINED is effectively a no-op */
1188    if (traceCurrentMIR->OptimizationFlags & MIR_INLINED)
1189        return false;
1190    /*
1191     * If the invoke has non-null misPredBranchOver, we need to generate
1192     * the non-inlined version of the invoke here to handle the
1193     * mispredicted case.
1194     */
1195    if (traceCurrentMIR->meta.callsiteInfo->misPredBranchOver) {
1196        genLandingPadForMispredictedCallee(); //cUnit, mir, bb, labelList);
1197    }
1198#endif
1199    export_pc();
1200    constVREndOfBB();
1201    beforeCall("exception"); //dump GG, GL VRs
1202    /////////////////////////////////////////////////
1203    get_virtual_reg(vD, OpndSize_32, 1, false);
1204    simpleNullCheck(1, false, vD);
1205#ifndef PREDICTED_CHAINING
1206    move_mem_to_reg(OpndSize_32, 0, 1, false, 2, false);
1207    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 2, false, 3, false);
1208    move_mem_to_reg(OpndSize_32, IMMC, 3, false, PhysicalReg_ECX, true);
1209
1210    if(hasRange) {
1211        common_invokeMethodRange(ArgsDone_Full);
1212    }
1213    else {
1214        common_invokeMethodNoRange(ArgsDone_Full);
1215    }
1216#else
1217    gen_predicted_chain(hasRange, -1, IMMC, false, 1/*tmp1*/);
1218#endif
1219    ////////////////////////
1220    return 0;
1221}
1222#undef P_GPR_1
1223#undef P_SCRATCH_1
1224#undef P_SCRATCH_2
1225#undef PP_GPR_1
1226#undef PP_GPR_2
1227#undef PP_GPR_3
1228#undef PP_GPR_4
1229//! lower bytecode INVOKE_VIRTUAL_QUICK by calling common_invoke_virtual_quick
1230
1231//!
1232int op_invoke_virtual_quick() {
1233    u2 vD = FETCH(2) & 0xf;
1234    u2 IMMC = 4*FETCH(1);
1235    int retval = common_invoke_virtual_quick(false, vD, IMMC);
1236#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1237    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1238#endif
1239    rPC += 3;
1240    return retval;
1241}
1242//! lower bytecode INVOKE_VIRTUAL_QUICK_RANGE by calling common_invoke_virtual_quick
1243
1244//!
1245int op_invoke_virtual_quick_range() {
1246    u2 vD = FETCH(2);
1247    u2 IMMC = 4*FETCH(1);
1248    int retval = common_invoke_virtual_quick(true, vD, IMMC);
1249#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1250    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1251#endif
1252    rPC += 3;
1253    return retval;
1254}
1255#define P_GPR_1 PhysicalReg_EBX
1256#define P_GPR_2 PhysicalReg_ESI
1257#define P_SCRATCH_1 PhysicalReg_EDX
1258//! common code to lower INVOKE_SUPER_QUICK
1259
1260//!
1261int common_invoke_super_quick(bool hasRange, u2 vD, u2 IMMC) {
1262    export_pc();
1263    constVREndOfBB();
1264    beforeCall("exception"); //dump GG, GL VRs
1265    compare_imm_VR(OpndSize_32, 0, vD);
1266
1267    conditional_jump_global_API(Condition_E, "common_errNullObject", false);
1268    /* for trace-based JIT, callee is already resolved */
1269    int mIndex = IMMC/4;
1270    const Method *calleeMethod = currentMethod->clazz->super->vtable[mIndex];
1271    move_imm_to_reg(OpndSize_32, (int) calleeMethod, PhysicalReg_ECX, true);
1272    if(hasRange) {
1273        common_invokeMethodRange(convertCalleeToType(calleeMethod));
1274    }
1275    else {
1276        common_invokeMethodNoRange(convertCalleeToType(calleeMethod));
1277    }
1278    return 0;
1279}
1280#undef P_GPR_1
1281#undef P_GPR_2
1282#undef P_SCRATCH_1
1283//! lower bytecode INVOKE_SUPER_QUICK by calling common_invoke_super_quick
1284
1285//!
1286int op_invoke_super_quick() {
1287    u2 vD = FETCH(2) & 0xf;
1288    u2 IMMC = 4*FETCH(1);
1289    int retval = common_invoke_super_quick(false, vD, IMMC);
1290#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1291    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1292#endif
1293    rPC += 3;
1294    return retval;
1295}
1296//! lower bytecode INVOKE_SUPER_QUICK_RANGE by calling common_invoke_super_quick
1297
1298//!
1299int op_invoke_super_quick_range() {
1300    u2 vD = FETCH(2);
1301    u2 IMMC = 4*FETCH(1);
1302    int retval = common_invoke_super_quick(true, vD, IMMC);
1303#if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2)
1304    insertMapWorklist(offsetPC+3, stream - streamMethodStart, 1); //check when helper switch is on
1305#endif
1306    rPC += 3;
1307    return retval;
1308}
1309/////// code to predict the callee method for invoke_virtual & invoke_interface
1310#define offChainingCell_clazz 8
1311#define offChainingCell_method 12
1312#define offChainingCell_counter 16
1313#define P_GPR_1 PhysicalReg_EBX
1314#define P_GPR_2 PhysicalReg_EAX
1315#define P_GPR_3 PhysicalReg_ESI
1316#define P_SCRATCH_2 PhysicalReg_EDX
1317/* TODO gingerbread: implemented for O1, but not for O0:
1318   valid input to JitToPatch & use icRechainCount */
1319/* update predicted method for invoke interface */
1320// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
1321void predicted_chain_interface_O0(u2 tmp) {
1322    ALOGI("TODO chain_interface_O0");
1323
1324    /* set up arguments to dvmFindInterfaceMethodInCache */
1325    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1326    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
1327    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
1328    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
1329    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
1330    scratchRegs[0] = PhysicalReg_EDX;
1331    call_dvmFindInterfaceMethodInCache();
1332    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1333
1334    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
1335       otherwise, jump to .find_interface_done */
1336    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
1337    conditional_jump(Condition_NE, ".find_interface_done", true);
1338    scratchRegs[0] = PhysicalReg_EAX;
1339    jumpToExceptionThrown(1/*exception number*/);
1340
1341    /* the interface method is found */
1342    insertLabel(".find_interface_done", true);
1343    /* reduce counter in chaining cell by 1 */
1344    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_SCRATCH_2, true); //counter
1345    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_SCRATCH_2, true);
1346    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, offChainingCell_counter, P_GPR_1, true);
1347
1348    /* if counter is still greater than zero, skip prediction
1349       if it is zero, update predicted method */
1350    compare_imm_reg(OpndSize_32, 0, P_SCRATCH_2, true);
1351    conditional_jump(Condition_G, ".skipPrediction", true);
1352
1353    /* call dvmJitToPatchPredictedChain to update predicted method */
1354    //%ecx has callee method for virtual, %eax has callee for interface
1355    /* set up arguments for dvmJitToPatchPredictedChain */
1356    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1357    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
1358    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1359    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
1360    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
1361    scratchRegs[0] = PhysicalReg_EAX;
1362    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1363    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1364    insertLabel(".skipPrediction", true);
1365    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1366}
1367
1368// 2 inputs: ChainingCell in temp 41, current class object in temp 40
1369void predicted_chain_interface_O1(u2 tmp) {
1370
1371    /* set up arguments to dvmFindInterfaceMethodInCache */
1372    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1373    move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true);
1374    move_imm_to_mem(OpndSize_32, (int) currentMethod->clazz->pDvmDex, 12, PhysicalReg_ESP, true);
1375    move_imm_to_mem(OpndSize_32, (int) currentMethod, 8, PhysicalReg_ESP, true);
1376    move_reg_to_mem(OpndSize_32, 40, false, 0, PhysicalReg_ESP, true);
1377    scratchRegs[0] = PhysicalReg_SCRATCH_10;
1378    call_dvmFindInterfaceMethodInCache();
1379    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1380
1381    /* if dvmFindInterfaceMethodInCache returns NULL, throw exception
1382       otherwise, jump to .find_interface_done */
1383    compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
1384    conditional_jump(Condition_NE, ".find_interface_done", true);
1385    rememberState(3);
1386    scratchRegs[0] = PhysicalReg_SCRATCH_9;
1387    jumpToExceptionThrown(1/*exception number*/);
1388
1389    goToState(3);
1390    /* the interface method is found */
1391    insertLabel(".find_interface_done", true);
1392#if 1 //
1393    /* for gingerbread, counter is stored in glue structure
1394       if clazz is not initialized, set icRechainCount to 0, otherwise, reduce it by 1 */
1395    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
1396    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 45, false);
1397    move_imm_to_reg(OpndSize_32, 0, 43, false);
1398    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
1399    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
1400    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
1401    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
1402    /* sub_opc will update control flags, so compare_imm_reg must happen after */
1403    compare_imm_reg(OpndSize_32, 0, 45, false);
1404    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
1405    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
1406    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
1407#else
1408    /* reduce counter in chaining cell by 1 */
1409    move_mem_to_reg(OpndSize_32, offChainingCell_counter, 41, false, 33, false); //counter
1410    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
1411    move_reg_to_mem(OpndSize_32, 33, false, offChainingCell_counter, 41, false);
1412#endif
1413
1414    /* if counter is still greater than zero, skip prediction
1415       if it is zero, update predicted method */
1416    compare_imm_reg(OpndSize_32, 0, 43, false);
1417    conditional_jump(Condition_G, ".skipPrediction", true);
1418
1419    rememberState(4);
1420    /* call dvmJitToPatchPredictedChain to update predicted method */
1421    //%ecx has callee method for virtual, %eax has callee for interface
1422    /* set up arguments for dvmJitToPatchPredictedChain */
1423    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1424    move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
1425    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
1426    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1427    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
1428    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
1429    scratchRegs[0] = PhysicalReg_SCRATCH_8;
1430    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1431    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1432    transferToState(4);
1433
1434    insertLabel(".skipPrediction", true);
1435    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1436}
1437
1438/* update predicted method for invoke virtual */
1439// 2 inputs: ChainingCell in P_GPR_1, current class object in P_GPR_3
1440void predicted_chain_virtual_O0(u2 IMMC) {
1441    ALOGI("TODO chain_virtual_O0");
1442
1443    /* reduce counter in chaining cell by 1 */
1444    move_mem_to_reg(OpndSize_32, offChainingCell_counter, P_GPR_1, true, P_GPR_2, true); //counter
1445    move_mem_to_reg(OpndSize_32, offClassObject_vtable, P_GPR_3, true, P_SCRATCH_2, true);
1446    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, P_GPR_2, true);
1447    move_mem_to_reg(OpndSize_32, IMMC, P_SCRATCH_2, true, PhysicalReg_ECX, true);
1448    move_reg_to_mem(OpndSize_32, P_GPR_2, true, offChainingCell_counter, P_GPR_1, true);
1449
1450    /* if counter is still greater than zero, skip prediction
1451       if it is zero, update predicted method */
1452    compare_imm_reg(OpndSize_32, 0, P_GPR_2, true);
1453    conditional_jump(Condition_G, ".skipPrediction", true);
1454
1455    /* call dvmJitToPatchPredictedChain to update predicted method */
1456    //%ecx has callee method for virtual, %eax has callee for interface
1457    /* set up arguments for dvmJitToPatchPredictedChain */
1458    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1459    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0,  PhysicalReg_ESP, true);
1460    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1461    move_chain_to_mem(OpndSize_32, traceCurrentBB->taken->id, 8, PhysicalReg_ESP, true); //predictedChainCell
1462    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 12, PhysicalReg_ESP, true);
1463    scratchRegs[0] = PhysicalReg_EAX;
1464    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1465    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1466
1467    //callee method in %ecx for invoke virtual
1468    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1469    insertLabel(".skipPrediction", true);
1470}
1471
1472// 2 inputs: ChainingCell in temp 41, current class object in temp 40
1473// extra input: predicted clazz in temp 32
1474void predicted_chain_virtual_O1(u2 IMMC) {
1475
1476    /* reduce counter in chaining cell by 1 */
1477    /* for gingerbread: t43 = 0 t44 = t33 t33-- cmov_ne t43 = t33 cmov_ne t44 = t33 */
1478    get_self_pointer(PhysicalReg_SCRATCH_7, isScratchPhysical);
1479    move_imm_to_reg(OpndSize_32, 0, 43, false);
1480    move_mem_to_reg(OpndSize_32, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical, 33, false); //counter
1481    move_mem_to_reg(OpndSize_32, offClassObject_vtable, 40, false, 34, false);
1482    move_reg_to_reg(OpndSize_32, 33, false, 44, false);
1483    alu_binary_imm_reg(OpndSize_32, sub_opc, 0x1, 33, false);
1484    compare_imm_reg(OpndSize_32, 0, 32, false); // after sub_opc
1485    move_mem_to_reg(OpndSize_32, IMMC, 34, false, PhysicalReg_ECX, true);
1486    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 43, false/*dst*/);
1487    conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 33, false/*src*/, 44, false/*dst*/);
1488    move_reg_to_mem(OpndSize_32, 44, false, offsetof(Thread, icRechainCount), PhysicalReg_SCRATCH_7, isScratchPhysical);
1489
1490    /* if counter is still greater than zero, skip prediction
1491       if it is zero, update predicted method */
1492    compare_imm_reg(OpndSize_32, 0, 43, false);
1493    conditional_jump(Condition_G, ".skipPrediction", true);
1494
1495    rememberState(2);
1496    /* call dvmJitToPatchPredictedChain to update predicted method */
1497    //%ecx has callee method for virtual, %eax has callee for interface
1498    /* set up arguments for dvmJitToPatchPredictedChain */
1499    load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1500    move_reg_to_mem(OpndSize_32, PhysicalReg_ECX, true, 0, PhysicalReg_ESP, true);
1501    move_reg_to_mem(OpndSize_32, PhysicalReg_SCRATCH_7, isScratchPhysical, 4, PhysicalReg_ESP, true);
1502    if(traceCurrentBB->taken)
1503        insertChainingWorklist(traceCurrentBB->taken->id, stream);
1504    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
1505    move_chain_to_mem(OpndSize_32, traceTakenId, 8, PhysicalReg_ESP, true); //predictedChainCell
1506    move_reg_to_mem(OpndSize_32, 40, false, 12, PhysicalReg_ESP, true);
1507    scratchRegs[0] = PhysicalReg_SCRATCH_10;
1508    call_dvmJitToPatchPredictedChain(); //inputs: method, unused, predictedChainCell, clazz
1509    load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1510
1511    //callee method in %ecx for invoke virtual
1512    move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, PhysicalReg_ECX, true);
1513    transferToState(2);
1514
1515    insertLabel(".skipPrediction", true);
1516}
1517
1518static int invokeChain_inst = 0;
1519/* object "this" is in %ebx */
1520void gen_predicted_chain_O0(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
1521    ALOGI("TODO predicted_chain_O0");
1522
1523    /* get current class object */
1524    move_mem_to_reg(OpndSize_32, offObject_clazz, PhysicalReg_EBX, true,
1525             P_GPR_3, true);
1526#ifdef DEBUG_CALL_STACK3
1527    scratchRegs[0] = PhysicalReg_EAX;
1528    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1529    move_imm_to_reg(OpndSize_32, 0xdd11, PhysicalReg_EBX, true);
1530    call_debug_dumpSwitch();
1531#endif
1532
1533    /* get predicted clazz
1534       get predicted method
1535    */
1536    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1537    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, P_GPR_1, true); //predictedChainCell
1538    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, P_GPR_1, true, P_SCRATCH_2, true);//predicted clazz
1539    move_mem_to_reg(OpndSize_32, offChainingCell_method, P_GPR_1, true, PhysicalReg_ECX, true);//predicted method
1540
1541#ifdef DEBUG_CALL_STACK3
1542    load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1543    move_reg_to_mem(OpndSize_32, P_GPR_1, true, 8, PhysicalReg_ESP, true);
1544    move_reg_to_mem(OpndSize_32, P_SCRATCH_2, true, 4, PhysicalReg_ESP, true);
1545    move_reg_to_mem(OpndSize_32, P_GPR_3, true, 0, PhysicalReg_ESP, true);
1546
1547    move_reg_to_reg(OpndSize_32, P_SCRATCH_2, true, PhysicalReg_EBX, true);
1548    call_debug_dumpSwitch();
1549    move_imm_to_reg(OpndSize_32, 0xdd22, PhysicalReg_EBX, true);
1550    scratchRegs[0] = PhysicalReg_EAX;
1551    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1552    move_reg_to_reg(OpndSize_32, P_GPR_3, true, PhysicalReg_EBX, true);
1553    call_debug_dumpSwitch();
1554    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
1555    call_debug_dumpSwitch();
1556
1557    move_mem_to_reg(OpndSize_32, 8, PhysicalReg_ESP, true, P_GPR_1, true);
1558    move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, P_SCRATCH_2, true);
1559    move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, P_GPR_3, true);
1560    load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
1561#endif
1562
1563    /* compare current class object against predicted clazz
1564       if equal, prediction is still valid, jump to .invokeChain */
1565    //live registers: P_GPR_1, P_GPR_3, P_SCRATCH_2
1566    compare_reg_reg(P_GPR_3, true, P_SCRATCH_2, true);
1567    conditional_jump(Condition_E, ".invokeChain", true);
1568    invokeChain_inst++;
1569
1570    //get callee method and update predicted method if necessary
1571    if(isInterface) {
1572        predicted_chain_interface_O0(tmp);
1573    } else {
1574        predicted_chain_virtual_O0(IMMC);
1575    }
1576
1577#ifdef DEBUG_CALL_STACK3
1578    move_imm_to_reg(OpndSize_32, 0xeeee, PhysicalReg_EBX, true);
1579    scratchRegs[0] = PhysicalReg_EAX;
1580    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1581    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1582    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
1583    call_debug_dumpSwitch();
1584#endif
1585
1586    if(isRange) {
1587        common_invokeMethodRange(ArgsDone_Full);
1588    }
1589    else {
1590        common_invokeMethodNoRange(ArgsDone_Full);
1591    }
1592
1593    insertLabel(".invokeChain", true);
1594#ifdef DEBUG_CALL_STACK3
1595    move_imm_to_reg(OpndSize_32, 0xdddd, PhysicalReg_EBX, true);
1596    scratchRegs[0] = PhysicalReg_EAX;
1597    call_debug_dumpSwitch(); //%ebx, %eax, %edx
1598    insertChainingWorklist(traceCurrentBB->taken->id, stream);
1599    move_chain_to_reg(OpndSize_32, traceCurrentBB->taken->id, PhysicalReg_EBX, true);
1600    call_debug_dumpSwitch();
1601    move_reg_to_reg(OpndSize_32, PhysicalReg_ECX, true, PhysicalReg_EBX, true);
1602    call_debug_dumpSwitch();
1603#endif
1604
1605    if(isRange) {
1606        common_invokeMethodRange(ArgsDone_Normal);
1607    }
1608    else {
1609        common_invokeMethodNoRange(ArgsDone_Normal);
1610    }
1611}
1612
1613/* object "this" is in inputReg: 5 for virtual, 1 for interface, 1 for virtual_quick */
1614void gen_predicted_chain_O1(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
1615
1616    /* get current class object */
1617    move_mem_to_reg(OpndSize_32, offObject_clazz, inputReg, false,
1618             40, false);
1619
1620    /* get predicted clazz
1621       get predicted method
1622    */
1623    if(traceCurrentBB->taken)
1624        insertChainingWorklist(traceCurrentBB->taken->id, stream);
1625    int traceTakenId = traceCurrentBB->taken ? traceCurrentBB->taken->id : 0;
1626    move_chain_to_reg(OpndSize_32, traceTakenId, 41, false); //predictedChainCell
1627    move_mem_to_reg(OpndSize_32, offChainingCell_clazz, 41, false, 32, false);//predicted clazz
1628    move_mem_to_reg(OpndSize_32, offChainingCell_method, 41, false, PhysicalReg_ECX, true);//predicted method
1629
1630    /* update stack with parameters first, then decide the callee */
1631    if(isRange) common_invokeMethodRange_noJmp();
1632    else common_invokeMethodNoRange_noJmp();
1633
1634    /* compare current class object against predicted clazz
1635       if equal, prediction is still valid, jump to .invokeChain */
1636    compare_reg_reg(40, false, 32, false);
1637    conditional_jump(Condition_E, ".invokeChain", true);
1638    rememberState(1);
1639    invokeChain_inst++;
1640
1641    //get callee method and update predicted method if necessary
1642    if(isInterface) {
1643        predicted_chain_interface_O1(tmp);
1644    } else {
1645        predicted_chain_virtual_O1(IMMC);
1646    }
1647
1648    common_invokeMethod_Jmp(ArgsDone_Full); //will touch %ecx
1649
1650    insertLabel(".invokeChain", true);
1651    goToState(1);
1652    common_invokeMethod_Jmp(ArgsDone_Normal);
1653}
1654
1655void gen_predicted_chain(bool isRange, u2 tmp, int IMMC, bool isInterface, int inputReg) {
1656    return gen_predicted_chain_O1(isRange, tmp, IMMC, isInterface, inputReg);
1657}
1658#undef P_GPR_1
1659#undef P_GPR_2
1660#undef P_GPR_3
1661#undef P_SCRATCH_2
1662