// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -*- mode: C++ -*- // // Copyright 2023-2024 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 "fidelity.h" #include #include #include #include #include #include #include #include "graph.h" #include "naming.h" namespace stg { namespace { struct Fidelity { Fidelity(const Graph& graph, NameCache& name_cache) : graph(graph), describe(graph, name_cache), seen(Id(0), graph.Limit()) {} void operator()(Id); void operator()(const std::vector&); void operator()(const std::map&); void operator()(const Special&, Id); void operator()(const PointerReference&, Id); void operator()(const PointerToMember&, Id); void operator()(const Typedef&, Id); void operator()(const Qualified&, Id); void operator()(const Primitive&, Id); void operator()(const Array&, Id); void operator()(const BaseClass&, Id); void operator()(const Method&, Id); void operator()(const Member&, Id); void operator()(const VariantMember&, Id); void operator()(const StructUnion&, Id); void operator()(const Enumeration&, Id); void operator()(const Variant&, Id); void operator()(const Function&, Id); void operator()(const ElfSymbol&, Id); void operator()(const Interface&, Id); const Graph& graph; Describe describe; DenseIdSet seen; std::unordered_map symbols; std::unordered_map types; }; void Fidelity::operator()(Id id) { if (seen.Insert(id)) { graph.Apply(*this, id, id); } } void Fidelity::operator()(const std::vector& x) { for (auto id : x) { (*this)(id); } } void Fidelity::operator()(const std::map& x) { for (const auto& [_, id] : x) { (*this)(id); } } void Fidelity::operator()(const Special&, Id) {} void Fidelity::operator()(const PointerReference& x, Id) { (*this)(x.pointee_type_id); } void Fidelity::operator()(const PointerToMember& x, Id) { (*this)(x.containing_type_id); (*this)(x.pointee_type_id); } void Fidelity::operator()(const Typedef& x, Id) { (*this)(x.referred_type_id); } void Fidelity::operator()(const Qualified& x, Id) { (*this)(x.qualified_type_id); } void Fidelity::operator()(const Primitive&, Id) {} void Fidelity::operator()(const Array& x, Id) { (*this)(x.element_type_id); } void Fidelity::operator()(const BaseClass& x, Id) { (*this)(x.type_id); } void Fidelity::operator()(const Method& x, Id) { (*this)(x.type_id); } void Fidelity::operator()(const Member& x, Id) { (*this)(x.type_id); } void Fidelity::operator()(const VariantMember& x, Id) { (*this)(x.type_id); } void Fidelity::operator()(const StructUnion& x, Id id) { if (!x.name.empty()) { auto [it, _] = types.emplace(describe(id).ToString(), TypeFidelity::DECLARATION_ONLY); if (x.definition) { it->second = TypeFidelity::FULLY_DEFINED; } } if (x.definition) { (*this)(x.definition->base_classes); (*this)(x.definition->methods); (*this)(x.definition->members); } } void Fidelity::operator()(const Enumeration& x, Id id) { if (!x.name.empty()) { auto [it, _] = types.emplace(describe(id).ToString(), TypeFidelity::DECLARATION_ONLY); if (x.definition) { it->second = TypeFidelity::FULLY_DEFINED; } } } void Fidelity::operator()(const Variant& x, Id id) { types.emplace(describe(id).ToString(), TypeFidelity::FULLY_DEFINED); (*this)(x.members); } void Fidelity::operator()(const Function& x, Id) { (*this)(x.return_type_id); (*this)(x.parameters); } void Fidelity::operator()(const ElfSymbol& x, Id) { auto symbol = VersionedSymbolName(x); auto [it, _] = symbols.emplace(symbol, SymbolFidelity::UNTYPED); if (x.type_id) { it->second = SymbolFidelity::TYPED; (*this)(*x.type_id); } } void Fidelity::operator()(const Interface& x, Id) { (*this)(x.symbols); (*this)(x.types); } template std::set GetKeys( const std::unordered_map& x1, const std::unordered_map& x2) { std::set keys; for (const auto& [key, _] : x1) { keys.insert(key); } for (const auto& [key, _] : x2) { keys.insert(key); } return keys; } void InsertTransition(FidelityDiff& diff, SymbolFidelityTransition transition, const std::string& symbol) { diff.symbol_transitions[transition].push_back(symbol); } void InsertTransition(FidelityDiff& diff, TypeFidelityTransition transition, const std::string& type) { diff.type_transitions[transition].push_back(type); } template void InsertTransitions(FidelityDiff& diff, const std::unordered_map& x1, const std::unordered_map& x2) { for (const auto& key : GetKeys(x1, x2)) { auto it1 = x1.find(key); auto it2 = x2.find(key); auto transition = std::make_pair(it1 == x1.end() ? T() : it1->second, it2 == x2.end() ? T() : it2->second); InsertTransition(diff, transition, key); } } } // namespace std::ostream& operator<<(std::ostream& os, SymbolFidelity x) { switch (x) { case SymbolFidelity::ABSENT: return os << "ABSENT"; case SymbolFidelity::TYPED: return os << "TYPED"; case SymbolFidelity::UNTYPED: return os << "UNTYPED"; } } std::ostream& operator<<(std::ostream& os, TypeFidelity x) { switch (x) { case TypeFidelity::ABSENT: return os << "ABSENT"; case TypeFidelity::DECLARATION_ONLY: return os << "DECLARATION_ONLY"; case TypeFidelity::FULLY_DEFINED: return os << "FULLY_DEFINED"; } } std::ostream& operator<<(std::ostream& os, SymbolFidelityTransition x) { return os << "symbol(s) changed from " << x.first << " to " << x.second; } std::ostream& operator<<(std::ostream& os, TypeFidelityTransition x) { return os << "type(s) changed from " << x.first << " to " << x.second; } FidelityDiff GetFidelityTransitions(const Graph& graph, Id root1, Id root2) { NameCache name_cache; Fidelity fidelity1(graph, name_cache); Fidelity fidelity2(graph, name_cache); fidelity1(root1); fidelity2(root2); FidelityDiff diff; InsertTransitions(diff, fidelity1.symbols, fidelity2.symbols); InsertTransitions(diff, fidelity1.types, fidelity2.types); return diff; } } // namespace stg