Add invoke infos to stack maps

Invoke info records the invoke type and dex method index for invokes
that may reach artQuickResolutionTrampoline. Having this information
recorded allows the runtime to avoid reading the dex code and pulling
in extra pages.

Code size increase for a large app:
93886360 -> 95811480 (2.05% increase)

1/2 of the code size increase is from making less stack maps deduped.
I suspect there is less deduping because of the invoke info method
index.

Merged disabled until we measure the RAM savings.

Test: test-art-host, N6P boots

Bug: 34109702

Change-Id: I6c5e4a60675a1d7c76dee0561a12909e4ab6d5d9
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index f7a6402..67f0b57 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -1145,6 +1145,94 @@
   }
 };
 
+// Format is [native pc, invoke type, method index].
+class InvokeInfoEncoding {
+ public:
+  void SetFromSizes(size_t native_pc_max,
+                    size_t invoke_type_max,
+                    size_t method_index_max) {
+    total_bit_size_ = 0;
+    DCHECK_EQ(kNativePcBitOffset, total_bit_size_);
+    total_bit_size_ += MinimumBitsToStore(native_pc_max);
+    invoke_type_bit_offset_ = total_bit_size_;
+    total_bit_size_ += MinimumBitsToStore(invoke_type_max);
+    method_index_bit_offset_ = total_bit_size_;
+    total_bit_size_ += MinimumBitsToStore(method_index_max);
+  }
+
+  ALWAYS_INLINE FieldEncoding GetNativePcEncoding() const {
+    return FieldEncoding(kNativePcBitOffset, invoke_type_bit_offset_);
+  }
+
+  ALWAYS_INLINE FieldEncoding GetInvokeTypeEncoding() const {
+    return FieldEncoding(invoke_type_bit_offset_, method_index_bit_offset_);
+  }
+
+  ALWAYS_INLINE FieldEncoding GetMethodIndexEncoding() const {
+    return FieldEncoding(method_index_bit_offset_, total_bit_size_);
+  }
+
+  ALWAYS_INLINE size_t BitSize() const {
+    return total_bit_size_;
+  }
+
+  template<typename Vector>
+  void Encode(Vector* dest) const {
+    static_assert(alignof(InvokeInfoEncoding) == 1, "Should not require alignment");
+    const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this);
+    dest->insert(dest->end(), ptr, ptr + sizeof(*this));
+  }
+
+  void Decode(const uint8_t** ptr) {
+    *this = *reinterpret_cast<const InvokeInfoEncoding*>(*ptr);
+    *ptr += sizeof(*this);
+  }
+
+ private:
+  static constexpr uint8_t kNativePcBitOffset = 0;
+  uint8_t invoke_type_bit_offset_;
+  uint8_t method_index_bit_offset_;
+  uint8_t total_bit_size_;
+};
+
+class InvokeInfo {
+ public:
+  explicit InvokeInfo(BitMemoryRegion region) : region_(region) {}
+
+  ALWAYS_INLINE uint32_t GetNativePcOffset(const InvokeInfoEncoding& encoding,
+                                           InstructionSet instruction_set) const {
+    CodeOffset offset(
+        CodeOffset::FromCompressedOffset(encoding.GetNativePcEncoding().Load(region_)));
+    return offset.Uint32Value(instruction_set);
+  }
+
+  ALWAYS_INLINE void SetNativePcCodeOffset(const InvokeInfoEncoding& encoding,
+                                           CodeOffset native_pc_offset) {
+    encoding.GetNativePcEncoding().Store(region_, native_pc_offset.CompressedValue());
+  }
+
+  ALWAYS_INLINE uint32_t GetInvokeType(const InvokeInfoEncoding& encoding) const {
+    return encoding.GetInvokeTypeEncoding().Load(region_);
+  }
+
+  ALWAYS_INLINE void SetInvokeType(const InvokeInfoEncoding& encoding, uint32_t invoke_type) {
+    encoding.GetInvokeTypeEncoding().Store(region_, invoke_type);
+  }
+
+  ALWAYS_INLINE uint32_t GetMethodIndex(const InvokeInfoEncoding& encoding) const {
+    return encoding.GetMethodIndexEncoding().Load(region_);
+  }
+
+  ALWAYS_INLINE void SetMethodIndex(const InvokeInfoEncoding& encoding, uint32_t method_index) {
+    encoding.GetMethodIndexEncoding().Store(region_, method_index);
+  }
+
+  bool IsValid() const { return region_.pointer() != nullptr; }
+
+ private:
+  BitMemoryRegion region_;
+};
+
 // Most of the fields are encoded as ULEB128 to save space.
 struct CodeInfoEncoding {
   static constexpr uint32_t kInvalidSize = static_cast<size_t>(-1);
@@ -1154,6 +1242,7 @@
   BitEncodingTable<StackMapEncoding> stack_map;
   BitEncodingTable<BitRegionEncoding> register_mask;
   BitEncodingTable<BitRegionEncoding> stack_mask;
+  BitEncodingTable<InvokeInfoEncoding> invoke_info;
   BitEncodingTable<InlineInfoEncoding> inline_info;
 
   CodeInfoEncoding() {}
@@ -1165,6 +1254,7 @@
     stack_map.Decode(&ptr);
     register_mask.Decode(&ptr);
     stack_mask.Decode(&ptr);
+    invoke_info.Decode(&ptr);
     if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
       inline_info.Decode(&ptr);
     } else {
@@ -1183,6 +1273,7 @@
     stack_map.Encode(dest);
     register_mask.Encode(dest);
     stack_mask.Encode(dest);
+    invoke_info.Encode(dest);
     if (stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) {
       inline_info.Encode(dest);
     }
@@ -1199,6 +1290,7 @@
     stack_map.UpdateBitOffset(&bit_offset);
     register_mask.UpdateBitOffset(&bit_offset);
     stack_mask.UpdateBitOffset(&bit_offset);
+    invoke_info.UpdateBitOffset(&bit_offset);
     inline_info.UpdateBitOffset(&bit_offset);
     cache_non_header_size = RoundUp(bit_offset, kBitsPerByte) / kBitsPerByte - HeaderSize();
   }
@@ -1303,6 +1395,10 @@
     return encoding.stack_map.encoding.BitSize() * GetNumberOfStackMaps(encoding);
   }
 
+  InvokeInfo GetInvokeInfo(const CodeInfoEncoding& encoding, size_t index) const {
+    return InvokeInfo(encoding.invoke_info.BitRegion(region_, index));
+  }
+
   DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
                                      const CodeInfoEncoding& encoding,
                                      size_t number_of_dex_registers) const {
@@ -1426,6 +1522,17 @@
     return StackMap();
   }
 
+  InvokeInfo GetInvokeInfoForNativePcOffset(uint32_t native_pc_offset,
+                                            const CodeInfoEncoding& encoding) {
+    for (size_t index = 0; index < encoding.invoke_info.num_entries; index++) {
+      InvokeInfo item = GetInvokeInfo(encoding, index);
+      if (item.GetNativePcOffset(encoding.invoke_info.encoding, kRuntimeISA) == native_pc_offset) {
+        return item;
+      }
+    }
+    return InvokeInfo(BitMemoryRegion());
+  }
+
   // Dump this CodeInfo object on `os`.  `code_offset` is the (absolute)
   // native PC of the compiled method and `number_of_dex_registers` the
   // number of Dex virtual registers used in this method.  If