Analysis of field iget/iset on first arg register

Analyze how many instance field gets/sets are operating on the first
arg register and how many are using one of the first 16 instance
fields.

Test: test-art-host

Change-Id: I26c21f209ef6fcbed0b4a8446b3f226e56a75de3
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index 244f45b..5299c9e 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -16,6 +16,7 @@
 
 #include "dexanalyze_experiments.h"
 
+#include <algorithm>
 #include <stdint.h>
 #include <inttypes.h>
 #include <iostream>
@@ -289,10 +290,21 @@
   for (ClassAccessor accessor : dex_file.GetClasses()) {
     std::set<size_t> unique_method_ids;
     std::set<size_t> unique_string_ids;
+    // Types accessed and count.
+    std::map<size_t, size_t> types_accessed;
+
+    // Map from dex field index -> class field index.
+    std::map<uint32_t, uint32_t> field_index_map_;
+    size_t current_idx = 0u;
+    for (const ClassAccessor::Field& field : accessor.GetInstanceFields()) {
+      field_index_map_[field.GetIndex()] = current_idx++;
+    }
+
     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
-      dex_code_bytes_ += method.GetInstructions().InsnsSizeInBytes();
+      CodeItemDataAccessor code_item(dex_file, method.GetCodeItem());
+      dex_code_bytes_ += code_item.InsnsSizeInBytes();
       unique_code_items.insert(method.GetCodeItemOffset());
-      for (const DexInstructionPcPair& inst : method.GetInstructions()) {
+      for (const DexInstructionPcPair& inst : code_item) {
         switch (inst->Opcode()) {
           case Instruction::CONST_STRING: {
             const dex::StringIndex string_index(inst->VRegB_21c());
@@ -300,6 +312,36 @@
             ++num_string_ids_from_code_;
             break;
           }
+          case Instruction::IGET:
+          case Instruction::IGET_WIDE:
+          case Instruction::IGET_OBJECT:
+          case Instruction::IGET_BOOLEAN:
+          case Instruction::IGET_BYTE:
+          case Instruction::IGET_CHAR:
+          case Instruction::IGET_SHORT:
+          case Instruction::IPUT:
+          case Instruction::IPUT_WIDE:
+          case Instruction::IPUT_OBJECT:
+          case Instruction::IPUT_BOOLEAN:
+          case Instruction::IPUT_BYTE:
+          case Instruction::IPUT_CHAR:
+          case Instruction::IPUT_SHORT: {
+            const uint32_t receiver = inst->VRegB_22c();
+            const uint32_t dex_field_idx = inst->VRegC_22c();
+            const uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize();
+            ++field_receiver_[(receiver - first_arg_reg) & 0xF];
+            ++types_accessed[dex_file.GetFieldId(dex_field_idx).class_idx_.index_];
+            if (first_arg_reg == receiver) {
+              auto it = field_index_map_.find(dex_field_idx);
+              if (it != field_index_map_.end() && it->second < kMaxFieldIndex) {
+                ++field_index_[it->second];
+              } else {
+                ++field_index_other_;
+              }
+            }
+            ++field_output_[inst->VRegA_22c()];
+            break;
+          }
           case Instruction::CONST_STRING_JUMBO: {
             const dex::StringIndex string_index(inst->VRegB_31c());
             unique_string_ids.insert(string_index.index_);
@@ -310,6 +352,7 @@
           case Instruction::INVOKE_VIRTUAL:
           case Instruction::INVOKE_VIRTUAL_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_virtual_;
             }
@@ -320,6 +363,7 @@
           case Instruction::INVOKE_DIRECT:
           case Instruction::INVOKE_DIRECT_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_direct_;
             }
@@ -330,6 +374,7 @@
           case Instruction::INVOKE_STATIC:
           case Instruction::INVOKE_STATIC_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_static_;
             }
@@ -340,6 +385,7 @@
           case Instruction::INVOKE_INTERFACE:
           case Instruction::INVOKE_INTERFACE_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_interface_;
             }
@@ -350,6 +396,7 @@
           case Instruction::INVOKE_SUPER:
           case Instruction::INVOKE_SUPER_RANGE: {
             uint32_t method_idx = DexMethodIndex(inst.Inst());
+            ++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_];
             if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
               ++same_class_super_;
             }
@@ -357,18 +404,69 @@
             unique_method_ids.insert(method_idx);
             break;
           }
+          case Instruction::NEW_ARRAY: {
+            ++types_accessed[inst->VRegC_22c()];
+            break;
+          }
+          case Instruction::FILLED_NEW_ARRAY: {
+            ++types_accessed[inst->VRegB_35c()];
+            break;
+          }
+          case Instruction::FILLED_NEW_ARRAY_RANGE: {
+            ++types_accessed[inst->VRegB_3rc()];
+            break;
+          }
+          case Instruction::CONST_CLASS:
+          case Instruction::CHECK_CAST:
+          case Instruction::NEW_INSTANCE: {
+            ++types_accessed[inst->VRegB_21c()];
+            break;
+          }
+          case Instruction::INSTANCE_OF: {
+            ++types_accessed[inst->VRegB_21c()];
+            break;
+          }
           default:
             break;
         }
       }
     }
-    total_unique_method_idx_ += unique_method_ids.size();
+    // Count uses of top 16n.
+    std::vector<size_t> uses;
+    for (auto&& p : types_accessed) {
+      uses.push_back(p.second);
+    }
+    std::sort(uses.rbegin(), uses.rend());
+    for (size_t i = 0; i < uses.size(); ++i) {
+      if (i < 16) {
+        uses_top_types_ += uses[i];
+      }
+      uses_all_types_ += uses[i];
+    }
+    total_unique_types_ += types_accessed.size();
+    total_unique_method_ids_ += unique_method_ids.size();
     total_unique_string_ids_ += unique_string_ids.size();
   }
   total_unique_code_items_ += unique_code_items.size();
 }
 
 void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const {
+  const uint64_t fields_total = std::accumulate(field_receiver_, field_receiver_ + 16u, 0u);
+  for (size_t i = 0; i < 16; ++i) {
+    os << "receiver_reg=" << i << ": " << Percent(field_receiver_[i], fields_total) << "\n";
+  }
+  for (size_t i = 0; i < 16; ++i) {
+    os << "output_reg=" << i << ": " << Percent(field_output_[i], fields_total) << "\n";
+  }
+  const uint64_t fields_idx_total = std::accumulate(field_index_,
+                                                    field_index_ + kMaxFieldIndex,
+                                                    0u) + field_index_other_;
+  for (size_t i = 0; i < kMaxFieldIndex; ++i) {
+    os << "field_idx=" << i << ": " << Percent(field_index_[i], fields_idx_total) << "\n";
+  }
+  os << "field_idx=other: " << Percent(field_index_other_, fields_idx_total) << "\n";
+  os << "field_idx_savings=" << Percent((fields_idx_total - field_index_other_) * 2, total_size)
+     << "\n";
   os << "Num string ids: " << num_string_ids_ << "\n";
   os << "Num method ids: " << num_method_ids_ << "\n";
   os << "Num field ids: " << num_field_ids_ << "\n";
@@ -380,8 +478,10 @@
   os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n";
   os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n";
   os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
-  os << "Unique(per class) method ids accessed from code: " << total_unique_method_idx_ << "\n";
-  os << "Unique(per class) string ids accessed from code: " << total_unique_string_ids_ << "\n";
+  os << "Avg unique methods accessed per class: "
+     << double(total_unique_method_ids_) / double(num_class_defs_) << "\n";
+  os << "Avg unique strings accessed per class: "
+     << double(total_unique_string_ids_) / double(num_class_defs_) << "\n";
   const size_t same_class_total =
       same_class_direct_ +
       same_class_virtual_ +
@@ -396,6 +496,9 @@
       total_super_;
   os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
   os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
+  os << "Type uses on top types: " << PercentDivide(uses_top_types_, uses_all_types_) << "\n";
+  os << "Type uses 1b savings: " << PercentDivide(uses_top_types_, total_size) << "\n";
+  os << "Total unique types accessed per class " << total_unique_types_ << "\n";
   os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n";
   os << "Total unique code items: " << total_unique_code_items_ << "\n";
   os << "Total Dex size: " << total_size << "\n";
@@ -420,7 +523,7 @@
           }
           case Instruction::MOVE_RESULT:
           case Instruction::MOVE_RESULT_OBJECT: {
-            if (space_for_out_arg) {
+            if (space_for_out_arg && inst->VRegA_11x() < 16) {
               move_result_savings_ += inst->SizeInCodeUnits() * 2;
             }
             break;
@@ -441,7 +544,7 @@
   }
   os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n";
   os << "One byte invoke savings: " << Percent(total, total_size) << "\n";
-  const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 3, 0u);
+  const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 2, 0u);
   os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
 }
 
diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h
index 2be53d6..d3a9c59 100644
--- a/tools/dexanalyze/dexanalyze_experiments.h
+++ b/tools/dexanalyze/dexanalyze_experiments.h
@@ -17,6 +17,7 @@
 #ifndef ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
 #define ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
 
+#include <cstdint>
 #include <iosfwd>
 #include <memory>
 #include <set>
@@ -90,10 +91,16 @@
  private:
   // Total string ids loaded from dex code.
   size_t num_string_ids_from_code_ = 0;
-  size_t total_unique_method_idx_ = 0;
+  size_t total_unique_method_ids_ = 0;
   size_t total_unique_string_ids_ = 0;
   uint64_t total_unique_code_items_ = 0u;
 
+  static constexpr size_t kMaxFieldIndex = 32;
+  uint64_t field_index_[kMaxFieldIndex] = {};
+  uint64_t field_index_other_ = 0u;
+  uint64_t field_receiver_[16] = {};
+  uint64_t field_output_[16] = {};
+
   // Other dex ids.
   size_t dex_code_bytes_ = 0;
   size_t num_string_ids_ = 0;
@@ -113,6 +120,11 @@
   size_t total_interface_ = 0;
   size_t same_class_super_ = 0;
   size_t total_super_ = 0;
+
+  // Type usage.
+  uint64_t uses_top_types_ = 0u;
+  uint64_t uses_all_types_ = 0u;
+  uint64_t total_unique_types_ = 0u;
 };
 
 // Measure various code metrics including args per invoke-virtual, fill/spill move patterns.