dex_file_method_inliner.cc revision b3e527b2a9ee28ecaba2045f4415b00c8b395039
1/* 2 * Copyright (C) 2013 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 "dex_file_method_inliner.h" 18 19#include <algorithm> 20 21#include "base/macros.h" 22#include "base/mutex.h" 23#include "base/mutex-inl.h" 24#include "dex/frontend.h" 25#include "thread.h" 26#include "thread-inl.h" 27#include "dex/mir_graph.h" 28#include "dex_instruction.h" 29#include "dex_instruction-inl.h" 30#include "verifier/method_verifier.h" 31#include "verifier/method_verifier-inl.h" 32 33namespace art { 34 35namespace { // anonymous namespace 36 37MIR* AllocReplacementMIR(MIRGraph* mir_graph, MIR* invoke, MIR* move_return) { 38 ArenaAllocator* arena = mir_graph->GetArena(); 39 MIR* insn = static_cast<MIR*>(arena->Alloc(sizeof(MIR), kArenaAllocMIR)); 40 insn->offset = invoke->offset; 41 insn->width = invoke->width; 42 insn->optimization_flags = MIR_CALLEE; 43 if (move_return != nullptr) { 44 DCHECK_EQ(move_return->offset, invoke->offset + invoke->width); 45 insn->width += move_return->width; 46 } 47 return insn; 48} 49 50uint32_t GetInvokeReg(MIR* invoke, uint32_t arg) { 51 DCHECK_LT(arg, invoke->dalvikInsn.vA); 52 if (Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc) { 53 return invoke->dalvikInsn.vC + arg; // Non-range invoke. 54 } else { 55 DCHECK_EQ(Instruction::FormatOf(invoke->dalvikInsn.opcode), Instruction::k35c); 56 return invoke->dalvikInsn.arg[arg]; // Range invoke. 57 } 58} 59 60bool WideArgIsInConsecutiveDalvikRegs(MIR* invoke, uint32_t arg) { 61 DCHECK_LT(arg + 1, invoke->dalvikInsn.vA); 62 return Instruction::FormatOf(invoke->dalvikInsn.opcode) == Instruction::k3rc || 63 invoke->dalvikInsn.arg[arg + 1u] == invoke->dalvikInsn.arg[arg] + 1u; 64} 65 66} // anonymous namespace 67 68const uint32_t DexFileMethodInliner::kIndexUnresolved; 69const char* const DexFileMethodInliner::kClassCacheNames[] = { 70 "Z", // kClassCacheBoolean 71 "B", // kClassCacheByte 72 "C", // kClassCacheChar 73 "S", // kClassCacheShort 74 "I", // kClassCacheInt 75 "J", // kClassCacheLong 76 "F", // kClassCacheFloat 77 "D", // kClassCacheDouble 78 "V", // kClassCacheVoid 79 "Ljava/lang/Object;", // kClassCacheJavaLangObject 80 "Ljava/lang/String;", // kClassCacheJavaLangString 81 "Ljava/lang/Double;", // kClassCacheJavaLangDouble 82 "Ljava/lang/Float;", // kClassCacheJavaLangFloat 83 "Ljava/lang/Integer;", // kClassCacheJavaLangInteger 84 "Ljava/lang/Long;", // kClassCacheJavaLangLong 85 "Ljava/lang/Short;", // kClassCacheJavaLangShort 86 "Ljava/lang/Math;", // kClassCacheJavaLangMath 87 "Ljava/lang/StrictMath;", // kClassCacheJavaLangStrictMath 88 "Ljava/lang/Thread;", // kClassCacheJavaLangThread 89 "Llibcore/io/Memory;", // kClassCacheLibcoreIoMemory 90 "Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe 91}; 92 93const char* const DexFileMethodInliner::kNameCacheNames[] = { 94 "reverseBytes", // kNameCacheReverseBytes 95 "doubleToRawLongBits", // kNameCacheDoubleToRawLongBits 96 "longBitsToDouble", // kNameCacheLongBitsToDouble 97 "floatToRawIntBits", // kNameCacheFloatToRawIntBits 98 "intBitsToFloat", // kNameCacheIntBitsToFloat 99 "abs", // kNameCacheAbs 100 "max", // kNameCacheMax 101 "min", // kNameCacheMin 102 "sqrt", // kNameCacheSqrt 103 "charAt", // kNameCacheCharAt 104 "compareTo", // kNameCacheCompareTo 105 "isEmpty", // kNameCacheIsEmpty 106 "indexOf", // kNameCacheIndexOf 107 "length", // kNameCacheLength 108 "currentThread", // kNameCacheCurrentThread 109 "peekByte", // kNameCachePeekByte 110 "peekIntNative", // kNameCachePeekIntNative 111 "peekLongNative", // kNameCachePeekLongNative 112 "peekShortNative", // kNameCachePeekShortNative 113 "pokeByte", // kNameCachePokeByte 114 "pokeIntNative", // kNameCachePokeIntNative 115 "pokeLongNative", // kNameCachePokeLongNative 116 "pokeShortNative", // kNameCachePokeShortNative 117 "compareAndSwapInt", // kNameCacheCompareAndSwapInt 118 "compareAndSwapLong", // kNameCacheCompareAndSwapLong 119 "compareAndSwapObject", // kNameCacheCompareAndSwapObject 120 "getInt", // kNameCacheGetInt 121 "getIntVolatile", // kNameCacheGetIntVolatile 122 "putInt", // kNameCachePutInt 123 "putIntVolatile", // kNameCachePutIntVolatile 124 "putOrderedInt", // kNameCachePutOrderedInt 125 "getLong", // kNameCacheGetLong 126 "getLongVolatile", // kNameCacheGetLongVolatile 127 "putLong", // kNameCachePutLong 128 "putLongVolatile", // kNameCachePutLongVolatile 129 "putOrderedLong", // kNameCachePutOrderedLong 130 "getObject", // kNameCacheGetObject 131 "getObjectVolatile", // kNameCacheGetObjectVolatile 132 "putObject", // kNameCachePutObject 133 "putObjectVolatile", // kNameCachePutObjectVolatile 134 "putOrderedObject", // kNameCachePutOrderedObject 135}; 136 137const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { 138 // kProtoCacheI_I 139 { kClassCacheInt, 1, { kClassCacheInt } }, 140 // kProtoCacheJ_J 141 { kClassCacheLong, 1, { kClassCacheLong } }, 142 // kProtoCacheS_S 143 { kClassCacheShort, 1, { kClassCacheShort } }, 144 // kProtoCacheD_D 145 { kClassCacheDouble, 1, { kClassCacheDouble } }, 146 // kProtoCacheF_F 147 { kClassCacheFloat, 1, { kClassCacheFloat } }, 148 // kProtoCacheD_J 149 { kClassCacheLong, 1, { kClassCacheDouble } }, 150 // kProtoCacheJ_D 151 { kClassCacheDouble, 1, { kClassCacheLong } }, 152 // kProtoCacheF_I 153 { kClassCacheInt, 1, { kClassCacheFloat } }, 154 // kProtoCacheI_F 155 { kClassCacheFloat, 1, { kClassCacheInt } }, 156 // kProtoCacheII_I 157 { kClassCacheInt, 2, { kClassCacheInt, kClassCacheInt } }, 158 // kProtoCacheI_C 159 { kClassCacheChar, 1, { kClassCacheInt } }, 160 // kProtoCacheString_I 161 { kClassCacheInt, 1, { kClassCacheJavaLangString } }, 162 // kProtoCache_Z 163 { kClassCacheBoolean, 0, { } }, 164 // kProtoCache_I 165 { kClassCacheInt, 0, { } }, 166 // kProtoCache_Thread 167 { kClassCacheJavaLangThread, 0, { } }, 168 // kProtoCacheJ_B 169 { kClassCacheByte, 1, { kClassCacheLong } }, 170 // kProtoCacheJ_I 171 { kClassCacheInt, 1, { kClassCacheLong } }, 172 // kProtoCacheJ_S 173 { kClassCacheShort, 1, { kClassCacheLong } }, 174 // kProtoCacheJB_V 175 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheByte } }, 176 // kProtoCacheJI_V 177 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheInt } }, 178 // kProtoCacheJJ_V 179 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheLong } }, 180 // kProtoCacheJS_V 181 { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } }, 182 // kProtoCacheObjectJII_Z 183 { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, 184 kClassCacheInt, kClassCacheInt } }, 185 // kProtoCacheObjectJJJ_Z 186 { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, 187 kClassCacheLong, kClassCacheLong } }, 188 // kProtoCacheObjectJObjectObject_Z 189 { kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong, 190 kClassCacheJavaLangObject, kClassCacheJavaLangObject } }, 191 // kProtoCacheObjectJ_I 192 { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, 193 // kProtoCacheObjectJI_V 194 { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, 195 // kProtoCacheObjectJ_J 196 { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, 197 // kProtoCacheObjectJJ_V 198 { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, 199 // kProtoCacheObjectJ_Object 200 { kClassCacheJavaLangObject, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, 201 // kProtoCacheObjectJObject_V 202 { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, 203 kClassCacheJavaLangObject } }, 204}; 205 206const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = { 207#define INTRINSIC(c, n, p, o, d) \ 208 { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, { d } } } 209 210 INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0), 211 INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0), 212 INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), 213 INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, 0), 214 215 INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, kWord), 216 INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, kLong), 217 INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), 218 219 INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), 220 INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), 221 INTRINSIC(JavaLangMath, Abs, J_J, kIntrinsicAbsLong, 0), 222 INTRINSIC(JavaLangStrictMath, Abs, J_J, kIntrinsicAbsLong, 0), 223 INTRINSIC(JavaLangMath, Abs, F_F, kIntrinsicAbsFloat, 0), 224 INTRINSIC(JavaLangStrictMath, Abs, F_F, kIntrinsicAbsFloat, 0), 225 INTRINSIC(JavaLangMath, Abs, D_D, kIntrinsicAbsDouble, 0), 226 INTRINSIC(JavaLangStrictMath, Abs, D_D, kIntrinsicAbsDouble, 0), 227 INTRINSIC(JavaLangMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), 228 INTRINSIC(JavaLangStrictMath, Min, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMin), 229 INTRINSIC(JavaLangMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), 230 INTRINSIC(JavaLangStrictMath, Max, II_I, kIntrinsicMinMaxInt, kIntrinsicFlagMax), 231 INTRINSIC(JavaLangMath, Sqrt, D_D, kIntrinsicSqrt, 0), 232 INTRINSIC(JavaLangStrictMath, Sqrt, D_D, kIntrinsicSqrt, 0), 233 234 INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), 235 INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), 236 INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), 237 INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), 238 INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), 239 INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), 240 241 INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), 242 243 INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), 244 INTRINSIC(LibcoreIoMemory, PeekIntNative, J_I, kIntrinsicPeek, kWord), 245 INTRINSIC(LibcoreIoMemory, PeekLongNative, J_J, kIntrinsicPeek, kLong), 246 INTRINSIC(LibcoreIoMemory, PeekShortNative, J_S, kIntrinsicPeek, kSignedHalf), 247 INTRINSIC(LibcoreIoMemory, PokeByte, JB_V, kIntrinsicPoke, kSignedByte), 248 INTRINSIC(LibcoreIoMemory, PokeIntNative, JI_V, kIntrinsicPoke, kWord), 249 INTRINSIC(LibcoreIoMemory, PokeLongNative, JJ_V, kIntrinsicPoke, kLong), 250 INTRINSIC(LibcoreIoMemory, PokeShortNative, JS_V, kIntrinsicPoke, kSignedHalf), 251 252 INTRINSIC(SunMiscUnsafe, CompareAndSwapInt, ObjectJII_Z, kIntrinsicCas, 253 kIntrinsicFlagNone), 254 INTRINSIC(SunMiscUnsafe, CompareAndSwapLong, ObjectJJJ_Z, kIntrinsicCas, 255 kIntrinsicFlagIsLong), 256 INTRINSIC(SunMiscUnsafe, CompareAndSwapObject, ObjectJObjectObject_Z, kIntrinsicCas, 257 kIntrinsicFlagIsObject), 258 259#define UNSAFE_GET_PUT(type, code, type_flags) \ 260 INTRINSIC(SunMiscUnsafe, Get ## type, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ 261 type_flags & ~kIntrinsicFlagIsObject), \ 262 INTRINSIC(SunMiscUnsafe, Get ## type ## Volatile, ObjectJ_ ## code, kIntrinsicUnsafeGet, \ 263 (type_flags | kIntrinsicFlagIsVolatile) & ~kIntrinsicFlagIsObject), \ 264 INTRINSIC(SunMiscUnsafe, Put ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ 265 type_flags), \ 266 INTRINSIC(SunMiscUnsafe, Put ## type ## Volatile, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ 267 type_flags | kIntrinsicFlagIsVolatile), \ 268 INTRINSIC(SunMiscUnsafe, PutOrdered ## type, ObjectJ ## code ## _V, kIntrinsicUnsafePut, \ 269 type_flags | kIntrinsicFlagIsOrdered) 270 271 UNSAFE_GET_PUT(Int, I, kIntrinsicFlagNone), 272 UNSAFE_GET_PUT(Long, J, kIntrinsicFlagIsLong), 273 UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), 274#undef UNSAFE_GET_PUT 275 276#undef INTRINSIC 277}; 278 279DexFileMethodInliner::DexFileMethodInliner() 280 : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock), 281 dex_file_(NULL) { 282 COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0); 283 COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames); 284 COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0); 285 COMPILE_ASSERT(arraysize(kNameCacheNames) == kNameCacheLast, bad_arraysize_kNameCacheNames); 286 COMPILE_ASSERT(kProtoCacheFirst == 0, kProtoCacheFirst_not_0); 287 COMPILE_ASSERT(arraysize(kProtoCacheDefs) == kProtoCacheLast, bad_arraysize_kProtoCacheNames); 288} 289 290DexFileMethodInliner::~DexFileMethodInliner() { 291} 292 293bool DexFileMethodInliner::AnalyseMethodCode(verifier::MethodVerifier* verifier) { 294 InlineMethod method; 295 bool success = InlineMethodAnalyser::AnalyseMethodCode(verifier, &method); 296 return success && AddInlineMethod(verifier->GetMethodReference().dex_method_index, method); 297} 298 299bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) { 300 ReaderMutexLock mu(Thread::Current(), lock_); 301 auto it = inline_methods_.find(method_index); 302 return it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0; 303} 304 305bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { 306 InlineMethod intrinsic; 307 { 308 ReaderMutexLock mu(Thread::Current(), lock_); 309 auto it = inline_methods_.find(info->index); 310 if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) { 311 return false; 312 } 313 intrinsic = it->second; 314 } 315 switch (intrinsic.opcode) { 316 case kIntrinsicDoubleCvt: 317 return backend->GenInlinedDoubleCvt(info); 318 case kIntrinsicFloatCvt: 319 return backend->GenInlinedFloatCvt(info); 320 case kIntrinsicReverseBytes: 321 return backend->GenInlinedReverseBytes(info, static_cast<OpSize>(intrinsic.d.data)); 322 case kIntrinsicAbsInt: 323 return backend->GenInlinedAbsInt(info); 324 case kIntrinsicAbsLong: 325 return backend->GenInlinedAbsLong(info); 326 case kIntrinsicAbsFloat: 327 return backend->GenInlinedAbsFloat(info); 328 case kIntrinsicAbsDouble: 329 return backend->GenInlinedAbsDouble(info); 330 case kIntrinsicMinMaxInt: 331 return backend->GenInlinedMinMaxInt(info, intrinsic.d.data & kIntrinsicFlagMin); 332 case kIntrinsicSqrt: 333 return backend->GenInlinedSqrt(info); 334 case kIntrinsicCharAt: 335 return backend->GenInlinedCharAt(info); 336 case kIntrinsicCompareTo: 337 return backend->GenInlinedStringCompareTo(info); 338 case kIntrinsicIsEmptyOrLength: 339 return backend->GenInlinedStringIsEmptyOrLength( 340 info, intrinsic.d.data & kIntrinsicFlagIsEmpty); 341 case kIntrinsicIndexOf: 342 return backend->GenInlinedIndexOf(info, intrinsic.d.data & kIntrinsicFlagBase0); 343 case kIntrinsicCurrentThread: 344 return backend->GenInlinedCurrentThread(info); 345 case kIntrinsicPeek: 346 return backend->GenInlinedPeek(info, static_cast<OpSize>(intrinsic.d.data)); 347 case kIntrinsicPoke: 348 return backend->GenInlinedPoke(info, static_cast<OpSize>(intrinsic.d.data)); 349 case kIntrinsicCas: 350 return backend->GenInlinedCas(info, intrinsic.d.data & kIntrinsicFlagIsLong, 351 intrinsic.d.data & kIntrinsicFlagIsObject); 352 case kIntrinsicUnsafeGet: 353 return backend->GenInlinedUnsafeGet(info, intrinsic.d.data & kIntrinsicFlagIsLong, 354 intrinsic.d.data & kIntrinsicFlagIsVolatile); 355 case kIntrinsicUnsafePut: 356 return backend->GenInlinedUnsafePut(info, intrinsic.d.data & kIntrinsicFlagIsLong, 357 intrinsic.d.data & kIntrinsicFlagIsObject, 358 intrinsic.d.data & kIntrinsicFlagIsVolatile, 359 intrinsic.d.data & kIntrinsicFlagIsOrdered); 360 default: 361 LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode; 362 return false; // avoid warning "control reaches end of non-void function" 363 } 364} 365 366bool DexFileMethodInliner::IsSpecial(uint32_t method_index) { 367 ReaderMutexLock mu(Thread::Current(), lock_); 368 auto it = inline_methods_.find(method_index); 369 return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0; 370} 371 372bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) { 373 InlineMethod special; 374 { 375 ReaderMutexLock mu(Thread::Current(), lock_); 376 auto it = inline_methods_.find(method_idx); 377 if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) { 378 return false; 379 } 380 special = it->second; 381 } 382 return backend->SpecialMIR2LIR(special); 383} 384 385bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, 386 uint32_t method_idx) { 387 InlineMethod method; 388 { 389 ReaderMutexLock mu(Thread::Current(), lock_); 390 auto it = inline_methods_.find(method_idx); 391 if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) { 392 return false; 393 } 394 method = it->second; 395 } 396 397 MIR* move_result = nullptr; 398 bool result = true; 399 switch (method.opcode) { 400 case kInlineOpNop: 401 break; 402 case kInlineOpNonWideConst: 403 move_result = mir_graph->FindMoveResult(bb, invoke); 404 result = GenInlineConst(mir_graph, bb, invoke, move_result, method); 405 break; 406 case kInlineOpReturnArg: 407 move_result = mir_graph->FindMoveResult(bb, invoke); 408 result = GenInlineReturnArg(mir_graph, bb, invoke, move_result, method); 409 break; 410 case kInlineOpIGet: 411 move_result = mir_graph->FindMoveResult(bb, invoke); 412 result = GenInlineIGet(mir_graph, bb, invoke, move_result, method, method_idx); 413 break; 414 case kInlineOpIPut: 415 result = GenInlineIPut(mir_graph, bb, invoke, method, method_idx); 416 break; 417 default: 418 LOG(FATAL) << "Unexpected inline op: " << method.opcode; 419 } 420 if (result) { 421 invoke->optimization_flags |= MIR_INLINED; 422 if (move_result != nullptr) { 423 move_result->optimization_flags |= MIR_INLINED; 424 move_result->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); 425 } 426 } 427 return result; 428} 429 430uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache, 431 ClassCacheIndex index) { 432 uint32_t* class_index = &cache->class_indexes[index]; 433 if (*class_index != kIndexUnresolved) { 434 return *class_index; 435 } 436 437 const DexFile::StringId* string_id = dex_file->FindStringId(kClassCacheNames[index]); 438 if (string_id == nullptr) { 439 *class_index = kIndexNotFound; 440 return *class_index; 441 } 442 uint32_t string_index = dex_file->GetIndexForStringId(*string_id); 443 444 const DexFile::TypeId* type_id = dex_file->FindTypeId(string_index); 445 if (type_id == nullptr) { 446 *class_index = kIndexNotFound; 447 return *class_index; 448 } 449 *class_index = dex_file->GetIndexForTypeId(*type_id); 450 return *class_index; 451} 452 453uint32_t DexFileMethodInliner::FindNameIndex(const DexFile* dex_file, IndexCache* cache, 454 NameCacheIndex index) { 455 uint32_t* name_index = &cache->name_indexes[index]; 456 if (*name_index != kIndexUnresolved) { 457 return *name_index; 458 } 459 460 const DexFile::StringId* string_id = dex_file->FindStringId(kNameCacheNames[index]); 461 if (string_id == nullptr) { 462 *name_index = kIndexNotFound; 463 return *name_index; 464 } 465 *name_index = dex_file->GetIndexForStringId(*string_id); 466 return *name_index; 467} 468 469uint32_t DexFileMethodInliner::FindProtoIndex(const DexFile* dex_file, IndexCache* cache, 470 ProtoCacheIndex index) { 471 uint32_t* proto_index = &cache->proto_indexes[index]; 472 if (*proto_index != kIndexUnresolved) { 473 return *proto_index; 474 } 475 476 const ProtoDef& proto_def = kProtoCacheDefs[index]; 477 uint32_t return_index = FindClassIndex(dex_file, cache, proto_def.return_type); 478 if (return_index == kIndexNotFound) { 479 *proto_index = kIndexNotFound; 480 return *proto_index; 481 } 482 uint16_t return_type = static_cast<uint16_t>(return_index); 483 DCHECK_EQ(static_cast<uint32_t>(return_type), return_index); 484 485 uint32_t signature_length = proto_def.param_count; 486 uint16_t signature_type_idxs[kProtoMaxParams]; 487 for (uint32_t i = 0; i != signature_length; ++i) { 488 uint32_t param_index = FindClassIndex(dex_file, cache, proto_def.params[i]); 489 if (param_index == kIndexNotFound) { 490 *proto_index = kIndexNotFound; 491 return *proto_index; 492 } 493 signature_type_idxs[i] = static_cast<uint16_t>(param_index); 494 DCHECK_EQ(static_cast<uint32_t>(signature_type_idxs[i]), param_index); 495 } 496 497 const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type, signature_type_idxs, 498 signature_length); 499 if (proto_id == nullptr) { 500 *proto_index = kIndexNotFound; 501 return *proto_index; 502 } 503 *proto_index = dex_file->GetIndexForProtoId(*proto_id); 504 return *proto_index; 505} 506 507uint32_t DexFileMethodInliner::FindMethodIndex(const DexFile* dex_file, IndexCache* cache, 508 const MethodDef& method_def) { 509 uint32_t declaring_class_index = FindClassIndex(dex_file, cache, method_def.declaring_class); 510 if (declaring_class_index == kIndexNotFound) { 511 return kIndexNotFound; 512 } 513 uint32_t name_index = FindNameIndex(dex_file, cache, method_def.name); 514 if (name_index == kIndexNotFound) { 515 return kIndexNotFound; 516 } 517 uint32_t proto_index = FindProtoIndex(dex_file, cache, method_def.proto); 518 if (proto_index == kIndexNotFound) { 519 return kIndexNotFound; 520 } 521 const DexFile::MethodId* method_id = 522 dex_file->FindMethodId(dex_file->GetTypeId(declaring_class_index), 523 dex_file->GetStringId(name_index), 524 dex_file->GetProtoId(proto_index)); 525 if (method_id == nullptr) { 526 return kIndexNotFound; 527 } 528 return dex_file->GetIndexForMethodId(*method_id); 529} 530 531DexFileMethodInliner::IndexCache::IndexCache() { 532 std::fill_n(class_indexes, arraysize(class_indexes), kIndexUnresolved); 533 std::fill_n(name_indexes, arraysize(name_indexes), kIndexUnresolved); 534 std::fill_n(proto_indexes, arraysize(proto_indexes), kIndexUnresolved); 535} 536 537void DexFileMethodInliner::FindIntrinsics(const DexFile* dex_file) { 538 DCHECK(dex_file != nullptr); 539 DCHECK(dex_file_ == nullptr); 540 IndexCache cache; 541 for (const IntrinsicDef& def : kIntrinsicMethods) { 542 uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def); 543 if (method_idx != kIndexNotFound) { 544 DCHECK(inline_methods_.find(method_idx) == inline_methods_.end()); 545 inline_methods_.Put(method_idx, def.intrinsic); 546 } 547 } 548 dex_file_ = dex_file; 549} 550 551bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, const InlineMethod& method) { 552 WriterMutexLock mu(Thread::Current(), lock_); 553 if (LIKELY(inline_methods_.find(method_idx) == inline_methods_.end())) { 554 inline_methods_.Put(method_idx, method); 555 return true; 556 } else { 557 if (PrettyMethod(method_idx, *dex_file_) == "int java.lang.String.length()") { 558 // TODO: String.length is both kIntrinsicIsEmptyOrLength and kInlineOpIGet. 559 } else { 560 LOG(ERROR) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline"; 561 } 562 return false; 563 } 564} 565 566bool DexFileMethodInliner::GenInlineConst(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, 567 MIR* move_result, const InlineMethod& method) { 568 if (move_result == nullptr) { 569 // Result is unused. 570 return true; 571 } 572 573 // Check the opcode and for MOVE_RESULT_OBJECT check also that the constant is null. 574 DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT || 575 (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT && 576 method.d.data == 0u)); 577 578 // Insert the CONST instruction. 579 MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); 580 insn->dalvikInsn.opcode = Instruction::CONST; 581 insn->dalvikInsn.vA = move_result->dalvikInsn.vA; 582 insn->dalvikInsn.vB = method.d.data; 583 bb->InsertMIRAfter(move_result, insn); 584 return true; 585} 586 587bool DexFileMethodInliner::GenInlineReturnArg(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, 588 MIR* move_result, const InlineMethod& method) { 589 if (move_result == nullptr) { 590 // Result is unused. 591 return true; 592 } 593 594 // Select opcode and argument. 595 const InlineReturnArgData& data = method.d.return_data; 596 Instruction::Code opcode = Instruction::MOVE_FROM16; 597 uint32_t arg = GetInvokeReg(invoke, data.arg); 598 if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) { 599 DCHECK_EQ(data.is_object, 1u); 600 DCHECK_EQ(data.is_wide, 0u); 601 opcode = Instruction::MOVE_OBJECT_FROM16; 602 } else if (move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT_WIDE) { 603 DCHECK_EQ(data.is_wide, 1u); 604 DCHECK_EQ(data.is_object, 0u); 605 opcode = Instruction::MOVE_WIDE_FROM16; 606 if (!WideArgIsInConsecutiveDalvikRegs(invoke, data.arg)) { 607 // The two halfs of the source value are not in consecutive dalvik registers in INVOKE. 608 return false; 609 } 610 } else { 611 DCHECK(move_result->dalvikInsn.opcode == Instruction::MOVE_RESULT); 612 DCHECK_EQ(data.is_wide, 0u); 613 DCHECK_EQ(data.is_object, 0u); 614 } 615 616 // Insert the move instruction 617 MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); 618 insn->dalvikInsn.opcode = opcode; 619 insn->dalvikInsn.vA = move_result->dalvikInsn.vA; 620 insn->dalvikInsn.vB = arg; 621 bb->InsertMIRAfter(move_result, insn); 622 return true; 623} 624 625bool DexFileMethodInliner::GenInlineIGet(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, 626 MIR* move_result, const InlineMethod& method, 627 uint32_t method_idx) { 628 CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit(); 629 if (cu->enable_debug & (1 << kDebugSlowFieldPath)) { 630 return false; 631 } 632 633 const InlineIGetIPutData& data = method.d.ifield_data; 634 Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IGET + data.op_variant); 635 DCHECK_EQ(InlineMethodAnalyser::IGetVariant(opcode), data.op_variant); 636 uint32_t object_reg = GetInvokeReg(invoke, data.object_arg); 637 638 if (move_result == nullptr) { 639 // Result is unused. If volatile, we still need to emit the IGET but we have no destination. 640 return !data.is_volatile; 641 } 642 643 DCHECK_EQ(data.method_is_static != 0u, 644 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || 645 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE); 646 bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u); 647 if (!object_is_this) { 648 // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE). 649 return false; 650 } 651 652 if (object_is_this) { 653 // Mark invoke as NOP, null-check is done on IGET. No aborts after this. 654 invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); 655 } 656 657 MIR* insn = AllocReplacementMIR(mir_graph, invoke, move_result); 658 insn->width += insn->offset - invoke->offset; 659 insn->offset = invoke->offset; 660 insn->dalvikInsn.opcode = opcode; 661 insn->dalvikInsn.vA = move_result->dalvikInsn.vA; 662 insn->dalvikInsn.vB = object_reg; 663 mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn); 664 665 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved()); 666 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastGet()); 667 DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value()); 668 DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u); 669 670 bb->InsertMIRAfter(move_result, insn); 671 return true; 672} 673 674bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, 675 const InlineMethod& method, uint32_t method_idx) { 676 CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit(); 677 if (cu->enable_debug & (1 << kDebugSlowFieldPath)) { 678 return false; 679 } 680 681 const InlineIGetIPutData& data = method.d.ifield_data; 682 Instruction::Code opcode = static_cast<Instruction::Code>(Instruction::IPUT + data.op_variant); 683 DCHECK_EQ(InlineMethodAnalyser::IPutVariant(opcode), data.op_variant); 684 uint32_t object_reg = GetInvokeReg(invoke, data.object_arg); 685 uint32_t src_reg = GetInvokeReg(invoke, data.src_arg); 686 687 if (opcode == Instruction::IPUT_WIDE && !WideArgIsInConsecutiveDalvikRegs(invoke, data.src_arg)) { 688 // The two halfs of the source value are not in consecutive dalvik registers in INVOKE. 689 return false; 690 } 691 692 DCHECK_EQ(data.method_is_static != 0u, 693 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC || 694 invoke->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE); 695 bool object_is_this = (data.method_is_static == 0u && data.object_arg == 0u); 696 if (!object_is_this) { 697 // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE). 698 return false; 699 } 700 701 if (object_is_this) { 702 // Mark invoke as NOP, null-check is done on IPUT. No aborts after this. 703 invoke->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); 704 } 705 706 MIR* insn = AllocReplacementMIR(mir_graph, invoke, nullptr); 707 insn->dalvikInsn.opcode = opcode; 708 insn->dalvikInsn.vA = src_reg; 709 insn->dalvikInsn.vB = object_reg; 710 mir_graph->ComputeInlineIFieldLoweringInfo(data.field_idx, invoke, insn); 711 712 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).IsResolved()); 713 DCHECK(mir_graph->GetIFieldLoweringInfo(insn).FastPut()); 714 DCHECK_EQ(data.field_offset, mir_graph->GetIFieldLoweringInfo(insn).FieldOffset().Uint32Value()); 715 DCHECK_EQ(data.is_volatile, mir_graph->GetIFieldLoweringInfo(insn).IsVolatile() ? 1u : 0u); 716 717 bb->InsertMIRAfter(invoke, insn); 718 return true; 719} 720 721} // namespace art 722