| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| /*! \file LowerReturn.cpp |
| \brief This file lowers the following bytecodes: RETURN |
| |
| */ |
| #include "libdex/DexOpcodes.h" |
| #include "libdex/DexFile.h" |
| #include "mterp/Mterp.h" |
| #include "Lower.h" |
| #include "enc_wrapper.h" |
| #include "NcgHelper.h" |
| |
| //4 GPRs and scratch registers used in get_self_pointer, set_glue_method and set_glue_dvmdex |
| //will jump to "gotoBail" if caller method is NULL or if debugger is active |
| //what is %edx for each case? for the latter case, it is 1 |
| #define P_GPR_1 PhysicalReg_ECX //must be ecx |
| #define P_GPR_2 PhysicalReg_EBX |
| #define P_SCRATCH_1 PhysicalReg_EDX |
| #define P_OLD_FP PhysicalReg_EAX |
| /*! |
| \brief common section to return from a method |
| |
| If the helper switch is on, this will generate a helper function |
| */ |
| int common_returnFromMethod() { |
| #if defined(ENABLE_TRACING) && !defined(TRACING_OPTION2) |
| insertMapWorklist(offsetPC, mapFromBCtoNCG[offsetPC], 1); //check when helper switch is on |
| #endif |
| |
| scratchRegs[0] = PhysicalReg_SCRATCH_7; |
| get_self_pointer(2, false); |
| |
| //update rFP to caller stack frame |
| move_reg_to_reg(OpndSize_32, PhysicalReg_FP, true, 10, false); |
| move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_prevFrame, PhysicalReg_FP, true, PhysicalReg_FP, true); //update rFP |
| //get caller method by accessing the stack save area |
| move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_method, PhysicalReg_FP, true, 6, false); |
| compare_imm_reg(OpndSize_32, 0, 6, false); |
| conditional_jump(Condition_E, "common_gotoBail_0", false); |
| get_self_pointer(3, false); |
| //update glue->method |
| move_reg_to_mem(OpndSize_32, 6, false, offsetof(Thread, interpSave.method), 2, false); |
| //get clazz of caller method |
| move_mem_to_reg(OpndSize_32, offMethod_clazz, 6, false, 14, false); |
| //update self->frame |
| move_reg_to_mem(OpndSize_32, PhysicalReg_FP, true, offThread_curFrame, 3, false); |
| //get method->clazz->pDvmDex |
| move_mem_to_reg(OpndSize_32, offClassObject_pDvmDex, 14, false, 7, false); |
| move_reg_to_mem(OpndSize_32, 7, false, offsetof(Thread, interpSave.methodClassDex), 2, false); |
| |
| compare_imm_mem(OpndSize_32, 0, offsetof(Thread, suspendCount), 2, false); /* suspendCount */ |
| move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_returnAddr, 10, false, PhysicalReg_EBX, true); |
| move_imm_to_reg(OpndSize_32, 0, 17, false); |
| /* if suspendCount is not zero, clear the chaining cell address */ |
| conditional_move_reg_to_reg(OpndSize_32, Condition_NZ, 17, false/*src*/, PhysicalReg_EBX, true/*dst*/); |
| move_mem_to_reg(OpndSize_32, -sizeofStackSaveArea+offStackSaveArea_savedPc, 10, false, PhysicalReg_EAX, true); |
| //if returnAddr is not NULL, the thread is still in code cache |
| move_reg_to_mem(OpndSize_32, PhysicalReg_EBX, true, offThread_inJitCodeCache, 3, false); |
| |
| insertLabel(".LreturnToInterp", true); //local label |
| //move rPC by 6 (3 bytecode units for INVOKE) |
| alu_binary_imm_reg(OpndSize_32, add_opc, 6, PhysicalReg_EAX, true); |
| |
| //returnAddr in %ebx, if not zero, jump to returnAddr |
| compare_imm_reg(OpndSize_32, 0, PhysicalReg_EBX, true); |
| conditional_jump(Condition_E, ".LcontinueToInterp", true); |
| #ifdef DEBUG_CALL_STACK3 |
| move_reg_to_reg(OpndSize_32, PhysicalReg_EBX, true, PhysicalReg_ESI, true); |
| move_imm_to_reg(OpndSize_32, 0xaabb, PhysicalReg_EBX, true); |
| scratchRegs[0] = PhysicalReg_EAX; |
| call_debug_dumpSwitch(); //%ebx, %eax, %edx |
| move_reg_to_reg(OpndSize_32, PhysicalReg_ESI, true, PhysicalReg_EBX, true); |
| call_debug_dumpSwitch(); |
| move_reg_to_reg(OpndSize_32, PhysicalReg_ESI, true, PhysicalReg_EBX, true); |
| #endif |
| unconditional_jump_reg(PhysicalReg_EBX, true); |
| insertLabel(".LcontinueToInterp", true); |
| scratchRegs[0] = PhysicalReg_SCRATCH_4; |
| typedef void (*vmHelper)(int); |
| vmHelper funcPtr = dvmJitToInterpNoChainNoProfile; //%eax is the input |
| move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical); |
| |
| unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical); |
| touchEax(); |
| return 0; |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #undef P_SCRATCH_1 |
| |
| //! lower bytecode RETURN_VOID |
| |
| //! It seems that shared code cache does not support helper switch |
| int op_return_void() { |
| int retval; |
| retval = common_returnFromMethod(); |
| rPC += 1; |
| return retval; |
| } |
| |
| //! lower bytecode RETURN |
| |
| //! It seems that shared code cache does not support helper switch |
| //! The return value is stored to glue->retval first |
| int op_return() { |
| u2 vA = INST_AA(inst); |
| |
| get_virtual_reg(vA, OpndSize_32, 22, false); |
| scratchRegs[0] = PhysicalReg_SCRATCH_1; |
| set_return_value(OpndSize_32, 22, false); |
| |
| common_returnFromMethod(); |
| rPC += 1; |
| return 0; |
| } |
| |
| //! lower bytecode RETURN_WIDE |
| |
| //! It seems that shared code cache does not support helper switch |
| //! The return value is stored to glue->retval first |
| int op_return_wide() { |
| u2 vA = INST_AA(inst); |
| get_virtual_reg(vA, OpndSize_64, 1, false); |
| scratchRegs[0] = PhysicalReg_SCRATCH_10; scratchRegs[1] = PhysicalReg_Null; |
| scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null; |
| set_return_value(OpndSize_64, 1, false); |
| |
| common_returnFromMethod(); |
| rPC += 1; |
| return 0; |
| } |