Deduplicate stackmaps at BitTable level.

Make it possible to share BitTables between CodeInfos.

This saves 1% of .oat file size.

Test: test-art-host-gtest
Change-Id: I14172cba6b65e734b94f8c232f24eeee1fc67113
diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc
index 62b9f35..e8746bc 100644
--- a/runtime/stack_map.cc
+++ b/runtime/stack_map.cc
@@ -31,6 +31,24 @@
   : CodeInfo(header->GetOptimizedCodeInfoPtr(), flags) {
 }
 
+template<typename Accessor>
+ALWAYS_INLINE static void DecodeTable(BitTable<Accessor>& table,
+                                      BitMemoryReader& reader,
+                                      const uint8_t* data) {
+  bool is_deduped = reader.ReadBit();
+  if (is_deduped) {
+    // 'data' points to the start of the reader's data.
+    uint32_t current_bit_offset = reader.GetBitOffset();
+    uint32_t bit_offset_backwards = DecodeVarintBits(reader) - current_bit_offset;
+    uint32_t byte_offset_backwards = BitsToBytesRoundUp(bit_offset_backwards);
+    BitMemoryReader reader2(data - byte_offset_backwards,
+                            byte_offset_backwards * kBitsPerByte - bit_offset_backwards);
+    table.Decode(reader2);
+  } else {
+    table.Decode(reader);
+  }
+}
+
 void CodeInfo::Decode(const uint8_t* data, DecodeFlags flags) {
   const uint8_t* begin = data;
   frame_size_in_bytes_ = DecodeUnsignedLeb128(&data);
@@ -38,19 +56,57 @@
   fp_spill_mask_ = DecodeUnsignedLeb128(&data);
   number_of_dex_registers_ = DecodeUnsignedLeb128(&data);
   BitMemoryReader reader(data, /* bit_offset */ 0);
-  stack_maps_.Decode(reader);
-  inline_infos_.Decode(reader);
+  DecodeTable(stack_maps_, reader, data);
+  DecodeTable(inline_infos_, reader, data);
   if (flags & DecodeFlags::InlineInfoOnly) {
     return;
   }
-  register_masks_.Decode(reader);
-  stack_masks_.Decode(reader);
-  dex_register_masks_.Decode(reader);
-  dex_register_maps_.Decode(reader);
-  dex_register_catalog_.Decode(reader);
+  DecodeTable(register_masks_, reader, data);
+  DecodeTable(stack_masks_, reader, data);
+  DecodeTable(dex_register_masks_, reader, data);
+  DecodeTable(dex_register_maps_, reader, data);
+  DecodeTable(dex_register_catalog_, reader, data);
   size_in_bits_ = (data - begin) * kBitsPerByte + reader.GetBitOffset();
 }
 
+template<typename Accessor>
+ALWAYS_INLINE static void DedupeTable(BitMemoryWriter<std::vector<uint8_t>>& writer,
+                                      BitMemoryReader& reader,
+                                      CodeInfo::DedupeMap* dedupe_map) {
+  bool is_deduped = reader.ReadBit();
+  DCHECK(!is_deduped);
+  BitTable<Accessor> bit_table(reader);
+  BitMemoryRegion region = reader.Tail(bit_table.BitSize());
+  auto it = dedupe_map->insert(std::make_pair(region, writer.GetBitOffset() + 1 /* dedupe bit */));
+  if (it.second /* new bit table */ || region.size_in_bits() < 32) {
+    writer.WriteBit(false);  // Is not deduped.
+    writer.WriteRegion(region);
+  } else {
+    writer.WriteBit(true);  // Is deduped.
+    EncodeVarintBits(writer, writer.GetBitOffset() - it.first->second);
+  }
+}
+
+size_t CodeInfo::Dedupe(std::vector<uint8_t>* out, const uint8_t* in, DedupeMap* dedupe_map) {
+  // Remember the current offset in the output buffer so that we can return it later.
+  const size_t result = out->size();
+  // Copy the header which encodes QuickMethodFrameInfo.
+  EncodeUnsignedLeb128(out, DecodeUnsignedLeb128(&in));
+  EncodeUnsignedLeb128(out, DecodeUnsignedLeb128(&in));
+  EncodeUnsignedLeb128(out, DecodeUnsignedLeb128(&in));
+  EncodeUnsignedLeb128(out, DecodeUnsignedLeb128(&in));
+  BitMemoryReader reader(in, /* bit_offset */ 0);
+  BitMemoryWriter<std::vector<uint8_t>> writer(out, /* bit_offset */ out->size() * kBitsPerByte);
+  DedupeTable<StackMap>(writer, reader, dedupe_map);
+  DedupeTable<InlineInfo>(writer, reader, dedupe_map);
+  DedupeTable<RegisterMask>(writer, reader, dedupe_map);
+  DedupeTable<MaskInfo>(writer, reader, dedupe_map);
+  DedupeTable<MaskInfo>(writer, reader, dedupe_map);
+  DedupeTable<DexRegisterMapInfo>(writer, reader, dedupe_map);
+  DedupeTable<DexRegisterInfo>(writer, reader, dedupe_map);
+  return result;
+}
+
 BitTable<StackMap>::const_iterator CodeInfo::BinarySearchNativePc(uint32_t packed_pc) const {
   return std::partition_point(
       stack_maps_.begin(),
@@ -217,10 +273,7 @@
                     bool verbose,
                     InstructionSet instruction_set,
                     const MethodInfo& method_info) const {
-  vios->Stream()
-      << "CodeInfo"
-      << " BitSize="  << size_in_bits_
-      << "\n";
+  vios->Stream() << "CodeInfo\n";
   ScopedIndentation indent1(vios);
   DumpTable<StackMap>(vios, "StackMaps", stack_maps_, verbose);
   DumpTable<RegisterMask>(vios, "RegisterMasks", register_masks_, verbose);