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