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