xref: /aosp_15_r20/art/dex2oat/linker/code_info_table_deduper.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2022 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "code_info_table_deduper.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include "oat/stack_map.h"
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker namespace art {
22*795d594fSAndroid Build Coastguard Worker namespace linker {
23*795d594fSAndroid Build Coastguard Worker 
ReserveDedupeBuffer(size_t num_code_infos)24*795d594fSAndroid Build Coastguard Worker void CodeInfoTableDeduper::ReserveDedupeBuffer(size_t num_code_infos) {
25*795d594fSAndroid Build Coastguard Worker   DCHECK(dedupe_set_.empty());
26*795d594fSAndroid Build Coastguard Worker   const size_t max_size = num_code_infos * CodeInfo::kNumBitTables;
27*795d594fSAndroid Build Coastguard Worker   // Reserve space for 1/2 of the maximum dedupe set size to avoid rehashing.
28*795d594fSAndroid Build Coastguard Worker   // Usually only 30%-40% of bit tables are unique.
29*795d594fSAndroid Build Coastguard Worker   dedupe_set_.reserve(max_size / 2u);
30*795d594fSAndroid Build Coastguard Worker }
31*795d594fSAndroid Build Coastguard Worker 
Dedupe(const uint8_t * code_info_data)32*795d594fSAndroid Build Coastguard Worker size_t CodeInfoTableDeduper::Dedupe(const uint8_t* code_info_data) {
33*795d594fSAndroid Build Coastguard Worker   static constexpr size_t kNumHeaders = CodeInfo::kNumHeaders;
34*795d594fSAndroid Build Coastguard Worker   static constexpr size_t kNumBitTables = CodeInfo::kNumBitTables;
35*795d594fSAndroid Build Coastguard Worker 
36*795d594fSAndroid Build Coastguard Worker   // The back-reference offset takes space so dedupe is not worth it for tiny tables.
37*795d594fSAndroid Build Coastguard Worker   constexpr size_t kMinDedupSize = 33;  // Assume 32-bit offset on average.
38*795d594fSAndroid Build Coastguard Worker 
39*795d594fSAndroid Build Coastguard Worker   size_t start_bit_offset = writer_.NumberOfWrittenBits();
40*795d594fSAndroid Build Coastguard Worker   DCHECK_ALIGNED(start_bit_offset, kBitsPerByte);
41*795d594fSAndroid Build Coastguard Worker 
42*795d594fSAndroid Build Coastguard Worker   // Reserve enough space in the `dedupe_set_` to avoid reashing later in this
43*795d594fSAndroid Build Coastguard Worker   // function and allow using direct pointers to the `HashSet<>` entries.
44*795d594fSAndroid Build Coastguard Worker   size_t elements_until_expand = dedupe_set_.ElementsUntilExpand();
45*795d594fSAndroid Build Coastguard Worker   if (UNLIKELY(elements_until_expand - dedupe_set_.size() < kNumBitTables)) {
46*795d594fSAndroid Build Coastguard Worker     // When resizing, try to make the load factor close to the minimum load factor.
47*795d594fSAndroid Build Coastguard Worker     size_t required_capacity = dedupe_set_.size() + kNumBitTables;
48*795d594fSAndroid Build Coastguard Worker     double factor = dedupe_set_.GetMaxLoadFactor() / dedupe_set_.GetMinLoadFactor();
49*795d594fSAndroid Build Coastguard Worker     size_t reservation = required_capacity * factor;
50*795d594fSAndroid Build Coastguard Worker     DCHECK_GE(reservation, required_capacity);
51*795d594fSAndroid Build Coastguard Worker     dedupe_set_.reserve(reservation);
52*795d594fSAndroid Build Coastguard Worker     elements_until_expand = dedupe_set_.ElementsUntilExpand();
53*795d594fSAndroid Build Coastguard Worker     DCHECK_GE(elements_until_expand - dedupe_set_.size(), kNumBitTables);
54*795d594fSAndroid Build Coastguard Worker   }
55*795d594fSAndroid Build Coastguard Worker 
56*795d594fSAndroid Build Coastguard Worker   // Read the existing code info and record bit table starts and end.
57*795d594fSAndroid Build Coastguard Worker   BitMemoryReader reader(code_info_data);
58*795d594fSAndroid Build Coastguard Worker   std::array<uint32_t, kNumHeaders> header = reader.ReadInterleavedVarints<kNumHeaders>();
59*795d594fSAndroid Build Coastguard Worker   CodeInfo code_info;
60*795d594fSAndroid Build Coastguard Worker   CodeInfo::ForEachHeaderField([&code_info, &header](size_t i, auto member_pointer) {
61*795d594fSAndroid Build Coastguard Worker     code_info.*member_pointer = header[i];
62*795d594fSAndroid Build Coastguard Worker   });
63*795d594fSAndroid Build Coastguard Worker   DCHECK(!code_info.HasDedupedBitTables());  // Input `CodeInfo` has no deduped tables.
64*795d594fSAndroid Build Coastguard Worker   std::array<uint32_t, kNumBitTables + 1u> bit_table_bit_starts;
65*795d594fSAndroid Build Coastguard Worker   CodeInfo::ForEachBitTableField([&](size_t i, auto member_pointer) {
66*795d594fSAndroid Build Coastguard Worker     bit_table_bit_starts[i] = dchecked_integral_cast<uint32_t>(reader.NumberOfReadBits());
67*795d594fSAndroid Build Coastguard Worker     DCHECK(!code_info.IsBitTableDeduped(i));
68*795d594fSAndroid Build Coastguard Worker     if (LIKELY(code_info.HasBitTable(i))) {
69*795d594fSAndroid Build Coastguard Worker       auto& table = code_info.*member_pointer;
70*795d594fSAndroid Build Coastguard Worker       table.Decode(reader);
71*795d594fSAndroid Build Coastguard Worker     }
72*795d594fSAndroid Build Coastguard Worker   });
73*795d594fSAndroid Build Coastguard Worker   bit_table_bit_starts[kNumBitTables] = dchecked_integral_cast<uint32_t>(reader.NumberOfReadBits());
74*795d594fSAndroid Build Coastguard Worker 
75*795d594fSAndroid Build Coastguard Worker   // Copy the source data.
76*795d594fSAndroid Build Coastguard Worker   BitMemoryRegion read_region = reader.GetReadRegion();
77*795d594fSAndroid Build Coastguard Worker   writer_.WriteBytesAligned(code_info_data, BitsToBytesRoundUp(read_region.size_in_bits()));
78*795d594fSAndroid Build Coastguard Worker 
79*795d594fSAndroid Build Coastguard Worker   // Insert entries for large tables to the `dedupe_set_` and check for duplicates.
80*795d594fSAndroid Build Coastguard Worker   std::array<DedupeSetEntry*, kNumBitTables> dedupe_entries;
81*795d594fSAndroid Build Coastguard Worker   std::fill(dedupe_entries.begin(), dedupe_entries.end(), nullptr);
82*795d594fSAndroid Build Coastguard Worker   CodeInfo::ForEachBitTableField([&](size_t i, [[maybe_unused]] auto member_pointer) {
83*795d594fSAndroid Build Coastguard Worker     if (LIKELY(code_info.HasBitTable(i))) {
84*795d594fSAndroid Build Coastguard Worker       uint32_t table_bit_size = bit_table_bit_starts[i + 1u] - bit_table_bit_starts[i];
85*795d594fSAndroid Build Coastguard Worker       if (table_bit_size >= kMinDedupSize) {
86*795d594fSAndroid Build Coastguard Worker         uint32_t table_bit_start = start_bit_offset + bit_table_bit_starts[i];
87*795d594fSAndroid Build Coastguard Worker         BitMemoryRegion region(
88*795d594fSAndroid Build Coastguard Worker             const_cast<uint8_t*>(writer_.data()), table_bit_start, table_bit_size);
89*795d594fSAndroid Build Coastguard Worker         DedupeSetEntry entry{table_bit_start, table_bit_size};
90*795d594fSAndroid Build Coastguard Worker         auto [it, inserted] = dedupe_set_.insert(entry);
91*795d594fSAndroid Build Coastguard Worker         dedupe_entries[i] = &*it;
92*795d594fSAndroid Build Coastguard Worker         if (!inserted) {
93*795d594fSAndroid Build Coastguard Worker           code_info.SetBitTableDeduped(i);  // Mark as deduped before we write header.
94*795d594fSAndroid Build Coastguard Worker         }
95*795d594fSAndroid Build Coastguard Worker       }
96*795d594fSAndroid Build Coastguard Worker     }
97*795d594fSAndroid Build Coastguard Worker   });
98*795d594fSAndroid Build Coastguard Worker   DCHECK_EQ(elements_until_expand, dedupe_set_.ElementsUntilExpand()) << "Unexpected resizing!";
99*795d594fSAndroid Build Coastguard Worker 
100*795d594fSAndroid Build Coastguard Worker   if (code_info.HasDedupedBitTables()) {
101*795d594fSAndroid Build Coastguard Worker     // Reset the writer to the original position. This makes new entries in the
102*795d594fSAndroid Build Coastguard Worker     // `dedupe_set_` effectively point to non-existent data. We shall write the
103*795d594fSAndroid Build Coastguard Worker     // new data again at the correct position and update these entries.
104*795d594fSAndroid Build Coastguard Worker     writer_.Truncate(start_bit_offset);
105*795d594fSAndroid Build Coastguard Worker     // Update bit table flags in the `header` and write the `header`.
106*795d594fSAndroid Build Coastguard Worker     header[kNumHeaders - 1u] = code_info.bit_table_flags_;
107*795d594fSAndroid Build Coastguard Worker     CodeInfo::ForEachHeaderField([&code_info, &header](size_t i, auto member_pointer) {
108*795d594fSAndroid Build Coastguard Worker       DCHECK_EQ(code_info.*member_pointer, header[i]);
109*795d594fSAndroid Build Coastguard Worker     });
110*795d594fSAndroid Build Coastguard Worker     writer_.WriteInterleavedVarints(header);
111*795d594fSAndroid Build Coastguard Worker     // Write bit tables and update offsets in `dedupe_set_` after encoding the `header`.
112*795d594fSAndroid Build Coastguard Worker     CodeInfo::ForEachBitTableField([&](size_t i, [[maybe_unused]] auto member_pointer) {
113*795d594fSAndroid Build Coastguard Worker       if (code_info.HasBitTable(i)) {
114*795d594fSAndroid Build Coastguard Worker         size_t current_bit_offset = writer_.NumberOfWrittenBits();
115*795d594fSAndroid Build Coastguard Worker         if (code_info.IsBitTableDeduped(i)) {
116*795d594fSAndroid Build Coastguard Worker           DCHECK_GE(bit_table_bit_starts[i + 1u] - bit_table_bit_starts[i], kMinDedupSize);
117*795d594fSAndroid Build Coastguard Worker           DCHECK(dedupe_entries[i] != nullptr);
118*795d594fSAndroid Build Coastguard Worker           size_t deduped_offset = dedupe_entries[i]->bit_start;
119*795d594fSAndroid Build Coastguard Worker           writer_.WriteVarint(current_bit_offset - deduped_offset);
120*795d594fSAndroid Build Coastguard Worker         } else {
121*795d594fSAndroid Build Coastguard Worker           uint32_t table_bit_size = bit_table_bit_starts[i + 1u] - bit_table_bit_starts[i];
122*795d594fSAndroid Build Coastguard Worker           writer_.WriteRegion(read_region.Subregion(bit_table_bit_starts[i], table_bit_size));
123*795d594fSAndroid Build Coastguard Worker           if (table_bit_size >= kMinDedupSize) {
124*795d594fSAndroid Build Coastguard Worker             // Update offset in the `dedupe_set_` entry.
125*795d594fSAndroid Build Coastguard Worker             DCHECK(dedupe_entries[i] != nullptr);
126*795d594fSAndroid Build Coastguard Worker             dedupe_entries[i]->bit_start = current_bit_offset;
127*795d594fSAndroid Build Coastguard Worker           }
128*795d594fSAndroid Build Coastguard Worker         }
129*795d594fSAndroid Build Coastguard Worker       }
130*795d594fSAndroid Build Coastguard Worker     });
131*795d594fSAndroid Build Coastguard Worker     writer_.ByteAlign();
132*795d594fSAndroid Build Coastguard Worker   }  // else nothing to do - we already copied the data.
133*795d594fSAndroid Build Coastguard Worker 
134*795d594fSAndroid Build Coastguard Worker   if (kIsDebugBuild) {
135*795d594fSAndroid Build Coastguard Worker     CodeInfo old_code_info(code_info_data);
136*795d594fSAndroid Build Coastguard Worker     CodeInfo new_code_info(writer_.data() + start_bit_offset / kBitsPerByte);
137*795d594fSAndroid Build Coastguard Worker     CodeInfo::ForEachHeaderField([&old_code_info, &new_code_info](size_t, auto member_pointer) {
138*795d594fSAndroid Build Coastguard Worker       if (member_pointer != &CodeInfo::bit_table_flags_) {  // Expected to differ.
139*795d594fSAndroid Build Coastguard Worker         DCHECK_EQ(old_code_info.*member_pointer, new_code_info.*member_pointer);
140*795d594fSAndroid Build Coastguard Worker       }
141*795d594fSAndroid Build Coastguard Worker     });
142*795d594fSAndroid Build Coastguard Worker     CodeInfo::ForEachBitTableField([&old_code_info, &new_code_info](size_t i, auto member_pointer) {
143*795d594fSAndroid Build Coastguard Worker       DCHECK_EQ(old_code_info.HasBitTable(i), new_code_info.HasBitTable(i));
144*795d594fSAndroid Build Coastguard Worker       DCHECK((old_code_info.*member_pointer).Equals(new_code_info.*member_pointer));
145*795d594fSAndroid Build Coastguard Worker     });
146*795d594fSAndroid Build Coastguard Worker   }
147*795d594fSAndroid Build Coastguard Worker 
148*795d594fSAndroid Build Coastguard Worker   return start_bit_offset / kBitsPerByte;
149*795d594fSAndroid Build Coastguard Worker }
150*795d594fSAndroid Build Coastguard Worker 
151*795d594fSAndroid Build Coastguard Worker }  //  namespace linker
152*795d594fSAndroid Build Coastguard Worker }  //  namespace art
153