• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "dex_instruction.h"
19 #include "dex_instruction-inl.h"
20 #include "mirror/art_field.h"
21 #include "mirror/art_field-inl.h"
22 #include "mirror/art_method.h"
23 #include "mirror/art_method-inl.h"
24 #include "mirror/class.h"
25 #include "mirror/class-inl.h"
26 #include "mirror/dex_cache.h"
27 #include "mirror/dex_cache-inl.h"
28 #include "verifier/method_verifier.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 
36 namespace art {
37 
38 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET),
39                check_iget_type);
40 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE),
41                check_iget_wide_type);
42 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
43                check_iget_object_type);
44 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN),
45                check_iget_boolean_type);
46 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE),
47                check_iget_byte_type);
48 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR),
49                check_iget_char_type);
50 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT),
51                check_iget_short_type);
52 
53 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT),
54                check_iput_type);
55 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE),
56                check_iput_wide_type);
57 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT),
58                check_iput_object_type);
59 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN),
60                check_iput_boolean_type);
61 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE),
62                check_iput_byte_type);
63 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR),
64                check_iput_char_type);
65 COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT),
66                check_iput_short_type);
67 
68 COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET) ==
69     InlineMethodAnalyser::IPutVariant(Instruction::IPUT), check_iget_iput_variant);
70 COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) ==
71     InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), check_iget_iput_wide_variant);
72 COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) ==
73     InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), check_iget_iput_object_variant);
74 COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) ==
75     InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), check_iget_iput_boolean_variant);
76 COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) ==
77     InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), check_iget_iput_byte_variant);
78 COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) ==
79     InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), check_iget_iput_char_variant);
80 COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
81     InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), check_iget_iput_short_variant);
82 
83 // This is used by compiler and debugger. We look into the dex cache for resolved methods and
84 // fields. However, in the context of the debugger, not all methods and fields are resolved. Since
85 // we need to be able to detect possibly inlined method, we pass a null inline method to indicate
86 // we don't want to take unresolved methods and fields into account during analysis.
AnalyseMethodCode(verifier::MethodVerifier * verifier,InlineMethod * method)87 bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
88                                              InlineMethod* method) {
89   DCHECK(verifier != nullptr);
90   DCHECK_EQ(Runtime::Current()->IsCompiler(), method != nullptr);
91   DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
92   // We currently support only plain return or 2-instruction methods.
93 
94   const DexFile::CodeItem* code_item = verifier->CodeItem();
95   DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
96   const Instruction* instruction = Instruction::At(code_item->insns_);
97   Instruction::Code opcode = instruction->Opcode();
98 
99   switch (opcode) {
100     case Instruction::RETURN_VOID:
101       if (method != nullptr) {
102         method->opcode = kInlineOpNop;
103         method->flags = kInlineSpecial;
104         method->d.data = 0u;
105       }
106       return true;
107     case Instruction::RETURN:
108     case Instruction::RETURN_OBJECT:
109     case Instruction::RETURN_WIDE:
110       return AnalyseReturnMethod(code_item, method);
111     case Instruction::CONST:
112     case Instruction::CONST_4:
113     case Instruction::CONST_16:
114     case Instruction::CONST_HIGH16:
115       // TODO: Support wide constants (RETURN_WIDE).
116       return AnalyseConstMethod(code_item, method);
117     case Instruction::IGET:
118     case Instruction::IGET_OBJECT:
119     case Instruction::IGET_BOOLEAN:
120     case Instruction::IGET_BYTE:
121     case Instruction::IGET_CHAR:
122     case Instruction::IGET_SHORT:
123     case Instruction::IGET_WIDE:
124       return AnalyseIGetMethod(verifier, method);
125     case Instruction::IPUT:
126     case Instruction::IPUT_OBJECT:
127     case Instruction::IPUT_BOOLEAN:
128     case Instruction::IPUT_BYTE:
129     case Instruction::IPUT_CHAR:
130     case Instruction::IPUT_SHORT:
131     case Instruction::IPUT_WIDE:
132       return AnalyseIPutMethod(verifier, method);
133     default:
134       return false;
135   }
136 }
137 
IsSyntheticAccessor(MethodReference ref)138 bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) {
139   const DexFile::MethodId& method_id = ref.dex_file->GetMethodId(ref.dex_method_index);
140   const char* method_name = ref.dex_file->GetMethodName(method_id);
141   return strncmp(method_name, "access$", strlen("access$")) == 0;
142 }
143 
AnalyseReturnMethod(const DexFile::CodeItem * code_item,InlineMethod * result)144 bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
145                                                InlineMethod* result) {
146   const Instruction* return_instruction = Instruction::At(code_item->insns_);
147   Instruction::Code return_opcode = return_instruction->Opcode();
148   uint32_t reg = return_instruction->VRegA_11x();
149   uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
150   DCHECK_GE(reg, arg_start);
151   DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg,
152       code_item->registers_size_);
153 
154   if (result != nullptr) {
155     result->opcode = kInlineOpReturnArg;
156     result->flags = kInlineSpecial;
157     InlineReturnArgData* data = &result->d.return_data;
158     data->arg = reg - arg_start;
159     data->is_wide = (return_opcode == Instruction::RETURN_WIDE) ? 1u : 0u;
160     data->is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u;
161     data->reserved = 0u;
162     data->reserved2 = 0u;
163   }
164   return true;
165 }
166 
AnalyseConstMethod(const DexFile::CodeItem * code_item,InlineMethod * result)167 bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item,
168                                               InlineMethod* result) {
169   const Instruction* instruction = Instruction::At(code_item->insns_);
170   const Instruction* return_instruction = instruction->Next();
171   Instruction::Code return_opcode = return_instruction->Opcode();
172   if (return_opcode != Instruction::RETURN &&
173       return_opcode != Instruction::RETURN_OBJECT) {
174     return false;
175   }
176 
177   int32_t return_reg = return_instruction->VRegA_11x();
178   DCHECK_LT(return_reg, code_item->registers_size_);
179 
180   int32_t const_value = instruction->VRegB();
181   if (instruction->Opcode() == Instruction::CONST_HIGH16) {
182     const_value <<= 16;
183   }
184   DCHECK_LT(instruction->VRegA(), code_item->registers_size_);
185   if (instruction->VRegA() != return_reg) {
186     return false;  // Not returning the value set by const?
187   }
188   if (return_opcode == Instruction::RETURN_OBJECT && const_value != 0) {
189     return false;  // Returning non-null reference constant?
190   }
191   if (result != nullptr) {
192     result->opcode = kInlineOpNonWideConst;
193     result->flags = kInlineSpecial;
194     result->d.data = static_cast<uint64_t>(const_value);
195   }
196   return true;
197 }
198 
AnalyseIGetMethod(verifier::MethodVerifier * verifier,InlineMethod * result)199 bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
200                                              InlineMethod* result) {
201   const DexFile::CodeItem* code_item = verifier->CodeItem();
202   const Instruction* instruction = Instruction::At(code_item->insns_);
203   Instruction::Code opcode = instruction->Opcode();
204   DCHECK(IsInstructionIGet(opcode));
205 
206   const Instruction* return_instruction = instruction->Next();
207   Instruction::Code return_opcode = return_instruction->Opcode();
208   if (!(return_opcode == Instruction::RETURN_WIDE && opcode == Instruction::IGET_WIDE) &&
209       !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT) &&
210       !(return_opcode == Instruction::RETURN && opcode != Instruction::IGET_WIDE &&
211           opcode != Instruction::IGET_OBJECT)) {
212     return false;
213   }
214 
215   uint32_t return_reg = return_instruction->VRegA_11x();
216   DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
217             code_item->registers_size_);
218 
219   uint32_t dst_reg = instruction->VRegA_22c();
220   uint32_t object_reg = instruction->VRegB_22c();
221   uint32_t field_idx = instruction->VRegC_22c();
222   uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
223   DCHECK_GE(object_reg, arg_start);
224   DCHECK_LT(object_reg, code_item->registers_size_);
225   uint32_t object_arg = object_reg - arg_start;
226 
227   DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_);
228   if (dst_reg != return_reg) {
229     return false;  // Not returning the value retrieved by IGET?
230   }
231 
232   if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
233     // TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
234     // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
235     if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
236       return false;
237     }
238   }
239 
240   // InlineIGetIPutData::object_arg is only 4 bits wide.
241   static constexpr uint16_t kMaxObjectArg = 15u;
242   if (object_arg > kMaxObjectArg) {
243     return false;
244   }
245 
246   if (result != nullptr) {
247     InlineIGetIPutData* data = &result->d.ifield_data;
248     if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, data)) {
249       return false;
250     }
251     result->opcode = kInlineOpIGet;
252     result->flags = kInlineSpecial;
253     data->op_variant = IGetVariant(opcode);
254     data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
255     data->object_arg = object_arg;  // Allow IGET on any register, not just "this".
256     data->src_arg = 0u;
257     data->return_arg_plus1 = 0u;
258   }
259   return true;
260 }
261 
AnalyseIPutMethod(verifier::MethodVerifier * verifier,InlineMethod * result)262 bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
263                                              InlineMethod* result) {
264   const DexFile::CodeItem* code_item = verifier->CodeItem();
265   const Instruction* instruction = Instruction::At(code_item->insns_);
266   Instruction::Code opcode = instruction->Opcode();
267   DCHECK(IsInstructionIPut(opcode));
268 
269   const Instruction* return_instruction = instruction->Next();
270   Instruction::Code return_opcode = return_instruction->Opcode();
271   uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
272   uint16_t return_arg_plus1 = 0u;
273   if (return_opcode != Instruction::RETURN_VOID) {
274     if (return_opcode != Instruction::RETURN &&
275         return_opcode != Instruction::RETURN_OBJECT &&
276         return_opcode != Instruction::RETURN_WIDE) {
277       return false;
278     }
279     // Returning an argument.
280     uint32_t return_reg = return_instruction->VRegA_11x();
281     DCHECK_GE(return_reg, arg_start);
282     DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg,
283               code_item->registers_size_);
284     return_arg_plus1 = return_reg - arg_start + 1u;
285   }
286 
287   uint32_t src_reg = instruction->VRegA_22c();
288   uint32_t object_reg = instruction->VRegB_22c();
289   uint32_t field_idx = instruction->VRegC_22c();
290   DCHECK_GE(object_reg, arg_start);
291   DCHECK_LT(object_reg, code_item->registers_size_);
292   DCHECK_GE(src_reg, arg_start);
293   DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_);
294   uint32_t object_arg = object_reg - arg_start;
295   uint32_t src_arg = src_reg - arg_start;
296 
297   if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
298     // TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
299     // Allow synthetic accessors. We don't care about losing their stack frame in NPE.
300     if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
301       return false;
302     }
303   }
304 
305   // InlineIGetIPutData::object_arg/src_arg/return_arg_plus1 are each only 4 bits wide.
306   static constexpr uint16_t kMaxObjectArg = 15u;
307   static constexpr uint16_t kMaxSrcArg = 15u;
308   static constexpr uint16_t kMaxReturnArgPlus1 = 15u;
309   if (object_arg > kMaxObjectArg || src_arg > kMaxSrcArg || return_arg_plus1 > kMaxReturnArgPlus1) {
310     return false;
311   }
312 
313   if (result != nullptr) {
314     InlineIGetIPutData* data = &result->d.ifield_data;
315     if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, data)) {
316       return false;
317     }
318     result->opcode = kInlineOpIPut;
319     result->flags = kInlineSpecial;
320     data->op_variant = IPutVariant(opcode);
321     data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
322     data->object_arg = object_arg;  // Allow IPUT on any register, not just "this".
323     data->src_arg = src_arg;
324     data->return_arg_plus1 = return_arg_plus1;
325   }
326   return true;
327 }
328 
ComputeSpecialAccessorInfo(uint32_t field_idx,bool is_put,verifier::MethodVerifier * verifier,InlineIGetIPutData * result)329 bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
330                                                       verifier::MethodVerifier* verifier,
331                                                       InlineIGetIPutData* result) {
332   mirror::DexCache* dex_cache = verifier->GetDexCache();
333   uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
334   mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx);
335   mirror::ArtField* field = dex_cache->GetResolvedField(field_idx);
336   if (method == nullptr || field == nullptr || field->IsStatic()) {
337     return false;
338   }
339   mirror::Class* method_class = method->GetDeclaringClass();
340   mirror::Class* field_class = field->GetDeclaringClass();
341   if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) ||
342       (is_put && field->IsFinal() && method_class != field_class)) {
343     return false;
344   }
345   DCHECK_GE(field->GetOffset().Int32Value(), 0);
346   result->field_idx = field_idx;
347   result->field_offset = field->GetOffset().Int32Value();
348   result->is_volatile = field->IsVolatile();
349   return true;
350 }
351 
352 }  // namespace art
353