Home | History | Annotate | Download | only in dexanalyze
      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