| /* |
| * 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 void 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; |
| |
| dexDecodeInstruction(calleeMethod->insns, &getterInsn); |
| |
| if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn)) |
| return; |
| |
| /* |
| * 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; |
| } |
| |
| int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode]; |
| |
| /* Expecting vA to be the destination register */ |
| if (dfFlags & (DF_UA | DF_UA_WIDE)) { |
| LOGE("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 = 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; |
| } |
| |
| static void inlineSetter(CompilationUnit *cUnit, |
| const Method *calleeMethod, |
| MIR *invokeMIR, |
| BasicBlock *invokeBB, |
| bool isPredicted, |
| bool isRange) |
| { |
| MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true); |
| DecodedInstruction setterInsn; |
| |
| dexDecodeInstruction(calleeMethod->insns, &setterInsn); |
| |
| if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn)) |
| return; |
| |
| 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 = 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; |
| } |
| |
| static void tryInlineSingletonCallsite(CompilationUnit *cUnit, |
| const Method *calleeMethod, |
| MIR *invokeMIR, |
| BasicBlock *invokeBB, |
| bool isRange) |
| { |
| /* Not a Java method */ |
| if (dvmIsNativeMethod(calleeMethod)) return; |
| |
| 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; |
| } |
| |
| if (methodStats->attributes & METHOD_IS_GETTER) { |
| inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange); |
| return; |
| } else if (methodStats->attributes & METHOD_IS_SETTER) { |
| inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange); |
| return; |
| } |
| } |
| |
| static void inlineEmptyVirtualCallee(CompilationUnit *cUnit, |
| const Method *calleeMethod, |
| MIR *invokeMIR, |
| BasicBlock *invokeBB) |
| { |
| MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); |
| *invokeMIRSlow = *invokeMIR; |
| invokeMIR->dalvikInsn.opcode = kMirOpCheckInlinePrediction; |
| |
| dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow); |
| invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; |
| } |
| |
| static void tryInlineVirtualCallsite(CompilationUnit *cUnit, |
| const Method *calleeMethod, |
| MIR *invokeMIR, |
| BasicBlock *invokeBB, |
| bool isRange) |
| { |
| /* Not a Java method */ |
| if (dvmIsNativeMethod(calleeMethod)) return; |
| |
| CompilerMethodStats *methodStats = |
| dvmCompilerAnalyzeMethodBody(calleeMethod, true); |
| |
| /* Empty callee - do nothing by checking the clazz pointer */ |
| if (methodStats->attributes & METHOD_IS_EMPTY) { |
| inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, invokeBB); |
| return; |
| } |
| |
| if (methodStats->attributes & METHOD_IS_GETTER) { |
| inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange); |
| return; |
| } else if (methodStats->attributes & METHOD_IS_SETTER) { |
| inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange); |
| return; |
| } |
| } |
| |
| |
| void dvmCompilerInlineMIR(CompilationUnit *cUnit) |
| { |
| 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; |
| int opcode = lastMIRInsn->dalvikInsn.opcode; |
| int flags = 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) { |
| tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb, |
| isRange); |
| 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) { |
| tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb, |
| isRange); |
| return; |
| } |
| } |
| } |