/* * Copyright (C) 2010 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. */ #include "Dalvik.h" #include "Dataflow.h" #include "libdex/DexOpcodes.h" /* Convert the reg id from the callee to the original id passed by the caller */ static inline u4 convertRegId(const DecodedInstruction *invoke, const Method *calleeMethod, int calleeRegId, bool isRange) { /* The order in the original arg passing list */ int rank = calleeRegId - (calleeMethod->registersSize - calleeMethod->insSize); assert(rank >= 0); if (!isRange) { return invoke->arg[rank]; } else { return invoke->vC + rank; } } static bool inlineGetter(CompilationUnit *cUnit, const Method *calleeMethod, MIR *invokeMIR, BasicBlock *invokeBB, bool isPredicted, bool isRange) { BasicBlock *moveResultBB = invokeBB->fallThrough; MIR *moveResultMIR = moveResultBB->firstMIRInsn; MIR *newGetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true); DecodedInstruction getterInsn; /* * Not all getter instructions have vC but vC will be read by * dvmCompilerGetDalvikDisassembly unconditionally. * Initialize it here to get Valgrind happy. */ getterInsn.vC = 0; dexDecodeInstruction(calleeMethod->insns, &getterInsn); if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn)) return false; /* * Some getters (especially invoked through interface) are not followed * by a move result. */ if ((moveResultMIR == NULL) || (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT && moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT && moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) { return false; } int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode]; /* Expecting vA to be the destination register */ if (dfFlags & (DF_UA | DF_UA_WIDE)) { ALOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode); dvmAbort(); } if (dfFlags & DF_UB) { getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, getterInsn.vB, isRange); } if (dfFlags & DF_UC) { getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, getterInsn.vC, isRange); } getterInsn.vA = moveResultMIR->dalvikInsn.vA; /* Now setup the Dalvik instruction with converted src/dst registers */ newGetterMIR->dalvikInsn = getterInsn; newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode); newGetterMIR->OptimizationFlags |= MIR_CALLEE; /* * If the getter instruction is about to raise any exception, punt to the * interpreter and re-execute the invoke. */ newGetterMIR->offset = invokeMIR->offset; newGetterMIR->meta.calleeMethod = calleeMethod; dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR); if (isPredicted) { MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); *invokeMIRSlow = *invokeMIR; invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; /* Use vC to denote the first argument (ie this) */ if (!isRange) { invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; } moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED; dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow); invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; #if defined(WITH_JIT_TUNING) gDvmJit.invokePolyGetterInlined++; #endif } else { invokeMIR->OptimizationFlags |= MIR_INLINED; moveResultMIR->OptimizationFlags |= MIR_INLINED; #if defined(WITH_JIT_TUNING) gDvmJit.invokeMonoGetterInlined++; #endif } return true; } static bool inlineSetter(CompilationUnit *cUnit, const Method *calleeMethod, MIR *invokeMIR, BasicBlock *invokeBB, bool isPredicted, bool isRange) { MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true); DecodedInstruction setterInsn; /* * Not all setter instructions have vC but vC will be read by * dvmCompilerGetDalvikDisassembly unconditionally. * Initialize it here to get Valgrind happy. */ setterInsn.vC = 0; dexDecodeInstruction(calleeMethod->insns, &setterInsn); if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn)) return false; int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode]; if (dfFlags & (DF_UA | DF_UA_WIDE)) { setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, setterInsn.vA, isRange); } if (dfFlags & DF_UB) { setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, setterInsn.vB, isRange); } if (dfFlags & DF_UC) { setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, setterInsn.vC, isRange); } /* Now setup the Dalvik instruction with converted src/dst registers */ newSetterMIR->dalvikInsn = setterInsn; newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode); newSetterMIR->OptimizationFlags |= MIR_CALLEE; /* * If the setter instruction is about to raise any exception, punt to the * interpreter and re-execute the invoke. */ newSetterMIR->offset = invokeMIR->offset; newSetterMIR->meta.calleeMethod = calleeMethod; dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR); if (isPredicted) { MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); *invokeMIRSlow = *invokeMIR; invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; /* Use vC to denote the first argument (ie this) */ if (!isRange) { invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; } dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow); invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; #if defined(WITH_JIT_TUNING) gDvmJit.invokePolySetterInlined++; #endif } else { /* * The invoke becomes no-op so it needs an explicit branch to jump to * the chaining cell. */ invokeBB->needFallThroughBranch = true; invokeMIR->OptimizationFlags |= MIR_INLINED; #if defined(WITH_JIT_TUNING) gDvmJit.invokeMonoSetterInlined++; #endif } return true; } static bool tryInlineSingletonCallsite(CompilationUnit *cUnit, const Method *calleeMethod, MIR *invokeMIR, BasicBlock *invokeBB, bool isRange) { /* Not a Java method */ if (dvmIsNativeMethod(calleeMethod)) return false; CompilerMethodStats *methodStats = dvmCompilerAnalyzeMethodBody(calleeMethod, true); /* Empty callee - do nothing */ if (methodStats->attributes & METHOD_IS_EMPTY) { /* The original invoke instruction is effectively turned into NOP */ invokeMIR->OptimizationFlags |= MIR_INLINED; /* * Need to insert an explicit branch to catch the falling knife (into * the PC reconstruction or chaining cell). */ invokeBB->needFallThroughBranch = true; return true; } if (methodStats->attributes & METHOD_IS_GETTER) { return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange); } else if (methodStats->attributes & METHOD_IS_SETTER) { return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange); } return false; } static bool inlineEmptyVirtualCallee(CompilationUnit *cUnit, const Method *calleeMethod, MIR *invokeMIR, BasicBlock *invokeBB) { MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); *invokeMIRSlow = *invokeMIR; invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow); invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; return true; } static bool tryInlineVirtualCallsite(CompilationUnit *cUnit, const Method *calleeMethod, MIR *invokeMIR, BasicBlock *invokeBB, bool isRange) { /* Not a Java method */ if (dvmIsNativeMethod(calleeMethod)) return false; CompilerMethodStats *methodStats = dvmCompilerAnalyzeMethodBody(calleeMethod, true); /* Empty callee - do nothing by checking the clazz pointer */ if (methodStats->attributes & METHOD_IS_EMPTY) { return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, invokeBB); } if (methodStats->attributes & METHOD_IS_GETTER) { return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange); } else if (methodStats->attributes & METHOD_IS_SETTER) { return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange); } return false; } 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; } } }