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. 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 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 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 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 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 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 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