• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
28 char* streamMisPred = NULL;
29 
30 /* according to callee, decide the ArgsDoneType*/
convertCalleeToType(const Method * calleeMethod)31 ArgsDoneType 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 }
38 int common_invokeMethodRange(ArgsDoneType);
39 int common_invokeMethodNoRange(ArgsDoneType);
40 void 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  */
genLandingPadForMispredictedCallee(MIR * mir)58 static 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 //!
common_invoke_virtual_nohelper(bool isRange,u2 tmp,u2 vD)78 int 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 //!
common_invoke_virtual(bool isRange,u2 tmp,u2 vD)120 int 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
common_invoke_super(bool isRange,u2 tmp)142 int 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 //!
invoke_super_nsm()170 int 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
common_invoke_direct(bool isRange,u2 tmp,u2 vD)191 int 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
common_invoke_static(bool isRange,u2 tmp)230 int 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
common_invoke_interface(bool isRange,u2 tmp,u2 vD)267 int 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 //!
op_invoke_virtual()327 int 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 //!
op_invoke_super()349 int 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 //!
op_invoke_direct()370 int 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 //!
op_invoke_static()392 int 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 //!
op_invoke_interface()413 int 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 //!
op_invoke_virtual_range()435 int 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 //!
op_invoke_super_range()456 int 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 //!
op_invoke_direct_range()473 int 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 //!
op_invoke_static_range()491 int 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 //!
op_invoke_interface_range()508 int 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 //!
common_invokeMethodNoRange_noJmp()534 int 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 
common_invokeMethod_Jmp(ArgsDoneType form)569 int 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 
common_invokeMethodNoRange(ArgsDoneType form)592 int 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
common_invokeMethodRange_noJmp()622 int 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 
common_invokeMethodRange(ArgsDoneType form)692 int 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)
spill_reg(int reg,bool isPhysical)723 int 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
unspill_reg(int reg,bool isPhysical)731 int 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 
737 void generate_invokeNative(bool generateForNcg); //forward declaration
738 void 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 */
common_invokeArgsDone(ArgsDoneType form,bool isJitFull)750 int 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 */
generate_invokeNative(bool generateForNcg)861 void 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 }
generate_stackOverflow()924 void 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 //!
op_execute_inline(bool isRange)954 int 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 //!
op_invoke_object_init_range()1182 int 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
common_invoke_virtual_quick(bool hasRange,u2 vD,u2 IMMC)1196 int 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 //!
op_invoke_virtual_quick()1243 int 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 //!
op_invoke_virtual_quick_range()1256 int 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 //!
common_invoke_super_quick(bool hasRange,u2 vD,u2 IMMC)1272 int 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 //!
op_invoke_super_quick()1297 int 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 //!
op_invoke_super_quick_range()1310 int 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
predicted_chain_interface_O0(u2 tmp)1332 void 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
predicted_chain_interface_O1(u2 tmp)1380 void 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
predicted_chain_virtual_O0(u2 IMMC)1451 void 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
predicted_chain_virtual_O1(u2 IMMC)1485 void 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 
1529 static int invokeChain_inst = 0;
1530 /* object "this" is in %ebx */
gen_predicted_chain_O0(bool isRange,u2 tmp,int IMMC,bool isInterface,int inputReg)1531 void 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 */
gen_predicted_chain_O1(bool isRange,u2 tmp,int IMMC,bool isInterface,int inputReg)1625 void 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 
gen_predicted_chain(bool isRange,u2 tmp,int IMMC,bool isInterface,int inputReg)1666 void 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