/*
* 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 LowerObject.cpp
\brief This file lowers the following bytecodes: CHECK_CAST,
*/
#include "libdex/DexOpcodes.h"
#include "libdex/DexFile.h"
#include "Lower.h"
#include "NcgAot.h"
#include "enc_wrapper.h"
extern void markCard_filled(int tgtAddrReg, bool isTgtPhysical, int scratchReg, bool isScratchPhysical);
#define P_GPR_1 PhysicalReg_EBX
#define P_GPR_2 PhysicalReg_ECX
#define P_GPR_3 PhysicalReg_ESI
//! LOWER bytecode CHECK_CAST and INSTANCE_OF
//! CALL class_resolve (%ebx is live across the call)
//! dvmInstanceofNonTrivial
//! NO register is live through function check_cast_helper
int check_cast_nohelper(u2 vA, u4 tmp, bool instance, u2 vDest) {
get_virtual_reg(vA, OpndSize_32, 1, false); //object
scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
/* for trace-based JIT, it is likely that the class is already resolved */
bool needToResolve = true;
ClassObject *classPtr =
(currentMethod->clazz->pDvmDex->pResClasses[tmp]);
ALOGV("in check_cast, class is resolved to %p", classPtr);
if(classPtr != NULL) {
needToResolve = false;
ALOGV("check_cast class %s", classPtr->descriptor);
}
if(needToResolve) {
//get_res_classes is moved here for NCG O1 to improve performance of GLUE optimization
scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
get_res_classes(4, false);
}
compare_imm_reg(OpndSize_32, 0, 1, false);
rememberState(1);
//for private code cache, previously it jumped to .instance_of_okay_1
//if object reference is null, jump to the handler for this special case
if(instance) {
conditional_jump(Condition_E, ".instance_of_null", true);
}
else {
conditional_jump(Condition_E, ".check_cast_null", true);
}
//check whether the class is already resolved
//if yes, jump to check_cast_resolved
//if not, call class_resolve
if(needToResolve) {
move_mem_to_reg(OpndSize_32, tmp*4, 4, false, PhysicalReg_EAX, true);
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
if(instance)
conditional_jump(Condition_NE, ".instance_of_resolved", true);
else
conditional_jump(Condition_NE, ".check_cast_resolved", true);
//try to resolve the class
rememberState(2);
move_imm_to_reg(OpndSize_32, tmp, PhysicalReg_EAX, true);
export_pc(); //trying to resolve the class
call_helper_API(".class_resolve");
transferToState(2);
} //needToResolve
else {
/* the class is already resolved and is constant */
move_imm_to_reg(OpndSize_32, (int)classPtr, PhysicalReg_EAX, true);
}
//class is resolved, and it is in %eax
if(!instance) {
insertLabel(".check_cast_resolved", true);
}
else insertLabel(".instance_of_resolved", true);
move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 6, false); //object->clazz
//%eax: resolved class
//compare resolved class and object->clazz
//if the same, jump to the handler for this special case
compare_reg_reg(PhysicalReg_EAX, true, 6, false);
rememberState(3);
if(instance) {
conditional_jump(Condition_E, ".instance_of_equal", true);
} else {
conditional_jump(Condition_E, ".check_cast_equal", true);
}
//prepare to call dvmInstanceofNonTrivial
//INPUT: the resolved class & object reference
load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, 6, false, 0, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true); //resolved class
scratchRegs[0] = PhysicalReg_SCRATCH_3;
nextVersionOfHardReg(PhysicalReg_EAX, 2); //next version has 2 refs
call_dvmInstanceofNonTrivial();
load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
//
if(instance) {
//move return value to P_GPR_2
move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, 3, false);
rememberState(4);
unconditional_jump(".instance_of_okay", true);
} else {
//if return value of dvmInstanceofNonTrivial is zero, throw exception
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
rememberState(4);
conditional_jump(Condition_NE, ".check_cast_okay", true);
//two inputs for common_throw_message: object reference in eax, exception pointer in ecx
nextVersionOfHardReg(PhysicalReg_EAX, 1); //next version has 1 ref
move_reg_to_reg(OpndSize_32, 1, false, PhysicalReg_EAX, true);
load_imm_global_data_API("strClassCastExceptionPtr", OpndSize_32, PhysicalReg_ECX, true);
nextVersionOfHardReg(PhysicalReg_EDX, 2); //next version has 2 ref count
export_pc();
unconditional_jump_global_API("common_throw_message", false);
}
//handler for speical case where object reference is null
if(instance)
insertLabel(".instance_of_null", true);
else insertLabel(".check_cast_null", true);
goToState(1);
if(instance) {
move_imm_to_reg(OpndSize_32, 0, 3, false);
}
transferToState(4);
if(instance)
unconditional_jump(".instance_of_okay", true);
else
unconditional_jump(".check_cast_okay", true);
//handler for special case where class of object is the same as the resolved class
if(instance)
insertLabel(".instance_of_equal", true);
else insertLabel(".check_cast_equal", true);
goToState(3);
if(instance) {
move_imm_to_reg(OpndSize_32, 1, 3, false);
}
transferToState(4);
if(instance)
insertLabel(".instance_of_okay", true);
else insertLabel(".check_cast_okay", true);
//all cases merge here and the value is put to virtual register
if(instance) {
set_virtual_reg(vDest, OpndSize_32, 3, false);
}
return 0;
}
//! common code to lower CHECK_CAST & INSTANCE_OF
//!
int common_check_cast_instance_of(u2 vA, u4 tmp, bool instance, u2 vDest) {
return check_cast_nohelper(vA, tmp, instance, vDest);
}
#undef P_GPR_1
#undef P_GPR_2
#undef P_GPR_3
//! LOWER bytecode CHECK_CAST
//!
int op_check_cast() {
u2 vA = INST_AA(inst);
u4 tmp = (u4)FETCH(1);
common_check_cast_instance_of(vA, tmp, false, 0);
rPC += 2;
return 0;
}
//!LOWER bytecode INSTANCE_OF
//!
int op_instance_of() {
u2 vB = INST_B(inst);
u2 vA = INST_A(inst);
u4 tmp = (u4)FETCH(1);
common_check_cast_instance_of(vB, tmp, true, vA);
rPC += 2;
return 0;
}
#define P_GPR_1 PhysicalReg_EBX
#define P_GPR_2 PhysicalReg_ECX
//! LOWER bytecode MONITOR_ENTER without usage of helper function
//! CALL dvmLockObject
int monitor_enter_nohelper(u2 vA) {
scratchRegs[0] = PhysicalReg_SCRATCH_1;
scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
requestVRFreeDelay(vA,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
//get_self_pointer is separated
get_virtual_reg(vA, OpndSize_32, 1, false);
//to optimize redundant null check, NCG O1 wraps up null check in a function: nullCheck
get_self_pointer(3, false);
nullCheck(1, false, 1, vA); //maybe optimized away
cancelVRFreeDelayRequest(vA,VRDELAY_NULLCHECK);
/////////////////////////////
//prepare to call dvmLockObject, inputs: object reference and self
// TODO: Should reset inJitCodeCache before calling dvmLockObject
// so that code cache can be reset if needed when locking object
// taking a long time. Not resetting inJitCodeCache may delay
// code cache reset when code cache is full, preventing traces from
// JIT compilation. This has performance implication.
// However, after resetting inJitCodeCache, the code should be
// wrapped in a helper instead of directly inlined in code cache.
// If the code after dvmLockObject call is in code cache and the code
// cache is reset during dvmLockObject call, execution after
// dvmLockObject will return to a cleared code cache region,
// resulting in seg fault.
load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, 1, false, 4, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, 3, false, 0, PhysicalReg_ESP, true);
scratchRegs[0] = PhysicalReg_SCRATCH_2;
call_dvmLockObject();
load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
/////////////////////////////
return 0;
}
//! lower bytecode MONITOR_ENTER
//! It will use helper function if switch is on
int op_monitor_enter() {
u2 vA = INST_AA(inst);
export_pc();
monitor_enter_nohelper(vA);
rPC += 1;
return 0;
}
#undef P_GPR_1
#undef P_GPR_2
#define P_GPR_1 PhysicalReg_EBX
#define P_GPR_2 PhysicalReg_ECX
//! lower bytecode MONITOR_EXIT
//! It will use helper function if switch is on
int op_monitor_exit() {
u2 vA = INST_AA(inst);
////////////////////
//LOWER bytecode MONITOR_EXIT without helper function
// CALL dvmUnlockObject
scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
requestVRFreeDelay(vA,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
get_virtual_reg(vA, OpndSize_32, 1, false);
nullCheck(1, false, 1, vA); //maybe optimized away
cancelVRFreeDelayRequest(vA,VRDELAY_NULLCHECK);
/////////////////////////////
//prepare to call dvmUnlockObject, inputs: object reference and self
push_reg_to_stack(OpndSize_32, 1, false);
push_mem_to_stack(OpndSize_32, offEBP_self, PhysicalReg_EBP, true);
scratchRegs[0] = PhysicalReg_SCRATCH_2;
call_dvmUnlockObject();
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
conditional_jump(Condition_NE, ".unlock_object_done", true);
//jump to dvmJitToExceptionThrown
scratchRegs[0] = PhysicalReg_SCRATCH_3;
jumpToExceptionThrown(2/*exception number*/);
insertLabel(".unlock_object_done", true);
///////////////////////////
rPC += 1;
return 0;
}
#undef P_GPR_1
#undef P_GPR_2
#define P_GPR_1 PhysicalReg_EBX
#define P_GPR_2 PhysicalReg_ECX
#define P_GPR_3 PhysicalReg_EDX /*vA*/
//! LOWER bytecode ARRAY_LENGTH
//! It will use helper function if switch is on
int op_array_length() {
u2 vA = INST_A(inst);
u2 vB = INST_B(inst);
////////////////////
//no usage of helper function
requestVRFreeDelay(vB,VRDELAY_NULLCHECK); // Request VR delay before transfer to temporary
get_virtual_reg(vB, OpndSize_32, 1, false);
nullCheck(1, false, 1, vB); //maybe optimized away
cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK);
move_mem_to_reg(OpndSize_32, offArrayObject_length, 1, false, 2, false);
set_virtual_reg(vA, OpndSize_32, 2, false);
///////////////////////
rPC += 1;
return 0;
}
#undef P_GPR_1
#undef P_GPR_2
#undef P_GPR_3
#define P_GPR_1 PhysicalReg_EBX
#define P_GPR_2 PhysicalReg_ECX
#define P_GPR_3 PhysicalReg_ESI
//! lower bytecode NEW_INSTANCE
//! It will use helper function if switch is on
int op_new_instance() {
u4 tmp = (u4)FETCH(1);
u2 vA = INST_AA(inst);
export_pc();
/* for trace-based JIT, class is already resolved */
ClassObject *classPtr =
(currentMethod->clazz->pDvmDex->pResClasses[tmp]);
assert(classPtr != NULL);
assert(classPtr->status & CLASS_INITIALIZED);
/*
* If it is going to throw, it should not make to the trace to begin
* with. However, Alloc might throw, so we need to genExportPC()
*/
assert((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) == 0);
//prepare to call dvmAllocObject, inputs: resolved class & flag ALLOC_DONT_TRACK
load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
/* 1st argument to dvmAllocObject at -8(%esp) */
move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 4, PhysicalReg_ESP, true);
scratchRegs[0] = PhysicalReg_SCRATCH_3;
nextVersionOfHardReg(PhysicalReg_EAX, 3); //next version has 3 refs
call_dvmAllocObject();
load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
//return value of dvmAllocObject is in %eax
//if return value is null, throw exception
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
conditional_jump(Condition_NE, ".new_instance_done", true);
//jump to dvmJitToExceptionThrown
scratchRegs[0] = PhysicalReg_SCRATCH_4;
jumpToExceptionThrown(3/*exception number*/);
insertLabel(".new_instance_done", true);
set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
rPC += 2;
return 0;
}
//! function to initialize a class
//!INPUT: %eax (class object) %eax is recovered before return
//!OUTPUT: none
//!CALL: dvmInitClass
//!%eax, %esi, %ebx are live through function new_instance_needinit
int new_instance_needinit() {
insertLabel(".new_instance_needinit", false);
load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 0, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, PhysicalReg_EAX, true, 4, PhysicalReg_ESP, true);
scratchRegs[0] = PhysicalReg_ECX;
call_dvmInitClass();
//if return value of dvmInitClass is zero, throw exception
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
//recover EAX with the class object
move_mem_to_reg(OpndSize_32, 4, PhysicalReg_ESP, true, PhysicalReg_EAX, true);
load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
conditional_jump(Condition_E, "common_exceptionThrown", false);
x86_return();
return 0;
}
#undef P_GPR_1
#undef P_GPR_2
#undef P_GPR_3
#define P_GPR_1 PhysicalReg_EBX //live through C function, must in callee-saved reg
#define P_GPR_2 PhysicalReg_ECX
#define P_GPR_3 PhysicalReg_EDX
//! lower bytecode NEW_ARRAY
//! It will use helper function if switch is on
int op_new_array() {
u4 tmp = (u4)FETCH(1);
u2 vA = INST_A(inst); //destination
u2 vB = INST_B(inst); //length
/////////////////////////
// REGS used: %esi, %eax, P_GPR_1, P_GPR_2
// CALL class_resolve, dvmAllocArrayByClass
export_pc(); //use %edx
//check size of the array, if negative, throw exception
get_virtual_reg(vB, OpndSize_32, 5, false);
compare_imm_reg(OpndSize_32, 0, 5, false);
handlePotentialException(Condition_S, Condition_NS,
1, "common_errNegArraySize");
void *classPtr = (void*)
(currentMethod->clazz->pDvmDex->pResClasses[tmp]);
assert(classPtr != NULL);
//here, class is already resolved, the class object is in %eax
//prepare to call dvmAllocArrayByClass with inputs: resolved class, array length, flag ALLOC_DONT_TRACK
insertLabel(".new_array_resolved", true);
load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
/* 1st argument to dvmAllocArrayByClass at 0(%esp) */
move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, 5, false, 4, PhysicalReg_ESP, true);
move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 8, PhysicalReg_ESP, true);
scratchRegs[0] = PhysicalReg_SCRATCH_3;
nextVersionOfHardReg(PhysicalReg_EAX, 3); //next version has 3 refs
call_dvmAllocArrayByClass();
load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
//the allocated object is in %eax
//check whether it is null, throw exception if null
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
conditional_jump(Condition_NE, ".new_array_done", true);
//jump to dvmJitToExceptionThrown
scratchRegs[0] = PhysicalReg_SCRATCH_4;
jumpToExceptionThrown(2/*exception number*/);
insertLabel(".new_array_done", true);
set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true);
//////////////////////////////////////
rPC += 2;
return 0;
}
#undef P_GPR_1
#undef P_GPR_2
#undef P_GPR_3
#define P_GPR_1 PhysicalReg_EBX
#define P_GPR_2 PhysicalReg_ECX
#define P_GPR_3 PhysicalReg_ESI
//! common code to lower FILLED_NEW_ARRAY
//! call: class_resolve call_dvmAllocPrimitiveArray
//! exception: filled_new_array_notimpl common_exceptionThrown
int common_filled_new_array(u2 length, u4 tmp, bool hasRange) {
ClassObject *classPtr =
(currentMethod->clazz->pDvmDex->pResClasses[tmp]);
if(classPtr != NULL) ALOGI("FILLED_NEW_ARRAY class %s", classPtr->descriptor);
//check whether class is resolved, if yes, jump to resolved
//if not, call class_resolve
scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
get_res_classes(3, false);
move_mem_to_reg(OpndSize_32, tmp*4, 3, false, PhysicalReg_EAX, true);
export_pc();
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); //resolved class
conditional_jump(Condition_NE, ".filled_new_array_resolved", true);
rememberState(1);
move_imm_to_reg(OpndSize_32, tmp, PhysicalReg_EAX, true);
call_helper_API(".class_resolve");
transferToState(1);
//here, class is already resolved
insertLabel(".filled_new_array_resolved", true);
//check descriptor of the class object, if not implemented, throws exception
move_mem_to_reg(OpndSize_32, 24, PhysicalReg_EAX, true, 5, false);
//load a single byte of the descriptor
movez_mem_to_reg(OpndSize_8, 1, 5, false, 6, false);
compare_imm_reg(OpndSize_32, 'I', 6, false);
conditional_jump(Condition_E, ".filled_new_array_impl", true);
compare_imm_reg(OpndSize_32, 'L', 6, false);
conditional_jump(Condition_E, ".filled_new_array_impl", true);
compare_imm_reg(OpndSize_32, '[', 6, false);
conditional_jump(Condition_NE, ".filled_new_array_notimpl", false);
insertLabel(".filled_new_array_impl", true);
//prepare to call dvmAllocArrayByClass with inputs: classObject, length, flag ALLOC_DONT_TRACK
load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
move_imm_to_mem(OpndSize_32, (int)classPtr, 0, PhysicalReg_ESP, true);
move_imm_to_mem(OpndSize_32, length, 4, PhysicalReg_ESP, true);
move_imm_to_mem(OpndSize_32, ALLOC_DONT_TRACK, 8, PhysicalReg_ESP, true);
scratchRegs[0] = PhysicalReg_SCRATCH_3; scratchRegs[1] = PhysicalReg_Null;
if(hasRange) {
nextVersionOfHardReg(PhysicalReg_EAX, 5+(length >= 1 ? LOOP_COUNT : 0)); //next version
}
else {
nextVersionOfHardReg(PhysicalReg_EAX, 5+length); //next version
}
call_dvmAllocArrayByClass();
load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
//return value of dvmAllocPrimitiveArray is in %eax
//if the return value is null, throw exception
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
handlePotentialException(
Condition_E, Condition_NE,
3, "common_exceptionThrown");
/* we need to mark the card of the new array, if it's not an int */
compare_imm_reg(OpndSize_32, 'I', 6, false);
conditional_jump(Condition_E, ".dont_mark_filled_new_array", true);
// Need to make copy of EAX, because it's used later in op_filled_new_array()
move_reg_to_reg(OpndSize_32, PhysicalReg_EAX, true, 6, false);
markCard_filled(6, false, PhysicalReg_SCRATCH_4, false);
insertLabel(".dont_mark_filled_new_array", true);
//return value of bytecode FILLED_NEW_ARRAY is in GLUE structure
scratchRegs[0] = PhysicalReg_SCRATCH_4; scratchRegs[1] = PhysicalReg_Null;
set_return_value(OpndSize_32, PhysicalReg_EAX, true);
return 0;
}
//! LOWER bytecode FILLED_NEW_ARRAY
//!
int op_filled_new_array() {
u2 length = INST_B(inst);
u4 tmp = (u4)FETCH(1);
u2 v5 = INST_A(inst);
u2 vv = FETCH(2);
u2 v1 = vv & 0xf;
u2 v2 = (vv >> 4) & 0xf;
u2 v3 = (vv >> 8) & 0xf;
u2 v4 = (vv >> 12) & 0xf;
common_filled_new_array(length, tmp, false);
if(length >= 1) {
//move from virtual register to contents of array object
get_virtual_reg(v1, OpndSize_32, 7, false);
move_reg_to_mem(OpndSize_32, 7, false, offArrayObject_contents, PhysicalReg_EAX, true);
}
if(length >= 2) {
//move from virtual register to contents of array object
get_virtual_reg(v2, OpndSize_32, 8, false);
move_reg_to_mem(OpndSize_32, 8, false, offArrayObject_contents+4, PhysicalReg_EAX, true);
}
if(length >= 3) {
//move from virtual register to contents of array object
get_virtual_reg(v3, OpndSize_32, 9, false);
move_reg_to_mem(OpndSize_32, 9, false, offArrayObject_contents+8, PhysicalReg_EAX, true);
}
if(length >= 4) {
//move from virtual register to contents of array object
get_virtual_reg(v4, OpndSize_32, 10, false);
move_reg_to_mem(OpndSize_32, 10, false, offArrayObject_contents+12, PhysicalReg_EAX, true);
}
if(length >= 5) {
//move from virtual register to contents of array object
get_virtual_reg(v5, OpndSize_32, 11, false);
move_reg_to_mem(OpndSize_32, 11, false, offArrayObject_contents+16, PhysicalReg_EAX, true);
}
rPC += 3;
return 0;
}
//! function to handle the error of array not implemented
//!
int filled_new_array_notimpl() {
//two inputs for common_throw:
insertLabel(".filled_new_array_notimpl", false);
move_imm_to_reg(OpndSize_32, LstrFilledNewArrayNotImpl, PhysicalReg_EAX, true);
move_imm_to_reg(OpndSize_32, (int) gDvm.exInternalError, PhysicalReg_ECX, true);
unconditional_jump("common_throw", false);
return 0;
}
#define P_SCRATCH_1 PhysicalReg_EDX
//! LOWER bytecode FILLED_NEW_ARRAY_RANGE
//!
int op_filled_new_array_range() {
u2 length = INST_AA(inst);
u4 tmp = (u4)FETCH(1);
u4 vC = (u4)FETCH(2);
common_filled_new_array(length, tmp, true/*hasRange*/);
//here, %eax points to the array object
if(length >= 1) {
//dump all virtual registers used by this bytecode to stack, for NCG O1
int k;
for(k = 0; k < length; k++) {
spillVirtualReg(vC+k, LowOpndRegType_gp, true); //will update refCount
}
//address of the first virtual register that will be moved to the array object
load_effective_addr(vC*4, PhysicalReg_FP, true, 7, false); //addr
//start address for contents of the array object
load_effective_addr(offArrayObject_contents, PhysicalReg_EAX, true, 8, false); //addr
//loop counter
move_imm_to_reg(OpndSize_32, length-1, 9, false); //counter
//start of the loop
insertLabel(".filled_new_array_range_loop1", true);
rememberState(1);
move_mem_to_reg(OpndSize_32, 0, 7, false, 10, false);
load_effective_addr(4, 7, false, 7, false);
move_reg_to_mem(OpndSize_32, 10, false, 0, 8, false);
load_effective_addr(4, 8, false, 8, false);
alu_binary_imm_reg(OpndSize_32, sub_opc, 1, 9, false);
transferToState(1);
//jump back to the loop start
conditional_jump(Condition_NS, ".filled_new_array_range_loop1", true);
}
rPC += 3;
return 0;
}
#undef P_GPR_1
#undef P_GPR_2
#undef P_GPR_3
#undef P_SCRATCH_1
#define P_GPR_1 PhysicalReg_EBX
//! LOWER bytecode FILL_ARRAY_DATA
//!use 1 GPR and scratch regs (export_pc dvmInterpHandleFillArrayData)
//!CALL: dvmInterpHandleFillArrayData
int op_fill_array_data() {
u2 vA = INST_AA(inst);
u4 tmp = (u4)FETCH(1);
tmp |= (u4)FETCH(2) << 16;
scratchRegs[0] = PhysicalReg_SCRATCH_1;
scratchRegs[1] = PhysicalReg_Null;
scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
get_virtual_reg(vA, OpndSize_32, 1, false);
//prepare to call dvmInterpHandleFillArrayData, input: array object, address of the data
load_effective_addr(-8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
/* 2nd argument to dvmInterpHandleFillArrayData at 4(%esp) */
move_imm_to_mem(OpndSize_32, (int)(rPC+tmp), 4, PhysicalReg_ESP, true);
call_dvmInterpHandleFillArrayData();
load_effective_addr(8, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
//check return value of dvmInterpHandleFillArrayData, if zero, throw exception
compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true);
conditional_jump(Condition_NE, ".fill_array_data_done", true);
//jump to dvmJitToExceptionThrown
scratchRegs[0] = PhysicalReg_SCRATCH_2;
jumpToExceptionThrown(2/*exception number*/);
insertLabel(".fill_array_data_done", true);
rPC += 3;
return 0;
}
#undef P_GPR_1
#define P_GPR_1 PhysicalReg_EBX
//! LOWER bytecode THROW
//!
int op_throw() {
u2 vA = INST_AA(inst);
export_pc();
get_virtual_reg(vA, OpndSize_32, 1, false);
//null check
compare_imm_reg(OpndSize_32, 0, 1, false);
conditional_jump(Condition_E, "common_errNullObject", false);
//set glue->exception & throw exception
scratchRegs[2] = PhysicalReg_Null; scratchRegs[3] = PhysicalReg_Null;
scratchRegs[0] = PhysicalReg_SCRATCH_1; scratchRegs[1] = PhysicalReg_SCRATCH_2;
set_exception(1, false);
unconditional_jump("common_exceptionThrown", false);
rPC += 1;
return 0;
}
#undef P_GPR_1
#define P_GPR_1 PhysicalReg_EBX
//! LOWER bytecode THROW_VERIFICATION_ERROR
//! op AA, ref@BBBB
int op_throw_verification_error() {
u2 vA, vB;
vA = INST_AA(inst);
vB = FETCH(1);
export_pc();
scratchRegs[0] = PhysicalReg_SCRATCH_1;
get_glue_method(1, false);
load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
move_imm_to_mem(OpndSize_32, vB, 8, PhysicalReg_ESP, true);
move_imm_to_mem(OpndSize_32, vA, 4, PhysicalReg_ESP, true);
move_reg_to_mem(OpndSize_32, 1, false, 0, PhysicalReg_ESP, true);
scratchRegs[0] = PhysicalReg_SCRATCH_2;
call_dvmThrowVerificationError();
load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true);
unconditional_jump("common_exceptionThrown", false);
rPC += 2;
return 0;
}
#undef P_GPR_1