1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- mode: C++ -*-
3 //
4 // Copyright 2023-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 "fidelity.h"
21
22 #include <map>
23 #include <ostream>
24 #include <set>
25 #include <string>
26 #include <unordered_map>
27 #include <utility>
28 #include <vector>
29
30 #include "graph.h"
31 #include "naming.h"
32
33 namespace stg {
34
35 namespace {
36
37 struct Fidelity {
Fidelitystg::__anon65cde4720111::Fidelity38 Fidelity(const Graph& graph, NameCache& name_cache)
39 : graph(graph), describe(graph, name_cache), seen(Id(0)) {
40 seen.Reserve(graph.Limit());
41 }
42
43 void operator()(Id);
44 void operator()(const std::vector<Id>&);
45 void operator()(const std::map<std::string, Id>&);
46 void operator()(const Special&, Id);
47 void operator()(const PointerReference&, Id);
48 void operator()(const PointerToMember&, Id);
49 void operator()(const Typedef&, Id);
50 void operator()(const Qualified&, Id);
51 void operator()(const Primitive&, Id);
52 void operator()(const Array&, Id);
53 void operator()(const BaseClass&, Id);
54 void operator()(const Method&, Id);
55 void operator()(const Member&, Id);
56 void operator()(const VariantMember&, Id);
57 void operator()(const StructUnion&, Id);
58 void operator()(const Enumeration&, Id);
59 void operator()(const Variant&, Id);
60 void operator()(const Function&, Id);
61 void operator()(const ElfSymbol&, Id);
62 void operator()(const Interface&, Id);
63
64 const Graph& graph;
65 Describe describe;
66 DenseIdSet seen;
67 std::unordered_map<std::string, SymbolFidelity> symbols;
68 std::unordered_map<std::string, TypeFidelity> types;
69 };
70
operator ()(Id id)71 void Fidelity::operator()(Id id) {
72 if (seen.Insert(id)) {
73 graph.Apply(*this, id, id);
74 }
75 }
76
operator ()(const std::vector<Id> & x)77 void Fidelity::operator()(const std::vector<Id>& x) {
78 for (auto id : x) {
79 (*this)(id);
80 }
81 }
82
operator ()(const std::map<std::string,Id> & x)83 void Fidelity::operator()(const std::map<std::string, Id>& x) {
84 for (const auto& [_, id] : x) {
85 (*this)(id);
86 }
87 }
88
operator ()(const Special &,Id)89 void Fidelity::operator()(const Special&, Id) {}
90
operator ()(const PointerReference & x,Id)91 void Fidelity::operator()(const PointerReference& x, Id) {
92 (*this)(x.pointee_type_id);
93 }
94
operator ()(const PointerToMember & x,Id)95 void Fidelity::operator()(const PointerToMember& x, Id) {
96 (*this)(x.containing_type_id);
97 (*this)(x.pointee_type_id);
98 }
99
operator ()(const Typedef & x,Id)100 void Fidelity::operator()(const Typedef& x, Id) {
101 (*this)(x.referred_type_id);
102 }
103
operator ()(const Qualified & x,Id)104 void Fidelity::operator()(const Qualified& x, Id) {
105 (*this)(x.qualified_type_id);
106 }
107
operator ()(const Primitive &,Id)108 void Fidelity::operator()(const Primitive&, Id) {}
109
operator ()(const Array & x,Id)110 void Fidelity::operator()(const Array& x, Id) {
111 (*this)(x.element_type_id);
112 }
113
operator ()(const BaseClass & x,Id)114 void Fidelity::operator()(const BaseClass& x, Id) {
115 (*this)(x.type_id);
116 }
117
operator ()(const Method & x,Id)118 void Fidelity::operator()(const Method& x, Id) {
119 (*this)(x.type_id);
120 }
121
operator ()(const Member & x,Id)122 void Fidelity::operator()(const Member& x, Id) {
123 (*this)(x.type_id);
124 }
125
operator ()(const VariantMember & x,Id)126 void Fidelity::operator()(const VariantMember& x, Id) {
127 (*this)(x.type_id);
128 }
129
operator ()(const StructUnion & x,Id id)130 void Fidelity::operator()(const StructUnion& x, Id id) {
131 if (!x.name.empty()) {
132 auto [it, _] =
133 types.emplace(describe(id).ToString(), TypeFidelity::DECLARATION_ONLY);
134 if (x.definition) {
135 it->second = TypeFidelity::FULLY_DEFINED;
136 }
137 }
138 if (x.definition) {
139 (*this)(x.definition->base_classes);
140 (*this)(x.definition->methods);
141 (*this)(x.definition->members);
142 }
143 }
144
operator ()(const Enumeration & x,Id id)145 void Fidelity::operator()(const Enumeration& x, Id id) {
146 if (!x.name.empty()) {
147 auto [it, _] =
148 types.emplace(describe(id).ToString(), TypeFidelity::DECLARATION_ONLY);
149 if (x.definition) {
150 it->second = TypeFidelity::FULLY_DEFINED;
151 }
152 }
153 }
154
operator ()(const Variant & x,Id id)155 void Fidelity::operator()(const Variant& x, Id id) {
156 types.emplace(describe(id).ToString(), TypeFidelity::FULLY_DEFINED);
157 (*this)(x.members);
158 }
159
operator ()(const Function & x,Id)160 void Fidelity::operator()(const Function& x, Id) {
161 (*this)(x.return_type_id);
162 (*this)(x.parameters);
163 }
164
operator ()(const ElfSymbol & x,Id)165 void Fidelity::operator()(const ElfSymbol& x, Id) {
166 auto symbol = VersionedSymbolName(x);
167 auto [it, _] = symbols.emplace(symbol, SymbolFidelity::UNTYPED);
168 if (x.type_id) {
169 it->second = SymbolFidelity::TYPED;
170 (*this)(*x.type_id);
171 }
172 }
173
operator ()(const Interface & x,Id)174 void Fidelity::operator()(const Interface& x, Id) {
175 (*this)(x.symbols);
176 (*this)(x.types);
177 }
178
179 template <typename T>
GetKeys(const std::unordered_map<std::string,T> & x1,const std::unordered_map<std::string,T> & x2)180 std::set<std::string> GetKeys(
181 const std::unordered_map<std::string, T>& x1,
182 const std::unordered_map<std::string, T>& x2) {
183 std::set<std::string> keys;
184 for (const auto& [key, _] : x1) {
185 keys.insert(key);
186 }
187 for (const auto& [key, _] : x2) {
188 keys.insert(key);
189 }
190 return keys;
191 }
192
InsertTransition(FidelityDiff & diff,SymbolFidelityTransition transition,const std::string & symbol)193 void InsertTransition(FidelityDiff& diff, SymbolFidelityTransition transition,
194 const std::string& symbol) {
195 diff.symbol_transitions[transition].push_back(symbol);
196 }
197
InsertTransition(FidelityDiff & diff,TypeFidelityTransition transition,const std::string & type)198 void InsertTransition(FidelityDiff& diff, TypeFidelityTransition transition,
199 const std::string& type) {
200 diff.type_transitions[transition].push_back(type);
201 }
202
203 template <typename T>
InsertTransitions(FidelityDiff & diff,const std::unordered_map<std::string,T> & x1,const std::unordered_map<std::string,T> & x2)204 void InsertTransitions(FidelityDiff& diff,
205 const std::unordered_map<std::string, T>& x1,
206 const std::unordered_map<std::string, T>& x2) {
207 for (const auto& key : GetKeys(x1, x2)) {
208 auto it1 = x1.find(key);
209 auto it2 = x2.find(key);
210 auto transition = std::make_pair(it1 == x1.end() ? T() : it1->second,
211 it2 == x2.end() ? T() : it2->second);
212 InsertTransition(diff, transition, key);
213 }
214 }
215
216 } // namespace
217
operator <<(std::ostream & os,SymbolFidelity x)218 std::ostream& operator<<(std::ostream& os, SymbolFidelity x) {
219 switch (x) {
220 case SymbolFidelity::ABSENT:
221 return os << "ABSENT";
222 case SymbolFidelity::TYPED:
223 return os << "TYPED";
224 case SymbolFidelity::UNTYPED:
225 return os << "UNTYPED";
226 }
227 }
228
operator <<(std::ostream & os,TypeFidelity x)229 std::ostream& operator<<(std::ostream& os, TypeFidelity x) {
230 switch (x) {
231 case TypeFidelity::ABSENT:
232 return os << "ABSENT";
233 case TypeFidelity::DECLARATION_ONLY:
234 return os << "DECLARATION_ONLY";
235 case TypeFidelity::FULLY_DEFINED:
236 return os << "FULLY_DEFINED";
237 }
238 }
239
operator <<(std::ostream & os,SymbolFidelityTransition x)240 std::ostream& operator<<(std::ostream& os, SymbolFidelityTransition x) {
241 return os << "symbol(s) changed from " << x.first << " to " << x.second;
242 }
243
operator <<(std::ostream & os,TypeFidelityTransition x)244 std::ostream& operator<<(std::ostream& os, TypeFidelityTransition x) {
245 return os << "type(s) changed from " << x.first << " to " << x.second;
246 }
247
GetFidelityTransitions(const Graph & graph,Id root1,Id root2)248 FidelityDiff GetFidelityTransitions(const Graph& graph, Id root1, Id root2) {
249 NameCache name_cache;
250 Fidelity fidelity1(graph, name_cache);
251 Fidelity fidelity2(graph, name_cache);
252 fidelity1(root1);
253 fidelity2(root2);
254
255 FidelityDiff diff;
256 InsertTransitions(diff, fidelity1.symbols, fidelity2.symbols);
257 InsertTransitions(diff, fidelity1.types, fidelity2.types);
258 return diff;
259 }
260
261 } // namespace stg
262