1 /* 2 * Copyright (C) 2014 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 "inline_method_analyser.h" 18 19 #include "art_field-inl.h" 20 #include "art_method-inl.h" 21 #include "base/enums.h" 22 #include "class_linker-inl.h" 23 #include "dex/code_item_accessors-inl.h" 24 #include "dex/dex_file-inl.h" 25 #include "dex/dex_instruction-inl.h" 26 #include "dex/dex_instruction.h" 27 #include "dex/dex_instruction_utils.h" 28 #include "mirror/class-inl.h" 29 #include "mirror/dex_cache-inl.h" 30 31 /* 32 * NOTE: This code is part of the quick compiler. It lives in the runtime 33 * only to allow the debugger to check whether a method has been inlined. 34 */ 35 36 namespace art { 37 38 namespace { // anonymous namespace 39 40 // Helper class for matching a pattern. 41 class Matcher { 42 public: 43 // Match function type. 44 using MatchFn = bool(Matcher*); 45 46 template <size_t size> 47 static bool Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]); 48 49 // Match and advance. 50 51 static bool Mark(Matcher* matcher); 52 53 template <bool (Matcher::*Fn)()> 54 static bool Required(Matcher* matcher); 55 56 template <bool (Matcher::*Fn)()> 57 static bool Repeated(Matcher* matcher); // On match, returns to the mark. 58 59 // Match an individual instruction. 60 61 template <Instruction::Code opcode> bool Opcode(); 62 bool Const0(); 63 bool IPutOnThis(); 64 65 private: 66 explicit Matcher(const CodeItemDataAccessor* code_item) 67 : code_item_(code_item), 68 instruction_(code_item->begin()) {} 69 70 static bool DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size); 71 72 const CodeItemDataAccessor* const code_item_; 73 DexInstructionIterator instruction_; 74 size_t pos_ = 0u; 75 size_t mark_ = 0u; 76 }; 77 78 template <size_t size> 79 bool Matcher::Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]) { 80 return DoMatch(code_item, pattern, size); 81 } 82 83 bool Matcher::Mark(Matcher* matcher) { 84 matcher->pos_ += 1u; // Advance to the next match function before marking. 85 matcher->mark_ = matcher->pos_; 86 return true; 87 } 88 89 template <bool (Matcher::*Fn)()> 90 bool Matcher::Required(Matcher* matcher) { 91 if (!(matcher->*Fn)()) { 92 return false; 93 } 94 matcher->pos_ += 1u; 95 ++matcher->instruction_; 96 return true; 97 } 98 99 template <bool (Matcher::*Fn)()> 100 bool Matcher::Repeated(Matcher* matcher) { 101 if (!(matcher->*Fn)()) { 102 // Didn't match optional instruction, try the next match function. 103 matcher->pos_ += 1u; 104 return true; 105 } 106 matcher->pos_ = matcher->mark_; 107 ++matcher->instruction_; 108 return true; 109 } 110 111 template <Instruction::Code opcode> 112 bool Matcher::Opcode() { 113 return instruction_->Opcode() == opcode; 114 } 115 116 // Match const 0. 117 bool Matcher::Const0() { 118 return IsInstructionDirectConst(instruction_->Opcode()) && 119 (instruction_->Opcode() == Instruction::CONST_WIDE ? instruction_->VRegB_51l() == 0 120 : instruction_->VRegB() == 0); 121 } 122 123 bool Matcher::IPutOnThis() { 124 DCHECK_NE(code_item_->InsSize(), 0u); 125 return IsInstructionIPut(instruction_->Opcode()) && 126 instruction_->VRegB_22c() == code_item_->RegistersSize() - code_item_->InsSize(); 127 } 128 129 bool Matcher::DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size) { 130 Matcher matcher(code_item); 131 while (matcher.pos_ != size) { 132 if (!pattern[matcher.pos_](&matcher)) { 133 return false; 134 } 135 } 136 return true; 137 } 138 139 // Used for a single invoke in a constructor. In that situation, the method verifier makes 140 // sure we invoke a constructor either in the same class or superclass with at least "this". 141 ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_direct) 142 REQUIRES_SHARED(Locks::mutator_lock_) { 143 DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT); 144 if (kIsDebugBuild) { 145 CodeItemDataAccessor accessor(method->DexInstructionData()); 146 DCHECK_EQ(invoke_direct->VRegC_35c(), 147 accessor.RegistersSize() - accessor.InsSize()); 148 } 149 uint32_t method_index = invoke_direct->VRegB_35c(); 150 ArtMethod* target_method = Runtime::Current()->GetClassLinker()->LookupResolvedMethod( 151 method_index, method->GetDexCache(), method->GetClassLoader()); 152 if (kIsDebugBuild && target_method != nullptr) { 153 CHECK(!target_method->IsStatic()); 154 CHECK(target_method->IsConstructor()); 155 CHECK(target_method->GetDeclaringClass() == method->GetDeclaringClass() || 156 target_method->GetDeclaringClass() == method->GetDeclaringClass()->GetSuperClass()); 157 } 158 return target_method; 159 } 160 161 // Return the forwarded arguments and check that all remaining arguments are zero. 162 // If the check fails, return static_cast<size_t>(-1). 163 size_t CountForwardedConstructorArguments(const CodeItemDataAccessor* code_item, 164 const Instruction* invoke_direct, 165 uint16_t zero_vreg_mask) { 166 DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT); 167 size_t number_of_args = invoke_direct->VRegA_35c(); 168 DCHECK_NE(number_of_args, 0u); 169 uint32_t args[Instruction::kMaxVarArgRegs]; 170 invoke_direct->GetVarArgs(args); 171 uint16_t this_vreg = args[0]; 172 DCHECK_EQ(this_vreg, code_item->RegistersSize() - code_item->InsSize()); // Checked by verifier. 173 size_t forwarded = 1u; 174 while (forwarded < number_of_args && 175 args[forwarded] == this_vreg + forwarded && 176 (zero_vreg_mask & (1u << args[forwarded])) == 0) { 177 ++forwarded; 178 } 179 for (size_t i = forwarded; i != number_of_args; ++i) { 180 if ((zero_vreg_mask & (1u << args[i])) == 0) { 181 return static_cast<size_t>(-1); 182 } 183 } 184 return forwarded; 185 } 186 187 uint16_t GetZeroVRegMask(const Instruction* const0) { 188 DCHECK(IsInstructionDirectConst(const0->Opcode())); 189 DCHECK((const0->Opcode() == Instruction::CONST_WIDE) ? const0->VRegB_51l() == 0u 190 : const0->VRegB() == 0); 191 uint16_t base_mask = IsInstructionConstWide(const0->Opcode()) ? 3u : 1u; 192 return base_mask << const0->VRegA(); 193 } 194 195 // We limit the number of IPUTs storing parameters. There can be any number 196 // of IPUTs that store the value 0 as they are useless in a constructor as 197 // the object always starts zero-initialized. We also eliminate all but the 198 // last store to any field as they are not observable; not even if the field 199 // is volatile as no reference to the object can escape from a constructor 200 // with this pattern. 201 static constexpr size_t kMaxConstructorIPuts = 3u; 202 203 struct ConstructorIPutData { 204 ConstructorIPutData() : field_index(DexFile::kDexNoIndex16), arg(0u) { } 205 206 uint16_t field_index; 207 uint16_t arg; 208 }; 209 210 bool RecordConstructorIPut(ArtMethod* method, 211 const Instruction* new_iput, 212 uint16_t this_vreg, 213 uint16_t zero_vreg_mask, 214 /*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts]) 215 REQUIRES_SHARED(Locks::mutator_lock_) { 216 DCHECK(IsInstructionIPut(new_iput->Opcode())); 217 uint32_t field_index = new_iput->VRegC_22c(); 218 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 219 ArtField* field = class_linker->LookupResolvedField(field_index, method, /* is_static= */ false); 220 if (UNLIKELY(field == nullptr)) { 221 return false; 222 } 223 // Remove previous IPUT to the same field, if any. Different field indexes may refer 224 // to the same field, so we need to compare resolved fields from the dex cache. 225 for (size_t old_pos = 0; old_pos != arraysize(iputs); ++old_pos) { 226 if (iputs[old_pos].field_index == DexFile::kDexNoIndex16) { 227 break; 228 } 229 ArtField* f = class_linker->LookupResolvedField(iputs[old_pos].field_index, 230 method, 231 /* is_static= */ false); 232 DCHECK(f != nullptr); 233 if (f == field) { 234 auto back_it = std::copy(iputs + old_pos + 1, iputs + arraysize(iputs), iputs + old_pos); 235 *back_it = ConstructorIPutData(); 236 break; 237 } 238 } 239 // If the stored value isn't zero, record the IPUT. 240 if ((zero_vreg_mask & (1u << new_iput->VRegA_22c())) == 0u) { 241 size_t new_pos = 0; 242 while (new_pos != arraysize(iputs) && iputs[new_pos].field_index != DexFile::kDexNoIndex16) { 243 ++new_pos; 244 } 245 if (new_pos == arraysize(iputs)) { 246 return false; // Exceeded capacity of the output array. 247 } 248 iputs[new_pos].field_index = field_index; 249 iputs[new_pos].arg = new_iput->VRegA_22c() - this_vreg; 250 } 251 return true; 252 } 253 254 bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item, 255 ArtMethod* method, 256 /*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts]) 257 REQUIRES_SHARED(Locks::mutator_lock_) { 258 // On entry we should not have any IPUTs yet. 259 DCHECK_EQ(0, std::count_if( 260 iputs, 261 iputs + arraysize(iputs), 262 [](const ConstructorIPutData& iput_data) { 263 return iput_data.field_index != DexFile::kDexNoIndex16; 264 })); 265 266 // Limit the maximum number of code units we're willing to match. 267 static constexpr size_t kMaxCodeUnits = 16u; 268 269 // Limit the number of registers that the constructor may use to 16. 270 // Given that IPUTs must use low 16 registers and we do not match MOVEs, 271 // this is a reasonable limitation. 272 static constexpr size_t kMaxVRegs = 16u; 273 274 // We try to match a constructor that calls another constructor (either in 275 // superclass or in the same class) with the same parameters, or with some 276 // parameters truncated (allowed only for calls to superclass constructor) 277 // or with extra parameters with value 0 (with any type, including null). 278 // This call can be followed by optional IPUTs on "this" storing either one 279 // of the parameters or 0 and the code must then finish with RETURN_VOID. 280 // The called constructor must be either java.lang.Object.<init>() or it 281 // must also match the same pattern. 282 static Matcher::MatchFn* const kConstructorPattern[] = { 283 &Matcher::Mark, 284 &Matcher::Repeated<&Matcher::Const0>, 285 &Matcher::Required<&Matcher::Opcode<Instruction::INVOKE_DIRECT>>, 286 &Matcher::Mark, 287 &Matcher::Repeated<&Matcher::Const0>, 288 &Matcher::Repeated<&Matcher::IPutOnThis>, 289 &Matcher::Required<&Matcher::Opcode<Instruction::RETURN_VOID>>, 290 }; 291 292 DCHECK(method != nullptr); 293 DCHECK(!method->IsStatic()); 294 DCHECK(method->IsConstructor()); 295 DCHECK(code_item != nullptr); 296 if (!method->GetDeclaringClass()->IsVerified() || 297 code_item->InsnsSizeInCodeUnits() > kMaxCodeUnits || 298 code_item->RegistersSize() > kMaxVRegs || 299 !Matcher::Match(code_item, kConstructorPattern)) { 300 return false; 301 } 302 303 // Verify the invoke, prevent a few odd cases and collect IPUTs. 304 uint16_t this_vreg = code_item->RegistersSize() - code_item->InsSize(); 305 uint16_t zero_vreg_mask = 0u; 306 307 for (const DexInstructionPcPair& pair : *code_item) { 308 const Instruction& instruction = pair.Inst(); 309 if (instruction.Opcode() == Instruction::RETURN_VOID) { 310 break; 311 } else if (instruction.Opcode() == Instruction::INVOKE_DIRECT) { 312 ArtMethod* target_method = GetTargetConstructor(method, &instruction); 313 if (target_method == nullptr) { 314 return false; 315 } 316 // We allow forwarding constructors only if they pass more arguments 317 // to prevent infinite recursion. 318 if (target_method->GetDeclaringClass() == method->GetDeclaringClass() && 319 instruction.VRegA_35c() <= code_item->InsSize()) { 320 return false; 321 } 322 size_t forwarded = CountForwardedConstructorArguments(code_item, &instruction, zero_vreg_mask); 323 if (forwarded == static_cast<size_t>(-1)) { 324 return false; 325 } 326 if (target_method->GetDeclaringClass()->IsObjectClass()) { 327 DCHECK_EQ(target_method->DexInstructionData().begin()->Opcode(), Instruction::RETURN_VOID); 328 } else { 329 CodeItemDataAccessor target_code_item(target_method->DexInstructionData()); 330 if (!target_code_item.HasCodeItem()) { 331 return false; // Native constructor? 332 } 333 if (!DoAnalyseConstructor(&target_code_item, target_method, iputs)) { 334 return false; 335 } 336 // Prune IPUTs with zero input. 337 auto kept_end = std::remove_if( 338 iputs, 339 iputs + arraysize(iputs), 340 [forwarded](const ConstructorIPutData& iput_data) { 341 return iput_data.arg >= forwarded; 342 }); 343 std::fill(kept_end, iputs + arraysize(iputs), ConstructorIPutData()); 344 // If we have any IPUTs from the call, check that the target method is in the same 345 // dex file (compare DexCache references), otherwise field_indexes would be bogus. 346 if (iputs[0].field_index != DexFile::kDexNoIndex16 && 347 target_method->GetDexCache() != method->GetDexCache()) { 348 return false; 349 } 350 } 351 } else if (IsInstructionDirectConst(instruction.Opcode())) { 352 zero_vreg_mask |= GetZeroVRegMask(&instruction); 353 if ((zero_vreg_mask & (1u << this_vreg)) != 0u) { 354 return false; // Overwriting `this` is unsupported. 355 } 356 } else { 357 DCHECK(IsInstructionIPut(instruction.Opcode())); 358 DCHECK_EQ(instruction.VRegB_22c(), this_vreg); 359 if (!RecordConstructorIPut(method, &instruction, this_vreg, zero_vreg_mask, iputs)) { 360 return false; 361 } 362 } 363 } 364 return true; 365 } 366 367 } // anonymous namespace 368 369 bool AnalyseConstructor(const CodeItemDataAccessor* code_item, 370 ArtMethod* method, 371 InlineMethod* result) 372 REQUIRES_SHARED(Locks::mutator_lock_) { 373 ConstructorIPutData iputs[kMaxConstructorIPuts]; 374 if (!DoAnalyseConstructor(code_item, method, iputs)) { 375 return false; 376 } 377 static_assert(kMaxConstructorIPuts == 3, "Unexpected limit"); // Code below depends on this. 378 DCHECK(iputs[0].field_index != DexFile::kDexNoIndex16 || 379 iputs[1].field_index == DexFile::kDexNoIndex16); 380 DCHECK(iputs[1].field_index != DexFile::kDexNoIndex16 || 381 iputs[2].field_index == DexFile::kDexNoIndex16); 382 383 #define STORE_IPUT(n) \ 384 do { \ 385 result->d.constructor_data.iput##n##_field_index = iputs[n].field_index; \ 386 result->d.constructor_data.iput##n##_arg = iputs[n].arg; \ 387 } while (false) 388 389 STORE_IPUT(0); 390 STORE_IPUT(1); 391 STORE_IPUT(2); 392 #undef STORE_IPUT 393 394 result->opcode = kInlineOpConstructor; 395 result->d.constructor_data.reserved = 0u; 396 return true; 397 } 398 399 static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET), "iget type"); 400 static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE), "iget_wide type"); 401 static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT), 402 "iget_object type"); 403 static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN), 404 "iget_boolean type"); 405 static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE), "iget_byte type"); 406 static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR), "iget_char type"); 407 static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT), "iget_short type"); 408 static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT), "iput type"); 409 static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE), "iput_wide type"); 410 static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT), 411 "iput_object type"); 412 static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN), 413 "iput_boolean type"); 414 static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE), "iput_byte type"); 415 static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR), "iput_char type"); 416 static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT), "iput_short type"); 417 static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET) == 418 InlineMethodAnalyser::IPutVariant(Instruction::IPUT), "iget/iput variant"); 419 static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) == 420 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), "iget/iput_wide variant"); 421 static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) == 422 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), "iget/iput_object variant"); 423 static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) == 424 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), "iget/iput_boolean variant"); 425 static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) == 426 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), "iget/iput_byte variant"); 427 static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) == 428 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), "iget/iput_char variant"); 429 static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == 430 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant"); 431 432 bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) { 433 CodeItemDataAccessor code_item(method->DexInstructionData()); 434 if (!code_item.HasCodeItem()) { 435 // Native or abstract. 436 return false; 437 } 438 return AnalyseMethodCode(&code_item, 439 MethodReference(method->GetDexFile(), method->GetDexMethodIndex()), 440 method->IsStatic(), 441 method, 442 result); 443 } 444 445 bool InlineMethodAnalyser::AnalyseMethodCode(const CodeItemDataAccessor* code_item, 446 const MethodReference& method_ref, 447 bool is_static, 448 ArtMethod* method, 449 InlineMethod* result) { 450 // We currently support only plain return or 2-instruction methods. 451 452 DCHECK_NE(code_item->InsnsSizeInCodeUnits(), 0u); 453 Instruction::Code opcode = code_item->begin()->Opcode(); 454 455 switch (opcode) { 456 case Instruction::RETURN_VOID: 457 if (result != nullptr) { 458 result->opcode = kInlineOpNop; 459 result->d.data = 0u; 460 } 461 return true; 462 case Instruction::RETURN: 463 case Instruction::RETURN_OBJECT: 464 case Instruction::RETURN_WIDE: 465 return AnalyseReturnMethod(code_item, result); 466 case Instruction::CONST: 467 case Instruction::CONST_4: 468 case Instruction::CONST_16: 469 case Instruction::CONST_HIGH16: 470 // TODO: Support wide constants (RETURN_WIDE). 471 if (AnalyseConstMethod(code_item, result)) { 472 return true; 473 } 474 FALLTHROUGH_INTENDED; 475 case Instruction::CONST_WIDE: 476 case Instruction::CONST_WIDE_16: 477 case Instruction::CONST_WIDE_32: 478 case Instruction::CONST_WIDE_HIGH16: 479 case Instruction::INVOKE_DIRECT: 480 if (method != nullptr && !method->IsStatic() && method->IsConstructor()) { 481 return AnalyseConstructor(code_item, method, result); 482 } 483 return false; 484 case Instruction::IGET: 485 case Instruction::IGET_OBJECT: 486 case Instruction::IGET_BOOLEAN: 487 case Instruction::IGET_BYTE: 488 case Instruction::IGET_CHAR: 489 case Instruction::IGET_SHORT: 490 case Instruction::IGET_WIDE: 491 // TODO: Add handling for JIT. 492 // case Instruction::IGET_QUICK: 493 // case Instruction::IGET_WIDE_QUICK: 494 // case Instruction::IGET_OBJECT_QUICK: 495 return AnalyseIGetMethod(code_item, method_ref, is_static, method, result); 496 case Instruction::IPUT: 497 case Instruction::IPUT_OBJECT: 498 case Instruction::IPUT_BOOLEAN: 499 case Instruction::IPUT_BYTE: 500 case Instruction::IPUT_CHAR: 501 case Instruction::IPUT_SHORT: 502 case Instruction::IPUT_WIDE: 503 // TODO: Add handling for JIT. 504 // case Instruction::IPUT_QUICK: 505 // case Instruction::IPUT_WIDE_QUICK: 506 // case Instruction::IPUT_OBJECT_QUICK: 507 return AnalyseIPutMethod(code_item, method_ref, is_static, method, result); 508 default: 509 return false; 510 } 511 } 512 513 bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) { 514 const dex::MethodId& method_id = ref.dex_file->GetMethodId(ref.index); 515 const char* method_name = ref.dex_file->GetMethodName(method_id); 516 // javac names synthetic accessors "access$nnn", 517 // jack names them "-getN", "-putN", "-wrapN". 518 return strncmp(method_name, "access$", strlen("access$")) == 0 || 519 strncmp(method_name, "-", strlen("-")) == 0; 520 } 521 522 bool InlineMethodAnalyser::AnalyseReturnMethod(const CodeItemDataAccessor* code_item, 523 InlineMethod* result) { 524 DexInstructionIterator return_instruction = code_item->begin(); 525 Instruction::Code return_opcode = return_instruction->Opcode(); 526 uint32_t reg = return_instruction->VRegA_11x(); 527 uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize(); 528 DCHECK_GE(reg, arg_start); 529 DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg, 530 code_item->RegistersSize()); 531 532 if (result != nullptr) { 533 result->opcode = kInlineOpReturnArg; 534 InlineReturnArgData* data = &result->d.return_data; 535 data->arg = reg - arg_start; 536 data->is_wide = (return_opcode == Instruction::RETURN_WIDE) ? 1u : 0u; 537 data->is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u; 538 data->reserved = 0u; 539 data->reserved2 = 0u; 540 } 541 return true; 542 } 543 544 bool InlineMethodAnalyser::AnalyseConstMethod(const CodeItemDataAccessor* code_item, 545 InlineMethod* result) { 546 DexInstructionIterator instruction = code_item->begin(); 547 const Instruction* return_instruction = instruction->Next(); 548 Instruction::Code return_opcode = return_instruction->Opcode(); 549 if (return_opcode != Instruction::RETURN && 550 return_opcode != Instruction::RETURN_OBJECT) { 551 return false; 552 } 553 554 int32_t return_reg = return_instruction->VRegA_11x(); 555 DCHECK_LT(return_reg, code_item->RegistersSize()); 556 557 int32_t const_value = instruction->VRegB(); 558 if (instruction->Opcode() == Instruction::CONST_HIGH16) { 559 const_value <<= 16; 560 } 561 DCHECK_LT(instruction->VRegA(), code_item->RegistersSize()); 562 if (instruction->VRegA() != return_reg) { 563 return false; // Not returning the value set by const? 564 } 565 if (return_opcode == Instruction::RETURN_OBJECT && const_value != 0) { 566 return false; // Returning non-null reference constant? 567 } 568 if (result != nullptr) { 569 result->opcode = kInlineOpNonWideConst; 570 result->d.data = static_cast<uint64_t>(const_value); 571 } 572 return true; 573 } 574 575 bool InlineMethodAnalyser::AnalyseIGetMethod(const CodeItemDataAccessor* code_item, 576 const MethodReference& method_ref, 577 bool is_static, 578 ArtMethod* method, 579 InlineMethod* result) { 580 DexInstructionIterator instruction = code_item->begin(); 581 Instruction::Code opcode = instruction->Opcode(); 582 DCHECK(IsInstructionIGet(opcode)); 583 584 const Instruction* return_instruction = instruction->Next(); 585 Instruction::Code return_opcode = return_instruction->Opcode(); 586 if (!(return_opcode == Instruction::RETURN_WIDE && opcode == Instruction::IGET_WIDE) && 587 !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT) && 588 !(return_opcode == Instruction::RETURN && opcode != Instruction::IGET_WIDE && 589 opcode != Instruction::IGET_OBJECT)) { 590 return false; 591 } 592 593 uint32_t return_reg = return_instruction->VRegA_11x(); 594 DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg, 595 code_item->RegistersSize()); 596 597 uint32_t dst_reg = instruction->VRegA_22c(); 598 uint32_t object_reg = instruction->VRegB_22c(); 599 uint32_t field_idx = instruction->VRegC_22c(); 600 uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize(); 601 DCHECK_GE(object_reg, arg_start); 602 DCHECK_LT(object_reg, code_item->RegistersSize()); 603 uint32_t object_arg = object_reg - arg_start; 604 605 DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->RegistersSize()); 606 if (dst_reg != return_reg) { 607 return false; // Not returning the value retrieved by IGET? 608 } 609 610 if (is_static || object_arg != 0u) { 611 // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE). 612 // Allow synthetic accessors. We don't care about losing their stack frame in NPE. 613 if (!IsSyntheticAccessor(method_ref)) { 614 return false; 615 } 616 } 617 618 // InlineIGetIPutData::object_arg is only 4 bits wide. 619 static constexpr uint16_t kMaxObjectArg = 15u; 620 if (object_arg > kMaxObjectArg) { 621 return false; 622 } 623 624 if (result != nullptr) { 625 InlineIGetIPutData* data = &result->d.ifield_data; 626 if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) { 627 return false; 628 } 629 result->opcode = kInlineOpIGet; 630 data->op_variant = IGetVariant(opcode); 631 data->method_is_static = is_static ? 1u : 0u; 632 data->object_arg = object_arg; // Allow IGET on any register, not just "this". 633 data->src_arg = 0u; 634 data->return_arg_plus1 = 0u; 635 } 636 return true; 637 } 638 639 bool InlineMethodAnalyser::AnalyseIPutMethod(const CodeItemDataAccessor* code_item, 640 const MethodReference& method_ref, 641 bool is_static, 642 ArtMethod* method, 643 InlineMethod* result) { 644 DexInstructionIterator instruction = code_item->begin(); 645 Instruction::Code opcode = instruction->Opcode(); 646 DCHECK(IsInstructionIPut(opcode)); 647 648 const Instruction* return_instruction = instruction->Next(); 649 Instruction::Code return_opcode = return_instruction->Opcode(); 650 uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize(); 651 uint16_t return_arg_plus1 = 0u; 652 if (return_opcode != Instruction::RETURN_VOID) { 653 if (return_opcode != Instruction::RETURN && 654 return_opcode != Instruction::RETURN_OBJECT && 655 return_opcode != Instruction::RETURN_WIDE) { 656 return false; 657 } 658 // Returning an argument. 659 uint32_t return_reg = return_instruction->VRegA_11x(); 660 DCHECK_GE(return_reg, arg_start); 661 DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg, 662 code_item->RegistersSize()); 663 return_arg_plus1 = return_reg - arg_start + 1u; 664 } 665 666 uint32_t src_reg = instruction->VRegA_22c(); 667 uint32_t object_reg = instruction->VRegB_22c(); 668 uint32_t field_idx = instruction->VRegC_22c(); 669 DCHECK_GE(object_reg, arg_start); 670 DCHECK_LT(object_reg, code_item->RegistersSize()); 671 DCHECK_GE(src_reg, arg_start); 672 DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->RegistersSize()); 673 uint32_t object_arg = object_reg - arg_start; 674 uint32_t src_arg = src_reg - arg_start; 675 676 if (is_static || object_arg != 0u) { 677 // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE). 678 // Allow synthetic accessors. We don't care about losing their stack frame in NPE. 679 if (!IsSyntheticAccessor(method_ref)) { 680 return false; 681 } 682 } 683 684 // InlineIGetIPutData::object_arg/src_arg/return_arg_plus1 are each only 4 bits wide. 685 static constexpr uint16_t kMaxObjectArg = 15u; 686 static constexpr uint16_t kMaxSrcArg = 15u; 687 static constexpr uint16_t kMaxReturnArgPlus1 = 15u; 688 if (object_arg > kMaxObjectArg || src_arg > kMaxSrcArg || return_arg_plus1 > kMaxReturnArgPlus1) { 689 return false; 690 } 691 692 if (result != nullptr) { 693 InlineIGetIPutData* data = &result->d.ifield_data; 694 if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) { 695 return false; 696 } 697 result->opcode = kInlineOpIPut; 698 data->op_variant = IPutVariant(opcode); 699 data->method_is_static = is_static ? 1u : 0u; 700 data->object_arg = object_arg; // Allow IPUT on any register, not just "this". 701 data->src_arg = src_arg; 702 data->return_arg_plus1 = return_arg_plus1; 703 } 704 return true; 705 } 706 707 bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, 708 uint32_t field_idx, 709 bool is_put, 710 InlineIGetIPutData* result) { 711 if (method == nullptr) { 712 return false; 713 } 714 ObjPtr<mirror::DexCache> dex_cache = method->GetDexCache(); 715 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 716 ArtField* field = class_linker->LookupResolvedField(field_idx, method, /* is_static= */ false); 717 if (field == nullptr || field->IsStatic()) { 718 return false; 719 } 720 ObjPtr<mirror::Class> method_class = method->GetDeclaringClass(); 721 ObjPtr<mirror::Class> field_class = field->GetDeclaringClass(); 722 if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) || 723 (is_put && field->IsFinal() && method_class != field_class)) { 724 return false; 725 } 726 DCHECK_GE(field->GetOffset().Int32Value(), 0); 727 // Historical note: We made sure not to interleave function calls with bit field writes to 728 // placate Valgrind. Bug: 27552451. 729 uint32_t field_offset = field->GetOffset().Uint32Value(); 730 bool is_volatile = field->IsVolatile(); 731 result->field_idx = field_idx; 732 result->field_offset = field_offset; 733 result->is_volatile = is_volatile ? 1u : 0u; 734 return true; 735 } 736 737 } // namespace art 738