blob: 4388d31698f06b174caef98f7b64cc60457b980d [file] [log] [blame]
Vladimir Markoe3e02602014-03-12 15:42:41 +00001/*
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
36namespace art {
37
38COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET),
39 check_iget_type);
40COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_WIDE),
41 check_iget_wide_type);
42COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_OBJECT),
43 check_iget_object_type);
44COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BOOLEAN),
45 check_iget_boolean_type);
46COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_BYTE),
47 check_iget_byte_type);
48COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_CHAR),
49 check_iget_char_type);
50COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIGet(Instruction::IGET_SHORT),
51 check_iget_short_type);
52
53COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT),
54 check_iput_type);
55COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_WIDE),
56 check_iput_wide_type);
57COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_OBJECT),
58 check_iput_object_type);
59COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BOOLEAN),
60 check_iput_boolean_type);
61COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_BYTE),
62 check_iput_byte_type);
63COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_CHAR),
64 check_iput_char_type);
65COMPILE_ASSERT(InlineMethodAnalyser::IsInstructionIPut(Instruction::IPUT_SHORT),
66 check_iput_short_type);
67
68COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET) ==
69 InlineMethodAnalyser::IPutVariant(Instruction::IPUT), check_iget_iput_variant);
70COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_WIDE) ==
71 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_WIDE), check_iget_iput_wide_variant);
72COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_OBJECT) ==
73 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_OBJECT), check_iget_iput_object_variant);
74COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BOOLEAN) ==
75 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BOOLEAN), check_iget_iput_boolean_variant);
76COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_BYTE) ==
77 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_BYTE), check_iget_iput_byte_variant);
78COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_CHAR) ==
79 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_CHAR), check_iget_iput_char_variant);
80COMPILE_ASSERT(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
81 InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), check_iget_iput_short_variant);
82
83bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
84 InlineMethod* method) {
85 // We currently support only plain return or 2-instruction methods.
86
87 const DexFile::CodeItem* code_item = verifier->CodeItem();
88 DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
89 const Instruction* instruction = Instruction::At(code_item->insns_);
90 Instruction::Code opcode = instruction->Opcode();
91
92 switch (opcode) {
93 case Instruction::RETURN_VOID:
94 method->opcode = kInlineOpNop;
95 method->flags = kInlineSpecial;
96 method->d.data = 0u;
97 return true;
98 case Instruction::RETURN:
99 case Instruction::RETURN_OBJECT:
100 case Instruction::RETURN_WIDE:
101 return AnalyseReturnMethod(code_item, method);
102 case Instruction::CONST:
103 case Instruction::CONST_4:
104 case Instruction::CONST_16:
105 case Instruction::CONST_HIGH16:
106 // TODO: Support wide constants (RETURN_WIDE).
107 return AnalyseConstMethod(code_item, method);
108 case Instruction::IGET:
109 case Instruction::IGET_OBJECT:
110 case Instruction::IGET_BOOLEAN:
111 case Instruction::IGET_BYTE:
112 case Instruction::IGET_CHAR:
113 case Instruction::IGET_SHORT:
114 case Instruction::IGET_WIDE:
115 return AnalyseIGetMethod(verifier, method);
116 case Instruction::IPUT:
117 case Instruction::IPUT_OBJECT:
118 case Instruction::IPUT_BOOLEAN:
119 case Instruction::IPUT_BYTE:
120 case Instruction::IPUT_CHAR:
121 case Instruction::IPUT_SHORT:
122 case Instruction::IPUT_WIDE:
123 return AnalyseIPutMethod(verifier, method);
124 default:
125 return false;
126 }
127}
128
129bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
130 InlineMethod* result) {
131 const Instruction* return_instruction = Instruction::At(code_item->insns_);
132 Instruction::Code return_opcode = return_instruction->Opcode();
133 uint32_t reg = return_instruction->VRegA_11x();
134 uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
135 DCHECK_GE(reg, arg_start);
136 DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg,
137 code_item->registers_size_);
138
139 result->opcode = kInlineOpReturnArg;
140 result->flags = kInlineSpecial;
141 InlineReturnArgData* data = &result->d.return_data;
142 data->arg = reg - arg_start;
143 data->is_wide = (return_opcode == Instruction::RETURN_WIDE) ? 1u : 0u;
144 data->is_object = (return_opcode == Instruction::RETURN_OBJECT) ? 1u : 0u;
145 data->reserved = 0u;
146 data->reserved2 = 0u;
147 return true;
148}
149
150bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item,
151 InlineMethod* result) {
152 const Instruction* instruction = Instruction::At(code_item->insns_);
153 const Instruction* return_instruction = instruction->Next();
154 Instruction::Code return_opcode = return_instruction->Opcode();
155 if (return_opcode != Instruction::RETURN &&
156 return_opcode != Instruction::RETURN_OBJECT) {
157 return false;
158 }
159
160 uint32_t return_reg = return_instruction->VRegA_11x();
161 DCHECK_LT(return_reg, code_item->registers_size_);
162
163 uint32_t vA, vB, dummy;
164 uint64_t dummy_wide;
165 instruction->Decode(vA, vB, dummy_wide, dummy, nullptr);
166 if (instruction->Opcode() == Instruction::CONST_HIGH16) {
167 vB <<= 16;
168 }
169 DCHECK_LT(vA, code_item->registers_size_);
170 if (vA != return_reg) {
171 return false; // Not returning the value set by const?
172 }
173 if (return_opcode == Instruction::RETURN_OBJECT && vB != 0) {
174 return false; // Returning non-null reference constant?
175 }
176 result->opcode = kInlineOpNonWideConst;
177 result->flags = kInlineSpecial;
178 result->d.data = static_cast<uint64_t>(vB);
179 return true;
180}
181
182bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
183 InlineMethod* result) {
184 const DexFile::CodeItem* code_item = verifier->CodeItem();
185 const Instruction* instruction = Instruction::At(code_item->insns_);
186 Instruction::Code opcode = instruction->Opcode();
187 DCHECK(IsInstructionIGet(opcode));
188
189 const Instruction* return_instruction = instruction->Next();
190 Instruction::Code return_opcode = return_instruction->Opcode();
191 if (!(return_opcode == Instruction::RETURN_WIDE && opcode == Instruction::IGET_WIDE) &&
192 !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT) &&
193 !(return_opcode == Instruction::RETURN && opcode != Instruction::IGET_WIDE &&
194 opcode != Instruction::IGET_OBJECT)) {
195 return false;
196 }
197
198 uint32_t return_reg = return_instruction->VRegA_11x();
199 DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
200 code_item->registers_size_);
201
202 uint32_t dst_reg = instruction->VRegA_22c();
203 uint32_t object_reg = instruction->VRegB_22c();
204 uint32_t field_idx = instruction->VRegC_22c();
205 uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
206 DCHECK_GE(object_reg, arg_start);
207 DCHECK_LT(object_reg, code_item->registers_size_);
208 DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_);
209 if (dst_reg != return_reg) {
210 return false; // Not returning the value retrieved by IGET?
211 }
212
213 if ((verifier->GetAccessFlags() & kAccStatic) != 0 || object_reg != arg_start) {
214 // TODO: Support inlining IGET on other register than "this".
215 return false;
216 }
217
218 if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, &result->d.ifield_data)) {
219 return false;
220 }
221
222 result->opcode = kInlineOpIGet;
223 result->flags = kInlineSpecial;
224 InlineIGetIPutData* data = &result->d.ifield_data;
225 data->op_variant = IGetVariant(opcode);
226 data->object_arg = object_reg - arg_start; // Allow IGET on any register, not just "this".
227 data->src_arg = 0;
228 data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0;
229 data->reserved = 0;
230 return true;
231}
232
233bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
234 InlineMethod* result) {
235 const DexFile::CodeItem* code_item = verifier->CodeItem();
236 const Instruction* instruction = Instruction::At(code_item->insns_);
237 Instruction::Code opcode = instruction->Opcode();
238 DCHECK(IsInstructionIPut(opcode));
239
240 const Instruction* return_instruction = instruction->Next();
241 Instruction::Code return_opcode = return_instruction->Opcode();
242 if (return_opcode != Instruction::RETURN_VOID) {
243 // TODO: Support returning an argument.
244 // This is needed by builder classes and generated accessor setters.
245 // builder.setX(value): iput value, this, fieldX; return-object this;
246 // object.access$nnn(value): iput value, this, fieldX; return value;
247 // Use InlineIGetIPutData::reserved to hold the information.
248 return false;
249 }
250
251 uint32_t src_reg = instruction->VRegA_22c();
252 uint32_t object_reg = instruction->VRegB_22c();
253 uint32_t field_idx = instruction->VRegC_22c();
254 uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
255 DCHECK_GE(object_reg, arg_start);
256 DCHECK_LT(object_reg, code_item->registers_size_);
257 DCHECK_GE(src_reg, arg_start);
258 DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_);
259
260 if ((verifier->GetAccessFlags() & kAccStatic) != 0 || object_reg != arg_start) {
261 // TODO: Support inlining IPUT on other register than "this".
262 return false;
263 }
264
265 if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, &result->d.ifield_data)) {
266 return false;
267 }
268
269 result->opcode = kInlineOpIPut;
270 result->flags = kInlineSpecial;
271 InlineIGetIPutData* data = &result->d.ifield_data;
272 data->op_variant = IPutVariant(opcode);
273 data->object_arg = object_reg - arg_start; // Allow IPUT on any register, not just "this".
274 data->src_arg = src_reg - arg_start;
275 data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0;
276 data->reserved = 0;
277 return true;
278}
279
280bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
281 verifier::MethodVerifier* verifier,
282 InlineIGetIPutData* result) {
283 mirror::DexCache* dex_cache = verifier->GetDexCache();
284 uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
285 mirror::ArtMethod* method = dex_cache->GetResolvedMethod(method_idx);
286 mirror::ArtField* field = dex_cache->GetResolvedField(field_idx);
287 if (method == nullptr || field == nullptr || field->IsStatic()) {
288 return false;
289 }
290 mirror::Class* method_class = method->GetDeclaringClass();
291 mirror::Class* field_class = field->GetDeclaringClass();
292 if (!method_class->CanAccessResolvedField(field_class, field, dex_cache, field_idx) ||
293 (is_put && field->IsFinal() && method_class != field_class)) {
294 return false;
295 }
296 DCHECK_GE(field->GetOffset().Int32Value(), 0);
297 result->field_idx = field_idx;
298 result->field_offset = field->GetOffset().Int32Value();
299 result->is_volatile = field->IsVolatile();
300 return true;
301}
302
303} // namespace art