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