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/pointer_size.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 HIDDEN {
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:
Matcher(const CodeItemDataAccessor * code_item)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>
Match(const CodeItemDataAccessor * code_item,MatchFn * const (& pattern)[size])79 bool Matcher::Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]) {
80   return DoMatch(code_item, pattern, size);
81 }
82 
Mark(Matcher * matcher)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)()>
Required(Matcher * matcher)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)()>
Repeated(Matcher * matcher)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>
Opcode()112 bool Matcher::Opcode() {
113   return instruction_->Opcode() == opcode;
114 }
115 
116 // Match const 0.
Const0()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 
IPutOnThis()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 
DoMatch(const CodeItemDataAccessor * code_item,MatchFn * const * pattern,size_t size)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".
GetTargetConstructor(ArtMethod * method,const Instruction * invoke_direct)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(method->GetDeclaringClass()->IsSubClass(target_method->GetDeclaringClass()));
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).
CountForwardedConstructorArguments(const CodeItemDataAccessor * code_item,const Instruction * invoke_direct,uint16_t zero_vreg_mask)162 size_t CountForwardedConstructorArguments(const CodeItemDataAccessor* 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->RegistersSize() - code_item->InsSize());  // 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 
GetZeroVRegMask(const Instruction * const0)186 uint16_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.
200 static constexpr size_t kMaxConstructorIPuts = 3u;
201 
202 struct ConstructorIPutData {
ConstructorIPutDataart::__anonab18f0a00111::ConstructorIPutData203   ConstructorIPutData() : field_index(DexFile::kDexNoIndex16), arg(0u) { }
204 
205   uint16_t field_index;
206   uint16_t arg;
207 };
208 
RecordConstructorIPut(ArtMethod * method,const Instruction * new_iput,uint16_t this_vreg,uint16_t zero_vreg_mask,ConstructorIPutData (& iputs)[kMaxConstructorIPuts])209 bool 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 
DoAnalyseConstructor(const CodeItemDataAccessor * code_item,ArtMethod * method,ConstructorIPutData (& iputs)[kMaxConstructorIPuts])253 bool DoAnalyseConstructor(const CodeItemDataAccessor* 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(std::all_of(
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->InsnsSizeInCodeUnits() > kMaxCodeUnits ||
297       code_item->RegistersSize() > 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->RegistersSize() - code_item->InsSize();
304   uint16_t zero_vreg_mask = 0u;
305 
306   for (const DexInstructionPcPair& pair : *code_item) {
307     const Instruction& instruction = pair.Inst();
308     if (instruction.Opcode() == Instruction::RETURN_VOID) {
309       break;
310     } else if (instruction.Opcode() == Instruction::INVOKE_DIRECT) {
311       ArtMethod* target_method = GetTargetConstructor(method, &instruction);
312       if (target_method == nullptr) {
313         return false;
314       }
315       // We allow forwarding constructors only if they pass more arguments
316       // to prevent infinite recursion.
317       if (target_method->GetDeclaringClass() == method->GetDeclaringClass() &&
318           instruction.VRegA_35c() <= code_item->InsSize()) {
319         return false;
320       }
321       size_t forwarded = CountForwardedConstructorArguments(code_item, &instruction, zero_vreg_mask);
322       if (forwarded == static_cast<size_t>(-1)) {
323         return false;
324       }
325       if (target_method->GetDeclaringClass()->IsObjectClass()) {
326         DCHECK_EQ(target_method->DexInstructionData().begin()->Opcode(), Instruction::RETURN_VOID);
327       } else {
328         CodeItemDataAccessor target_code_item(target_method->DexInstructionData());
329         if (!target_code_item.HasCodeItem()) {
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 
AnalyseConstructor(const CodeItemDataAccessor * code_item,ArtMethod * method,InlineMethod * result)368 bool AnalyseConstructor(const CodeItemDataAccessor* 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_IMPLIES(iputs[0].field_index == DexFile::kDexNoIndex16,
378                  iputs[1].field_index == DexFile::kDexNoIndex16);
379   DCHECK_IMPLIES(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->d.constructor_data.reserved = 0u;
395   return true;
396 }
397 
398 static_assert(IsInstructionIGet(Instruction::IGET));
399 static_assert(IsInstructionIGet(Instruction::IGET_WIDE));
400 static_assert(IsInstructionIGet(Instruction::IGET_OBJECT));
401 static_assert(IsInstructionIGet(Instruction::IGET_BOOLEAN));
402 static_assert(IsInstructionIGet(Instruction::IGET_BYTE));
403 static_assert(IsInstructionIGet(Instruction::IGET_CHAR));
404 static_assert(IsInstructionIGet(Instruction::IGET_SHORT));
405 static_assert(IsInstructionIPut(Instruction::IPUT));
406 static_assert(IsInstructionIPut(Instruction::IPUT_WIDE));
407 static_assert(IsInstructionIPut(Instruction::IPUT_OBJECT));
408 static_assert(IsInstructionIPut(Instruction::IPUT_BOOLEAN));
409 static_assert(IsInstructionIPut(Instruction::IPUT_BYTE));
410 static_assert(IsInstructionIPut(Instruction::IPUT_CHAR));
411 static_assert(IsInstructionIPut(Instruction::IPUT_SHORT));
412 static_assert(IGetMemAccessType(Instruction::IGET) == IPutMemAccessType(Instruction::IPUT));
413 static_assert(
414     IGetMemAccessType(Instruction::IGET_WIDE) == IPutMemAccessType(Instruction::IPUT_WIDE));
415 static_assert(
416     IGetMemAccessType(Instruction::IGET_OBJECT) == IPutMemAccessType(Instruction::IPUT_OBJECT));
417 static_assert(
418     IGetMemAccessType(Instruction::IGET_BOOLEAN) == IPutMemAccessType(Instruction::IPUT_BOOLEAN));
419 static_assert(
420     IGetMemAccessType(Instruction::IGET_BYTE) == IPutMemAccessType(Instruction::IPUT_BYTE));
421 static_assert(
422     IGetMemAccessType(Instruction::IGET_CHAR) == IPutMemAccessType(Instruction::IPUT_CHAR));
423 static_assert(
424     IGetMemAccessType(Instruction::IGET_SHORT) == IPutMemAccessType(Instruction::IPUT_SHORT));
425 
AnalyseMethodCode(ArtMethod * method,const CodeItemDataAccessor * code_item,InlineMethod * result)426 bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method,
427                                              const CodeItemDataAccessor* code_item,
428                                              InlineMethod* result) {
429   // We currently support only plain return or 2-instruction methods.
430 
431   DCHECK_NE(code_item->InsnsSizeInCodeUnits(), 0u);
432   Instruction::Code opcode = code_item->begin()->Opcode();
433 
434   switch (opcode) {
435     case Instruction::RETURN_VOID:
436       if (result != nullptr) {
437         result->opcode = kInlineOpNop;
438         result->d.data = 0u;
439       }
440       return true;
441     case Instruction::RETURN:
442     case Instruction::RETURN_OBJECT:
443     case Instruction::RETURN_WIDE:
444       return AnalyseReturnMethod(code_item, result);
445     case Instruction::CONST:
446     case Instruction::CONST_4:
447     case Instruction::CONST_16:
448     case Instruction::CONST_HIGH16:
449       // TODO: Support wide constants (RETURN_WIDE).
450       if (AnalyseConstMethod(code_item, result)) {
451         return true;
452       }
453       FALLTHROUGH_INTENDED;
454     case Instruction::CONST_WIDE:
455     case Instruction::CONST_WIDE_16:
456     case Instruction::CONST_WIDE_32:
457     case Instruction::CONST_WIDE_HIGH16:
458     case Instruction::INVOKE_DIRECT:
459       if (method != nullptr && !method->IsStatic() && method->IsConstructor()) {
460         return AnalyseConstructor(code_item, method, result);
461       }
462       return false;
463     case Instruction::IGET:
464     case Instruction::IGET_OBJECT:
465     case Instruction::IGET_BOOLEAN:
466     case Instruction::IGET_BYTE:
467     case Instruction::IGET_CHAR:
468     case Instruction::IGET_SHORT:
469     case Instruction::IGET_WIDE:
470       return AnalyseIGetMethod(method, code_item, result);
471     case Instruction::IPUT:
472     case Instruction::IPUT_OBJECT:
473     case Instruction::IPUT_BOOLEAN:
474     case Instruction::IPUT_BYTE:
475     case Instruction::IPUT_CHAR:
476     case Instruction::IPUT_SHORT:
477     case Instruction::IPUT_WIDE:
478       return AnalyseIPutMethod(method, code_item, result);
479     default:
480       return false;
481   }
482 }
483 
IsSyntheticAccessor(ArtMethod * method)484 bool InlineMethodAnalyser::IsSyntheticAccessor(ArtMethod* method) {
485   const DexFile* dex_file = method->GetDexFile();
486   const dex::MethodId& method_id = dex_file->GetMethodId(method->GetDexMethodIndex());
487   const char* method_name = dex_file->GetMethodName(method_id);
488   // javac names synthetic accessors "access$nnn",
489   // jack names them "-getN", "-putN", "-wrapN".
490   return strncmp(method_name, "access$", strlen("access$")) == 0 ||
491       strncmp(method_name, "-", strlen("-")) == 0;
492 }
493 
AnalyseReturnMethod(const CodeItemDataAccessor * code_item,InlineMethod * result)494 bool InlineMethodAnalyser::AnalyseReturnMethod(const CodeItemDataAccessor* code_item,
495                                                InlineMethod* result) {
496   DexInstructionIterator return_instruction = code_item->begin();
497   Instruction::Code return_opcode = return_instruction->Opcode();
498   uint32_t reg = return_instruction->VRegA_11x();
499   uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
500   DCHECK_GE(reg, arg_start);
501   DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg,
502       code_item->RegistersSize());
503 
504   if (result != nullptr) {
505     result->opcode = kInlineOpReturnArg;
506     InlineReturnArgData* data = &result->d.return_data;
507     data->arg = reg - arg_start;
508     data->is_wide = (return_opcode == Instruction::RETURN_WIDE) ? 1u : 0u;
509     data->is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u;
510     data->reserved = 0u;
511     data->reserved2 = 0u;
512   }
513   return true;
514 }
515 
AnalyseConstMethod(const CodeItemDataAccessor * code_item,InlineMethod * result)516 bool InlineMethodAnalyser::AnalyseConstMethod(const CodeItemDataAccessor* code_item,
517                                               InlineMethod* result) {
518   DexInstructionIterator instruction = code_item->begin();
519   const Instruction* return_instruction = instruction->Next();
520   Instruction::Code return_opcode = return_instruction->Opcode();
521   if (return_opcode != Instruction::RETURN &&
522       return_opcode != Instruction::RETURN_OBJECT) {
523     return false;
524   }
525 
526   int32_t return_reg = return_instruction->VRegA_11x();
527   DCHECK_LT(return_reg, code_item->RegistersSize());
528 
529   int32_t const_value = instruction->VRegB();
530   if (instruction->Opcode() == Instruction::CONST_HIGH16) {
531     const_value <<= 16;
532   }
533   DCHECK_LT(instruction->VRegA(), code_item->RegistersSize());
534   if (instruction->VRegA() != return_reg) {
535     return false;  // Not returning the value set by const?
536   }
537   if (return_opcode == Instruction::RETURN_OBJECT && const_value != 0) {
538     return false;  // Returning non-null reference constant?
539   }
540   if (result != nullptr) {
541     result->opcode = kInlineOpNonWideConst;
542     result->d.data = static_cast<uint64_t>(const_value);
543   }
544   return true;
545 }
546 
AnalyseIGetMethod(ArtMethod * method,const CodeItemDataAccessor * code_item,InlineMethod * result)547 bool InlineMethodAnalyser::AnalyseIGetMethod(ArtMethod* method,
548                                              const CodeItemDataAccessor* code_item,
549                                              InlineMethod* result) {
550   DexInstructionIterator instruction = code_item->begin();
551   Instruction::Code opcode = instruction->Opcode();
552   DCHECK(IsInstructionIGet(opcode));
553 
554   const Instruction* return_instruction = instruction->Next();
555   Instruction::Code return_opcode = return_instruction->Opcode();
556   if (!(return_opcode == Instruction::RETURN_WIDE && opcode == Instruction::IGET_WIDE) &&
557       !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT) &&
558       !(return_opcode == Instruction::RETURN && opcode != Instruction::IGET_WIDE &&
559           opcode != Instruction::IGET_OBJECT)) {
560     return false;
561   }
562 
563   uint32_t return_reg = return_instruction->VRegA_11x();
564   DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
565             code_item->RegistersSize());
566 
567   uint32_t dst_reg = instruction->VRegA_22c();
568   uint32_t object_reg = instruction->VRegB_22c();
569   uint32_t field_idx = instruction->VRegC_22c();
570   uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
571   DCHECK_GE(object_reg, arg_start);
572   DCHECK_LT(object_reg, code_item->RegistersSize());
573   uint32_t object_arg = object_reg - arg_start;
574 
575   DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->RegistersSize());
576   if (dst_reg != return_reg) {
577     return false;  // Not returning the value retrieved by IGET?
578   }
579 
580   // InlineIGetIPutData::object_arg is only 4 bits wide.
581   static constexpr uint16_t kMaxObjectArg = 15u;
582   if (object_arg > kMaxObjectArg) {
583     return false;
584   }
585 
586   bool is_static = method->IsStatic();
587   if (is_static || object_arg != 0u) {
588     // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
589     // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
590     if (!IsSyntheticAccessor(method)) {
591       return false;
592     }
593   }
594 
595   DCHECK(result != nullptr);
596   InlineIGetIPutData* data = &result->d.ifield_data;
597   if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
598     return false;
599   }
600   result->opcode = kInlineOpIGet;
601   data->op_variant = enum_cast<uint16_t>(IGetMemAccessType(opcode));
602   data->method_is_static = is_static ? 1u : 0u;
603   data->object_arg = object_arg;  // Allow IGET on any register, not just "this".
604   data->src_arg = 0u;
605   data->return_arg_plus1 = 0u;
606   return true;
607 }
608 
AnalyseIPutMethod(ArtMethod * method,const CodeItemDataAccessor * code_item,InlineMethod * result)609 bool InlineMethodAnalyser::AnalyseIPutMethod(ArtMethod* method,
610                                              const CodeItemDataAccessor* code_item,
611                                              InlineMethod* result) {
612   DexInstructionIterator instruction = code_item->begin();
613   Instruction::Code opcode = instruction->Opcode();
614   DCHECK(IsInstructionIPut(opcode));
615 
616   const Instruction* return_instruction = instruction->Next();
617   Instruction::Code return_opcode = return_instruction->Opcode();
618   uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
619   uint16_t return_arg_plus1 = 0u;
620   if (return_opcode != Instruction::RETURN_VOID) {
621     if (return_opcode != Instruction::RETURN &&
622         return_opcode != Instruction::RETURN_OBJECT &&
623         return_opcode != Instruction::RETURN_WIDE) {
624       return false;
625     }
626     // Returning an argument.
627     uint32_t return_reg = return_instruction->VRegA_11x();
628     DCHECK_GE(return_reg, arg_start);
629     DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg,
630               code_item->RegistersSize());
631     return_arg_plus1 = return_reg - arg_start + 1u;
632   }
633 
634   uint32_t src_reg = instruction->VRegA_22c();
635   uint32_t object_reg = instruction->VRegB_22c();
636   uint32_t field_idx = instruction->VRegC_22c();
637   DCHECK_GE(object_reg, arg_start);
638   DCHECK_LT(object_reg, code_item->RegistersSize());
639   DCHECK_GE(src_reg, arg_start);
640   DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->RegistersSize());
641   uint32_t object_arg = object_reg - arg_start;
642   uint32_t src_arg = src_reg - arg_start;
643 
644   // InlineIGetIPutData::object_arg/src_arg/return_arg_plus1 are each only 4 bits wide.
645   static constexpr uint16_t kMaxObjectArg = 15u;
646   static constexpr uint16_t kMaxSrcArg = 15u;
647   static constexpr uint16_t kMaxReturnArgPlus1 = 15u;
648   if (object_arg > kMaxObjectArg || src_arg > kMaxSrcArg || return_arg_plus1 > kMaxReturnArgPlus1) {
649     return false;
650   }
651 
652   bool is_static = method->IsStatic();
653   if (is_static || object_arg != 0u) {
654     // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
655     // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
656     if (!IsSyntheticAccessor(method)) {
657       return false;
658     }
659   }
660 
661   DCHECK(result != nullptr);
662   InlineIGetIPutData* data = &result->d.ifield_data;
663   if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
664     return false;
665   }
666   result->opcode = kInlineOpIPut;
667   data->op_variant = enum_cast<uint16_t>(IPutMemAccessType(opcode));
668   data->method_is_static = is_static ? 1u : 0u;
669   data->object_arg = object_arg;  // Allow IPUT on any register, not just "this".
670   data->src_arg = src_arg;
671   data->return_arg_plus1 = return_arg_plus1;
672   return true;
673 }
674 
ComputeSpecialAccessorInfo(ArtMethod * method,uint32_t field_idx,bool is_put,InlineIGetIPutData * result)675 bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method,
676                                                       uint32_t field_idx,
677                                                       bool is_put,
678                                                       InlineIGetIPutData* result) {
679   if (method == nullptr) {
680     return false;
681   }
682   ObjPtr<mirror::DexCache> dex_cache = method->GetDexCache();
683   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
684   ArtField* field = class_linker->LookupResolvedField(field_idx, method, /* is_static= */ false);
685   if (field == nullptr || field->IsStatic()) {
686     return false;
687   }
688   ObjPtr<mirror::Class> method_class = method->GetDeclaringClass();
689   ObjPtr<mirror::Class> field_class = field->GetDeclaringClass();
690   if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) ||
691       (is_put && field->IsFinal() && method_class != field_class)) {
692     return false;
693   }
694   DCHECK_GE(field->GetOffset().Int32Value(), 0);
695   // Historical note: We made sure not to interleave function calls with bit field writes to
696   // placate Valgrind. Bug: 27552451.
697   uint32_t field_offset = field->GetOffset().Uint32Value();
698   bool is_volatile = field->IsVolatile();
699   result->field_idx = field_idx;
700   result->field_offset = field_offset;
701   result->is_volatile = is_volatile ? 1u : 0u;
702   return true;
703 }
704 
705 }  // namespace art
706