| /* |
| * 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 Lower.cpp |
| \brief This file implements the high-level wrapper for lowering |
| |
| */ |
| |
| //#include "uthash.h" |
| #include "libdex/DexOpcodes.h" |
| #include "libdex/DexFile.h" |
| #include <math.h> |
| #include <sys/mman.h> |
| #include "Translator.h" |
| #include "Lower.h" |
| #include "enc_wrapper.h" |
| #include "vm/mterp/Mterp.h" |
| #include "NcgHelper.h" |
| #include "libdex/DexCatch.h" |
| #include "compiler/CompilerIR.h" |
| |
| //statistics for optimization |
| int num_removed_nullCheck; |
| |
| PhysicalReg scratchRegs[4]; |
| |
| LowOp* ops[BUFFER_SIZE]; |
| LowOp* op; |
| u2* rPC; //PC pointer to bytecode |
| u2 inst; //current bytecode |
| int offsetPC/*offset in bytecode*/, offsetNCG/*byte offset in native code*/; |
| int ncg_rPC; |
| //! map from PC in bytecode to PC in native code |
| int mapFromBCtoNCG[BYTECODE_SIZE_PER_METHOD]; //initially mapped to -1 |
| char* streamStart = NULL; //start of the Pure CodeItem?, not include the global symbols |
| char* streamCode = NULL; //start of the Pure CodeItem?, not include the global symbols |
| char* streamMethodStart; //start of the method |
| char* stream; //current stream pointer |
| int lowOpTimeStamp = 0; |
| Method* currentMethod = NULL; |
| int currentExceptionBlockIdx = -1; |
| LowOpBlockLabel* traceLabelList = NULL; |
| BasicBlock* traceCurrentBB = NULL; |
| MIR* traceCurrentMIR = NULL; |
| bool scheduling_is_on = false; |
| |
| int common_invokeMethodNoRange(); |
| int common_invokeMethodRange(); |
| int common_invokeArgsDone(ArgsDoneType, bool); |
| |
| //data section of .ia32: |
| char globalData[128]; |
| |
| char strClassCastException[] = "Ljava/lang/ClassCastException;"; |
| char strInstantiationError[] = "Ljava/lang/InstantiationError;"; |
| char strInternalError[] = "Ljava/lang/InternalError;"; |
| char strFilledNewArrayNotImpl[] = "filled-new-array only implemented for 'int'"; |
| char strArithmeticException[] = "Ljava/lang/ArithmeticException;"; |
| char strArrayIndexException[] = "Ljava/lang/ArrayIndexOutOfBoundsException;"; |
| char strArrayStoreException[] = "Ljava/lang/ArrayStoreException;"; |
| char strDivideByZero[] = "divide by zero"; |
| char strNegativeArraySizeException[] = "Ljava/lang/NegativeArraySizeException;"; |
| char strNoSuchMethodError[] = "Ljava/lang/NoSuchMethodError;"; |
| char strNullPointerException[] = "Ljava/lang/NullPointerException;"; |
| char strStringIndexOutOfBoundsException[] = "Ljava/lang/StringIndexOutOfBoundsException;"; |
| |
| int LstrClassCastExceptionPtr, LstrInstantiationErrorPtr, LstrInternalError, LstrFilledNewArrayNotImpl; |
| int LstrArithmeticException, LstrArrayIndexException, LstrArrayStoreException, LstrStringIndexOutOfBoundsException; |
| int LstrDivideByZero, LstrNegativeArraySizeException, LstrNoSuchMethodError, LstrNullPointerException; |
| int LdoubNeg, LvaluePosInfLong, LvalueNegInfLong, LvalueNanLong, LshiftMask, Lvalue64, L64bits, LintMax, LintMin; |
| |
| void initConstDataSec() { |
| char* tmpPtr = globalData; |
| |
| LdoubNeg = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0x00000000; |
| tmpPtr += sizeof(u4); |
| *((u4*)tmpPtr) = 0x80000000; |
| tmpPtr += sizeof(u4); |
| |
| LvaluePosInfLong = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0xFFFFFFFF; |
| tmpPtr += sizeof(u4); |
| *((u4*)tmpPtr) = 0x7FFFFFFF; |
| tmpPtr += sizeof(u4); |
| |
| LvalueNegInfLong = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0x00000000; |
| tmpPtr += sizeof(u4); |
| *((u4*)tmpPtr) = 0x80000000; |
| tmpPtr += sizeof(u4); |
| |
| LvalueNanLong = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0; |
| tmpPtr += sizeof(u4); |
| *((u4*)tmpPtr) = 0; |
| tmpPtr += sizeof(u4); |
| |
| LshiftMask = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0x3f; |
| tmpPtr += sizeof(u4); |
| *((u4*)tmpPtr) = 0; |
| tmpPtr += sizeof(u4); |
| |
| Lvalue64 = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0x40; |
| tmpPtr += sizeof(u4); |
| *((u4*)tmpPtr) = 0; |
| tmpPtr += sizeof(u4); |
| |
| L64bits = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0xFFFFFFFF; |
| tmpPtr += sizeof(u4); |
| *((u4*)tmpPtr) = 0xFFFFFFFF; |
| tmpPtr += sizeof(u4); |
| |
| LintMin = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0x80000000; |
| tmpPtr += sizeof(u4); |
| |
| LintMax = (int)tmpPtr; |
| *((u4*)tmpPtr) = 0x7FFFFFFF; |
| tmpPtr += sizeof(u4); |
| |
| LstrClassCastExceptionPtr = (int)strClassCastException; |
| LstrInstantiationErrorPtr = (int)strInstantiationError; |
| LstrInternalError = (int)strInternalError; |
| LstrFilledNewArrayNotImpl = (int)strFilledNewArrayNotImpl; |
| LstrArithmeticException = (int)strArithmeticException; |
| LstrArrayIndexException = (int)strArrayIndexException; |
| LstrArrayStoreException = (int)strArrayStoreException; |
| LstrDivideByZero = (int)strDivideByZero; |
| LstrNegativeArraySizeException = (int)strNegativeArraySizeException; |
| LstrNoSuchMethodError = (int)strNoSuchMethodError; |
| LstrNullPointerException = (int)strNullPointerException; |
| LstrStringIndexOutOfBoundsException = (int)strStringIndexOutOfBoundsException; |
| } |
| |
| //declarations of functions used in this file |
| int spill_reg(int reg, bool isPhysical); |
| int unspill_reg(int reg, bool isPhysical); |
| |
| int const_string_resolve(); |
| int sget_sput_resolve(); |
| int new_instance_needinit(); |
| int new_instance_abstract(); |
| int invoke_virtual_resolve(); |
| int invoke_direct_resolve(); |
| int invoke_static_resolve(); |
| int filled_new_array_notimpl(); |
| int resolve_class2( |
| int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/, |
| bool indexPhysical, |
| int thirdArg); |
| int resolve_method2( |
| int startLR/*logical register index*/, bool isPhysical, int indexReg/*const pool index*/, |
| bool indexPhysical, |
| int thirdArg/*VIRTUAL*/); |
| int resolve_inst_field2( |
| int startLR/*logical register index*/, bool isPhysical, |
| int indexReg/*const pool index*/, |
| bool indexPhysical); |
| int resolve_static_field2( |
| int startLR/*logical register index*/, bool isPhysical, |
| int indexReg/*const pool index*/, |
| bool indexPhysical); |
| |
| int invokeMethodNoRange_1_helper(); |
| int invokeMethodNoRange_2_helper(); |
| int invokeMethodNoRange_3_helper(); |
| int invokeMethodNoRange_4_helper(); |
| int invokeMethodNoRange_5_helper(); |
| int invokeMethodRange_helper(); |
| |
| int invoke_virtual_helper(); |
| int invoke_virtual_quick_helper(); |
| int invoke_static_helper(); |
| int invoke_direct_helper(); |
| int new_instance_helper(); |
| int sget_sput_helper(int flag); |
| int aput_obj_helper(); |
| int aget_helper(int flag); |
| int aput_helper(int flag); |
| int monitor_enter_helper(); |
| int monitor_exit_helper(); |
| int throw_helper(); |
| int const_string_helper(); |
| int array_length_helper(); |
| int invoke_super_helper(); |
| int invoke_interface_helper(); |
| int iget_iput_helper(int flag); |
| int check_cast_helper(bool instance); |
| int new_array_helper(); |
| |
| int common_returnFromMethod(); |
| |
| /*! |
| \brief dump helper functions |
| |
| */ |
| int performCGWorklist() { |
| filled_new_array_notimpl(); |
| freeShortMap(); |
| const_string_resolve(); |
| freeShortMap(); |
| |
| resolve_class2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, 0); |
| freeShortMap(); |
| resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_VIRTUAL); |
| freeShortMap(); |
| resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_DIRECT); |
| freeShortMap(); |
| resolve_method2(PhysicalReg_EAX, true, PhysicalReg_EAX, true, METHOD_STATIC); |
| freeShortMap(); |
| resolve_inst_field2(PhysicalReg_EAX, true, PhysicalReg_EAX, true); |
| freeShortMap(); |
| resolve_static_field2(PhysicalReg_EAX, true, PhysicalReg_EAX, true); |
| freeShortMap(); |
| throw_exception_message(PhysicalReg_ECX, PhysicalReg_EAX, true, PhysicalReg_Null, true); |
| freeShortMap(); |
| throw_exception(PhysicalReg_ECX, PhysicalReg_EAX, PhysicalReg_Null, true); |
| freeShortMap(); |
| new_instance_needinit(); |
| freeShortMap(); |
| return 0; |
| } |
| |
| int aput_object_count; |
| int common_periodicChecks_entry(); |
| int common_periodicChecks4(); |
| /*! |
| \brief for debugging purpose, dump the sequence of native code for each bytecode |
| |
| */ |
| int ncgMethodFake(Method* method) { |
| //to measure code size expansion, no need to patch up labels |
| methodDataWorklist = NULL; |
| globalShortWorklist = NULL; |
| globalNCGWorklist = NULL; |
| streamMethodStart = stream; |
| |
| //initialize mapFromBCtoNCG |
| memset(&mapFromBCtoNCG[0], -1, BYTECODE_SIZE_PER_METHOD * sizeof(mapFromBCtoNCG[0])); |
| unsigned int i; |
| u2* rStart = (u2*)malloc(5*sizeof(u2)); |
| if(rStart == NULL) { |
| ALOGE("Memory allocation failed"); |
| return -1; |
| } |
| rPC = rStart; |
| method->insns = rStart; |
| for(i = 0; i < 5; i++) *rPC++ = 0; |
| for(i = 0; i < 256; i++) { |
| rPC = rStart; |
| //modify the opcode |
| char* tmp = (char*)rStart; |
| *tmp++ = i; |
| *tmp = i; |
| inst = FETCH(0); |
| char* tmpStart = stream; |
| lowerByteCode(method); //use inst, rPC, method, modify rPC |
| int size_in_u2 = rPC - rStart; |
| if(stream - tmpStart > 0) |
| ALOGI("LOWER bytecode %x size in u2: %d ncg size in byte: %d", i, size_in_u2, stream - tmpStart); |
| } |
| exit(0); |
| } |
| |
| bool existATryBlock(Method* method, int startPC, int endPC) { |
| const DexCode* pCode = dvmGetMethodCode(method); |
| u4 triesSize = pCode->triesSize; |
| const DexTry* pTries = dexGetTries(pCode); |
| unsigned int i; |
| for (i = 0; i < triesSize; i++) { |
| const DexTry* pTry = &pTries[i]; |
| u4 start = pTry->startAddr; //offsetPC |
| u4 end = start + pTry->insnCount; |
| //if [start, end] overlaps with [startPC, endPC] returns true |
| if((int)end < startPC || (int)start > endPC) { //no overlap |
| } else { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| int mm_bytecode_size = 0; |
| int mm_ncg_size = 0; |
| int mm_relocation_size = 0; |
| int mm_map_size = 0; |
| void resetCodeSize() { |
| mm_bytecode_size = 0; |
| mm_ncg_size = 0; |
| mm_relocation_size = 0; |
| mm_map_size = 0; |
| } |
| |
| bool bytecodeIsRemoved(const Method* method, u4 bytecodeOffset) { |
| if(gDvm.executionMode == kExecutionModeNcgO0) return false; |
| u4 ncgOff = mapFromBCtoNCG[bytecodeOffset]; |
| int k = bytecodeOffset+1; |
| u2 insnsSize = dvmGetMethodInsnsSize(method); |
| while(k < insnsSize) { |
| if(mapFromBCtoNCG[k] < 0) { |
| k++; |
| continue; |
| } |
| if(mapFromBCtoNCG[k] == (int)ncgOff) return true; |
| return false; |
| } |
| return false; |
| } |
| |
| int invoke_super_nsm(); |
| void init_common(const char* curFileName, DvmDex *pDvmDex, bool forNCG); //forward declaration |
| void initGlobalMethods(); //forward declaration |
| |
| //called once when compiler thread starts up |
| void initJIT(const char* curFileName, DvmDex *pDvmDex) { |
| init_common(curFileName, pDvmDex, false); |
| } |
| |
| void init_common(const char* curFileName, DvmDex *pDvmDex, bool forNCG) { |
| if(!gDvm.constInit) { |
| globalMapNum = 0; |
| globalMap = NULL; |
| initConstDataSec(); |
| gDvm.constInit = true; |
| } |
| |
| //for initJIT: stream is already set |
| if(!gDvm.commonInit) { |
| initGlobalMethods(); |
| gDvm.commonInit = true; |
| } |
| } |
| |
| void initGlobalMethods() { |
| dump_x86_inst = false; /* DEBUG */ |
| // generate native code for function ncgGetEIP |
| insertLabel("ncgGetEIP", false); |
| move_mem_to_reg(OpndSize_32, 0, PhysicalReg_ESP, true, PhysicalReg_EDX, true); |
| x86_return(); |
| |
| //generate code for common labels |
| //jumps within a helper function is treated as short labels |
| globalShortMap = NULL; |
| common_periodicChecks_entry(); |
| freeShortMap(); |
| common_periodicChecks4(); |
| freeShortMap(); |
| //common_invokeMethodNoRange(); |
| //common_invokeMethodRange(); |
| |
| if(dump_x86_inst) ALOGI("ArgsDone_Normal start"); |
| common_invokeArgsDone(ArgsDone_Normal, false); |
| freeShortMap(); |
| if(dump_x86_inst) ALOGI("ArgsDone_Native start"); |
| common_invokeArgsDone(ArgsDone_Native, false); |
| freeShortMap(); |
| if(dump_x86_inst) ALOGI("ArgsDone_Full start"); |
| common_invokeArgsDone(ArgsDone_Full, true/*isJitFull*/); |
| if(dump_x86_inst) ALOGI("ArgsDone_Full end"); |
| freeShortMap(); |
| |
| common_backwardBranch(); |
| freeShortMap(); |
| common_exceptionThrown(); |
| freeShortMap(); |
| common_errNullObject(); |
| freeShortMap(); |
| common_errArrayIndex(); |
| freeShortMap(); |
| common_errArrayStore(); |
| freeShortMap(); |
| common_errNegArraySize(); |
| freeShortMap(); |
| common_errNoSuchMethod(); |
| freeShortMap(); |
| common_errDivideByZero(); |
| freeShortMap(); |
| common_gotoBail(); |
| freeShortMap(); |
| common_gotoBail_0(); |
| freeShortMap(); |
| invoke_super_nsm(); |
| freeShortMap(); |
| |
| performCGWorklist(); //generate code for helper functions |
| performLabelWorklist(); //it is likely that the common labels will jump to other common labels |
| |
| dump_x86_inst = false; |
| } |
| |
| ExecutionMode origMode; |
| //when to update streamMethodStart |
| bool lowerByteCodeJit(const Method* method, const u2* codePtr, MIR* mir) { |
| rPC = (u2*)codePtr; |
| inst = FETCH(0); |
| traceCurrentMIR = mir; |
| int retCode = lowerByteCode(method); |
| traceCurrentMIR = NULL; |
| freeShortMap(); |
| if(retCode >= 0) return false; //handled |
| return true; //not handled |
| } |
| |
| void startOfBasicBlock(BasicBlock* bb) { |
| traceCurrentBB = bb; |
| if(gDvm.executionMode == kExecutionModeNcgO0) { |
| isScratchPhysical = true; |
| } else { |
| isScratchPhysical = false; |
| } |
| } |
| |
| void startOfTrace(const Method* method, LowOpBlockLabel* labelList, int exceptionBlockId, |
| CompilationUnit *cUnit) { |
| origMode = gDvm.executionMode; |
| gDvm.executionMode = kExecutionModeNcgO1; |
| if(gDvm.executionMode == kExecutionModeNcgO0) { |
| isScratchPhysical = true; |
| } else { |
| isScratchPhysical = false; |
| } |
| currentMethod = (Method*)method; |
| currentExceptionBlockIdx = exceptionBlockId; |
| methodDataWorklist = NULL; |
| globalShortWorklist = NULL; |
| globalNCGWorklist = NULL; |
| |
| streamMethodStart = stream; |
| //initialize mapFromBCtoNCG |
| memset(&mapFromBCtoNCG[0], -1, BYTECODE_SIZE_PER_METHOD * sizeof(mapFromBCtoNCG[0])); |
| traceLabelList = labelList; |
| if(gDvm.executionMode == kExecutionModeNcgO1) |
| startOfTraceO1(method, labelList, exceptionBlockId, cUnit); |
| } |
| |
| void endOfTrace(bool freeOnly) { |
| if(freeOnly) { |
| freeLabelWorklist(); |
| freeNCGWorklist(); |
| freeDataWorklist(); |
| freeChainingWorklist(); |
| } |
| else { |
| performLabelWorklist(); |
| performNCGWorklist(); //handle forward jump (GOTO, IF) |
| performDataWorklist(); //handle SWITCH & FILL_ARRAY_DATA |
| performChainingWorklist(); |
| } |
| if(gDvm.executionMode == kExecutionModeNcgO1) { |
| endOfTraceO1(); |
| } |
| gDvm.executionMode = origMode; |
| } |
| |
| /////////////////////////////////////////////////////////////////// |
| //! |
| //! each bytecode is translated to a sequence of machine codes |
| int lowerByteCode(const Method* method) { //inputs: rPC & inst & stream & streamMethodStart |
| /* offsetPC is used in O1 code generator, where it is defined as the sequence number |
| use a local version to avoid overwriting */ |
| int offsetPC = rPC - (u2*)method->insns; |
| |
| if(dump_x86_inst) |
| ALOGI("LOWER bytecode %x at offsetPC %x offsetNCG %x @%p", |
| INST_INST(inst), offsetPC, stream - streamMethodStart, stream); |
| |
| //update mapFromBCtoNCG |
| offsetNCG = stream - streamMethodStart; |
| if(offsetPC >= BYTECODE_SIZE_PER_METHOD) ALOGE("offsetPC %d exceeds BYTECODE_SIZE_PER_METHOD", offsetPC); |
| mapFromBCtoNCG[offsetPC] = offsetNCG; |
| #if defined(ENABLE_TRACING) && defined(TRACING_OPTION2) |
| insertMapWorklist(offsetPC, mapFromBCtoNCG[offsetPC], 1); |
| #endif |
| //return number of LowOps generated |
| switch (INST_INST(inst)) { |
| case OP_NOP: |
| return op_nop(); |
| case OP_MOVE: |
| case OP_MOVE_OBJECT: |
| return op_move(); |
| case OP_MOVE_FROM16: |
| case OP_MOVE_OBJECT_FROM16: |
| return op_move_from16(); |
| case OP_MOVE_16: |
| case OP_MOVE_OBJECT_16: |
| return op_move_16(); |
| case OP_MOVE_WIDE: |
| return op_move_wide(); |
| case OP_MOVE_WIDE_FROM16: |
| return op_move_wide_from16(); |
| case OP_MOVE_WIDE_16: |
| return op_move_wide_16(); |
| case OP_MOVE_RESULT: |
| case OP_MOVE_RESULT_OBJECT: |
| return op_move_result(); |
| case OP_MOVE_RESULT_WIDE: |
| return op_move_result_wide(); |
| case OP_MOVE_EXCEPTION: |
| return op_move_exception(); |
| case OP_RETURN_VOID: |
| case OP_RETURN_VOID_BARRIER: |
| return op_return_void(); |
| case OP_RETURN: |
| case OP_RETURN_OBJECT: |
| return op_return(); |
| case OP_RETURN_WIDE: |
| return op_return_wide(); |
| case OP_CONST_4: |
| return op_const_4(); |
| case OP_CONST_16: |
| return op_const_16(); |
| case OP_CONST: |
| return op_const(); |
| case OP_CONST_HIGH16: |
| return op_const_high16(); |
| case OP_CONST_WIDE_16: |
| return op_const_wide_16(); |
| case OP_CONST_WIDE_32: |
| return op_const_wide_32(); |
| case OP_CONST_WIDE: |
| return op_const_wide(); |
| case OP_CONST_WIDE_HIGH16: |
| return op_const_wide_high16(); |
| case OP_CONST_STRING: |
| return op_const_string(); |
| case OP_CONST_STRING_JUMBO: |
| return op_const_string_jumbo(); |
| case OP_CONST_CLASS: |
| return op_const_class(); |
| case OP_MONITOR_ENTER: |
| return op_monitor_enter(); |
| case OP_MONITOR_EXIT: |
| return op_monitor_exit(); |
| case OP_CHECK_CAST: |
| return op_check_cast(); |
| case OP_INSTANCE_OF: |
| return op_instance_of(); |
| case OP_ARRAY_LENGTH: |
| return op_array_length(); |
| case OP_NEW_INSTANCE: |
| return op_new_instance(); |
| case OP_NEW_ARRAY: |
| return op_new_array(); |
| case OP_FILLED_NEW_ARRAY: |
| return op_filled_new_array(); |
| case OP_FILLED_NEW_ARRAY_RANGE: |
| return op_filled_new_array_range(); |
| case OP_FILL_ARRAY_DATA: |
| return op_fill_array_data(); |
| case OP_THROW: |
| return op_throw(); |
| case OP_THROW_VERIFICATION_ERROR: |
| return op_throw_verification_error(); |
| case OP_GOTO: |
| return op_goto(); |
| case OP_GOTO_16: |
| return op_goto_16(); |
| case OP_GOTO_32: |
| return op_goto_32(); |
| case OP_PACKED_SWITCH: |
| return op_packed_switch(); |
| case OP_SPARSE_SWITCH: |
| return op_sparse_switch(); |
| case OP_CMPL_FLOAT: |
| return op_cmpl_float(); |
| case OP_CMPG_FLOAT: |
| return op_cmpg_float(); |
| case OP_CMPL_DOUBLE: |
| return op_cmpl_double(); |
| case OP_CMPG_DOUBLE: |
| return op_cmpg_double(); |
| case OP_CMP_LONG: |
| return op_cmp_long(); |
| case OP_IF_EQ: |
| return op_if_eq(); |
| case OP_IF_NE: |
| return op_if_ne(); |
| case OP_IF_LT: |
| return op_if_lt(); |
| case OP_IF_GE: |
| return op_if_ge(); |
| case OP_IF_GT: |
| return op_if_gt(); |
| case OP_IF_LE: |
| return op_if_le(); |
| case OP_IF_EQZ: |
| return op_if_eqz(); |
| case OP_IF_NEZ: |
| return op_if_nez(); |
| case OP_IF_LTZ: |
| return op_if_ltz(); |
| case OP_IF_GEZ: |
| return op_if_gez(); |
| case OP_IF_GTZ: |
| return op_if_gtz(); |
| case OP_IF_LEZ: |
| return op_if_lez(); |
| case OP_AGET: |
| return op_aget(); |
| case OP_AGET_WIDE: |
| return op_aget_wide(); |
| case OP_AGET_OBJECT: |
| return op_aget_object(); |
| case OP_AGET_BOOLEAN: |
| return op_aget_boolean(); |
| case OP_AGET_BYTE: |
| return op_aget_byte(); |
| case OP_AGET_CHAR: |
| return op_aget_char(); |
| case OP_AGET_SHORT: |
| return op_aget_short(); |
| case OP_APUT: |
| return op_aput(); |
| case OP_APUT_WIDE: |
| return op_aput_wide(); |
| case OP_APUT_OBJECT: |
| return op_aput_object(); |
| case OP_APUT_BOOLEAN: |
| return op_aput_boolean(); |
| case OP_APUT_BYTE: |
| return op_aput_byte(); |
| case OP_APUT_CHAR: |
| return op_aput_char(); |
| case OP_APUT_SHORT: |
| return op_aput_short(); |
| case OP_IGET: |
| case OP_IGET_VOLATILE: |
| return op_iget(); |
| case OP_IGET_WIDE: |
| return op_iget_wide(false); // isVolatile==false |
| case OP_IGET_WIDE_VOLATILE: |
| return op_iget_wide(true); // isVolatile==true |
| case OP_IGET_OBJECT: |
| case OP_IGET_OBJECT_VOLATILE: |
| return op_iget_object(); |
| case OP_IGET_BOOLEAN: |
| return op_iget_boolean(); |
| case OP_IGET_BYTE: |
| return op_iget_byte(); |
| case OP_IGET_CHAR: |
| return op_iget_char(); |
| case OP_IGET_SHORT: |
| return op_iget_short(); |
| case OP_IPUT: |
| case OP_IPUT_VOLATILE: |
| return op_iput(); |
| case OP_IPUT_WIDE: |
| return op_iput_wide(false); // isVolatile==false |
| case OP_IPUT_WIDE_VOLATILE: |
| return op_iput_wide(true); // isVolatile==true |
| case OP_IPUT_OBJECT: |
| case OP_IPUT_OBJECT_VOLATILE: |
| return op_iput_object(); |
| case OP_IPUT_BOOLEAN: |
| return op_iput_boolean(); |
| case OP_IPUT_BYTE: |
| return op_iput_byte(); |
| case OP_IPUT_CHAR: |
| return op_iput_char(); |
| case OP_IPUT_SHORT: |
| return op_iput_short(); |
| case OP_SGET: |
| case OP_SGET_VOLATILE: |
| return op_sget(); |
| case OP_SGET_WIDE: |
| return op_sget_wide(false); // isVolatile==false |
| case OP_SGET_WIDE_VOLATILE: |
| return op_sget_wide(true); // isVolatile==true |
| case OP_SGET_OBJECT: |
| case OP_SGET_OBJECT_VOLATILE: |
| return op_sget_object(); |
| case OP_SGET_BOOLEAN: |
| return op_sget_boolean(); |
| case OP_SGET_BYTE: |
| return op_sget_byte(); |
| case OP_SGET_CHAR: |
| return op_sget_char(); |
| case OP_SGET_SHORT: |
| return op_sget_short(); |
| case OP_SPUT: |
| case OP_SPUT_VOLATILE: |
| return op_sput(false); |
| case OP_SPUT_WIDE: |
| return op_sput_wide(false); // isVolatile==false |
| case OP_SPUT_WIDE_VOLATILE: |
| return op_sput_wide(true); // isVolatile==true |
| case OP_SPUT_OBJECT: |
| case OP_SPUT_OBJECT_VOLATILE: |
| return op_sput_object(); |
| case OP_SPUT_BOOLEAN: |
| return op_sput_boolean(); |
| case OP_SPUT_BYTE: |
| return op_sput_byte(); |
| case OP_SPUT_CHAR: |
| return op_sput_char(); |
| case OP_SPUT_SHORT: |
| return op_sput_short(); |
| case OP_INVOKE_VIRTUAL: |
| return op_invoke_virtual(); |
| case OP_INVOKE_SUPER: |
| return op_invoke_super(); |
| case OP_INVOKE_DIRECT: |
| return op_invoke_direct(); |
| case OP_INVOKE_STATIC: |
| return op_invoke_static(); |
| case OP_INVOKE_INTERFACE: |
| return op_invoke_interface(); |
| case OP_INVOKE_VIRTUAL_RANGE: |
| return op_invoke_virtual_range(); |
| case OP_INVOKE_SUPER_RANGE: |
| return op_invoke_super_range(); |
| case OP_INVOKE_DIRECT_RANGE: |
| return op_invoke_direct_range(); |
| case OP_INVOKE_STATIC_RANGE: |
| return op_invoke_static_range(); |
| case OP_INVOKE_INTERFACE_RANGE: |
| return op_invoke_interface_range(); |
| case OP_NEG_INT: |
| return op_neg_int(); |
| case OP_NOT_INT: |
| return op_not_int(); |
| case OP_NEG_LONG: |
| return op_neg_long(); |
| case OP_NOT_LONG: |
| return op_not_long(); |
| case OP_NEG_FLOAT: |
| return op_neg_float(); |
| case OP_NEG_DOUBLE: |
| return op_neg_double(); |
| case OP_INT_TO_LONG: |
| return op_int_to_long(); |
| case OP_INT_TO_FLOAT: |
| return op_int_to_float(); |
| case OP_INT_TO_DOUBLE: |
| return op_int_to_double(); |
| case OP_LONG_TO_INT: |
| return op_long_to_int(); |
| case OP_LONG_TO_FLOAT: |
| return op_long_to_float(); |
| case OP_LONG_TO_DOUBLE: |
| return op_long_to_double(); |
| case OP_FLOAT_TO_INT: |
| return op_float_to_int(); |
| case OP_FLOAT_TO_LONG: |
| return op_float_to_long(); |
| case OP_FLOAT_TO_DOUBLE: |
| return op_float_to_double(); |
| case OP_DOUBLE_TO_INT: |
| return op_double_to_int(); |
| case OP_DOUBLE_TO_LONG: |
| return op_double_to_long(); |
| case OP_DOUBLE_TO_FLOAT: |
| return op_double_to_float(); |
| case OP_INT_TO_BYTE: |
| return op_int_to_byte(); |
| case OP_INT_TO_CHAR: |
| return op_int_to_char(); |
| case OP_INT_TO_SHORT: |
| return op_int_to_short(); |
| case OP_ADD_INT: |
| return op_add_int(); |
| case OP_SUB_INT: |
| return op_sub_int(); |
| case OP_MUL_INT: |
| return op_mul_int(); |
| case OP_DIV_INT: |
| return op_div_int(); |
| case OP_REM_INT: |
| return op_rem_int(); |
| case OP_AND_INT: |
| return op_and_int(); |
| case OP_OR_INT: |
| return op_or_int(); |
| case OP_XOR_INT: |
| return op_xor_int(); |
| case OP_SHL_INT: |
| return op_shl_int(); |
| case OP_SHR_INT: |
| return op_shr_int(); |
| case OP_USHR_INT: |
| return op_ushr_int(); |
| case OP_ADD_LONG: |
| return op_add_long(); |
| case OP_SUB_LONG: |
| return op_sub_long(); |
| case OP_MUL_LONG: |
| return op_mul_long(); |
| case OP_DIV_LONG: |
| return op_div_long(); |
| case OP_REM_LONG: |
| return op_rem_long(); |
| case OP_AND_LONG: |
| return op_and_long(); |
| case OP_OR_LONG: |
| return op_or_long(); |
| case OP_XOR_LONG: |
| return op_xor_long(); |
| case OP_SHL_LONG: |
| return op_shl_long(); |
| case OP_SHR_LONG: |
| return op_shr_long(); |
| case OP_USHR_LONG: |
| return op_ushr_long(); |
| case OP_ADD_FLOAT: |
| return op_add_float(); |
| case OP_SUB_FLOAT: |
| return op_sub_float(); |
| case OP_MUL_FLOAT: |
| return op_mul_float(); |
| case OP_DIV_FLOAT: |
| return op_div_float(); |
| case OP_REM_FLOAT: |
| return op_rem_float(); |
| case OP_ADD_DOUBLE: |
| return op_add_double(); |
| case OP_SUB_DOUBLE: |
| return op_sub_double(); |
| case OP_MUL_DOUBLE: |
| return op_mul_double(); |
| case OP_DIV_DOUBLE: |
| return op_div_double(); |
| case OP_REM_DOUBLE: |
| return op_rem_double(); |
| case OP_ADD_INT_2ADDR: |
| return op_add_int_2addr(); |
| case OP_SUB_INT_2ADDR: |
| return op_sub_int_2addr(); |
| case OP_MUL_INT_2ADDR: |
| return op_mul_int_2addr(); |
| case OP_DIV_INT_2ADDR: |
| return op_div_int_2addr(); |
| case OP_REM_INT_2ADDR: |
| return op_rem_int_2addr(); |
| case OP_AND_INT_2ADDR: |
| return op_and_int_2addr(); |
| case OP_OR_INT_2ADDR: |
| return op_or_int_2addr(); |
| case OP_XOR_INT_2ADDR: |
| return op_xor_int_2addr(); |
| case OP_SHL_INT_2ADDR: |
| return op_shl_int_2addr(); |
| case OP_SHR_INT_2ADDR: |
| return op_shr_int_2addr(); |
| case OP_USHR_INT_2ADDR: |
| return op_ushr_int_2addr(); |
| case OP_ADD_LONG_2ADDR: |
| return op_add_long_2addr(); |
| case OP_SUB_LONG_2ADDR: |
| return op_sub_long_2addr(); |
| case OP_MUL_LONG_2ADDR: |
| return op_mul_long_2addr(); |
| case OP_DIV_LONG_2ADDR: |
| return op_div_long_2addr(); |
| case OP_REM_LONG_2ADDR: |
| return op_rem_long_2addr(); |
| case OP_AND_LONG_2ADDR: |
| return op_and_long_2addr(); |
| case OP_OR_LONG_2ADDR: |
| return op_or_long_2addr(); |
| case OP_XOR_LONG_2ADDR: |
| return op_xor_long_2addr(); |
| case OP_SHL_LONG_2ADDR: |
| return op_shl_long_2addr(); |
| case OP_SHR_LONG_2ADDR: |
| return op_shr_long_2addr(); |
| case OP_USHR_LONG_2ADDR: |
| return op_ushr_long_2addr(); |
| case OP_ADD_FLOAT_2ADDR: |
| return op_add_float_2addr(); |
| case OP_SUB_FLOAT_2ADDR: |
| return op_sub_float_2addr(); |
| case OP_MUL_FLOAT_2ADDR: |
| return op_mul_float_2addr(); |
| case OP_DIV_FLOAT_2ADDR: |
| return op_div_float_2addr(); |
| case OP_REM_FLOAT_2ADDR: |
| return op_rem_float_2addr(); |
| case OP_ADD_DOUBLE_2ADDR: |
| return op_add_double_2addr(); |
| case OP_SUB_DOUBLE_2ADDR: |
| return op_sub_double_2addr(); |
| case OP_MUL_DOUBLE_2ADDR: |
| return op_mul_double_2addr(); |
| case OP_DIV_DOUBLE_2ADDR: |
| return op_div_double_2addr(); |
| case OP_REM_DOUBLE_2ADDR: |
| return op_rem_double_2addr(); |
| case OP_ADD_INT_LIT16: |
| return op_add_int_lit16(); |
| case OP_RSUB_INT: |
| return op_rsub_int(); |
| case OP_MUL_INT_LIT16: |
| return op_mul_int_lit16(); |
| case OP_DIV_INT_LIT16: |
| return op_div_int_lit16(); |
| case OP_REM_INT_LIT16: |
| return op_rem_int_lit16(); |
| case OP_AND_INT_LIT16: |
| return op_and_int_lit16(); |
| case OP_OR_INT_LIT16: |
| return op_or_int_lit16(); |
| case OP_XOR_INT_LIT16: |
| return op_xor_int_lit16(); |
| case OP_ADD_INT_LIT8: |
| return op_add_int_lit8(); |
| case OP_RSUB_INT_LIT8: |
| return op_rsub_int_lit8(); |
| case OP_MUL_INT_LIT8: |
| return op_mul_int_lit8(); |
| case OP_DIV_INT_LIT8: |
| return op_div_int_lit8(); |
| case OP_REM_INT_LIT8: |
| return op_rem_int_lit8(); |
| case OP_AND_INT_LIT8: |
| return op_and_int_lit8(); |
| case OP_OR_INT_LIT8: |
| return op_or_int_lit8(); |
| case OP_XOR_INT_LIT8: |
| return op_xor_int_lit8(); |
| case OP_SHL_INT_LIT8: |
| return op_shl_int_lit8(); |
| case OP_SHR_INT_LIT8: |
| return op_shr_int_lit8(); |
| case OP_USHR_INT_LIT8: |
| return op_ushr_int_lit8(); |
| case OP_EXECUTE_INLINE: |
| return op_execute_inline(false); |
| case OP_EXECUTE_INLINE_RANGE: |
| return op_execute_inline(true); |
| case OP_BREAKPOINT: |
| ALOGE("found bytecode OP_BREAKPOINT"); |
| dvmAbort(); |
| case OP_INVOKE_OBJECT_INIT_RANGE: |
| return op_invoke_object_init_range(); |
| case OP_IGET_QUICK: |
| return op_iget_quick(); |
| case OP_IGET_WIDE_QUICK: |
| return op_iget_wide_quick(); |
| case OP_IGET_OBJECT_QUICK: |
| return op_iget_object_quick(); |
| case OP_IPUT_QUICK: |
| return op_iput_quick(); |
| case OP_IPUT_WIDE_QUICK: |
| return op_iput_wide_quick(); |
| case OP_IPUT_OBJECT_QUICK: |
| return op_iput_object_quick(); |
| case OP_INVOKE_VIRTUAL_QUICK: |
| return op_invoke_virtual_quick(); |
| case OP_INVOKE_VIRTUAL_QUICK_RANGE: |
| return op_invoke_virtual_quick_range(); |
| case OP_INVOKE_SUPER_QUICK: |
| return op_invoke_super_quick(); |
| case OP_INVOKE_SUPER_QUICK_RANGE: |
| return op_invoke_super_quick_range(); |
| } |
| |
| ALOGE("No JIT support for bytecode %x at offsetPC %x", |
| INST_INST(inst), offsetPC); |
| return -1; |
| } |
| int op_nop() { |
| rPC++; |
| return 0; |
| } |