| /* |
| * 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 LowerGetPut.cpp |
| \brief This file lowers the following bytecodes: XGET|PUT_XXX |
| */ |
| #include "libdex/DexOpcodes.h" |
| #include "libdex/DexFile.h" |
| #include "Lower.h" |
| #include "NcgAot.h" |
| #include "enc_wrapper.h" |
| |
| #define P_GPR_1 PhysicalReg_EBX |
| #define P_GPR_2 PhysicalReg_ECX |
| #define P_GPR_3 PhysicalReg_ESI |
| #define P_GPR_4 PhysicalReg_EDX |
| //! LOWER bytecode AGET without usage of helper function |
| |
| //! It has null check and length check |
| int aget_common_nohelper(int flag, u2 vA, u2 vref, u2 vindex) { |
| //////////////////////////// |
| // Request VR free delays before register allocation for the temporaries |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) |
| requestVRFreeDelay(vref,VRDELAY_NULLCHECK); |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { |
| requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK); |
| requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK); |
| } |
| |
| get_virtual_reg(vref, OpndSize_32, 1, false); //array |
| get_virtual_reg(vindex, OpndSize_32, 2, false); //index |
| |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { |
| //last argument is the exception number for this bytecode |
| nullCheck(1, false, 1, vref); //maybe optimized away, if not, call |
| cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK); |
| } else { |
| updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1 |
| } |
| |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { |
| boundCheck(vref, 1, false, |
| vindex, 2, false, |
| 2); |
| cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK); |
| cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK); |
| } else { |
| updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1 |
| updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2 |
| } |
| |
| if(flag == AGET) { |
| move_mem_disp_scale_to_reg(OpndSize_32, 1, false, offArrayObject_contents, 2, false, 4, 4, false); |
| } |
| else if(flag == AGET_WIDE) { |
| move_mem_disp_scale_to_reg(OpndSize_64, 1, false, offArrayObject_contents, 2, false, 8, 1, false); |
| } |
| else if(flag == AGET_CHAR) { |
| movez_mem_disp_scale_to_reg(OpndSize_16, 1, false, offArrayObject_contents, 2, false, 2, 4, false); |
| } |
| else if(flag == AGET_SHORT) { |
| moves_mem_disp_scale_to_reg(OpndSize_16, 1, false, offArrayObject_contents, 2, false, 2, 4, false); |
| } |
| else if(flag == AGET_BOOLEAN) { |
| movez_mem_disp_scale_to_reg(OpndSize_8, 1, false, offArrayObject_contents, 2, false, 1, 4, false); |
| } |
| else if(flag == AGET_BYTE) { |
| moves_mem_disp_scale_to_reg(OpndSize_8, 1, false, offArrayObject_contents, 2, false, 1, 4, false); |
| } |
| if(flag == AGET_WIDE) { |
| set_virtual_reg(vA, OpndSize_64, 1, false); |
| } |
| else { |
| set_virtual_reg(vA, OpndSize_32, 4, false); |
| } |
| ////////////////////////////////// |
| return 0; |
| } |
| //! wrapper to call either aget_common_helper or aget_common_nohelper |
| |
| //! |
| int aget_common(int flag, u2 vA, u2 vref, u2 vindex) { |
| return aget_common_nohelper(flag, vA, vref, vindex); |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #undef P_GPR_3 |
| #undef P_GPR_4 |
| //! lower bytecode AGET by calling aget_common |
| |
| //! |
| int op_aget() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aget_common(AGET, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode AGET_WIDE by calling aget_common |
| |
| //! |
| int op_aget_wide() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aget_common(AGET_WIDE, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode AGET_OBJECT by calling aget_common |
| |
| //! |
| int op_aget_object() { |
| return op_aget(); |
| } |
| //! lower bytecode BOOLEAN by calling aget_common |
| |
| //! |
| int op_aget_boolean() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aget_common(AGET_BOOLEAN, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode AGET_BYTE by calling aget_common |
| |
| //! |
| int op_aget_byte() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aget_common(AGET_BYTE, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode AGET_CHAR by calling aget_common |
| |
| //! |
| int op_aget_char() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aget_common(AGET_CHAR, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode AGET_SHORT by calling aget_common |
| |
| //! |
| int op_aget_short() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aget_common(AGET_SHORT, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| |
| #define P_GPR_1 PhysicalReg_EBX |
| #define P_GPR_2 PhysicalReg_ECX |
| #define P_GPR_3 PhysicalReg_ESI |
| #define P_GPR_4 PhysicalReg_EDX |
| //! LOWER bytecode APUT without usage of helper function |
| |
| //! It has null check and length check |
| int aput_common_nohelper(int flag, u2 vA, u2 vref, u2 vindex) { |
| ////////////////////////////////////// |
| // Request VR free delays before register allocation for the temporaries. |
| // No need to request delay for vA since it will be transferred to temporary |
| // after the null check and bound check. |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) |
| requestVRFreeDelay(vref,VRDELAY_NULLCHECK); |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { |
| requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK); |
| requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK); |
| } |
| |
| get_virtual_reg(vref, OpndSize_32, 1, false); //array |
| get_virtual_reg(vindex, OpndSize_32, 2, false); //index |
| |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { |
| //last argument is the exception number for this bytecode |
| nullCheck(1, false, 1, vref); //maybe optimized away, if not, call |
| cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK); |
| } else { |
| updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1 |
| } |
| |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { |
| boundCheck(vref, 1, false, |
| vindex, 2, false, |
| 2); |
| cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK); |
| cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK); |
| } else { |
| updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1 |
| updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2 |
| } |
| |
| if(flag == APUT_WIDE) { |
| get_virtual_reg(vA, OpndSize_64, 1, false); |
| } |
| else { |
| get_virtual_reg(vA, OpndSize_32, 4, false); |
| } |
| if(flag == APUT) |
| move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4); |
| else if(flag == APUT_WIDE) |
| move_reg_to_mem_disp_scale(OpndSize_64, 1, false, 1, false, offArrayObject_contents, 2, false, 8); |
| else if(flag == APUT_CHAR || flag == APUT_SHORT) |
| move_reg_to_mem_disp_scale(OpndSize_16, 4, false, 1, false, offArrayObject_contents, 2, false, 2); |
| else if(flag == APUT_BOOLEAN || flag == APUT_BYTE) |
| move_reg_to_mem_disp_scale(OpndSize_8, 4, false, 1, false, offArrayObject_contents, 2, false, 1); |
| ////////////////////////////////// |
| return 0; |
| } |
| //! wrapper to call either aput_common_helper or aput_common_nohelper |
| |
| //! |
| int aput_common(int flag, u2 vA, u2 vref, u2 vindex) { |
| return aput_common_nohelper(flag, vA, vref, vindex); |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #undef P_GPR_3 |
| #undef P_GPR_4 |
| //! lower bytecode APUT by calling aput_common |
| |
| //! |
| int op_aput() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aput_common(APUT, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode APUT_WIDE by calling aput_common |
| |
| //! |
| int op_aput_wide() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aput_common(APUT_WIDE, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode APUT_BOOLEAN by calling aput_common |
| |
| //! |
| int op_aput_boolean() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aput_common(APUT_BOOLEAN, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode APUT_BYTE by calling aput_common |
| |
| //! |
| int op_aput_byte() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aput_common(APUT_BYTE, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode APUT_CHAR by calling aput_common |
| |
| //! |
| int op_aput_char() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aput_common(APUT_CHAR, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode APUT_SHORT by calling aput_common |
| |
| //! |
| int op_aput_short() { |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| int retval = aput_common(APUT_SHORT, vA, vref, vindex); |
| rPC += 2; |
| return retval; |
| } |
| |
| #define P_GPR_1 PhysicalReg_EBX //callee-saved valid after CanPutArray |
| #define P_GPR_2 PhysicalReg_ECX |
| #define P_GPR_3 PhysicalReg_ESI //callee-saved |
| #define P_SCRATCH_1 PhysicalReg_EDX |
| #define P_SCRATCH_2 PhysicalReg_EAX |
| #define P_SCRATCH_3 PhysicalReg_EDX |
| |
| void markCard_notNull(int tgtAddrReg, int scratchReg, bool isPhysical); |
| |
| //! lower bytecode APUT_OBJECT |
| |
| //! Lower the bytecode using helper function ".aput_obj_helper" if helper switch is on |
| int op_aput_object() { //type checking |
| u2 vA = INST_AA(inst); |
| u2 vref = FETCH(1) & 0xff; |
| u2 vindex = FETCH(1) >> 8; |
| |
| /////////////////////////// |
| // Request VR free delays before register allocation for the temporaries |
| // No need to request delay for vA since it will be transferred to temporary |
| // after the null check and bound check. |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) |
| requestVRFreeDelay(vref,VRDELAY_NULLCHECK); |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { |
| requestVRFreeDelay(vref,VRDELAY_BOUNDCHECK); |
| requestVRFreeDelay(vindex,VRDELAY_BOUNDCHECK); |
| } |
| |
| get_virtual_reg(vref, OpndSize_32, 1, false); //array |
| export_pc(); //use %edx |
| |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) { |
| compare_imm_reg(OpndSize_32, 0, 1, false); |
| conditional_jump_global_API(Condition_E, "common_errNullObject", false); |
| cancelVRFreeDelayRequest(vref,VRDELAY_NULLCHECK); |
| } else { |
| updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1 |
| } |
| |
| get_virtual_reg(vindex, OpndSize_32, 2, false); //index |
| if(!(traceCurrentMIR->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) { |
| compare_mem_reg(OpndSize_32, offArrayObject_length, 1, false, 2, false); |
| conditional_jump_global_API(Condition_NC, "common_errArrayIndex", false); |
| cancelVRFreeDelayRequest(vref,VRDELAY_BOUNDCHECK); |
| cancelVRFreeDelayRequest(vindex,VRDELAY_BOUNDCHECK); |
| } else { |
| updateRefCount2(1, LowOpndRegType_gp, false); //update reference count for tmp1 |
| updateRefCount2(2, LowOpndRegType_gp, false); //update reference count for tmp2 |
| } |
| |
| get_virtual_reg(vA, OpndSize_32, 4, false); |
| compare_imm_reg(OpndSize_32, 0, 4, false); |
| conditional_jump(Condition_E, ".aput_object_skip_check", true); |
| rememberState(1); |
| move_mem_to_reg(OpndSize_32, offObject_clazz, 4, false, 5, false); |
| load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| move_reg_to_mem(OpndSize_32, 5, false, 0, PhysicalReg_ESP, true); |
| move_mem_to_reg(OpndSize_32, offObject_clazz, 1, false, 6, false); |
| move_reg_to_mem(OpndSize_32, 6, false, 4, PhysicalReg_ESP, true); |
| |
| scratchRegs[0] = PhysicalReg_SCRATCH_1; |
| call_dvmCanPutArrayElement(); //scratch?? |
| load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| compare_imm_reg(OpndSize_32, 0, PhysicalReg_EAX, true); |
| conditional_jump_global_API(Condition_E, "common_errArrayStore", false); |
| |
| //NOTE: "2, false" is live through function call |
| move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4); |
| markCard_notNull(1, 11, false); |
| rememberState(2); |
| ////TODO NCG O1 + code cache |
| unconditional_jump(".aput_object_after_check", true); |
| |
| insertLabel(".aput_object_skip_check", true); |
| goToState(1); |
| //NOTE: "2, false" is live through function call |
| move_reg_to_mem_disp_scale(OpndSize_32, 4, false, 1, false, offArrayObject_contents, 2, false, 4); |
| |
| transferToState(2); |
| insertLabel(".aput_object_after_check", true); |
| /////////////////////////////// |
| rPC += 2; |
| return 0; |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #undef P_GPR_3 |
| #undef P_SCRATCH_1 |
| #undef P_SCRATCH_2 |
| #undef P_SCRATCH_3 |
| |
| ////////////////////////////////////////// |
| #define P_GPR_1 PhysicalReg_ECX |
| #define P_GPR_2 PhysicalReg_EBX //should be callee-saved to avoid overwritten by inst_field_resolve |
| #define P_GPR_3 PhysicalReg_ESI |
| #define P_SCRATCH_1 PhysicalReg_EDX |
| |
| /* |
| movl offThread_cardTable(self), scratchReg |
| compare_imm_reg 0, valReg (testl valReg, valReg) |
| je .markCard_skip |
| shrl $GC_CARD_SHIFT, tgtAddrReg |
| movb %, (scratchReg, tgtAddrReg) |
| NOTE: scratchReg can be accessed with the corresponding byte |
| tgtAddrReg will be updated |
| for O1, update the corresponding reference count |
| */ |
| void markCard(int valReg, int tgtAddrReg, bool targetPhysical, int scratchReg, bool isPhysical) { |
| get_self_pointer(PhysicalReg_SCRATCH_6, isScratchPhysical); |
| move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_6, isScratchPhysical, scratchReg, isPhysical); |
| compare_imm_reg(OpndSize_32, 0, valReg, isPhysical); |
| conditional_jump(Condition_E, ".markCard_skip", true); |
| alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, targetPhysical); |
| move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isPhysical, scratchReg, isPhysical, 0, tgtAddrReg, targetPhysical, 1); |
| insertLabel(".markCard_skip", true); |
| } |
| |
| void markCard_notNull(int tgtAddrReg, int scratchReg, bool isPhysical) { |
| get_self_pointer(PhysicalReg_SCRATCH_2, isScratchPhysical); |
| move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_2, isScratchPhysical, scratchReg, isPhysical); |
| alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, isPhysical); |
| move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isPhysical, scratchReg, isPhysical, 0, tgtAddrReg, isPhysical, 1); |
| } |
| |
| void markCard_filled(int tgtAddrReg, bool isTgtPhysical, int scratchReg, bool isScratchPhysical) { |
| get_self_pointer(PhysicalReg_SCRATCH_2, false/*isPhysical*/); |
| move_mem_to_reg(OpndSize_32, offsetof(Thread, cardTable), PhysicalReg_SCRATCH_2, isScratchPhysical, scratchReg, isScratchPhysical); |
| alu_binary_imm_reg(OpndSize_32, shr_opc, GC_CARD_SHIFT, tgtAddrReg, isTgtPhysical); |
| move_reg_to_mem_disp_scale(OpndSize_8, scratchReg, isScratchPhysical, scratchReg, isScratchPhysical, 0, tgtAddrReg, isTgtPhysical, 1); |
| } |
| //! LOWER bytecode IGET,IPUT without usage of helper function |
| |
| //! It has null check and calls assembly function inst_field_resolve |
| int iget_iput_common_nohelper(int tmp, int flag, u2 vA, u2 vB, int isObj, bool isVolatile) { |
| #ifdef WITH_JIT_INLINING |
| const Method *method = (traceCurrentMIR->OptimizationFlags & MIR_CALLEE) ? |
| traceCurrentMIR->meta.calleeMethod : currentMethod; |
| InstField *pInstField = (InstField *) |
| method->clazz->pDvmDex->pResFields[tmp]; |
| #else |
| InstField *pInstField = (InstField *) |
| currentMethod->clazz->pDvmDex->pResFields[tmp]; |
| #endif |
| int fieldOffset; |
| |
| assert(pInstField != NULL); |
| fieldOffset = pInstField->byteOffset; |
| move_imm_to_reg(OpndSize_32, fieldOffset, 8, false); |
| // Request VR delay before transfer to temporary. Only vB needs delay. |
| // vA will have non-zero reference count since transfer to temporary for |
| // it happens after null check, thus no delay is needed. |
| requestVRFreeDelay(vB,VRDELAY_NULLCHECK); |
| get_virtual_reg(vB, OpndSize_32, 7, false); |
| nullCheck(7, false, 2, vB); //maybe optimized away, if not, call |
| cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK); |
| if(flag == IGET) { |
| move_mem_scale_to_reg(OpndSize_32, 7, false, 8, false, 1, 9, false); |
| set_virtual_reg(vA, OpndSize_32, 9, false); |
| #ifdef DEBUG_IGET_OBJ |
| if(isObj > 0) { |
| pushAllRegs(); |
| load_effective_addr(-16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| move_reg_to_mem(OpndSize_32, 9, false, 12, PhysicalReg_ESP, true); //field |
| move_reg_to_mem(OpndSize_32, 7, false, 8, PhysicalReg_ESP, true); //object |
| move_imm_to_mem(OpndSize_32, tmp, 4, PhysicalReg_ESP, true); //field |
| move_imm_to_mem(OpndSize_32, 0, 0, PhysicalReg_ESP, true); //iget |
| call_dvmDebugIgetIput(); |
| load_effective_addr(16, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| popAllRegs(); |
| } |
| #endif |
| } else if(flag == IGET_WIDE) { |
| if(isVolatile) { |
| /* call dvmQuasiAtomicRead64(addr) */ |
| load_effective_addr(fieldOffset, 7, false, 9, false); |
| move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //1st argument |
| load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| nextVersionOfHardReg(PhysicalReg_EAX, 2); |
| nextVersionOfHardReg(PhysicalReg_EDX, 2); |
| scratchRegs[0] = PhysicalReg_SCRATCH_3; |
| call_dvmQuasiAtomicRead64(); |
| load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| //memory content in %edx, %eax |
| set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true); |
| set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true); |
| } else { |
| move_mem_scale_to_reg(OpndSize_64, 7, false, 8, false, 1, 1, false); //access field |
| set_virtual_reg(vA, OpndSize_64, 1, false); |
| } |
| } else if(flag == IPUT) { |
| get_virtual_reg(vA, OpndSize_32, 9, false); |
| move_reg_to_mem_scale(OpndSize_32, 9, false, 7, false, 8, false, 1); //access field |
| if(isObj) { |
| markCard(9, 7, false, 11, false); |
| } |
| } else if(flag == IPUT_WIDE) { |
| get_virtual_reg(vA, OpndSize_64, 1, false); |
| if(isVolatile) { |
| /* call dvmQuasiAtomicSwap64(val, addr) */ |
| load_effective_addr(fieldOffset, 7, false, 9, false); |
| move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //2nd argument |
| move_reg_to_mem(OpndSize_64, 1, false, -12, PhysicalReg_ESP, true); //1st argument |
| load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| scratchRegs[0] = PhysicalReg_SCRATCH_3; |
| call_dvmQuasiAtomicSwap64(); |
| load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| } |
| else { |
| move_reg_to_mem_scale(OpndSize_64, 1, false, 7, false, 8, false, 1); |
| } |
| } |
| /////////////////////////// |
| return 0; |
| } |
| //! wrapper to call either iget_iput_common_helper or iget_iput_common_nohelper |
| |
| //! |
| int iget_iput_common(int tmp, int flag, u2 vA, u2 vB, int isObj, bool isVolatile) { |
| return iget_iput_common_nohelper(tmp, flag, vA, vB, isObj, isVolatile); |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #undef P_GPR_3 |
| #undef P_SCRATCH_1 |
| //! lower bytecode IGET by calling iget_iput_common |
| |
| //! |
| int op_iget() { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); |
| u2 tmp = FETCH(1); |
| int retval = iget_iput_common(tmp, IGET, vA, vB, 0, false); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode IGET_WIDE by calling iget_iput_common |
| |
| //! |
| int op_iget_wide(bool isVolatile) { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); |
| u2 tmp = FETCH(1); |
| int retval = iget_iput_common(tmp, IGET_WIDE, vA, vB, 0, isVolatile); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode IGET_OBJECT by calling iget_iput_common |
| |
| //! |
| int op_iget_object() { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); |
| u2 tmp = FETCH(1); |
| int retval = iget_iput_common(tmp, IGET, vA, vB, 1, false); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode IGET_BOOLEAN by calling iget_iput_common |
| |
| //! |
| int op_iget_boolean() { |
| return op_iget(); |
| } |
| //! lower bytecode IGET_BYTE by calling iget_iput_common |
| |
| //! |
| int op_iget_byte() { |
| return op_iget(); |
| } |
| //! lower bytecode IGET_CHAR by calling iget_iput_common |
| |
| //! |
| int op_iget_char() { |
| return op_iget(); |
| } |
| //! lower bytecode IGET_SHORT by calling iget_iput_common |
| |
| //! |
| int op_iget_short() { |
| return op_iget(); |
| } |
| //! lower bytecode IPUT by calling iget_iput_common |
| |
| //! |
| int op_iput() { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); |
| u2 tmp = FETCH(1); |
| int retval = iget_iput_common(tmp, IPUT, vA, vB, 0, false); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode IPUT_WIDE by calling iget_iput_common |
| |
| //! |
| int op_iput_wide(bool isVolatile) { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); |
| u2 tmp = FETCH(1); |
| int retval = iget_iput_common(tmp, IPUT_WIDE, vA, vB, 0, isVolatile); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode IPUT_OBJECT by calling iget_iput_common |
| |
| //! |
| int op_iput_object() { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); |
| u2 tmp = FETCH(1); |
| int retval = iget_iput_common(tmp, IPUT, vA, vB, 1, false); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode IPUT_BOOLEAN by calling iget_iput_common |
| |
| //! |
| int op_iput_boolean() { |
| return op_iput(); |
| } |
| //! lower bytecode IPUT_BYTE by calling iget_iput_common |
| |
| //! |
| int op_iput_byte() { |
| return op_iput(); |
| } |
| //! lower bytecode IPUT_CHAR by calling iget_iput_common |
| |
| //! |
| int op_iput_char() { |
| return op_iput(); |
| } |
| //! lower bytecode IPUT_SHORT by calling iget_iput_common |
| |
| //! |
| int op_iput_short() { |
| return op_iput(); |
| } |
| |
| #define P_GPR_1 PhysicalReg_EBX |
| #define P_GPR_2 PhysicalReg_ECX |
| #define P_GPR_3 PhysicalReg_EDX //used by helper only |
| |
| //! common section to lower IGET & IPUT |
| |
| //! It will use helper function sget_helper if the switch is on |
| int sget_sput_common(int flag, u2 vA, u2 tmp, bool isObj, bool isVolatile) { |
| //call assembly static_field_resolve |
| //no exception |
| //glue: get_res_fields |
| //hard-coded: eax (one version?) |
| ////////////////////////////////////////// |
| #ifdef WITH_JIT_INLINING |
| const Method *method = (traceCurrentMIR->OptimizationFlags & MIR_CALLEE) ? traceCurrentMIR->meta.calleeMethod : currentMethod; |
| void *fieldPtr = (void*) |
| (method->clazz->pDvmDex->pResFields[tmp]); |
| #else |
| void *fieldPtr = (void*) |
| (currentMethod->clazz->pDvmDex->pResFields[tmp]); |
| #endif |
| assert(fieldPtr != NULL); |
| move_imm_to_reg(OpndSize_32, (int)fieldPtr, PhysicalReg_EAX, true); |
| if(flag == SGET) { |
| move_mem_to_reg(OpndSize_32, offStaticField_value, PhysicalReg_EAX, true, 7, false); //access field |
| set_virtual_reg(vA, OpndSize_32, 7, false); |
| } else if(flag == SGET_WIDE) { |
| if(isVolatile) { |
| /* call dvmQuasiAtomicRead64(addr) */ |
| load_effective_addr(offStaticField_value, PhysicalReg_EAX, true, 9, false); |
| move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //1st argument |
| load_effective_addr(-4, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| nextVersionOfHardReg(PhysicalReg_EAX, 2); |
| nextVersionOfHardReg(PhysicalReg_EDX, 2); |
| scratchRegs[0] = PhysicalReg_SCRATCH_3; |
| call_dvmQuasiAtomicRead64(); |
| load_effective_addr(4, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| //memory content in %edx, %eax |
| set_virtual_reg(vA, OpndSize_32, PhysicalReg_EAX, true); |
| set_virtual_reg(vA+1, OpndSize_32, PhysicalReg_EDX, true); |
| } |
| else { |
| move_mem_to_reg(OpndSize_64, offStaticField_value, PhysicalReg_EAX, true, 1, false); //access field |
| set_virtual_reg(vA, OpndSize_64, 1, false); |
| } |
| } else if(flag == SPUT) { |
| get_virtual_reg(vA, OpndSize_32, 7, false); |
| move_reg_to_mem(OpndSize_32, 7, false, offStaticField_value, PhysicalReg_EAX, true); //access field |
| if(isObj) { |
| /* get clazz object, then use clazz object to mark card */ |
| move_mem_to_reg(OpndSize_32, offField_clazz, PhysicalReg_EAX, true, 12, false); |
| markCard(7/*valReg*/, 12, false, 11, false); |
| } |
| } else if(flag == SPUT_WIDE) { |
| get_virtual_reg(vA, OpndSize_64, 1, false); |
| if(isVolatile) { |
| /* call dvmQuasiAtomicSwap64(val, addr) */ |
| load_effective_addr(offStaticField_value, PhysicalReg_EAX, true, 9, false); |
| move_reg_to_mem(OpndSize_32, 9, false, -4, PhysicalReg_ESP, true); //2nd argument |
| move_reg_to_mem(OpndSize_64, 1, false, -12, PhysicalReg_ESP, true); //1st argument |
| load_effective_addr(-12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| scratchRegs[0] = PhysicalReg_SCRATCH_3; |
| call_dvmQuasiAtomicSwap64(); |
| load_effective_addr(12, PhysicalReg_ESP, true, PhysicalReg_ESP, true); |
| } |
| else { |
| move_reg_to_mem(OpndSize_64, 1, false, offStaticField_value, PhysicalReg_EAX, true); //access field |
| } |
| } |
| ////////////////////////////////////////////// |
| return 0; |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #undef P_GPR_3 |
| //! lower bytecode SGET by calling sget_sput_common |
| |
| //! |
| int op_sget() { |
| u2 vA = INST_AA(inst); |
| u2 tmp = FETCH(1); |
| int retval = sget_sput_common(SGET, vA, tmp, false, false); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode SGET_WIDE by calling sget_sput_common |
| |
| //! |
| int op_sget_wide(bool isVolatile) { |
| u2 vA = INST_AA(inst); |
| u2 tmp = FETCH(1); |
| int retval = sget_sput_common(SGET_WIDE, vA, tmp, false, isVolatile); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode SGET_OBJECT by calling sget_sput_common |
| |
| //! |
| int op_sget_object() { |
| return op_sget(); |
| } |
| //! lower bytecode SGET_BOOLEAN by calling sget_sput_common |
| |
| //! |
| int op_sget_boolean() { |
| return op_sget(); |
| } |
| //! lower bytecode SGET_BYTE by calling sget_sput_common |
| |
| //! |
| int op_sget_byte() { |
| return op_sget(); |
| } |
| //! lower bytecode SGET_CHAR by calling sget_sput_common |
| |
| //! |
| int op_sget_char() { |
| return op_sget(); |
| } |
| //! lower bytecode SGET_SHORT by calling sget_sput_common |
| |
| //! |
| int op_sget_short() { |
| return op_sget(); |
| } |
| //! lower bytecode SPUT by calling sget_sput_common |
| |
| //! |
| int op_sput(bool isObj) { |
| u2 vA = INST_AA(inst); |
| u2 tmp = FETCH(1); |
| int retval = sget_sput_common(SPUT, vA, tmp, isObj, false); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode SPUT_WIDE by calling sget_sput_common |
| |
| //! |
| int op_sput_wide(bool isVolatile) { |
| u2 vA = INST_AA(inst); |
| u2 tmp = FETCH(1); |
| int retval = sget_sput_common(SPUT_WIDE, vA, tmp, false, isVolatile); |
| rPC += 2; |
| return retval; |
| } |
| //! lower bytecode SPUT_OBJECT by calling sget_sput_common |
| |
| //! |
| int op_sput_object() { |
| return op_sput(true); |
| } |
| //! lower bytecode SPUT_OBJECT by calling sget_sput_common |
| |
| //! |
| int op_sput_boolean() { |
| return op_sput(false); |
| } |
| //! lower bytecode SPUT_BOOLEAN by calling sget_sput_common |
| |
| //! |
| int op_sput_byte() { |
| return op_sput(false); |
| } |
| //! lower bytecode SPUT_BYTE by calling sget_sput_common |
| |
| //! |
| int op_sput_char() { |
| return op_sput(false); |
| } |
| //! lower bytecode SPUT_SHORT by calling sget_sput_common |
| |
| //! |
| int op_sput_short() { |
| return op_sput(false); |
| } |
| #define P_GPR_1 PhysicalReg_EBX |
| #define P_GPR_2 PhysicalReg_ECX |
| //! lower bytecode IGET_QUICK |
| |
| //! |
| int op_iget_quick() { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); //object |
| u2 tmp = FETCH(1); |
| |
| 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, if not, call |
| cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK); |
| |
| move_mem_to_reg(OpndSize_32, tmp, 1, false, 2, false); |
| set_virtual_reg(vA, OpndSize_32, 2, false); |
| rPC += 2; |
| return 0; |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #define P_GPR_1 PhysicalReg_EBX |
| //! lower bytecode IGET_WIDE_QUICK |
| |
| //! |
| int op_iget_wide_quick() { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); //object |
| u2 tmp = FETCH(1); |
| |
| 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, if not, call |
| cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK); |
| |
| move_mem_to_reg(OpndSize_64, tmp, 1, false, 1, false); |
| set_virtual_reg(vA, OpndSize_64, 1, false); |
| rPC += 2; |
| return 0; |
| } |
| #undef P_GPR_1 |
| //! lower bytecode IGET_OBJECT_QUICK |
| |
| //! |
| int op_iget_object_quick() { |
| return op_iget_quick(); |
| } |
| #define P_GPR_1 PhysicalReg_EBX |
| #define P_GPR_2 PhysicalReg_ECX |
| //! lower bytecode IPUT_QUICK |
| |
| //! |
| int iput_quick_common(bool isObj) { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); //object |
| u2 tmp = FETCH(1); |
| |
| // Request VR delay before transfer to temporary. Only vB needs delay. |
| // vA will have non-zero reference count since transfer to temporary for |
| // it happens after null check, thus no delay is needed. |
| requestVRFreeDelay(vB,VRDELAY_NULLCHECK); |
| get_virtual_reg(vB, OpndSize_32, 1, false); |
| nullCheck(1, false, 1, vB); //maybe optimized away, if not, call |
| cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK); |
| |
| get_virtual_reg(vA, OpndSize_32, 2, false); |
| move_reg_to_mem(OpndSize_32, 2, false, tmp, 1, false); |
| if(isObj) { |
| markCard(2/*valReg*/, 1, false, 11, false); |
| } |
| rPC += 2; |
| return 0; |
| } |
| int op_iput_quick() { |
| return iput_quick_common(false); |
| } |
| #undef P_GPR_1 |
| #undef P_GPR_2 |
| #define P_GPR_1 PhysicalReg_EBX |
| //! lower bytecode IPUT_WIDE_QUICK |
| |
| //! |
| int op_iput_wide_quick() { |
| u2 vA = INST_A(inst); |
| u2 vB = INST_B(inst); //object |
| u2 tmp = FETCH(1); //byte offset |
| |
| // Request VR delay before transfer to temporary. Only vB needs delay. |
| // vA will have non-zero reference count since transfer to temporary for |
| // it happens after null check, thus no delay is needed. |
| requestVRFreeDelay(vB,VRDELAY_NULLCHECK); |
| get_virtual_reg(vB, OpndSize_32, 1, false); |
| nullCheck(1, false, 1, vB); //maybe optimized away, if not, call |
| cancelVRFreeDelayRequest(vB,VRDELAY_NULLCHECK); |
| |
| get_virtual_reg(vA, OpndSize_64, 1, false); |
| move_reg_to_mem(OpndSize_64, 1, false, tmp, 1, false); |
| rPC += 2; |
| return 0; |
| } |
| #undef P_GPR_1 |
| //! lower bytecode IPUT_OBJECT_QUICK |
| |
| //! |
| int op_iput_object_quick() { |
| return iput_quick_common(true); |
| } |
| |