xref: /aosp_15_r20/external/stg/stable_hash.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: 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