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