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