1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "Dalvik.h" 18 #include "Dataflow.h" 19 #include "libdex/OpCodeNames.h" 20 21 /* Convert the reg id from the callee to the original id passed by the caller */ 22 static inline u4 convertRegId(const DecodedInstruction *invoke, 23 const Method *calleeMethod, 24 int calleeRegId, bool isRange) 25 { 26 /* The order in the original arg passing list */ 27 int rank = calleeRegId - 28 (calleeMethod->registersSize - calleeMethod->insSize); 29 assert(rank >= 0); 30 if (!isRange) { 31 return invoke->arg[rank]; 32 } else { 33 return invoke->vC + rank; 34 } 35 } 36 37 static void inlineGetter(CompilationUnit *cUnit, 38 const Method *calleeMethod, 39 MIR *invokeMIR, 40 BasicBlock *invokeBB, 41 bool isPredicted, 42 bool isRange) 43 { 44 BasicBlock *moveResultBB = invokeBB->fallThrough; 45 MIR *moveResultMIR = moveResultBB->firstMIRInsn; 46 MIR *newGetterMIR = dvmCompilerNew(sizeof(MIR), true); 47 DecodedInstruction getterInsn; 48 49 dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &getterInsn); 50 51 if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn)) 52 return; 53 54 /* 55 * Some getters (especially invoked through interface) are not followed 56 * by a move result. 57 */ 58 if ((moveResultMIR == NULL) || 59 (moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT && 60 moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT_OBJECT && 61 moveResultMIR->dalvikInsn.opCode != OP_MOVE_RESULT_WIDE)) { 62 return; 63 } 64 65 int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opCode]; 66 67 /* Expecting vA to be the destination register */ 68 if (dfFlags & (DF_UA | DF_UA_WIDE)) { 69 LOGE("opcode %d has DF_UA set (not expected)", getterInsn.opCode); 70 dvmAbort(); 71 } 72 73 if (dfFlags & DF_UB) { 74 getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 75 getterInsn.vB, isRange); 76 } 77 78 if (dfFlags & DF_UC) { 79 getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 80 getterInsn.vC, isRange); 81 } 82 83 getterInsn.vA = moveResultMIR->dalvikInsn.vA; 84 85 /* Now setup the Dalvik instruction with converted src/dst registers */ 86 newGetterMIR->dalvikInsn = getterInsn; 87 88 newGetterMIR->width = gDvm.instrWidth[getterInsn.opCode]; 89 90 newGetterMIR->OptimizationFlags |= MIR_CALLEE; 91 92 /* 93 * If the getter instruction is about to raise any exception, punt to the 94 * interpreter and re-execute the invoke. 95 */ 96 newGetterMIR->offset = invokeMIR->offset; 97 98 newGetterMIR->meta.calleeMethod = calleeMethod; 99 100 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR); 101 102 if (isPredicted) { 103 MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true); 104 *invokeMIRSlow = *invokeMIR; 105 invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction; 106 107 /* Use vC to denote the first argument (ie this) */ 108 if (!isRange) { 109 invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; 110 } 111 112 moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED; 113 114 dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow); 115 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 116 #if defined(WITH_JIT_TUNING) 117 gDvmJit.invokePolyGetterInlined++; 118 #endif 119 } else { 120 invokeMIR->OptimizationFlags |= MIR_INLINED; 121 moveResultMIR->OptimizationFlags |= MIR_INLINED; 122 #if defined(WITH_JIT_TUNING) 123 gDvmJit.invokeMonoGetterInlined++; 124 #endif 125 } 126 127 return; 128 } 129 130 static void inlineSetter(CompilationUnit *cUnit, 131 const Method *calleeMethod, 132 MIR *invokeMIR, 133 BasicBlock *invokeBB, 134 bool isPredicted, 135 bool isRange) 136 { 137 MIR *newSetterMIR = dvmCompilerNew(sizeof(MIR), true); 138 DecodedInstruction setterInsn; 139 140 dexDecodeInstruction(gDvm.instrFormat, calleeMethod->insns, &setterInsn); 141 142 if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn)) 143 return; 144 145 int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opCode]; 146 147 if (dfFlags & (DF_UA | DF_UA_WIDE)) { 148 setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 149 setterInsn.vA, isRange); 150 151 } 152 153 if (dfFlags & DF_UB) { 154 setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 155 setterInsn.vB, isRange); 156 157 } 158 159 if (dfFlags & DF_UC) { 160 setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 161 setterInsn.vC, isRange); 162 } 163 164 /* Now setup the Dalvik instruction with converted src/dst registers */ 165 newSetterMIR->dalvikInsn = setterInsn; 166 167 newSetterMIR->width = gDvm.instrWidth[setterInsn.opCode]; 168 169 newSetterMIR->OptimizationFlags |= MIR_CALLEE; 170 171 /* 172 * If the setter instruction is about to raise any exception, punt to the 173 * interpreter and re-execute the invoke. 174 */ 175 newSetterMIR->offset = invokeMIR->offset; 176 177 newSetterMIR->meta.calleeMethod = calleeMethod; 178 179 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR); 180 181 if (isPredicted) { 182 MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true); 183 *invokeMIRSlow = *invokeMIR; 184 invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction; 185 186 /* Use vC to denote the first argument (ie this) */ 187 if (!isRange) { 188 invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; 189 } 190 191 dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow); 192 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 193 #if defined(WITH_JIT_TUNING) 194 gDvmJit.invokePolySetterInlined++; 195 #endif 196 } else { 197 /* 198 * The invoke becomes no-op so it needs an explicit branch to jump to 199 * the chaining cell. 200 */ 201 invokeBB->needFallThroughBranch = true; 202 invokeMIR->OptimizationFlags |= MIR_INLINED; 203 #if defined(WITH_JIT_TUNING) 204 gDvmJit.invokeMonoSetterInlined++; 205 #endif 206 } 207 208 return; 209 } 210 211 static void tryInlineSingletonCallsite(CompilationUnit *cUnit, 212 const Method *calleeMethod, 213 MIR *invokeMIR, 214 BasicBlock *invokeBB, 215 bool isRange) 216 { 217 /* Not a Java method */ 218 if (dvmIsNativeMethod(calleeMethod)) return; 219 220 CompilerMethodStats *methodStats = 221 dvmCompilerAnalyzeMethodBody(calleeMethod, true); 222 223 /* Empty callee - do nothing */ 224 if (methodStats->attributes & METHOD_IS_EMPTY) { 225 /* The original invoke instruction is effectively turned into NOP */ 226 invokeMIR->OptimizationFlags |= MIR_INLINED; 227 /* 228 * Need to insert an explicit branch to catch the falling knife (into 229 * the PC reconstruction or chaining cell). 230 */ 231 invokeBB->needFallThroughBranch = true; 232 return; 233 } 234 235 if (methodStats->attributes & METHOD_IS_GETTER) { 236 inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange); 237 return; 238 } else if (methodStats->attributes & METHOD_IS_SETTER) { 239 inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, isRange); 240 return; 241 } 242 } 243 244 static void inlineEmptyVirtualCallee(CompilationUnit *cUnit, 245 const Method *calleeMethod, 246 MIR *invokeMIR, 247 BasicBlock *invokeBB) 248 { 249 MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true); 250 *invokeMIRSlow = *invokeMIR; 251 invokeMIR->dalvikInsn.opCode = kMirOpCheckInlinePrediction; 252 253 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow); 254 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 255 } 256 257 static void tryInlineVirtualCallsite(CompilationUnit *cUnit, 258 const Method *calleeMethod, 259 MIR *invokeMIR, 260 BasicBlock *invokeBB, 261 bool isRange) 262 { 263 /* Not a Java method */ 264 if (dvmIsNativeMethod(calleeMethod)) return; 265 266 CompilerMethodStats *methodStats = 267 dvmCompilerAnalyzeMethodBody(calleeMethod, true); 268 269 /* Empty callee - do nothing by checking the clazz pointer */ 270 if (methodStats->attributes & METHOD_IS_EMPTY) { 271 inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, invokeBB); 272 return; 273 } 274 275 if (methodStats->attributes & METHOD_IS_GETTER) { 276 inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange); 277 return; 278 } else if (methodStats->attributes & METHOD_IS_SETTER) { 279 inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, isRange); 280 return; 281 } 282 } 283 284 285 void dvmCompilerInlineMIR(CompilationUnit *cUnit) 286 { 287 int i; 288 bool isRange = false; 289 290 /* 291 * Analyze the basic block containing an invoke to see if it can be inlined 292 */ 293 for (i = 0; i < cUnit->numBlocks; i++) { 294 BasicBlock *bb = cUnit->blockList[i]; 295 if (bb->blockType != kDalvikByteCode) 296 continue; 297 MIR *lastMIRInsn = bb->lastMIRInsn; 298 int opCode = lastMIRInsn->dalvikInsn.opCode; 299 int flags = dexGetInstrFlags(gDvm.instrFlags, opCode); 300 301 /* No invoke - continue */ 302 if ((flags & kInstrInvoke) == 0) 303 continue; 304 305 /* Not a real invoke - continue */ 306 if (opCode == OP_INVOKE_DIRECT_EMPTY) 307 continue; 308 309 /* 310 * If the invoke itself is selected for single stepping, don't bother 311 * to inline it. 312 */ 313 if (SINGLE_STEP_OP(opCode)) 314 continue; 315 316 const Method *calleeMethod; 317 318 switch (opCode) { 319 case OP_INVOKE_SUPER: 320 case OP_INVOKE_DIRECT: 321 case OP_INVOKE_STATIC: 322 case OP_INVOKE_SUPER_QUICK: 323 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 324 break; 325 case OP_INVOKE_SUPER_RANGE: 326 case OP_INVOKE_DIRECT_RANGE: 327 case OP_INVOKE_STATIC_RANGE: 328 case OP_INVOKE_SUPER_QUICK_RANGE: 329 isRange = true; 330 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 331 break; 332 default: 333 calleeMethod = NULL; 334 break; 335 } 336 337 if (calleeMethod) { 338 tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb, 339 isRange); 340 return; 341 } 342 343 switch (opCode) { 344 case OP_INVOKE_VIRTUAL: 345 case OP_INVOKE_VIRTUAL_QUICK: 346 case OP_INVOKE_INTERFACE: 347 isRange = false; 348 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 349 break; 350 case OP_INVOKE_VIRTUAL_RANGE: 351 case OP_INVOKE_VIRTUAL_QUICK_RANGE: 352 case OP_INVOKE_INTERFACE_RANGE: 353 isRange = true; 354 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 355 break; 356 default: 357 break; 358 } 359 360 if (calleeMethod) { 361 tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb, 362 isRange); 363 return; 364 } 365 } 366 } 367