• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
SortByOrder(const SafeMap<T,U> & usage,Order order)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>
operator <<(std::ostream & os,const std::pair<A,B> & pair)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>
MakeUsageMap(const ArrayType<T,Args...> & array)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>
PrintMostUsed(std::ostream & os,const Map<T,U,Args...> & usage,size_t max_count,std::function<void (std::ostream & os,T)> printer=[](std::ostream & os,T v){})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 
operator <<(std::ostream & os,const std::vector<uint8_t> & bytes)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 
ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>> & dex_files)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 
Dump(std::ostream & os,uint64_t total_size) const167 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 
ProcessCodeItem(const DexFile & dex_file,const CodeItemDataAccessor & code_item,dex::TypeIndex current_class_type,bool count_types,std::map<size_t,TypeLinkage> & types)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 
Add(Instruction::Code opcode,const Instruction & inst)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 
ExtendPrefix(uint32_t * value1,uint32_t * value2)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 
InstNibbles(uint8_t opcode,const std::vector<uint32_t> & args)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