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 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 */
generate_invokeNative(bool generateForNcg)855 void 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 }
generate_stackOverflow()913 void 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 //!
op_execute_inline(bool isRange)943 int 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 //!
op_invoke_object_init_range()1171 int 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
common_invoke_virtual_quick(bool hasRange,u2 vD,u2 IMMC)1185 int 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 //!
op_invoke_virtual_quick()1232 int 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 //!
op_invoke_virtual_quick_range()1245 int 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 //!
common_invoke_super_quick(bool hasRange,u2 vD,u2 IMMC)1261 int 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 //!
op_invoke_super_quick()1286 int 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 //!
op_invoke_super_quick_range()1299 int 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
predicted_chain_interface_O0(u2 tmp)1321 void 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
predicted_chain_interface_O1(u2 tmp)1369 void 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
predicted_chain_virtual_O0(u2 IMMC)1440 void 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
predicted_chain_virtual_O1(u2 IMMC)1474 void 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
1518 static int invokeChain_inst = 0;
1519 /* object "this" is in %ebx */
gen_predicted_chain_O0(bool isRange,u2 tmp,int IMMC,bool isInterface,int inputReg)1520 void 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 */
gen_predicted_chain_O1(bool isRange,u2 tmp,int IMMC,bool isInterface,int inputReg)1614 void 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
gen_predicted_chain(bool isRange,u2 tmp,int IMMC,bool isInterface,int inputReg)1655 void 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