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: Siddharth Nayyar
19
20 #include "stable_hash.h"
21
22 #include <cstdint>
23 #include <string>
24 #include <unordered_map>
25 #include <utility>
26 #include <vector>
27
28 #include "graph.h"
29 #include "hashing.h"
30
31 namespace stg {
32
33 namespace {
34
35 // This combines 2 hash values while decaying (by shifting right) the second
36 // value. This prevents the most significant bits of the first hash from being
37 // affected by the decayed hash. Hash combination is done using a simple XOR
38 // operation to preserve the separation of higher and lower bits. Note that XOR
39 // is not a very effective method of mixing hash values if the values are
40 // generated with a weak hashing algorithm.
41 template <uint8_t decay>
DecayHashCombine(HashValue a,HashValue b)42 constexpr HashValue DecayHashCombine(HashValue a, HashValue b) {
43 static_assert(decay > 0 && decay < 32, "decay must lie inside (0, 32)");
44 return HashValue(a.value ^ (b.value >> decay));
45 }
46
47 // Decaying hashes are combined in reverse since the each successive hashable
48 // should be decayed 1 more time than the previous hashable and the last
49 // hashable should receieve the most decay.
50 template <uint8_t decay, typename Type, typename Hash>
DecayHashCombineInReverse(const std::vector<Type> & hashables,Hash & hash)51 HashValue DecayHashCombineInReverse(const std::vector<Type>& hashables,
52 Hash& hash) {
53 HashValue result(0);
54 for (auto it = hashables.crbegin(); it != hashables.crend(); ++it) {
55 result = DecayHashCombine<decay>(hash(*it), result);
56 }
57 return result;
58 }
59
60 struct StableHashWorker {
StableHashWorkerstg::__anon9c7ff0960111::StableHashWorker61 StableHashWorker(const Graph& graph, std::unordered_map<Id, HashValue>& cache)
62 : graph(graph), cache(cache) {}
63
operator ()stg::__anon9c7ff0960111::StableHashWorker64 HashValue operator()(Id id) {
65 auto [it, inserted] = cache.emplace(id, 0);
66 if (inserted) {
67 it->second = graph.Apply(*this, id);
68 }
69 return it->second;
70 }
71
operator ()stg::__anon9c7ff0960111::StableHashWorker72 HashValue operator()(const Special& x) {
73 switch (x.kind) {
74 case Special::Kind::VOID:
75 return hash("void");
76 case Special::Kind::VARIADIC:
77 return hash("variadic");
78 case Special::Kind::NULLPTR:
79 return hash("nullptr");
80 }
81 }
82
operator ()stg::__anon9c7ff0960111::StableHashWorker83 HashValue operator()(const PointerReference& x) {
84 return DecayHashCombine<2>(hash('r', static_cast<uint32_t>(x.kind)),
85 (*this)(x.pointee_type_id));
86 }
87
operator ()stg::__anon9c7ff0960111::StableHashWorker88 HashValue operator()(const PointerToMember& x) {
89 return DecayHashCombine<16>(hash('n', (*this)(x.containing_type_id)),
90 (*this)(x.pointee_type_id));
91 }
92
operator ()stg::__anon9c7ff0960111::StableHashWorker93 HashValue operator()(const Typedef& x) {
94 return hash('t', x.name);
95 }
96
operator ()stg::__anon9c7ff0960111::StableHashWorker97 HashValue operator()(const Qualified& x) {
98 return DecayHashCombine<2>(hash('q', static_cast<uint32_t>(x.qualifier)),
99 (*this)(x.qualified_type_id));
100 }
101
operator ()stg::__anon9c7ff0960111::StableHashWorker102 HashValue operator()(const Primitive& x) {
103 return hash('p', x.name);
104 }
105
operator ()stg::__anon9c7ff0960111::StableHashWorker106 HashValue operator()(const Array& x) {
107 return DecayHashCombine<2>(hash('a', x.number_of_elements),
108 (*this)(x.element_type_id));
109 }
110
operator ()stg::__anon9c7ff0960111::StableHashWorker111 HashValue operator()(const BaseClass& x) {
112 return DecayHashCombine<2>(hash('b', static_cast<uint32_t>(x.inheritance)),
113 (*this)(x.type_id));
114 }
115
operator ()stg::__anon9c7ff0960111::StableHashWorker116 HashValue operator()(const Method& x) {
117 return hash(x.mangled_name);
118 }
119
operator ()stg::__anon9c7ff0960111::StableHashWorker120 HashValue operator()(const Member& x) {
121 HashValue value = hash('m', x.name, x.bitsize);
122 value = DecayHashCombine<20>(value, hash(x.offset));
123 if (x.name.empty()) {
124 return DecayHashCombine<2>(value, (*this)(x.type_id));
125 } else {
126 return DecayHashCombine<8>(value, (*this)(x.type_id));
127 }
128 }
129
operator ()stg::__anon9c7ff0960111::StableHashWorker130 HashValue operator()(const VariantMember& x) {
131 HashValue value = hash('v', x.name);
132 value = DecayHashCombine<8>(value, (*this)(x.type_id));
133 return x.discriminant_value
134 ? DecayHashCombine<20>(value, hash(*x.discriminant_value))
135 : value;
136 }
137
operator ()stg::__anon9c7ff0960111::StableHashWorker138 HashValue operator()(const StructUnion& x) {
139 HashValue value = hash('S', static_cast<uint32_t>(x.kind), x.name,
140 static_cast<bool>(x.definition));
141 if (!x.name.empty() || !x.definition) {
142 return value;
143 }
144
145 auto h1 = DecayHashCombineInReverse<8>(x.definition->methods, *this);
146 auto h2 = DecayHashCombineInReverse<8>(x.definition->members, *this);
147 return DecayHashCombine<2>(value, HashValue(h1.value ^ h2.value));
148 }
149
operator ()stg::__anon9c7ff0960111::StableHashWorker150 HashValue operator()(const Enumeration& x) {
151 HashValue value = hash('e', x.name, static_cast<bool>(x.definition));
152 if (!x.name.empty() || !x.definition) {
153 return value;
154 }
155
156 auto hash_enum = [this](const std::pair<std::string, int64_t>& e) {
157 return hash(e.first, e.second);
158 };
159 return DecayHashCombine<2>(value, DecayHashCombineInReverse<8>(
160 x.definition->enumerators, hash_enum));
161 }
162
operator ()stg::__anon9c7ff0960111::StableHashWorker163 HashValue operator()(const Variant& x) {
164 HashValue value = hash('V', x.name, x.bytesize);
165 if (x.discriminant.has_value()) {
166 value = DecayHashCombine<12>(value, (*this)(x.discriminant.value()));
167 }
168 return DecayHashCombine<2>(value,
169 DecayHashCombineInReverse<8>(x.members, *this));
170 }
171
operator ()stg::__anon9c7ff0960111::StableHashWorker172 HashValue operator()(const Function& x) {
173 return DecayHashCombine<2>(
174 hash('f', (*this)(x.return_type_id)),
175 DecayHashCombineInReverse<4>(x.parameters, *this));
176 }
177
operator ()stg::__anon9c7ff0960111::StableHashWorker178 HashValue operator()(const ElfSymbol& x) {
179 HashValue value = hash('s', x.symbol_name);
180 if (x.version_info) {
181 value = DecayHashCombine<16>(
182 value, hash(x.version_info->name, x.version_info->is_default));
183 }
184 return value;
185 }
186
operator ()stg::__anon9c7ff0960111::StableHashWorker187 HashValue operator()(const Interface&) {
188 return hash("interface");
189 }
190
191 const Hash hash;
192 const Graph& graph;
193 std::unordered_map<Id, HashValue>& cache;
194 };
195
196 } // namespace
197
operator ()(Id id)198 HashValue StableHash::operator()(Id id) {
199 return StableHashWorker(graph_, cache_)(id);
200 }
201
202 } // namespace stg
203