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/DexOpcodes.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 bool 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 = (MIR *)dvmCompilerNew(sizeof(MIR), true); 47 DecodedInstruction getterInsn; 48 49 /* 50 * Not all getter instructions have vC but vC will be read by 51 * dvmCompilerGetDalvikDisassembly unconditionally. 52 * Initialize it here to get Valgrind happy. 53 */ 54 getterInsn.vC = 0; 55 56 dexDecodeInstruction(calleeMethod->insns, &getterInsn); 57 58 if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &getterInsn)) 59 return false; 60 61 /* 62 * Some getters (especially invoked through interface) are not followed 63 * by a move result. 64 */ 65 if ((moveResultMIR == NULL) || 66 (moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT && 67 moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_OBJECT && 68 moveResultMIR->dalvikInsn.opcode != OP_MOVE_RESULT_WIDE)) { 69 return false; 70 } 71 72 int dfFlags = dvmCompilerDataFlowAttributes[getterInsn.opcode]; 73 74 /* Expecting vA to be the destination register */ 75 if (dfFlags & (DF_UA | DF_UA_WIDE)) { 76 LOGE("opcode %d has DF_UA set (not expected)", getterInsn.opcode); 77 dvmAbort(); 78 } 79 80 if (dfFlags & DF_UB) { 81 getterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 82 getterInsn.vB, isRange); 83 } 84 85 if (dfFlags & DF_UC) { 86 getterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 87 getterInsn.vC, isRange); 88 } 89 90 getterInsn.vA = moveResultMIR->dalvikInsn.vA; 91 92 /* Now setup the Dalvik instruction with converted src/dst registers */ 93 newGetterMIR->dalvikInsn = getterInsn; 94 95 newGetterMIR->width = dexGetWidthFromOpcode(getterInsn.opcode); 96 97 newGetterMIR->OptimizationFlags |= MIR_CALLEE; 98 99 /* 100 * If the getter instruction is about to raise any exception, punt to the 101 * interpreter and re-execute the invoke. 102 */ 103 newGetterMIR->offset = invokeMIR->offset; 104 105 newGetterMIR->meta.calleeMethod = calleeMethod; 106 107 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR); 108 109 if (isPredicted) { 110 MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); 111 *invokeMIRSlow = *invokeMIR; 112 invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; 113 114 /* Use vC to denote the first argument (ie this) */ 115 if (!isRange) { 116 invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; 117 } 118 119 moveResultMIR->OptimizationFlags |= MIR_INLINED_PRED; 120 121 dvmCompilerInsertMIRAfter(invokeBB, newGetterMIR, invokeMIRSlow); 122 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 123 #if defined(WITH_JIT_TUNING) 124 gDvmJit.invokePolyGetterInlined++; 125 #endif 126 } else { 127 invokeMIR->OptimizationFlags |= MIR_INLINED; 128 moveResultMIR->OptimizationFlags |= MIR_INLINED; 129 #if defined(WITH_JIT_TUNING) 130 gDvmJit.invokeMonoGetterInlined++; 131 #endif 132 } 133 134 return true; 135 } 136 137 static bool inlineSetter(CompilationUnit *cUnit, 138 const Method *calleeMethod, 139 MIR *invokeMIR, 140 BasicBlock *invokeBB, 141 bool isPredicted, 142 bool isRange) 143 { 144 MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true); 145 DecodedInstruction setterInsn; 146 147 /* 148 * Not all setter instructions have vC but vC will be read by 149 * dvmCompilerGetDalvikDisassembly unconditionally. 150 * Initialize it here to get Valgrind happy. 151 */ 152 setterInsn.vC = 0; 153 154 dexDecodeInstruction(calleeMethod->insns, &setterInsn); 155 156 if (!dvmCompilerCanIncludeThisInstruction(calleeMethod, &setterInsn)) 157 return false; 158 159 int dfFlags = dvmCompilerDataFlowAttributes[setterInsn.opcode]; 160 161 if (dfFlags & (DF_UA | DF_UA_WIDE)) { 162 setterInsn.vA = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 163 setterInsn.vA, isRange); 164 165 } 166 167 if (dfFlags & DF_UB) { 168 setterInsn.vB = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 169 setterInsn.vB, isRange); 170 171 } 172 173 if (dfFlags & DF_UC) { 174 setterInsn.vC = convertRegId(&invokeMIR->dalvikInsn, calleeMethod, 175 setterInsn.vC, isRange); 176 } 177 178 /* Now setup the Dalvik instruction with converted src/dst registers */ 179 newSetterMIR->dalvikInsn = setterInsn; 180 181 newSetterMIR->width = dexGetWidthFromOpcode(setterInsn.opcode); 182 183 newSetterMIR->OptimizationFlags |= MIR_CALLEE; 184 185 /* 186 * If the setter instruction is about to raise any exception, punt to the 187 * interpreter and re-execute the invoke. 188 */ 189 newSetterMIR->offset = invokeMIR->offset; 190 191 newSetterMIR->meta.calleeMethod = calleeMethod; 192 193 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR); 194 195 if (isPredicted) { 196 MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); 197 *invokeMIRSlow = *invokeMIR; 198 invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; 199 200 /* Use vC to denote the first argument (ie this) */ 201 if (!isRange) { 202 invokeMIR->dalvikInsn.vC = invokeMIRSlow->dalvikInsn.arg[0]; 203 } 204 205 dvmCompilerInsertMIRAfter(invokeBB, newSetterMIR, invokeMIRSlow); 206 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 207 #if defined(WITH_JIT_TUNING) 208 gDvmJit.invokePolySetterInlined++; 209 #endif 210 } else { 211 /* 212 * The invoke becomes no-op so it needs an explicit branch to jump to 213 * the chaining cell. 214 */ 215 invokeBB->needFallThroughBranch = true; 216 invokeMIR->OptimizationFlags |= MIR_INLINED; 217 #if defined(WITH_JIT_TUNING) 218 gDvmJit.invokeMonoSetterInlined++; 219 #endif 220 } 221 222 return true; 223 } 224 225 static bool tryInlineSingletonCallsite(CompilationUnit *cUnit, 226 const Method *calleeMethod, 227 MIR *invokeMIR, 228 BasicBlock *invokeBB, 229 bool isRange) 230 { 231 /* Not a Java method */ 232 if (dvmIsNativeMethod(calleeMethod)) return false; 233 234 CompilerMethodStats *methodStats = 235 dvmCompilerAnalyzeMethodBody(calleeMethod, true); 236 237 /* Empty callee - do nothing */ 238 if (methodStats->attributes & METHOD_IS_EMPTY) { 239 /* The original invoke instruction is effectively turned into NOP */ 240 invokeMIR->OptimizationFlags |= MIR_INLINED; 241 /* 242 * Need to insert an explicit branch to catch the falling knife (into 243 * the PC reconstruction or chaining cell). 244 */ 245 invokeBB->needFallThroughBranch = true; 246 return true; 247 } 248 249 if (methodStats->attributes & METHOD_IS_GETTER) { 250 return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, 251 isRange); 252 } else if (methodStats->attributes & METHOD_IS_SETTER) { 253 return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, false, 254 isRange); 255 } 256 return false; 257 } 258 259 static bool inlineEmptyVirtualCallee(CompilationUnit *cUnit, 260 const Method *calleeMethod, 261 MIR *invokeMIR, 262 BasicBlock *invokeBB) 263 { 264 MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true); 265 *invokeMIRSlow = *invokeMIR; 266 invokeMIR->dalvikInsn.opcode = (Opcode)kMirOpCheckInlinePrediction; 267 268 dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, invokeMIRSlow); 269 invokeMIRSlow->OptimizationFlags |= MIR_INLINED_PRED; 270 return true; 271 } 272 273 static bool tryInlineVirtualCallsite(CompilationUnit *cUnit, 274 const Method *calleeMethod, 275 MIR *invokeMIR, 276 BasicBlock *invokeBB, 277 bool isRange) 278 { 279 /* Not a Java method */ 280 if (dvmIsNativeMethod(calleeMethod)) return false; 281 282 CompilerMethodStats *methodStats = 283 dvmCompilerAnalyzeMethodBody(calleeMethod, true); 284 285 /* Empty callee - do nothing by checking the clazz pointer */ 286 if (methodStats->attributes & METHOD_IS_EMPTY) { 287 return inlineEmptyVirtualCallee(cUnit, calleeMethod, invokeMIR, 288 invokeBB); 289 } 290 291 if (methodStats->attributes & METHOD_IS_GETTER) { 292 return inlineGetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, 293 isRange); 294 } else if (methodStats->attributes & METHOD_IS_SETTER) { 295 return inlineSetter(cUnit, calleeMethod, invokeMIR, invokeBB, true, 296 isRange); 297 } 298 return false; 299 } 300 301 302 void dvmCompilerInlineMIR(CompilationUnit *cUnit, JitTranslationInfo *info) 303 { 304 bool isRange = false; 305 GrowableListIterator iterator; 306 307 dvmGrowableListIteratorInit(&cUnit->blockList, &iterator); 308 /* 309 * Analyze the basic block containing an invoke to see if it can be inlined 310 */ 311 while (true) { 312 BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator); 313 if (bb == NULL) break; 314 if (bb->blockType != kDalvikByteCode) 315 continue; 316 MIR *lastMIRInsn = bb->lastMIRInsn; 317 Opcode opcode = lastMIRInsn->dalvikInsn.opcode; 318 int flags = (int)dexGetFlagsFromOpcode(opcode); 319 320 /* No invoke - continue */ 321 if ((flags & kInstrInvoke) == 0) 322 continue; 323 324 /* Disable inlining when doing method tracing */ 325 if (gDvmJit.methodTraceSupport) 326 continue; 327 328 /* 329 * If the invoke itself is selected for single stepping, don't bother 330 * to inline it. 331 */ 332 if (SINGLE_STEP_OP(opcode)) 333 continue; 334 335 const Method *calleeMethod; 336 337 switch (opcode) { 338 case OP_INVOKE_SUPER: 339 case OP_INVOKE_DIRECT: 340 case OP_INVOKE_STATIC: 341 case OP_INVOKE_SUPER_QUICK: 342 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 343 break; 344 case OP_INVOKE_SUPER_RANGE: 345 case OP_INVOKE_DIRECT_RANGE: 346 case OP_INVOKE_STATIC_RANGE: 347 case OP_INVOKE_SUPER_QUICK_RANGE: 348 isRange = true; 349 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 350 break; 351 default: 352 calleeMethod = NULL; 353 break; 354 } 355 356 if (calleeMethod) { 357 bool inlined = tryInlineSingletonCallsite(cUnit, calleeMethod, 358 lastMIRInsn, bb, isRange); 359 if (!inlined && 360 !(gDvmJit.disableOpt & (1 << kMethodJit)) && 361 !dvmIsNativeMethod(calleeMethod)) { 362 CompilerMethodStats *methodStats = 363 dvmCompilerAnalyzeMethodBody(calleeMethod, true); 364 if ((methodStats->attributes & METHOD_IS_LEAF) && 365 !(methodStats->attributes & METHOD_CANNOT_COMPILE)) { 366 /* Callee has been previously compiled */ 367 if (dvmJitGetMethodAddr(calleeMethod->insns)) { 368 lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; 369 } else { 370 /* Compile the callee first */ 371 dvmCompileMethod(calleeMethod, info); 372 if (dvmJitGetMethodAddr(calleeMethod->insns)) { 373 lastMIRInsn->OptimizationFlags |= 374 MIR_INVOKE_METHOD_JIT; 375 } else { 376 methodStats->attributes |= METHOD_CANNOT_COMPILE; 377 } 378 } 379 } 380 } 381 return; 382 } 383 384 switch (opcode) { 385 case OP_INVOKE_VIRTUAL: 386 case OP_INVOKE_VIRTUAL_QUICK: 387 case OP_INVOKE_INTERFACE: 388 isRange = false; 389 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 390 break; 391 case OP_INVOKE_VIRTUAL_RANGE: 392 case OP_INVOKE_VIRTUAL_QUICK_RANGE: 393 case OP_INVOKE_INTERFACE_RANGE: 394 isRange = true; 395 calleeMethod = lastMIRInsn->meta.callsiteInfo->method; 396 break; 397 default: 398 break; 399 } 400 401 if (calleeMethod) { 402 bool inlined = tryInlineVirtualCallsite(cUnit, calleeMethod, 403 lastMIRInsn, bb, isRange); 404 if (!inlined && 405 !(gDvmJit.disableOpt & (1 << kMethodJit)) && 406 !dvmIsNativeMethod(calleeMethod)) { 407 CompilerMethodStats *methodStats = 408 dvmCompilerAnalyzeMethodBody(calleeMethod, true); 409 if ((methodStats->attributes & METHOD_IS_LEAF) && 410 !(methodStats->attributes & METHOD_CANNOT_COMPILE)) { 411 /* Callee has been previously compiled */ 412 if (dvmJitGetMethodAddr(calleeMethod->insns)) { 413 lastMIRInsn->OptimizationFlags |= MIR_INVOKE_METHOD_JIT; 414 } else { 415 /* Compile the callee first */ 416 dvmCompileMethod(calleeMethod, info); 417 if (dvmJitGetMethodAddr(calleeMethod->insns)) { 418 lastMIRInsn->OptimizationFlags |= 419 MIR_INVOKE_METHOD_JIT; 420 } else { 421 methodStats->attributes |= METHOD_CANNOT_COMPILE; 422 } 423 } 424 } 425 } 426 return; 427 } 428 } 429 } 430