1 /* 2 * Copyright (C) 2018 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 "dexanalyze_bytecode.h" 18 19 #include <algorithm> 20 #include <iomanip> 21 #include <iostream> 22 23 #include "dex/class_accessor-inl.h" 24 #include "dex/code_item_accessors-inl.h" 25 #include "dex/dex_instruction-inl.h" 26 27 namespace art { 28 namespace dexanalyze { 29 30 // Given a map of <key, usage count>, sort by most used and assign index <key, index in most used> 31 enum class Order { 32 kMostUsed, 33 kNormal, 34 }; 35 36 template <typename T, typename U> 37 static inline SafeMap<T, U> SortByOrder(const SafeMap<T, U>& usage, Order order) { 38 std::vector<std::pair<U, T>> most_used; 39 for (const auto& pair : usage) { 40 most_used.emplace_back(pair.second, pair.first); 41 } 42 if (order == Order::kMostUsed) { 43 std::sort(most_used.rbegin(), most_used.rend()); 44 } 45 U current_index = 0u; 46 SafeMap<T, U> ret; 47 for (auto&& pair : most_used) { 48 CHECK(ret.emplace(pair.second, current_index++).second); 49 } 50 return ret; 51 } 52 53 template <typename A, typename B> 54 std::ostream& operator <<(std::ostream& os, const std::pair<A, B>& pair) { 55 return os << "{" << pair.first << ", " << pair.second << "}"; 56 } 57 58 template <typename T, typename... Args, template <typename...> class ArrayType> 59 SafeMap<size_t, T> MakeUsageMap(const ArrayType<T, Args...>& array) { 60 SafeMap<size_t, T> ret; 61 for (size_t i = 0; i < array.size(); ++i) { 62 if (array[i] > 0) { 63 ret.Put(i, array[i]); 64 } 65 } 66 return ret; 67 } 68 69 template <typename T, typename U, typename... Args, template <typename...> class Map> 70 void PrintMostUsed(std::ostream& os, 71 const Map<T, U, Args...>& usage, 72 size_t max_count, 73 std::function<void(std::ostream& os, T)> printer = 74 [](std::ostream& os, T v) { 75 os << v; 76 }) { 77 std::vector<std::pair<U, T>> sorted; 78 uint64_t total = 0u; 79 for (const auto& pair : usage) { 80 sorted.emplace_back(pair.second, pair.first); 81 total += pair.second; 82 } 83 std::sort(sorted.rbegin(), sorted.rend()); 84 uint64_t other = 0u; 85 for (auto&& pair : sorted) { 86 if (max_count > 0) { 87 os << Percent(pair.first, total) << " : "; 88 printer(os, pair.second); 89 os << "\n"; 90 --max_count; 91 } else { 92 other += pair.first; 93 } 94 } 95 if (other != 0u) { 96 os << "other: " << Percent(other, total) << "\n"; 97 } 98 } 99 100 static inline std::ostream& operator<<(std::ostream& os, const std::vector<uint8_t>& bytes) { 101 os << std::hex; 102 for (const uint8_t& c : bytes) { 103 os << std::setw(2) << std::setfill('0') << static_cast<uint32_t>(c) 104 << (&c != &bytes.back() ? " " : ""); 105 } 106 os << std::dec; 107 return os; 108 } 109 110 void NewRegisterInstructions::ProcessDexFiles( 111 const std::vector<std::unique_ptr<const DexFile>>& dex_files) { 112 std::set<std::vector<uint8_t>> deduped; 113 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { 114 std::map<size_t, TypeLinkage> types; 115 std::set<const void*> visited; 116 for (ClassAccessor accessor : dex_file->GetClasses()) { 117 for (const ClassAccessor::Method& method : accessor.GetMethods()) { 118 ProcessCodeItem(*dex_file, 119 method.GetInstructionsAndData(), 120 accessor.GetClassIdx(), 121 /*count_types=*/ true, 122 types); 123 } 124 } 125 // Reorder to get an index for each map instead of a count. 126 for (auto&& pair : types) { 127 pair.second.types_ = SortByOrder(pair.second.types_, Order::kMostUsed); 128 pair.second.fields_ = SortByOrder(pair.second.fields_, Order::kMostUsed); 129 pair.second.methods_ = SortByOrder(pair.second.methods_, Order::kMostUsed); 130 pair.second.strings_ = SortByOrder(pair.second.strings_, Order::kMostUsed); 131 } 132 // Visit classes and convert code items. 133 for (ClassAccessor accessor : dex_file->GetClasses()) { 134 for (const ClassAccessor::Method& method : accessor.GetMethods()) { 135 if (method.GetCodeItem() == nullptr || !visited.insert(method.GetCodeItem()).second) { 136 continue; 137 } 138 if (verbose_level_ >= VerboseLevel::kEverything) { 139 std::cout << std::endl 140 << "Processing " << dex_file->PrettyMethod(method.GetIndex(), true); 141 } 142 CodeItemDataAccessor data = method.GetInstructionsAndData(); 143 ProcessCodeItem(*dex_file, 144 data, 145 accessor.GetClassIdx(), 146 /*count_types=*/ false, 147 types); 148 std::vector<uint8_t> buffer = std::move(buffer_); 149 buffer_.clear(); 150 const size_t buffer_size = buffer.size(); 151 dex_code_bytes_ += data.InsnsSizeInBytes(); 152 output_size_ += buffer_size; 153 // Add extra data at the end to have fair dedupe. 154 EncodeUnsignedLeb128(&buffer, data.RegistersSize()); 155 EncodeUnsignedLeb128(&buffer, data.InsSize()); 156 EncodeUnsignedLeb128(&buffer, data.OutsSize()); 157 EncodeUnsignedLeb128(&buffer, data.TriesSize()); 158 EncodeUnsignedLeb128(&buffer, data.InsnsSizeInCodeUnits()); 159 if (deduped.insert(buffer).second) { 160 deduped_size_ += buffer_size; 161 } 162 } 163 } 164 } 165 } 166 167 void NewRegisterInstructions::Dump(std::ostream& os, uint64_t total_size) const { 168 os << "Enabled experiments " << experiments_ << std::endl; 169 os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n"; 170 os << "Total output code bytes: " << Percent(output_size_, total_size) << "\n"; 171 os << "Total deduped code bytes: " << Percent(deduped_size_, total_size) << "\n"; 172 std::vector<std::pair<size_t, std::vector<uint8_t>>> pairs; 173 for (auto&& pair : instruction_freq_) { 174 if (pair.second > 0 && !pair.first.empty()) { 175 // Savings exclude one byte per occurrence and one occurrence from having the macro 176 // dictionary. 177 pairs.emplace_back((pair.second - 1) * (pair.first.size() - 1), pair.first); 178 } 179 } 180 std::sort(pairs.rbegin(), pairs.rend()); 181 static constexpr size_t kMaxMacros = 128; 182 static constexpr size_t kMaxPrintedMacros = 32; 183 uint64_t top_instructions_savings = 0u; 184 for (size_t i = 0; i < kMaxMacros && i < pairs.size(); ++i) { 185 top_instructions_savings += pairs[i].first; 186 } 187 if (verbose_level_ >= VerboseLevel::kNormal) { 188 os << "Move result register distribution" << "\n"; 189 PrintMostUsed(os, MakeUsageMap(move_result_reg_), 16); 190 os << "First arg register usage\n"; 191 std::function<void(std::ostream& os, size_t)> printer = [&](std::ostream& os, size_t idx) { 192 os << Instruction::Name(static_cast<Instruction::Code>(idx)); 193 }; 194 PrintMostUsed(os, MakeUsageMap(first_arg_reg_count_), 16, printer); 195 os << "Most used field linkage pairs\n"; 196 PrintMostUsed(os, field_linkage_counts_, 32); 197 os << "Current extended " << extended_field_ << "\n"; 198 os << "Most used method linkage pairs\n"; 199 PrintMostUsed(os, method_linkage_counts_, 32); 200 os << "Current extended " << extended_method_ << "\n"; 201 os << "Top " << kMaxMacros << " instruction bytecode sizes and hex dump" << "\n"; 202 for (size_t i = 0; i < kMaxMacros && i < pairs.size(); ++i) { 203 auto bytes = pairs[i].second; 204 // Remove opcode bytes. 205 bytes.erase(bytes.begin()); 206 if (i < kMaxPrintedMacros) { 207 os << Percent(pairs[i].first, total_size) << " " 208 << Instruction::Name(static_cast<Instruction::Code>(pairs[i].second[0])) 209 << "(" << bytes << ")\n"; 210 } 211 } 212 } 213 os << "Top instructions 1b macro savings " 214 << Percent(top_instructions_savings, total_size) << "\n"; 215 } 216 217 void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file, 218 const CodeItemDataAccessor& code_item, 219 dex::TypeIndex current_class_type, 220 bool count_types, 221 std::map<size_t, TypeLinkage>& types) { 222 TypeLinkage& current_type = types[current_class_type.index_]; 223 bool skip_next = false; 224 for (auto inst = code_item.begin(); inst != code_item.end(); ++inst) { 225 if (verbose_level_ >= VerboseLevel::kEverything) { 226 std::cout << std::endl; 227 std::cout << inst->DumpString(nullptr); 228 if (skip_next) { 229 std::cout << " (SKIPPED)"; 230 } 231 } 232 if (skip_next) { 233 skip_next = false; 234 continue; 235 } 236 bool is_iget = false; 237 const Instruction::Code opcode = inst->Opcode(); 238 Instruction::Code new_opcode = opcode; 239 ++opcode_count_[opcode]; 240 switch (opcode) { 241 case Instruction::IGET: 242 case Instruction::IGET_WIDE: 243 case Instruction::IGET_OBJECT: 244 case Instruction::IGET_BOOLEAN: 245 case Instruction::IGET_BYTE: 246 case Instruction::IGET_CHAR: 247 case Instruction::IGET_SHORT: 248 is_iget = true; 249 FALLTHROUGH_INTENDED; 250 case Instruction::IPUT: 251 case Instruction::IPUT_WIDE: 252 case Instruction::IPUT_OBJECT: 253 case Instruction::IPUT_BOOLEAN: 254 case Instruction::IPUT_BYTE: 255 case Instruction::IPUT_CHAR: 256 case Instruction::IPUT_SHORT: { 257 const uint32_t dex_field_idx = inst->VRegC_22c(); 258 if (Enabled(kExperimentSingleGetSet)) { 259 // Test deduplication improvements from replacing all iget/set with the same opcode. 260 new_opcode = is_iget ? Instruction::IGET : Instruction::IPUT; 261 } 262 CHECK_LT(dex_field_idx, dex_file.NumFieldIds()); 263 dex::TypeIndex holder_type = dex_file.GetFieldId(dex_field_idx).class_idx_; 264 uint32_t receiver = inst->VRegB_22c(); 265 uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize(); 266 uint32_t out_reg = inst->VRegA_22c(); 267 if (Enabled(kExperimentInstanceFieldSelf) && 268 first_arg_reg == receiver && 269 holder_type == current_class_type) { 270 if (count_types) { 271 ++current_type.fields_.FindOrAdd(dex_field_idx)->second; 272 } else { 273 uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx); 274 ExtendPrefix(&out_reg, &field_idx); 275 CHECK(InstNibbles(new_opcode, {out_reg, field_idx})); 276 continue; 277 } 278 } else if (Enabled(kExperimentInstanceField)) { 279 if (count_types) { 280 ++current_type.types_.FindOrAdd(holder_type.index_)->second; 281 ++types[holder_type.index_].fields_.FindOrAdd(dex_field_idx)->second; 282 } else { 283 uint32_t type_idx = current_type.types_.Get(holder_type.index_); 284 uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx); 285 ExtendPrefix(&type_idx, &field_idx); 286 CHECK(InstNibbles(new_opcode, {out_reg, receiver, type_idx, field_idx})); 287 continue; 288 } 289 } 290 break; 291 } 292 case Instruction::CONST_STRING: 293 case Instruction::CONST_STRING_JUMBO: { 294 const bool is_jumbo = opcode == Instruction::CONST_STRING_JUMBO; 295 const uint16_t str_idx = is_jumbo ? inst->VRegB_31c() : inst->VRegB_21c(); 296 uint32_t out_reg = is_jumbo ? inst->VRegA_31c() : inst->VRegA_21c(); 297 if (Enabled(kExperimentString)) { 298 new_opcode = Instruction::CONST_STRING; 299 if (count_types) { 300 ++current_type.strings_.FindOrAdd(str_idx)->second; 301 } else { 302 uint32_t idx = current_type.strings_.Get(str_idx); 303 ExtendPrefix(&out_reg, &idx); 304 CHECK(InstNibbles(opcode, {out_reg, idx})); 305 continue; 306 } 307 } 308 break; 309 } 310 case Instruction::SGET: 311 case Instruction::SGET_WIDE: 312 case Instruction::SGET_OBJECT: 313 case Instruction::SGET_BOOLEAN: 314 case Instruction::SGET_BYTE: 315 case Instruction::SGET_CHAR: 316 case Instruction::SGET_SHORT: 317 case Instruction::SPUT: 318 case Instruction::SPUT_WIDE: 319 case Instruction::SPUT_OBJECT: 320 case Instruction::SPUT_BOOLEAN: 321 case Instruction::SPUT_BYTE: 322 case Instruction::SPUT_CHAR: 323 case Instruction::SPUT_SHORT: { 324 uint32_t out_reg = inst->VRegA_21c(); 325 const uint32_t dex_field_idx = inst->VRegB_21c(); 326 CHECK_LT(dex_field_idx, dex_file.NumFieldIds()); 327 dex::TypeIndex holder_type = dex_file.GetFieldId(dex_field_idx).class_idx_; 328 if (Enabled(kExperimentStaticField)) { 329 if (holder_type == current_class_type) { 330 if (count_types) { 331 ++types[holder_type.index_].fields_.FindOrAdd(dex_field_idx)->second; 332 } else { 333 uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx); 334 ExtendPrefix(&out_reg, &field_idx); 335 if (InstNibbles(new_opcode, {out_reg, field_idx})) { 336 continue; 337 } 338 } 339 } else { 340 if (count_types) { 341 ++types[current_class_type.index_].types_.FindOrAdd(holder_type.index_)->second; 342 ++types[holder_type.index_].fields_.FindOrAdd(dex_field_idx)->second; 343 } else { 344 uint32_t type_idx = current_type.types_.Get(holder_type.index_); 345 uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx); 346 ++field_linkage_counts_[std::make_pair(type_idx, field_idx)]; 347 extended_field_ += ExtendPrefix(&type_idx, &field_idx) ? 1u : 0u; 348 if (InstNibbles(new_opcode, {out_reg >> 4, out_reg & 0xF, type_idx, field_idx})) { 349 continue; 350 } 351 } 352 } 353 } 354 break; 355 } 356 // Invoke cases. 357 case Instruction::INVOKE_VIRTUAL: 358 case Instruction::INVOKE_DIRECT: 359 case Instruction::INVOKE_STATIC: 360 case Instruction::INVOKE_INTERFACE: 361 case Instruction::INVOKE_SUPER: { 362 const uint32_t method_idx = DexMethodIndex(inst.Inst()); 363 const dex::MethodId& method = dex_file.GetMethodId(method_idx); 364 const dex::TypeIndex receiver_type = method.class_idx_; 365 if (Enabled(kExperimentInvoke)) { 366 if (count_types) { 367 ++current_type.types_.FindOrAdd(receiver_type.index_)->second; 368 ++types[receiver_type.index_].methods_.FindOrAdd(method_idx)->second; 369 } else { 370 uint32_t args[6] = {}; 371 uint32_t arg_count = inst->GetVarArgs(args); 372 const uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize(); 373 374 bool next_move_result = false; 375 uint32_t dest_reg = 0; 376 auto next = std::next(inst); 377 if (next != code_item.end()) { 378 next_move_result = 379 next->Opcode() == Instruction::MOVE_RESULT || 380 next->Opcode() == Instruction::MOVE_RESULT_WIDE || 381 next->Opcode() == Instruction::MOVE_RESULT_OBJECT; 382 if (next_move_result) { 383 dest_reg = next->VRegA_11x(); 384 ++move_result_reg_[dest_reg]; 385 } 386 } 387 388 uint32_t type_idx = current_type.types_.Get(receiver_type.index_); 389 uint32_t local_idx = types[receiver_type.index_].methods_.Get(method_idx); 390 ++method_linkage_counts_[std::make_pair(type_idx, local_idx)]; 391 392 // If true, we always put the return value in r0. 393 static constexpr bool kMoveToDestReg = true; 394 395 std::vector<uint32_t> new_args; 396 if (kMoveToDestReg && arg_count % 2 == 1) { 397 // Use the extra nibble to sneak in part of the type index. 398 new_args.push_back(local_idx >> 4); 399 local_idx &= ~0xF0; 400 } 401 extended_method_ += ExtendPrefix(&type_idx, &local_idx) ? 1u : 0u; 402 new_args.push_back(type_idx); 403 new_args.push_back(local_idx); 404 if (!kMoveToDestReg) { 405 ExtendPrefix(&dest_reg, &local_idx); 406 new_args.push_back(dest_reg); 407 } 408 for (size_t i = 0; i < arg_count; ++i) { 409 if (args[i] == first_arg_reg) { 410 ++first_arg_reg_count_[opcode]; 411 break; 412 } 413 } 414 new_args.insert(new_args.end(), args, args + arg_count); 415 if (InstNibbles(opcode, new_args)) { 416 skip_next = next_move_result; 417 if (kMoveToDestReg && dest_reg != 0u) { 418 CHECK(InstNibbles(Instruction::MOVE, {dest_reg >> 4, dest_reg & 0xF})); 419 } 420 continue; 421 } 422 } 423 } 424 break; 425 } 426 case Instruction::IF_EQZ: 427 case Instruction::IF_NEZ: { 428 uint32_t reg = inst->VRegA_21t(); 429 int16_t offset = inst->VRegB_21t(); 430 if (!count_types && 431 Enabled(kExperimentSmallIf) && 432 InstNibbles(opcode, {reg, static_cast<uint16_t>(offset)})) { 433 continue; 434 } 435 break; 436 } 437 case Instruction::INSTANCE_OF: { 438 uint32_t type_idx = inst->VRegC_22c(); 439 uint32_t in_reg = inst->VRegB_22c(); 440 uint32_t out_reg = inst->VRegA_22c(); 441 if (count_types) { 442 ++current_type.types_.FindOrAdd(type_idx)->second; 443 } else { 444 uint32_t local_type = current_type.types_.Get(type_idx); 445 ExtendPrefix(&in_reg, &local_type); 446 CHECK(InstNibbles(new_opcode, {in_reg, out_reg, local_type})); 447 continue; 448 } 449 break; 450 } 451 case Instruction::NEW_ARRAY: { 452 uint32_t len_reg = inst->VRegB_22c(); 453 uint32_t type_idx = inst->VRegC_22c(); 454 uint32_t out_reg = inst->VRegA_22c(); 455 if (count_types) { 456 ++current_type.types_.FindOrAdd(type_idx)->second; 457 } else { 458 uint32_t local_type = current_type.types_.Get(type_idx); 459 ExtendPrefix(&out_reg, &local_type); 460 CHECK(InstNibbles(new_opcode, {len_reg, out_reg, local_type})); 461 continue; 462 } 463 break; 464 } 465 case Instruction::CONST_CLASS: 466 case Instruction::CHECK_CAST: 467 case Instruction::NEW_INSTANCE: { 468 uint32_t type_idx = inst->VRegB_21c(); 469 uint32_t out_reg = inst->VRegA_21c(); 470 if (Enabled(kExperimentLocalType)) { 471 if (count_types) { 472 ++current_type.types_.FindOrAdd(type_idx)->second; 473 } else { 474 bool next_is_init = false; 475 if (opcode == Instruction::NEW_INSTANCE) { 476 auto next = std::next(inst); 477 if (next != code_item.end() && next->Opcode() == Instruction::INVOKE_DIRECT) { 478 uint32_t args[6] = {}; 479 uint32_t arg_count = next->GetVarArgs(args); 480 uint32_t method_idx = DexMethodIndex(next.Inst()); 481 if (arg_count == 1u && 482 args[0] == out_reg && 483 dex_file.GetMethodName(dex_file.GetMethodId(method_idx)) == 484 std::string("<init>")) { 485 next_is_init = true; 486 } 487 } 488 } 489 uint32_t local_type = current_type.types_.Get(type_idx); 490 ExtendPrefix(&out_reg, &local_type); 491 CHECK(InstNibbles(opcode, {out_reg, local_type})); 492 skip_next = next_is_init; 493 continue; 494 } 495 } 496 break; 497 } 498 case Instruction::RETURN: 499 case Instruction::RETURN_OBJECT: 500 case Instruction::RETURN_WIDE: 501 case Instruction::RETURN_VOID: { 502 if (!count_types && Enabled(kExperimentReturn)) { 503 if (opcode == Instruction::RETURN_VOID || inst->VRegA_11x() == 0) { 504 if (InstNibbles(opcode, {})) { 505 continue; 506 } 507 } 508 } 509 break; 510 } 511 default: 512 break; 513 } 514 if (!count_types) { 515 Add(new_opcode, inst.Inst()); 516 } 517 } 518 if (verbose_level_ >= VerboseLevel::kEverything) { 519 std::cout << std::endl 520 << "Bytecode size " << code_item.InsnsSizeInBytes() << " -> " << buffer_.size(); 521 std::cout << std::endl; 522 } 523 } 524 525 void NewRegisterInstructions::Add(Instruction::Code opcode, const Instruction& inst) { 526 const uint8_t* start = reinterpret_cast<const uint8_t*>(&inst); 527 const size_t buffer_start = buffer_.size(); 528 buffer_.push_back(opcode); 529 buffer_.insert(buffer_.end(), start + 1, start + 2 * inst.SizeInCodeUnits()); 530 // Register the instruction blob. 531 ++instruction_freq_[std::vector<uint8_t>(buffer_.begin() + buffer_start, buffer_.end())]; 532 } 533 534 bool NewRegisterInstructions::ExtendPrefix(uint32_t* value1, uint32_t* value2) { 535 if (*value1 < 16 && *value2 < 16) { 536 return false; 537 } 538 if ((*value1 >> 4) == 1 && *value2 < 16) { 539 InstNibbles(0xE5, {}); 540 *value1 ^= 1u << 4; 541 return true; 542 } else if ((*value2 >> 4) == 1 && *value1 < 16) { 543 InstNibbles(0xE6, {}); 544 *value2 ^= 1u << 4; 545 return true; 546 } 547 if (*value1 < 256 && *value2 < 256) { 548 // Extend each value by 4 bits. 549 CHECK(InstNibbles(0xE3, {*value1 >> 4, *value2 >> 4})); 550 } else { 551 // Extend each value by 12 bits. 552 CHECK(InstNibbles(0xE4, { 553 (*value1 >> 12) & 0xF, 554 (*value1 >> 8) & 0xF, 555 (*value1 >> 4) & 0xF, 556 (*value2 >> 12) & 0xF, 557 (*value2 >> 8) & 0xF, 558 (*value2 >> 4) & 0xF})); 559 } 560 *value1 &= 0xF; 561 *value2 &= 0XF; 562 return true; 563 } 564 565 bool NewRegisterInstructions::InstNibbles(uint8_t opcode, const std::vector<uint32_t>& args) { 566 if (verbose_level_ >= VerboseLevel::kEverything) { 567 std::cout << " ==> " << Instruction::Name(static_cast<Instruction::Code>(opcode)) << " "; 568 for (int v : args) { 569 std::cout << v << ", "; 570 } 571 } 572 for (int v : args) { 573 if (v >= 16) { 574 if (verbose_level_ >= VerboseLevel::kEverything) { 575 std::cout << "(OUT_OF_RANGE)"; 576 } 577 return false; 578 } 579 } 580 const size_t buffer_start = buffer_.size(); 581 buffer_.push_back(opcode); 582 for (size_t i = 0; i < args.size(); i += 2) { 583 buffer_.push_back(args[i] << 4); 584 if (i + 1 < args.size()) { 585 buffer_.back() |= args[i + 1]; 586 } 587 } 588 while (buffer_.size() % alignment_ != 0) { 589 buffer_.push_back(0); 590 } 591 // Register the instruction blob. 592 ++instruction_freq_[std::vector<uint8_t>(buffer_.begin() + buffer_start, buffer_.end())]; 593 return true; 594 } 595 596 } // namespace dexanalyze 597 } // namespace art 598