static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir) { int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode); int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn | kInstrCanThrow; //If already optimized out, just ignore if (mir->dalvikInsn.opcode == OP_NOP) return; //Ugly, but necessary. Flush all Dalvik regs so Interp can find them dvmCompilerFlushAllRegs(cUnit); if ((mir->next == NULL) || (flags & flagsToCheck)) { genPuntToInterp(cUnit, mir->offset); return; } int entryAddr = offsetof(Thread, jitToInterpEntries.dvmJitToInterpSingleStep); loadWordDisp(cUnit, rEBP, 0, rECX); // Get glue loadWordDisp(cUnit, rECX, entryAddr, rEAX); // rEAX<- entry address /* rPC = dalvik pc */ loadConstant(cUnit, rPC, (int) (cUnit->method->insns + mir->offset)); /* rECX = dalvik pc of following instruction */ loadConstant(cUnit, rECX, (int) (cUnit->method->insns + mir->next->offset)); /* Pass on the stack */ storeWordDisp(cUnit, rESP, OUT_ARG0, rECX); opReg(cUnit, kOpCall, rEAX); }
/* * public static native boolean isInvoke(int opcode); */ static void Dalvik_dalvik_bytecode_OpcodeInfo_isInvoke(const u4 *args, JValue *pResult) { Opcode opcode = static_cast<Opcode>(args[0]); int flags = dexGetFlagsFromOpcode(opcode); bool result = (flags & kInstrInvoke) != 0; RETURN_BOOLEAN(result); }
/* * A loop is considered optimizable if: * 1) It has one basic induction variable. * 2) The loop back branch compares the BIV with a constant. * 3) We need to normalize the loop exit condition so that the loop is exited * via the taken path. * 4) If it is a count-up loop, the condition is GE/GT. Otherwise it is * LE/LT/LEZ/LTZ for a count-down loop. * * Return false for loops that fail the above tests. */ static bool isSimpleCountedLoop(CompilationUnit *cUnit) { unsigned int i; BasicBlock *loopBackBlock = cUnit->entryBlock->fallThrough; LoopAnalysis *loopAnalysis = cUnit->loopAnalysis; if (loopAnalysis->numBasicIV != 1) return false; for (i = 0; i < loopAnalysis->ivList->numUsed; i++) { InductionVariableInfo *ivInfo; ivInfo = GET_ELEM_N(loopAnalysis->ivList, InductionVariableInfo*, i); /* Count up or down loop? */ if (ivInfo->ssaReg == ivInfo->basicSSAReg) { /* Infinite loop */ if (ivInfo->inc == 0) { return false; } loopAnalysis->isCountUpLoop = ivInfo->inc > 0; break; } } /* Find the block that ends with a branch to exit the loop */ while (true) { loopBackBlock = findPredecessorBlock(cUnit, loopBackBlock); /* Loop structure not recognized as counted blocks */ if (loopBackBlock == NULL) { return false; } /* Unconditional goto - continue to trace up the predecessor chain */ if (loopBackBlock->taken == NULL) { continue; } break; } MIR *branch = loopBackBlock->lastMIRInsn; Opcode opcode = branch->dalvikInsn.opcode; /* Last instruction is not a conditional branch - bail */ if (dexGetFlagsFromOpcode(opcode) != (kInstrCanContinue|kInstrCanBranch)) { return false; } int endSSAReg; int endDalvikReg; /* reg/reg comparison */ if (branch->ssaRep->numUses == 2) { if (branch->ssaRep->uses[0] == loopAnalysis->ssaBIV) { endSSAReg = branch->ssaRep->uses[1]; } else if (branch->ssaRep->uses[1] == loopAnalysis->ssaBIV) { endSSAReg = branch->ssaRep->uses[0]; opcode = negateOpcode(opcode); } else { return false; } endDalvikReg = dvmConvertSSARegToDalvik(cUnit, endSSAReg); /* * If the comparison is not between the BIV and a loop invariant, * return false. endDalvikReg is loop invariant if one of the * following is true: * - It is not defined in the loop (ie DECODE_SUB returns 0) * - It is reloaded with a constant */ if ((DECODE_SUB(endDalvikReg) != 0) && !dvmIsBitSet(cUnit->isConstantV, endSSAReg)) { return false; } /* Compare against zero */ } else if (branch->ssaRep->numUses == 1) { if (branch->ssaRep->uses[0] == loopAnalysis->ssaBIV) { /* Keep the compiler happy */ endDalvikReg = -1; } else { return false; } } else { return false; } /* Normalize the loop exit check as "if (iv op end) exit;" */ if (loopBackBlock->taken->blockType == kDalvikByteCode) { opcode = negateOpcode(opcode); } if (loopAnalysis->isCountUpLoop) { /* * If the normalized condition op is not > or >=, this is not an * optimization candidate. */ switch (opcode) { case OP_IF_GT: case OP_IF_GE: break; default: return false; } loopAnalysis->endConditionReg = DECODE_REG(endDalvikReg); } else { /* * If the normalized condition op is not < or <=, this is not an * optimization candidate. */ switch (opcode) { case OP_IF_LT: case OP_IF_LE: loopAnalysis->endConditionReg = DECODE_REG(endDalvikReg); break; case OP_IF_LTZ: case OP_IF_LEZ: break; default: return false; } } /* * Remember the normalized opcode, which will be used to determine the end * value used for the yanked range checks. */ loopAnalysis->loopBranchOpcode = opcode; return true; }
/* Perform static verification on instructions. As a side effect, this sets the "branch target" flags in InsnFlags. "(CF)" items are handled during code-flow analysis. v3 4.10.1 - target of each jump and branch instruction must be valid - targets of switch statements must be valid - operands referencing constant pool entries must be valid - (CF) operands of getfield, putfield, getstatic, putstatic must be valid - (new) verify operands of "quick" field ops - (CF) operands of method invocation instructions must be valid - (new) verify operands of "quick" method invoke ops - (CF) only invoke-direct can call a method starting with '<' - (CF) <clinit> must never be called explicitly - operands of instanceof, checkcast, new (and variants) must be valid - new-array[-type] limited to 255 dimensions - can't use "new" on an array class - (?) limit dimensions in multi-array creation - local variable load/store register values must be in valid range v3 4.11.1.2 - branches must be within the bounds of the code array - targets of all control-flow instructions are the start of an instruction - register accesses fall within range of allocated registers - (N/A) access to constant pool must be of appropriate type - code does not end in the middle of an instruction - execution cannot fall off the end of the code - (earlier) for each exception handler, the "try" area must begin and end at the start of an instruction (end can be at the end of the code) - (earlier) for each exception handler, the handler must start at a valid instruction 执行指令的静态检查。 */ static bool verifyInstructions(VerifierData* vdata) { const Method* meth = vdata->method; const DvmDex* pDvmDex = meth->clazz->pDvmDex; InsnFlags* insnFlags = vdata->insnFlags; const u2* insns = meth->insns; unsigned int codeOffset; /* the start of the method is a "branch target" */ /* 方法起始是一个分支目标 */ dvmInsnSetBranchTarget(insnFlags, 0, true); for (codeOffset = 0; codeOffset < vdata->insnsSize; /**/) { /* Pull the instruction apart. 将指令分开。 */ int width = dvmInsnGetWidth(insnFlags, codeOffset); DecodedInstruction decInsn; bool okay = true; dexDecodeInstruction(meth->insns + codeOffset, &decInsn); /* Check register, type, class, field, method, and string indices for out-of-range values. Do additional checks on branch targets and some special cases like new-instance and new-array. 检查寄存器,类型,类,域,方法,字符串索引下标越界。 在分支目标上做额外检查和一些特殊的情况,如:new-instance 和 new-array。 */ switch (decInsn.opcode) { case OP_NOP: case OP_RETURN_VOID: /* nothing to check */ /* 不做任何检查 */ break; case OP_MOVE_RESULT: case OP_MOVE_RESULT_OBJECT: case OP_MOVE_EXCEPTION: case OP_RETURN: case OP_RETURN_OBJECT: case OP_CONST_4: case OP_CONST_16: case OP_CONST: case OP_CONST_HIGH16: case OP_MONITOR_ENTER: case OP_MONITOR_EXIT: case OP_THROW: okay &= checkRegisterIndex(meth, decInsn.vA); break; case OP_MOVE_RESULT_WIDE: case OP_RETURN_WIDE: case OP_CONST_WIDE_16: case OP_CONST_WIDE_32: case OP_CONST_WIDE: case OP_CONST_WIDE_HIGH16: okay &= checkWideRegisterIndex(meth, decInsn.vA); break; case OP_GOTO: case OP_GOTO_16: okay &= checkBranchTarget(meth, insnFlags, codeOffset, false); break; case OP_GOTO_32: okay &= checkBranchTarget(meth, insnFlags, codeOffset, true); break; case OP_MOVE: case OP_MOVE_FROM16: case OP_MOVE_16: case OP_MOVE_OBJECT: case OP_MOVE_OBJECT_FROM16: case OP_MOVE_OBJECT_16: case OP_ARRAY_LENGTH: case OP_NEG_INT: case OP_NOT_INT: case OP_NEG_FLOAT: case OP_INT_TO_FLOAT: case OP_FLOAT_TO_INT: case OP_INT_TO_BYTE: case OP_INT_TO_CHAR: case OP_INT_TO_SHORT: case OP_ADD_INT_2ADDR: case OP_SUB_INT_2ADDR: case OP_MUL_INT_2ADDR: case OP_DIV_INT_2ADDR: case OP_REM_INT_2ADDR: case OP_AND_INT_2ADDR: case OP_OR_INT_2ADDR: case OP_XOR_INT_2ADDR: case OP_SHL_INT_2ADDR: case OP_SHR_INT_2ADDR: case OP_USHR_INT_2ADDR: case OP_ADD_FLOAT_2ADDR: case OP_SUB_FLOAT_2ADDR: case OP_MUL_FLOAT_2ADDR: case OP_DIV_FLOAT_2ADDR: case OP_REM_FLOAT_2ADDR: case OP_ADD_INT_LIT16: case OP_RSUB_INT: case OP_MUL_INT_LIT16: case OP_DIV_INT_LIT16: case OP_REM_INT_LIT16: case OP_AND_INT_LIT16: case OP_OR_INT_LIT16: case OP_XOR_INT_LIT16: case OP_ADD_INT_LIT8: case OP_RSUB_INT_LIT8: case OP_MUL_INT_LIT8: case OP_DIV_INT_LIT8: case OP_REM_INT_LIT8: case OP_AND_INT_LIT8: case OP_OR_INT_LIT8: case OP_XOR_INT_LIT8: case OP_SHL_INT_LIT8: case OP_SHR_INT_LIT8: case OP_USHR_INT_LIT8: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); break; case OP_INT_TO_LONG: case OP_INT_TO_DOUBLE: case OP_FLOAT_TO_LONG: case OP_FLOAT_TO_DOUBLE: case OP_SHL_LONG_2ADDR: case OP_SHR_LONG_2ADDR: case OP_USHR_LONG_2ADDR: okay &= checkWideRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); break; case OP_LONG_TO_INT: case OP_LONG_TO_FLOAT: case OP_DOUBLE_TO_INT: case OP_DOUBLE_TO_FLOAT: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkWideRegisterIndex(meth, decInsn.vB); break; case OP_MOVE_WIDE: case OP_MOVE_WIDE_FROM16: case OP_MOVE_WIDE_16: case OP_DOUBLE_TO_LONG: case OP_LONG_TO_DOUBLE: case OP_NEG_DOUBLE: case OP_NEG_LONG: case OP_NOT_LONG: case OP_ADD_LONG_2ADDR: case OP_SUB_LONG_2ADDR: case OP_MUL_LONG_2ADDR: case OP_DIV_LONG_2ADDR: case OP_REM_LONG_2ADDR: case OP_AND_LONG_2ADDR: case OP_OR_LONG_2ADDR: case OP_XOR_LONG_2ADDR: case OP_ADD_DOUBLE_2ADDR: case OP_SUB_DOUBLE_2ADDR: case OP_MUL_DOUBLE_2ADDR: case OP_DIV_DOUBLE_2ADDR: case OP_REM_DOUBLE_2ADDR: okay &= checkWideRegisterIndex(meth, decInsn.vA); okay &= checkWideRegisterIndex(meth, decInsn.vB); break; case OP_CONST_STRING: case OP_CONST_STRING_JUMBO: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkStringIndex(pDvmDex, decInsn.vB); break; case OP_CONST_CLASS: case OP_CHECK_CAST: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkTypeIndex(pDvmDex, decInsn.vB); break; case OP_INSTANCE_OF: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); okay &= checkTypeIndex(pDvmDex, decInsn.vC); break; case OP_NEW_INSTANCE: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkNewInstance(pDvmDex, decInsn.vB); break; case OP_NEW_ARRAY: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); okay &= checkNewArray(pDvmDex, decInsn.vC); break; case OP_FILL_ARRAY_DATA: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkArrayData(meth, codeOffset); break; case OP_PACKED_SWITCH: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkSwitchTargets(meth, insnFlags, codeOffset); break; case OP_SPARSE_SWITCH: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkSwitchTargets(meth, insnFlags, codeOffset); break; case OP_CMPL_FLOAT: case OP_CMPG_FLOAT: case OP_AGET: case OP_AGET_OBJECT: case OP_AGET_BOOLEAN: case OP_AGET_BYTE: case OP_AGET_CHAR: case OP_AGET_SHORT: case OP_APUT: case OP_APUT_OBJECT: case OP_APUT_BOOLEAN: case OP_APUT_BYTE: case OP_APUT_CHAR: case OP_APUT_SHORT: case OP_ADD_INT: case OP_SUB_INT: case OP_MUL_INT: case OP_DIV_INT: case OP_REM_INT: case OP_AND_INT: case OP_OR_INT: case OP_XOR_INT: case OP_SHL_INT: case OP_SHR_INT: case OP_USHR_INT: case OP_ADD_FLOAT: case OP_SUB_FLOAT: case OP_MUL_FLOAT: case OP_DIV_FLOAT: case OP_REM_FLOAT: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); okay &= checkRegisterIndex(meth, decInsn.vC); break; case OP_AGET_WIDE: case OP_APUT_WIDE: okay &= checkWideRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); okay &= checkRegisterIndex(meth, decInsn.vC); break; case OP_CMPL_DOUBLE: case OP_CMPG_DOUBLE: case OP_CMP_LONG: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkWideRegisterIndex(meth, decInsn.vB); okay &= checkWideRegisterIndex(meth, decInsn.vC); break; case OP_ADD_DOUBLE: case OP_SUB_DOUBLE: case OP_MUL_DOUBLE: case OP_DIV_DOUBLE: case OP_REM_DOUBLE: case OP_ADD_LONG: case OP_SUB_LONG: case OP_MUL_LONG: case OP_DIV_LONG: case OP_REM_LONG: case OP_AND_LONG: case OP_OR_LONG: case OP_XOR_LONG: okay &= checkWideRegisterIndex(meth, decInsn.vA); okay &= checkWideRegisterIndex(meth, decInsn.vB); okay &= checkWideRegisterIndex(meth, decInsn.vC); break; case OP_SHL_LONG: case OP_SHR_LONG: case OP_USHR_LONG: okay &= checkWideRegisterIndex(meth, decInsn.vA); okay &= checkWideRegisterIndex(meth, decInsn.vB); okay &= checkRegisterIndex(meth, decInsn.vC); break; case OP_IF_EQ: case OP_IF_NE: case OP_IF_LT: case OP_IF_GE: case OP_IF_GT: case OP_IF_LE: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); okay &= checkBranchTarget(meth, insnFlags, codeOffset, false); break; case OP_IF_EQZ: case OP_IF_NEZ: case OP_IF_LTZ: case OP_IF_GEZ: case OP_IF_GTZ: case OP_IF_LEZ: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkBranchTarget(meth, insnFlags, codeOffset, false); break; case OP_IGET: case OP_IGET_OBJECT: case OP_IGET_BOOLEAN: case OP_IGET_BYTE: case OP_IGET_CHAR: case OP_IGET_SHORT: case OP_IPUT: case OP_IPUT_OBJECT: case OP_IPUT_BOOLEAN: case OP_IPUT_BYTE: case OP_IPUT_CHAR: case OP_IPUT_SHORT: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); okay &= checkFieldIndex(pDvmDex, decInsn.vC); break; case OP_IGET_WIDE: case OP_IPUT_WIDE: okay &= checkWideRegisterIndex(meth, decInsn.vA); okay &= checkRegisterIndex(meth, decInsn.vB); okay &= checkFieldIndex(pDvmDex, decInsn.vC); break; case OP_SGET: case OP_SGET_OBJECT: case OP_SGET_BOOLEAN: case OP_SGET_BYTE: case OP_SGET_CHAR: case OP_SGET_SHORT: case OP_SPUT: case OP_SPUT_OBJECT: case OP_SPUT_BOOLEAN: case OP_SPUT_BYTE: case OP_SPUT_CHAR: case OP_SPUT_SHORT: okay &= checkRegisterIndex(meth, decInsn.vA); okay &= checkFieldIndex(pDvmDex, decInsn.vB); break; case OP_SGET_WIDE: case OP_SPUT_WIDE: okay &= checkWideRegisterIndex(meth, decInsn.vA); okay &= checkFieldIndex(pDvmDex, decInsn.vB); break; case OP_FILLED_NEW_ARRAY: /* decoder uses B, not C, for type ref */ okay &= checkTypeIndex(pDvmDex, decInsn.vB); okay &= checkVarargRegs(meth, &decInsn); break; case OP_FILLED_NEW_ARRAY_RANGE: okay &= checkTypeIndex(pDvmDex, decInsn.vB); okay &= checkVarargRangeRegs(meth, &decInsn); break; case OP_INVOKE_VIRTUAL: case OP_INVOKE_SUPER: case OP_INVOKE_DIRECT: case OP_INVOKE_STATIC: case OP_INVOKE_INTERFACE: /* decoder uses B, not C, for type ref */ okay &= checkMethodIndex(pDvmDex, decInsn.vB); okay &= checkVarargRegs(meth, &decInsn); break; case OP_INVOKE_VIRTUAL_RANGE: case OP_INVOKE_SUPER_RANGE: case OP_INVOKE_DIRECT_RANGE: case OP_INVOKE_STATIC_RANGE: case OP_INVOKE_INTERFACE_RANGE: okay &= checkMethodIndex(pDvmDex, decInsn.vB); okay &= checkVarargRangeRegs(meth, &decInsn); break; /* verifier/optimizer output; we should never see these */ case OP_IGET_VOLATILE: case OP_IPUT_VOLATILE: case OP_SGET_VOLATILE: case OP_SPUT_VOLATILE: case OP_IGET_OBJECT_VOLATILE: case OP_IPUT_OBJECT_VOLATILE: case OP_SGET_OBJECT_VOLATILE: case OP_SPUT_OBJECT_VOLATILE: case OP_IGET_WIDE_VOLATILE: case OP_IPUT_WIDE_VOLATILE: case OP_SGET_WIDE_VOLATILE: case OP_SPUT_WIDE_VOLATILE: case OP_BREAKPOINT: case OP_THROW_VERIFICATION_ERROR: case OP_EXECUTE_INLINE: case OP_EXECUTE_INLINE_RANGE: case OP_INVOKE_OBJECT_INIT_RANGE: case OP_RETURN_VOID_BARRIER: case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: case OP_IPUT_QUICK: case OP_IPUT_WIDE_QUICK: case OP_IPUT_OBJECT_QUICK: case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: case OP_UNUSED_3E: case OP_UNUSED_3F: case OP_UNUSED_40: case OP_UNUSED_41: case OP_UNUSED_42: case OP_UNUSED_43: case OP_UNUSED_73: case OP_UNUSED_79: case OP_UNUSED_7A: case OP_UNUSED_FF: ALOGE("VFY: unexpected opcode %04x", decInsn.opcode); okay = false; break; /* * DO NOT add a "default" clause here. Without it the compiler will * complain if an instruction is missing (which is desirable). */ } if (!okay) { LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x", decInsn.opcode, codeOffset); return false; } OpcodeFlags opFlags = dexGetFlagsFromOpcode(decInsn.opcode); if ((opFlags & VERIFY_GC_INST_MASK) != 0) { /* * This instruction is a GC point. If space is a concern, * the set of GC points could be reduced by eliminating * foward branches. * * TODO: we could also scan the targets of a "switch" statement, * and if none of them branch backward we could ignore that * instruction as well. */ dvmInsnSetGcPoint(insnFlags, codeOffset, true); } assert(width > 0); codeOffset += width; insns += width; } /* make sure the last instruction ends at the end of the insn area */ if (codeOffset != vdata->insnsSize) { LOG_VFY_METH(meth, "VFY: code did not end when expected (end at %d, count %d)", codeOffset, vdata->insnsSize); return false; } return true; }
void dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info) { bool isRange = false; GrowableListIterator iterator; dvmGrowableListIteratorInit(&cUnit->blockList, &iterator); /* * Analyze the basic block containing an invoke to see if it can be inlined */ while (true) { BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator); if (bb == NULL) break; if (bb->blockType != kDalvikByteCode) continue; MIR *lastMIRInsn = bb->lastMIRInsn; Opcode opcode = lastMIRInsn->dalvikInsn.opcode; int flags = (int)dexGetFlagsFromOpcode(opcode); /* No invoke - continue */ if ((flags & kInstrInvoke) == 0) continue; /* Disable inlining when doing method tracing */ if (gDvmJit.methodTraceSupport) continue; /* * If the invoke itself is selected for single stepping, don't bother * to inline it. */ if (SINGLE_STEP_OP(opcode)) continue; const Method *calleeMethod; switch (opcode) { case OP_INVOKE_SUPER: case OP_INVOKE_DIRECT: case OP_INVOKE_STATIC: case OP_INVOKE_SUPER_QUICK: calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; case OP_INVOKE_SUPER_RANGE: case OP_INVOKE_DIRECT_RANGE: case OP_INVOKE_STATIC_RANGE: case OP_INVOKE_SUPER_QUICK_RANGE: isRange = true; calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; default: calleeMethod = NULL; break; } if (calleeMethod) { bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb, isRange); if (!inlined && !(gDvmJit.disableOpt & (1 << kMethodJit)) && !dvmIsNativeMethod(calleeMethod)) { CompilerMethodStats *methodStats = dvmCompilerAnalyzeMethodBody(calleeMethod, true); if ((methodStats->attributes & METHOD_IS_LEAF) && !(methodStats->attributes & METHOD_CANNOT_COMPILE)) { /* Callee has been previously compiled */ if (dvmJitGetMethodAddr(calleeMethod->insns)) { lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; } else { /* Compile the callee first */ dvmCompileMethod(calleeMethod, info); if (dvmJitGetMethodAddr(calleeMethod->insns)) { lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; } else { methodStats->attributes |= METHOD_CANNOT_COMPILE; } } } } return; } switch (opcode) { case OP_INVOKE_VIRTUAL: case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_INTERFACE: isRange = false; calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; case OP_INVOKE_VIRTUAL_RANGE: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_INTERFACE_RANGE: isRange = true; calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; default: break; } if (calleeMethod) { bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb, isRange); if (!inlined && !(gDvmJit.disableOpt & (1 << kMethodJit)) && !dvmIsNativeMethod(calleeMethod)) { CompilerMethodStats *methodStats = dvmCompilerAnalyzeMethodBody(calleeMethod, true); if ((methodStats->attributes & METHOD_IS_LEAF) && !(methodStats->attributes & METHOD_CANNOT_COMPILE)) { /* Callee has been previously compiled */ if (dvmJitGetMethodAddr(calleeMethod->insns)) { lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; } else { /* Compile the callee first */ dvmCompileMethod(calleeMethod, info); if (dvmJitGetMethodAddr(calleeMethod->insns)) { lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; } else { methodStats->attributes |= METHOD_CANNOT_COMPILE; } } } } return; } } }