blob: 928c05c9ff6ef6ebc8a28dc59ec4d710edc3f304 [file] [log] [blame]
/*
* 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;
}