xref: /aosp_15_r20/external/stg/fingerprint.cc (revision 9e3b08ae94a55201065475453d799e8b1378bea6)
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2022-2024 Google LLC
5 //
6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the
7 // "License"); you may not use this file except in compliance with the
8 // License.  You may obtain a copy of the License at
9 //
10 //     https://llvm.org/LICENSE.txt
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 //
18 // Author: Giuliano Procida
19 // Author: Siddharth Nayyar
20 
21 #include "fingerprint.h"
22 
23 #include <cstdint>
24 #include <map>
25 #include <string>
26 #include <unordered_map>
27 #include <unordered_set>
28 #include <utility>
29 #include <vector>
30 
31 #include "graph.h"
32 #include "hashing.h"
33 #include "runtime.h"
34 #include "scc.h"
35 
36 namespace stg {
37 namespace {
38 
39 struct Hasher {
Hasherstg::__anondad589c00111::Hasher40   Hasher(Runtime& runtime, const Graph& graph,
41          std::unordered_map<Id, HashValue>& hashes,
42          std::unordered_set<Id>& todo)
43       : graph(graph), hashes(hashes), todo(todo),
44         non_trivial_scc_size(runtime, "fingerprint.non_trivial_scc_size") {}
45 
46   // Graph function implementation
operator ()stg::__anondad589c00111::Hasher47   HashValue operator()(const Special& x) {
48     switch (x.kind) {
49       case Special::Kind::VOID:
50         return hash('O');
51       case Special::Kind::VARIADIC:
52         return hash('V');
53       case Special::Kind::NULLPTR:
54         return hash('L');
55     }
56   }
57 
operator ()stg::__anondad589c00111::Hasher58   HashValue operator()(const PointerReference& x) {
59     return hash('P', static_cast<uint32_t>(x.kind), (*this)(x.pointee_type_id));
60   }
61 
operator ()stg::__anondad589c00111::Hasher62   HashValue operator()(const PointerToMember& x) {
63     return hash('N', (*this)(x.containing_type_id), (*this)(x.pointee_type_id));
64   }
65 
operator ()stg::__anondad589c00111::Hasher66   HashValue operator()(const Typedef& x) {
67     todo.insert(x.referred_type_id);
68     return hash('T', x.name);
69   }
70 
operator ()stg::__anondad589c00111::Hasher71   HashValue operator()(const Qualified& x) {
72     return hash('Q', static_cast<uint32_t>(x.qualifier),
73                 (*this)(x.qualified_type_id));
74   }
75 
operator ()stg::__anondad589c00111::Hasher76   HashValue operator()(const Primitive& x) {
77     return hash('i', x.name);
78   }
79 
operator ()stg::__anondad589c00111::Hasher80   HashValue operator()(const Array& x) {
81     return hash('A', x.number_of_elements, (*this)(x.element_type_id));
82   }
83 
operator ()stg::__anondad589c00111::Hasher84   HashValue operator()(const BaseClass& x) {
85     return hash('B', (*this)(x.type_id));
86   }
87 
operator ()stg::__anondad589c00111::Hasher88   HashValue operator()(const Method& x) {
89     return hash('M', x.mangled_name, x.name, (*this)(x.type_id));
90   }
91 
operator ()stg::__anondad589c00111::Hasher92   HashValue operator()(const Member& x) {
93     return hash('D', x.name, x.offset, (*this)(x.type_id));
94   }
95 
operator ()stg::__anondad589c00111::Hasher96   HashValue operator()(const VariantMember& x) {
97     auto h = hash('m', x.name, (*this)(x.type_id));
98     if (x.discriminant_value) {
99       h = hash(h, *x.discriminant_value);
100     }
101     return h;
102   }
103 
operator ()stg::__anondad589c00111::Hasher104   HashValue operator()(const StructUnion& x) {
105     auto h = hash('U', static_cast<uint32_t>(x.kind), x.name);
106     if (x.definition.has_value()) {
107       h = hash(h, '1');
108       const auto& definition = *x.definition;
109       ToDo(definition.base_classes);
110       ToDo(definition.methods);
111       if (x.name.empty()) {
112         for (auto id : definition.members) {
113           h = hash(h, (*this)(id));
114         }
115       } else {
116         ToDo(definition.members);
117       }
118     } else {
119       h = hash(h, '0');
120     }
121     return h;
122   }
123 
operator ()stg::__anondad589c00111::Hasher124   HashValue operator()(const Enumeration& x) {
125     auto h = hash('E', x.name);
126     if (x.definition.has_value()) {
127       h = hash(h, '1');
128       const auto& definition = *x.definition;
129       todo.insert(definition.underlying_type_id);
130       if (x.name.empty()) {
131         for (const auto& e : definition.enumerators) {
132           h = hash(h, e.first);
133         }
134       }
135     } else {
136       h = hash(h, '0');
137     }
138     return h;
139   }
140 
operator ()stg::__anondad589c00111::Hasher141   HashValue operator()(const Variant& x) {
142     auto h = hash('v', x.name, x.bytesize);
143     if (x.discriminant.has_value()) {
144       todo.insert(x.discriminant.value());
145     }
146     ToDo(x.members);
147     return h;
148   }
149 
operator ()stg::__anondad589c00111::Hasher150   HashValue operator()(const Function& x) {
151     auto h = hash('F', (*this)(x.return_type_id));
152     for (const auto& parameter : x.parameters) {
153       h = hash(h, (*this)(parameter));
154     }
155     return h;
156   }
157 
operator ()stg::__anondad589c00111::Hasher158   HashValue operator()(const ElfSymbol& x) {
159     if (x.type_id.has_value()) {
160       todo.insert(x.type_id.value());
161     }
162     return hash('S', x.symbol_name);
163   }
164 
operator ()stg::__anondad589c00111::Hasher165   HashValue operator()(const Interface& x) {
166     ToDo(x.symbols);
167     ToDo(x.types);
168     return hash('Z');
169   }
170 
171   // main entry point
operator ()stg::__anondad589c00111::Hasher172   HashValue operator()(Id id) {
173     // Check if the id already has a fingerprint.
174     const auto it = hashes.find(id);
175     if (it != hashes.end()) {
176       return it->second;
177     }
178 
179     // Record the id with Strongly-Connected Component finder.
180     auto handle = scc.Open(id);
181     if (!handle) {
182       // Already open.
183       //
184       // Return a dummy fingerprint.
185       return HashValue(0);
186     }
187     // Comparison opened, need to close it before returning.
188 
189     auto result = graph.Apply(*this, id);
190 
191     // Check for a complete Strongly-Connected Component.
192     auto ids = scc.Close(*handle);
193     if (ids.empty()) {
194       // Note that result is tentative as the SCC is still open.
195       return result;
196     }
197 
198     // Closed SCC.
199     //
200     // Note that result is the combination of every fingerprint in the SCC via
201     // the DFS spanning tree.
202     //
203     // All nodes in a non-trivial SCCs are given the same fingerprint, but
204     // non-trivial SCCs should be extremely rare.
205     const auto size = ids.size();
206     if (size > 1) {
207       result = HashValue(size);
208       non_trivial_scc_size.Add(size);
209     }
210     for (auto id : ids) {
211       hashes.insert({id, result});
212     }
213     return result;
214   }
215 
ToDostg::__anondad589c00111::Hasher216   void ToDo(const std::vector<Id>& ids) {
217     for (auto id : ids) {
218       todo.insert(id);
219     }
220   }
221 
ToDostg::__anondad589c00111::Hasher222   void ToDo(const std::map<std::string, Id>& ids) {
223     for (const auto& [_, id] : ids) {
224       todo.insert(id);
225     }
226   }
227 
228   const Graph& graph;
229   std::unordered_map<Id, HashValue>& hashes;
230   std::unordered_set<Id> &todo;
231   Histogram non_trivial_scc_size;
232   SCC<Id> scc;
233 
234   // Function object: (Args...) -> HashValue
235   Hash hash;
236 };
237 
238 }  // namespace
239 
Fingerprint(Runtime & runtime,const Graph & graph,Id root)240 std::unordered_map<Id, HashValue> Fingerprint(
241     Runtime& runtime, const Graph& graph, Id root) {
242   const Time x(runtime, "hash nodes");
243   std::unordered_map<Id, HashValue> hashes;
244   std::unordered_set<Id> todo;
245   Hasher hasher(runtime, graph, hashes, todo);
246   todo.insert(root);
247   while (!todo.empty()) {
248     for (auto id : std::exchange(todo, {})) {
249       hasher(id);
250     }
251   }
252   return hashes;
253 }
254 
255 }  // namespace stg
256