/* * 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