blob: 8621aedbaf9a446e19991fc8cdd10bacaefad49d [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- mode: C++ -*-
//
// Copyright 2022 Google LLC
//
// Licensed under the Apache License v2.0 with LLVM Exceptions (the
// "License"); you may not use this file except in compliance with the
// License. You may obtain a copy of the License at
//
// https://llvm.org/LICENSE.txt
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Siddharth Nayyar
#include "stable_hash.h"
#include <algorithm>
#include <cstdint>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "graph.h"
#include "hashing.h"
namespace stg {
namespace {
// This combines 2 hash values while decaying (by shifting right) the second
// value. This prevents the most significant bits of the first hash from being
// affected by the decayed hash. Hash combination is done using a simple XOR
// operation to preserve the separation of higher and lower bits. Note that XOR
// is not a very effective method of mixing hash values if the values are
// generated with a weak hashing algorithm.
template <uint8_t decay>
constexpr HashValue DecayHashCombine(HashValue a, HashValue b) {
static_assert(decay > 0 && decay < 32, "decay must lie inside (0, 32)");
return HashValue(a.value ^ (b.value >> decay));
}
// Decaying hashes are combined in reverse since the each successive hashable
// should be decayed 1 more time than the previous hashable and the last
// hashable should receieve the most decay.
template <uint8_t decay, typename Type, typename Hash>
HashValue DecayHashCombineInReverse(const std::vector<Type>& hashables,
Hash& hash) {
HashValue result(0);
for (auto it = hashables.crbegin(); it != hashables.crend(); ++it) {
result = DecayHashCombine<decay>(hash(*it), result);
}
return result;
}
} // namespace
HashValue StableHash::operator()(Id id) {
auto [it, inserted] = cache_.emplace(id, 0);
if (inserted) {
it->second = graph_.Apply<HashValue>(*this, id);
}
return it->second;
}
HashValue StableHash::operator()(const Special& x) {
switch (x.kind) {
case Special::Kind::VOID:
return hash_("void");
case Special::Kind::VARIADIC:
return hash_("variadic");
case Special::Kind::NULLPTR:
return hash_("nullptr");
}
}
HashValue StableHash::operator()(const PointerReference& x) {
return DecayHashCombine<2>(hash_('r', static_cast<uint32_t>(x.kind)),
(*this)(x.pointee_type_id));
}
HashValue StableHash::operator()(const PointerToMember& x) {
return DecayHashCombine<16>(hash_('n', (*this)(x.containing_type_id)),
(*this)(x.pointee_type_id));
}
HashValue StableHash::operator()(const Typedef& x) {
return hash_('t', x.name);
}
HashValue StableHash::operator()(const Qualified& x) {
return DecayHashCombine<2>(hash_('q', static_cast<uint32_t>(x.qualifier)),
(*this)(x.qualified_type_id));
}
HashValue StableHash::operator()(const Primitive& x) {
return hash_('p', x.name);
}
HashValue StableHash::operator()(const Array& x) {
return DecayHashCombine<2>(hash_('a', x.number_of_elements),
(*this)(x.element_type_id));
}
HashValue StableHash::operator()(const BaseClass& x) {
return DecayHashCombine<2>(hash_('b', static_cast<uint32_t>(x.inheritance)),
(*this)(x.type_id));
}
HashValue StableHash::operator()(const Method& x) {
return hash_(x.mangled_name);
}
HashValue StableHash::operator()(const Member& x) {
HashValue hash = hash_('m', x.name, x.bitsize);
hash = DecayHashCombine<20>(hash, hash_(x.offset));
if (x.name.empty()) {
return DecayHashCombine<2>(hash, (*this)(x.type_id));
} else {
return DecayHashCombine<8>(hash, (*this)(x.type_id));
}
}
HashValue StableHash::operator()(const StructUnion& x) {
HashValue hash = hash_('S', static_cast<uint32_t>(x.kind), x.name,
static_cast<bool>(x.definition));
if (!x.name.empty() || !x.definition) {
return hash;
}
auto h1 = DecayHashCombineInReverse<8>(x.definition->methods, *this);
auto h2 = DecayHashCombineInReverse<8>(x.definition->members, *this);
return DecayHashCombine<2>(hash, HashValue(h1.value ^ h2.value));
}
HashValue StableHash::operator()(const Enumeration& x) {
HashValue hash = hash_('e', x.name, static_cast<bool>(x.definition));
if (!x.name.empty() || !x.definition) {
return hash;
}
auto hash_enum = [this](const std::pair<std::string, int64_t>& e) {
return hash_(e.first, e.second);
};
return DecayHashCombine<2>(
hash, DecayHashCombineInReverse<8>(x.definition->enumerators, hash_enum));
}
HashValue StableHash::operator()(const Function& x) {
return DecayHashCombine<2>(hash_('f', (*this)(x.return_type_id)),
DecayHashCombineInReverse<4>(x.parameters, *this));
}
HashValue StableHash::operator()(const ElfSymbol& x) {
HashValue hash = hash_('s', x.symbol_name);
if (x.version_info) {
hash = DecayHashCombine<16>(
hash, hash_(x.version_info->name, x.version_info->is_default));
}
return hash;
}
HashValue StableHash::operator()(const Interface&) {
return hash_("interface");
}
} // namespace stg