xref: /aosp_15_r20/external/stg/unification.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: Giuliano Procida
19 
20 #include "unification.h"
21 
22 #include <cstddef>
23 #include <map>
24 #include <optional>
25 #include <utility>
26 #include <unordered_map>
27 #include <unordered_set>
28 #include <vector>
29 
30 #include "graph.h"
31 
32 namespace stg {
33 
34 namespace {
35 
36 // Type Unification
37 //
38 // This is very similar to Equals. The differences are the recursion control,
39 // caching and handling of StructUnion and Enum nodes.
40 //
41 // During unification, keep track of which pairs of types need to be equal, but
42 // do not add them immediately to the unification substitutions. The caller can
43 // do that if the whole unification succeeds.
44 //
45 // A declaration and definition of the same named type can be unified. This is
46 // forward declaration resolution.
47 struct Unifier {
48   enum Winner { Neither, Right, Left };  // makes p ? Right : Neither a no-op
49 
Unifierstg::__anon655e05910111::Unifier50   Unifier(const Graph& graph, Unification& unification)
51       : graph(graph), unification(unification) {}
52 
operator ()stg::__anon655e05910111::Unifier53   bool operator()(Id id1, Id id2) {
54     Id fid1 = Find(id1);
55     Id fid2 = Find(id2);
56     if (fid1 == fid2) {
57       return true;
58     }
59 
60     // Check if the comparison has been (or is being) visited already. We don't
61     // need an SCC finder as any failure to unify will poison the entire DFS.
62     //
63     // This prevents infinite recursion, but maybe not immediately as seen is
64     // unaware of new mappings.
65     if (!seen.emplace(fid1, fid2).second) {
66       return true;
67     }
68 
69     const auto winner = graph.Apply2(*this, fid1, fid2);
70     if (winner == Neither) {
71       return false;
72     }
73 
74     // These will occasionally get substituted due to a recursive call.
75     fid1 = Find(fid1);
76     fid2 = Find(fid2);
77     if (fid1 == fid2) {
78       return true;
79     }
80 
81     if (winner == Left) {
82       std::swap(fid1, fid2);
83     }
84     mapping.insert({fid1, fid2});
85 
86     return true;
87   }
88 
operator ()stg::__anon655e05910111::Unifier89   bool operator()(const std::optional<Id>& opt1,
90                   const std::optional<Id>& opt2) {
91     if (opt1.has_value() && opt2.has_value()) {
92       return (*this)(opt1.value(), opt2.value());
93     }
94     return opt1.has_value() == opt2.has_value();
95   }
96 
operator ()stg::__anon655e05910111::Unifier97   bool operator()(const std::vector<Id>& ids1, const std::vector<Id>& ids2) {
98     bool result = ids1.size() == ids2.size();
99     for (size_t ix = 0; result && ix < ids1.size(); ++ix) {
100       result = (*this)(ids1[ix], ids2[ix]);
101     }
102     return result;
103   }
104 
105   template <typename Key>
operator ()stg::__anon655e05910111::Unifier106   bool operator()(const std::map<Key, Id>& ids1,
107                   const std::map<Key, Id>& ids2) {
108     bool result = ids1.size() == ids2.size();
109     auto it1 = ids1.begin();
110     auto it2 = ids2.begin();
111     const auto end1 = ids1.end();
112     const auto end2 = ids2.end();
113     while (result && it1 != end1 && it2 != end2) {
114       result = it1->first == it2->first
115                && (*this)(it1->second, it2->second);
116       ++it1;
117       ++it2;
118     }
119     return result && it1 == end1 && it2 == end2;
120   }
121 
operator ()stg::__anon655e05910111::Unifier122   Winner operator()(const Special& x1, const Special& x2) {
123     return x1.kind == x2.kind
124         ? Right : Neither;
125   }
126 
operator ()stg::__anon655e05910111::Unifier127   Winner operator()(const PointerReference& x1,
128                     const PointerReference& x2) {
129     return x1.kind == x2.kind
130         && (*this)(x1.pointee_type_id, x2.pointee_type_id)
131         ? Right : Neither;
132   }
133 
operator ()stg::__anon655e05910111::Unifier134   Winner operator()(const PointerToMember& x1, const PointerToMember& x2) {
135     return (*this)(x1.containing_type_id, x2.containing_type_id)
136         && (*this)(x1.pointee_type_id, x2.pointee_type_id)
137         ? Right : Neither;
138   }
139 
operator ()stg::__anon655e05910111::Unifier140   Winner operator()(const Typedef& x1, const Typedef& x2) {
141     return x1.name == x2.name
142         && (*this)(x1.referred_type_id, x2.referred_type_id)
143         ? Right : Neither;
144   }
145 
operator ()stg::__anon655e05910111::Unifier146   Winner operator()(const Qualified& x1, const Qualified& x2) {
147     return x1.qualifier == x2.qualifier
148         && (*this)(x1.qualified_type_id, x2.qualified_type_id)
149         ? Right : Neither;
150   }
151 
operator ()stg::__anon655e05910111::Unifier152   Winner operator()(const Primitive& x1, const Primitive& x2) {
153     return x1.name == x2.name
154         && x1.encoding == x2.encoding
155         && x1.bytesize == x2.bytesize
156         ? Right : Neither;
157   }
158 
operator ()stg::__anon655e05910111::Unifier159   Winner operator()(const Array& x1, const Array& x2) {
160     return x1.number_of_elements == x2.number_of_elements
161         && (*this)(x1.element_type_id, x2.element_type_id)
162         ? Right : Neither;
163   }
164 
operator ()stg::__anon655e05910111::Unifier165   Winner operator()(const BaseClass& x1, const BaseClass& x2) {
166     return x1.offset == x2.offset
167         && x1.inheritance == x2.inheritance
168         && (*this)(x1.type_id, x2.type_id)
169         ? Right : Neither;
170   }
171 
operator ()stg::__anon655e05910111::Unifier172   Winner operator()(const Method& x1, const Method& x2) {
173     return x1.mangled_name == x2.mangled_name
174         && x1.name == x2.name
175         && x1.vtable_offset == x2.vtable_offset
176         && (*this)(x1.type_id, x2.type_id)
177         ? Right : Neither;
178   }
179 
operator ()stg::__anon655e05910111::Unifier180   Winner operator()(const Member& x1, const Member& x2) {
181     return x1.name == x2.name
182         && x1.offset == x2.offset
183         && x1.bitsize == x2.bitsize
184         && (*this)(x1.type_id, x2.type_id)
185         ? Right : Neither;
186   }
187 
operator ()stg::__anon655e05910111::Unifier188   Winner operator()(const VariantMember& x1, const VariantMember& x2) {
189     return x1.name == x2.name
190         && x1.discriminant_value == x2.discriminant_value
191         && (*this)(x1.type_id, x2.type_id)
192         ? Right : Neither;
193   }
194 
operator ()stg::__anon655e05910111::Unifier195   Winner operator()(const StructUnion& x1, const StructUnion& x2) {
196     const auto& definition1 = x1.definition;
197     const auto& definition2 = x2.definition;
198     bool result = x1.kind == x2.kind
199                   && x1.name == x2.name;
200     // allow mismatches as forward declarations are always unifiable
201     if (result && definition1.has_value() && definition2.has_value()) {
202       result = definition1->bytesize == definition2->bytesize
203                && (*this)(definition1->base_classes, definition2->base_classes)
204                && (*this)(definition1->methods, definition2->methods)
205                && (*this)(definition1->members, definition2->members);
206     }
207     return result ? definition2.has_value() ? Right : Left : Neither;
208   }
209 
operator ()stg::__anon655e05910111::Unifier210   Winner operator()(const Enumeration& x1, const Enumeration& x2) {
211     const auto& definition1 = x1.definition;
212     const auto& definition2 = x2.definition;
213     bool result = x1.name == x2.name;
214     // allow mismatches as forward declarations are always unifiable
215     if (result && definition1.has_value() && definition2.has_value()) {
216       result = (*this)(definition1->underlying_type_id,
217                        definition2->underlying_type_id)
218                && definition1->enumerators == definition2->enumerators;
219     }
220     return result ? definition2.has_value() ? Right : Left : Neither;
221   }
222 
operator ()stg::__anon655e05910111::Unifier223   Winner operator()(const Variant& x1, const Variant& x2) {
224     return x1.name == x2.name
225         && x1.bytesize == x2.bytesize
226         && (*this)(x1.discriminant, x2.discriminant)
227         && (*this)(x1.members, x2.members)
228         ? Right : Neither;
229   }
230 
operator ()stg::__anon655e05910111::Unifier231   Winner operator()(const Function& x1, const Function& x2) {
232     return (*this)(x1.parameters, x2.parameters)
233         && (*this)(x1.return_type_id, x2.return_type_id)
234         ? Right : Neither;
235   }
236 
operator ()stg::__anon655e05910111::Unifier237   Winner operator()(const ElfSymbol& x1, const ElfSymbol& x2) {
238     bool result = x1.symbol_name == x2.symbol_name
239                   && x1.version_info == x2.version_info
240                   && x1.is_defined == x2.is_defined
241                   && x1.symbol_type == x2.symbol_type
242                   && x1.binding == x2.binding
243                   && x1.visibility == x2.visibility
244                   && x1.crc == x2.crc
245                   && x1.ns == x2.ns
246                   && x1.full_name == x2.full_name
247                   && x1.type_id.has_value() == x2.type_id.has_value();
248     if (result && x1.type_id.has_value()) {
249       result = (*this)(x1.type_id.value(), x2.type_id.value());
250     }
251     return result ? Right : Neither;
252   }
253 
operator ()stg::__anon655e05910111::Unifier254   Winner operator()(const Interface& x1, const Interface& x2) {
255     return (*this)(x1.symbols, x2.symbols)
256         && (*this)(x1.types, x2.types)
257         ? Right : Neither;
258   }
259 
Mismatchstg::__anon655e05910111::Unifier260   Winner Mismatch() {
261     return Neither;
262   }
263 
Findstg::__anon655e05910111::Unifier264   Id Find(Id id) {
265     while (true) {
266       id = unification.Find(id);
267       auto it = mapping.find(id);
268       if (it != mapping.end()) {
269         id = it->second;
270         continue;
271       }
272       return id;
273     }
274   }
275 
276   const Graph& graph;
277   Unification& unification;
278   std::unordered_set<Pair> seen;
279   std::unordered_map<Id, Id> mapping;
280 };
281 
282 }  // namespace
283 
Unify(Id id1,Id id2)284 bool Unification::Unify(Id id1, Id id2) {
285   // TODO: Unifier only needs access to Unification::Find
286   Unifier unifier(graph_, *this);
287   if (unifier(id1, id2)) {
288     // commit
289     for (const auto& s : unifier.mapping) {
290       Union(s.first, s.second);
291     }
292     return true;
293   }
294   return false;
295 }
296 
297 }  // namespace stg
298