inline_method_analyser.cc revision b95c74b9788886b24b04db782ce64867cb4960d5
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_file-inl.h" 24#include "dex_instruction.h" 25#include "dex_instruction-inl.h" 26#include "dex_instruction_utils.h" 27#include "mirror/class-inl.h" 28#include "mirror/dex_cache-inl.h" 29#include "verifier/method_verifier-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 36namespace art { 37 38namespace { // anonymous namespace 39 40// Helper class for matching a pattern. 41class Matcher { 42 public: 43 // Match function type. 44 typedef bool MatchFn(Matcher* matcher); 45 46 template <size_t size> 47 static bool Match(const DexFile::CodeItem* 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 DexFile::CodeItem* code_item) 67 : code_item_(code_item), 68 instruction_(Instruction::At(code_item->insns_)), 69 pos_(0u), 70 mark_(0u) { } 71 72 static bool DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size); 73 74 const DexFile::CodeItem* const code_item_; 75 const Instruction* instruction_; 76 size_t pos_; 77 size_t mark_; 78}; 79 80template <size_t size> 81bool Matcher::Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]) { 82 return DoMatch(code_item, pattern, size); 83} 84 85bool Matcher::Mark(Matcher* matcher) { 86 matcher->pos_ += 1u; // Advance to the next match function before marking. 87 matcher->mark_ = matcher->pos_; 88 return true; 89} 90 91template <bool (Matcher::*Fn)()> 92bool Matcher::Required(Matcher* matcher) { 93 if (!(matcher->*Fn)()) { 94 return false; 95 } 96 matcher->pos_ += 1u; 97 matcher->instruction_ = matcher->instruction_->Next(); 98 return true; 99} 100 101template <bool (Matcher::*Fn)()> 102bool Matcher::Repeated(Matcher* matcher) { 103 if (!(matcher->*Fn)()) { 104 // Didn't match optional instruction, try the next match function. 105 matcher->pos_ += 1u; 106 return true; 107 } 108 matcher->pos_ = matcher->mark_; 109 matcher->instruction_ = matcher->instruction_->Next(); 110 return true; 111} 112 113template <Instruction::Code opcode> 114bool Matcher::Opcode() { 115 return instruction_->Opcode() == opcode; 116} 117 118// Match const 0. 119bool Matcher::Const0() { 120 return IsInstructionDirectConst(instruction_->Opcode()) && 121 (instruction_->Opcode() == Instruction::CONST_WIDE ? instruction_->VRegB_51l() == 0 122 : instruction_->VRegB() == 0); 123} 124 125bool Matcher::IPutOnThis() { 126 DCHECK_NE(code_item_->ins_size_, 0u); 127 return IsInstructionIPut(instruction_->Opcode()) && 128 instruction_->VRegB_22c() == code_item_->registers_size_ - code_item_->ins_size_; 129} 130 131bool Matcher::DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size) { 132 Matcher matcher(code_item); 133 while (matcher.pos_ != size) { 134 if (!pattern[matcher.pos_](&matcher)) { 135 return false; 136 } 137 } 138 return true; 139} 140 141// Used for a single invoke in a constructor. In that situation, the method verifier makes 142// sure we invoke a constructor either in the same class or superclass with at least "this". 143ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_direct) 144 REQUIRES_SHARED(Locks::mutator_lock_) { 145 DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT); 146 DCHECK_EQ(invoke_direct->VRegC_35c(), 147 method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_); 148 uint32_t method_index = invoke_direct->VRegB_35c(); 149 PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); 150 ArtMethod* target_method = 151 method->GetDexCache()->GetResolvedMethod(method_index, pointer_size); 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). 163size_t CountForwardedConstructorArguments(const DexFile::CodeItem* 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->registers_size_ - code_item->ins_size_); // 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 187uint16_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. 201static constexpr size_t kMaxConstructorIPuts = 3u; 202 203struct ConstructorIPutData { 204 ConstructorIPutData() : field_index(DexFile::kDexNoIndex16), arg(0u) { } 205 206 uint16_t field_index; 207 uint16_t arg; 208}; 209 210bool 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 254bool DoAnalyseConstructor(const DexFile::CodeItem* 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->insns_size_in_code_units_ > kMaxCodeUnits || 298 code_item->registers_size_ > 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->registers_size_ - code_item->ins_size_; 305 uint16_t zero_vreg_mask = 0u; 306 for (const Instruction* instruction = Instruction::At(code_item->insns_); 307 instruction->Opcode() != Instruction::RETURN_VOID; 308 instruction = instruction->Next()) { 309 if (instruction->Opcode() == Instruction::INVOKE_DIRECT) { 310 ArtMethod* target_method = GetTargetConstructor(method, instruction); 311 if (target_method == nullptr) { 312 return false; 313 } 314 // We allow forwarding constructors only if they pass more arguments 315 // to prevent infinite recursion. 316 if (target_method->GetDeclaringClass() == method->GetDeclaringClass() && 317 instruction->VRegA_35c() <= code_item->ins_size_) { 318 return false; 319 } 320 size_t forwarded = CountForwardedConstructorArguments(code_item, instruction, zero_vreg_mask); 321 if (forwarded == static_cast<size_t>(-1)) { 322 return false; 323 } 324 if (target_method->GetDeclaringClass()->IsObjectClass()) { 325 DCHECK_EQ(Instruction::At(target_method->GetCodeItem()->insns_)->Opcode(), 326 Instruction::RETURN_VOID); 327 } else { 328 const DexFile::CodeItem* target_code_item = target_method->GetCodeItem(); 329 if (target_code_item == nullptr) { 330 return false; // Native constructor? 331 } 332 if (!DoAnalyseConstructor(target_code_item, target_method, iputs)) { 333 return false; 334 } 335 // Prune IPUTs with zero input. 336 auto kept_end = std::remove_if( 337 iputs, 338 iputs + arraysize(iputs), 339 [forwarded](const ConstructorIPutData& iput_data) { 340 return iput_data.arg >= forwarded; 341 }); 342 std::fill(kept_end, iputs + arraysize(iputs), ConstructorIPutData()); 343 // If we have any IPUTs from the call, check that the target method is in the same 344 // dex file (compare DexCache references), otherwise field_indexes would be bogus. 345 if (iputs[0].field_index != DexFile::kDexNoIndex16 && 346 target_method->GetDexCache() != method->GetDexCache()) { 347 return false; 348 } 349 } 350 } else if (IsInstructionDirectConst(instruction->Opcode())) { 351 zero_vreg_mask |= GetZeroVRegMask(instruction); 352 if ((zero_vreg_mask & (1u << this_vreg)) != 0u) { 353 return false; // Overwriting `this` is unsupported. 354 } 355 } else { 356 DCHECK(IsInstructionIPut(instruction->Opcode())); 357 DCHECK_EQ(instruction->VRegB_22c(), this_vreg); 358 if (!RecordConstructorIPut(method, instruction, this_vreg, zero_vreg_mask, iputs)) { 359 return false; 360 } 361 } 362 } 363 return true; 364} 365 366} // anonymous namespace 367 368bool AnalyseConstructor(const DexFile::CodeItem* code_item, 369 ArtMethod* method, 370 InlineMethod* result) 371 REQUIRES_SHARED(Locks::mutator_lock_) { 372 ConstructorIPutData iputs[kMaxConstructorIPuts]; 373 if (!DoAnalyseConstructor(code_item, method, iputs)) { 374 return false; 375 } 376 static_assert(kMaxConstructorIPuts == 3, "Unexpected limit"); // Code below depends on this. 377 DCHECK(iputs[0].field_index != DexFile::kDexNoIndex16 || 378 iputs[1].field_index == DexFile::kDexNoIndex16); 379 DCHECK(iputs[1].field_index != DexFile::kDexNoIndex16 || 380 iputs[2].field_index == DexFile::kDexNoIndex16); 381 382#define STORE_IPUT(n) \ 383 do { \ 384 result->d.constructor_data.iput##n##_field_index = iputs[n].field_index; \ 385 result->d.constructor_data.iput##n##_arg = iputs[n].arg; \ 386 } while (false) 387 388 STORE_IPUT(0); 389 STORE_IPUT(1); 390 STORE_IPUT(2); 391#undef STORE_IPUT 392 393 result->opcode = kInlineOpConstructor; 394 result->flags = kInlineSpecial; 395 result->d.constructor_data.reserved = 0u; 396 return true; 397} 398 399static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET), "iget type"); 400static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE), "iget_wide type"); 401static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT), 402 "iget_object type"); 403static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN), 404 "iget_boolean type"); 405static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE), "iget_byte type"); 406static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR), "iget_char type"); 407static_assert(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT), "iget_short type"); 408static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT), "iput type"); 409static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE), "iput_wide type"); 410static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT), 411 "iput_object type"); 412static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN), 413 "iput_boolean type"); 414static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE), "iput_byte type"); 415static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR), "iput_char type"); 416static_assert(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT), "iput_short type"); 417static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET) == 418 InlineMethodAnalyser::IPutVariant(Instruction::IPUT), "iget/iput variant"); 419static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) == 420 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), "iget/iput_wide variant"); 421static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) == 422 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), "iget/iput_object variant"); 423static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) == 424 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), "iget/iput_boolean variant"); 425static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) == 426 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), "iget/iput_byte variant"); 427static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) == 428 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), "iget/iput_char variant"); 429static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == 430 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant"); 431 432// This is used by compiler and debugger. We look into the dex cache for resolved methods and 433// fields. However, in the context of the debugger, not all methods and fields are resolved. Since 434// we need to be able to detect possibly inlined method, we pass a null inline method to indicate 435// we don't want to take unresolved methods and fields into account during analysis. 436bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier, 437 InlineMethod* result) { 438 DCHECK(verifier != nullptr); 439 if (!Runtime::Current()->UseJitCompilation()) { 440 DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr); 441 } 442 443 // Note: verifier->GetMethod() may be null. 444 return AnalyseMethodCode(verifier->CodeItem(), 445 verifier->GetMethodReference(), 446 (verifier->GetAccessFlags() & kAccStatic) != 0u, 447 verifier->GetMethod(), 448 result); 449} 450 451bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) { 452 const DexFile::CodeItem* code_item = method->GetCodeItem(); 453 if (code_item == nullptr) { 454 // Native or abstract. 455 return false; 456 } 457 return AnalyseMethodCode( 458 code_item, method->ToMethodReference(), method->IsStatic(), method, result); 459} 460 461bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item, 462 const MethodReference& method_ref, 463 bool is_static, 464 ArtMethod* method, 465 InlineMethod* result) { 466 // We currently support only plain return or 2-instruction methods. 467 468 DCHECK_NE(code_item->insns_size_in_code_units_, 0u); 469 const Instruction* instruction = Instruction::At(code_item->insns_); 470 Instruction::Code opcode = instruction->Opcode(); 471 472 switch (opcode) { 473 case Instruction::RETURN_VOID: 474 if (result != nullptr) { 475 result->opcode = kInlineOpNop; 476 result->flags = kInlineSpecial; 477 result->d.data = 0u; 478 } 479 return true; 480 case Instruction::RETURN: 481 case Instruction::RETURN_OBJECT: 482 case Instruction::RETURN_WIDE: 483 return AnalyseReturnMethod(code_item, result); 484 case Instruction::CONST: 485 case Instruction::CONST_4: 486 case Instruction::CONST_16: 487 case Instruction::CONST_HIGH16: 488 // TODO: Support wide constants (RETURN_WIDE). 489 if (AnalyseConstMethod(code_item, result)) { 490 return true; 491 } 492 FALLTHROUGH_INTENDED; 493 case Instruction::CONST_WIDE: 494 case Instruction::CONST_WIDE_16: 495 case Instruction::CONST_WIDE_32: 496 case Instruction::CONST_WIDE_HIGH16: 497 case Instruction::INVOKE_DIRECT: 498 if (method != nullptr && !method->IsStatic() && method->IsConstructor()) { 499 return AnalyseConstructor(code_item, method, result); 500 } 501 return false; 502 case Instruction::IGET: 503 case Instruction::IGET_OBJECT: 504 case Instruction::IGET_BOOLEAN: 505 case Instruction::IGET_BYTE: 506 case Instruction::IGET_CHAR: 507 case Instruction::IGET_SHORT: 508 case Instruction::IGET_WIDE: 509 // TODO: Add handling for JIT. 510 // case Instruction::IGET_QUICK: 511 // case Instruction::IGET_WIDE_QUICK: 512 // case Instruction::IGET_OBJECT_QUICK: 513 return AnalyseIGetMethod(code_item, method_ref, is_static, method, result); 514 case Instruction::IPUT: 515 case Instruction::IPUT_OBJECT: 516 case Instruction::IPUT_BOOLEAN: 517 case Instruction::IPUT_BYTE: 518 case Instruction::IPUT_CHAR: 519 case Instruction::IPUT_SHORT: 520 case Instruction::IPUT_WIDE: 521 // TODO: Add handling for JIT. 522 // case Instruction::IPUT_QUICK: 523 // case Instruction::IPUT_WIDE_QUICK: 524 // case Instruction::IPUT_OBJECT_QUICK: 525 return AnalyseIPutMethod(code_item, method_ref, is_static, method, result); 526 default: 527 return false; 528 } 529} 530 531bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) { 532 const DexFile::MethodId& method_id = ref.dex_file->GetMethodId(ref.dex_method_index); 533 const char* method_name = ref.dex_file->GetMethodName(method_id); 534 // javac names synthetic accessors "access$nnn", 535 // jack names them "-getN", "-putN", "-wrapN". 536 return strncmp(method_name, "access$", strlen("access$")) == 0 || 537 strncmp(method_name, "-", strlen("-")) == 0; 538} 539 540bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item, 541 InlineMethod* result) { 542 const Instruction* return_instruction = Instruction::At(code_item->insns_); 543 Instruction::Code return_opcode = return_instruction->Opcode(); 544 uint32_t reg = return_instruction->VRegA_11x(); 545 uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; 546 DCHECK_GE(reg, arg_start); 547 DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg, 548 code_item->registers_size_); 549 550 if (result != nullptr) { 551 result->opcode = kInlineOpReturnArg; 552 result->flags = kInlineSpecial; 553 InlineReturnArgData* data = &result->d.return_data; 554 data->arg = reg - arg_start; 555 data->is_wide = (return_opcode == Instruction::RETURN_WIDE) ? 1u : 0u; 556 data->is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u; 557 data->reserved = 0u; 558 data->reserved2 = 0u; 559 } 560 return true; 561} 562 563bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item, 564 InlineMethod* result) { 565 const Instruction* instruction = Instruction::At(code_item->insns_); 566 const Instruction* return_instruction = instruction->Next(); 567 Instruction::Code return_opcode = return_instruction->Opcode(); 568 if (return_opcode != Instruction::RETURN && 569 return_opcode != Instruction::RETURN_OBJECT) { 570 return false; 571 } 572 573 int32_t return_reg = return_instruction->VRegA_11x(); 574 DCHECK_LT(return_reg, code_item->registers_size_); 575 576 int32_t const_value = instruction->VRegB(); 577 if (instruction->Opcode() == Instruction::CONST_HIGH16) { 578 const_value <<= 16; 579 } 580 DCHECK_LT(instruction->VRegA(), code_item->registers_size_); 581 if (instruction->VRegA() != return_reg) { 582 return false; // Not returning the value set by const? 583 } 584 if (return_opcode == Instruction::RETURN_OBJECT && const_value != 0) { 585 return false; // Returning non-null reference constant? 586 } 587 if (result != nullptr) { 588 result->opcode = kInlineOpNonWideConst; 589 result->flags = kInlineSpecial; 590 result->d.data = static_cast<uint64_t>(const_value); 591 } 592 return true; 593} 594 595bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item, 596 const MethodReference& method_ref, 597 bool is_static, 598 ArtMethod* method, 599 InlineMethod* result) { 600 const Instruction* instruction = Instruction::At(code_item->insns_); 601 Instruction::Code opcode = instruction->Opcode(); 602 DCHECK(IsInstructionIGet(opcode)); 603 604 const Instruction* return_instruction = instruction->Next(); 605 Instruction::Code return_opcode = return_instruction->Opcode(); 606 if (!(return_opcode == Instruction::RETURN_WIDE && opcode == Instruction::IGET_WIDE) && 607 !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT) && 608 !(return_opcode == Instruction::RETURN && opcode != Instruction::IGET_WIDE && 609 opcode != Instruction::IGET_OBJECT)) { 610 return false; 611 } 612 613 uint32_t return_reg = return_instruction->VRegA_11x(); 614 DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg, 615 code_item->registers_size_); 616 617 uint32_t dst_reg = instruction->VRegA_22c(); 618 uint32_t object_reg = instruction->VRegB_22c(); 619 uint32_t field_idx = instruction->VRegC_22c(); 620 uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; 621 DCHECK_GE(object_reg, arg_start); 622 DCHECK_LT(object_reg, code_item->registers_size_); 623 uint32_t object_arg = object_reg - arg_start; 624 625 DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_); 626 if (dst_reg != return_reg) { 627 return false; // Not returning the value retrieved by IGET? 628 } 629 630 if (is_static || object_arg != 0u) { 631 // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE). 632 // Allow synthetic accessors. We don't care about losing their stack frame in NPE. 633 if (!IsSyntheticAccessor(method_ref)) { 634 return false; 635 } 636 } 637 638 // InlineIGetIPutData::object_arg is only 4 bits wide. 639 static constexpr uint16_t kMaxObjectArg = 15u; 640 if (object_arg > kMaxObjectArg) { 641 return false; 642 } 643 644 if (result != nullptr) { 645 InlineIGetIPutData* data = &result->d.ifield_data; 646 if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) { 647 return false; 648 } 649 result->opcode = kInlineOpIGet; 650 result->flags = kInlineSpecial; 651 data->op_variant = IGetVariant(opcode); 652 data->method_is_static = is_static ? 1u : 0u; 653 data->object_arg = object_arg; // Allow IGET on any register, not just "this". 654 data->src_arg = 0u; 655 data->return_arg_plus1 = 0u; 656 } 657 return true; 658} 659 660bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item, 661 const MethodReference& method_ref, 662 bool is_static, 663 ArtMethod* method, 664 InlineMethod* result) { 665 const Instruction* instruction = Instruction::At(code_item->insns_); 666 Instruction::Code opcode = instruction->Opcode(); 667 DCHECK(IsInstructionIPut(opcode)); 668 669 const Instruction* return_instruction = instruction->Next(); 670 Instruction::Code return_opcode = return_instruction->Opcode(); 671 uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_; 672 uint16_t return_arg_plus1 = 0u; 673 if (return_opcode != Instruction::RETURN_VOID) { 674 if (return_opcode != Instruction::RETURN && 675 return_opcode != Instruction::RETURN_OBJECT && 676 return_opcode != Instruction::RETURN_WIDE) { 677 return false; 678 } 679 // Returning an argument. 680 uint32_t return_reg = return_instruction->VRegA_11x(); 681 DCHECK_GE(return_reg, arg_start); 682 DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg, 683 code_item->registers_size_); 684 return_arg_plus1 = return_reg - arg_start + 1u; 685 } 686 687 uint32_t src_reg = instruction->VRegA_22c(); 688 uint32_t object_reg = instruction->VRegB_22c(); 689 uint32_t field_idx = instruction->VRegC_22c(); 690 DCHECK_GE(object_reg, arg_start); 691 DCHECK_LT(object_reg, code_item->registers_size_); 692 DCHECK_GE(src_reg, arg_start); 693 DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_); 694 uint32_t object_arg = object_reg - arg_start; 695 uint32_t src_arg = src_reg - arg_start; 696 697 if (is_static || object_arg != 0u) { 698 // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE). 699 // Allow synthetic accessors. We don't care about losing their stack frame in NPE. 700 if (!IsSyntheticAccessor(method_ref)) { 701 return false; 702 } 703 } 704 705 // InlineIGetIPutData::object_arg/src_arg/return_arg_plus1 are each only 4 bits wide. 706 static constexpr uint16_t kMaxObjectArg = 15u; 707 static constexpr uint16_t kMaxSrcArg = 15u; 708 static constexpr uint16_t kMaxReturnArgPlus1 = 15u; 709 if (object_arg > kMaxObjectArg || src_arg > kMaxSrcArg || return_arg_plus1 > kMaxReturnArgPlus1) { 710 return false; 711 } 712 713 if (result != nullptr) { 714 InlineIGetIPutData* data = &result->d.ifield_data; 715 if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) { 716 return false; 717 } 718 result->opcode = kInlineOpIPut; 719 result->flags = kInlineSpecial; 720 data->op_variant = IPutVariant(opcode); 721 data->method_is_static = is_static ? 1u : 0u; 722 data->object_arg = object_arg; // Allow IPUT on any register, not just "this". 723 data->src_arg = src_arg; 724 data->return_arg_plus1 = return_arg_plus1; 725 } 726 return true; 727} 728 729bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, 730 uint32_t field_idx, 731 bool is_put, 732 InlineIGetIPutData* result) { 733 if (method == nullptr) { 734 return false; 735 } 736 ObjPtr<mirror::DexCache> dex_cache = method->GetDexCache(); 737 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 738 ArtField* field = class_linker->LookupResolvedField(field_idx, method, /* is_static */ false); 739 if (field == nullptr || field->IsStatic()) { 740 return false; 741 } 742 ObjPtr<mirror::Class> method_class = method->GetDeclaringClass(); 743 ObjPtr<mirror::Class> field_class = field->GetDeclaringClass(); 744 if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) || 745 (is_put && field->IsFinal() && method_class != field_class)) { 746 return false; 747 } 748 DCHECK_GE(field->GetOffset().Int32Value(), 0); 749 // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451. 750 uint32_t field_offset = field->GetOffset().Uint32Value(); 751 bool is_volatile = field->IsVolatile(); 752 result->field_idx = field_idx; 753 result->field_offset = field_offset; 754 result->is_volatile = is_volatile ? 1u : 0u; 755 return true; 756} 757 758} // namespace art 759