1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <abi_diff_helpers.h>
16 #include <ir_representation.h>
17 #include <ir_representation_protobuf.h>
18
19 #pragma clang diagnostic push
20 #pragma clang diagnostic ignored "-Wunused-parameter"
21 #pragma clang diagnostic ignored "-Wnested-anon-types"
22 #include "proto/abi_dump.pb.h"
23 #pragma clang diagnostic pop
24
25 #include <google/protobuf/text_format.h>
26 #include <google/protobuf/io/zero_copy_stream_impl.h>
27
28 #include <llvm/Support/raw_ostream.h>
29
30 #include <string>
31 #include <memory>
32
33
34 namespace abi_util {
35
36 using MergeStatus = TextFormatToIRReader::MergeStatus;
37
CreateIRDumper(TextFormatIR text_format,const std::string & dump_path)38 std::unique_ptr<IRDumper> IRDumper::CreateIRDumper(
39 TextFormatIR text_format, const std::string &dump_path) {
40 switch (text_format) {
41 case TextFormatIR::ProtobufTextFormat:
42 return std::make_unique<ProtobufIRDumper>(dump_path);
43 default:
44 // Nothing else is supported yet.
45 llvm::errs() << "Text format not supported yet\n";
46 return nullptr;
47 }
48 }
49
CreateIRDiffDumper(TextFormatIR text_format,const std::string & dump_path)50 std::unique_ptr<IRDiffDumper> IRDiffDumper::CreateIRDiffDumper(
51 TextFormatIR text_format, const std::string &dump_path) {
52 switch (text_format) {
53 case TextFormatIR::ProtobufTextFormat:
54 return std::make_unique<ProtobufIRDiffDumper>(dump_path);
55 default:
56 // Nothing else is supported yet.
57 llvm::errs() << "Text format not supported yet\n";
58 return nullptr;
59 }
60 }
61
62 std::unique_ptr<TextFormatToIRReader>
CreateTextFormatToIRReader(TextFormatIR text_format,const std::set<std::string> * exported_headers)63 TextFormatToIRReader::CreateTextFormatToIRReader(
64 TextFormatIR text_format, const std::set<std::string> *exported_headers) {
65 switch (text_format) {
66 case TextFormatIR::ProtobufTextFormat:
67 return std::make_unique<ProtobufTextFormatToIRReader>(exported_headers);
68 default:
69 // Nothing else is supported yet.
70 llvm::errs() << "Text format not supported yet\n";
71 return nullptr;
72 }
73 }
74
AddToODRListMap(const std::string & key,const TypeIR * value)75 void TextFormatToIRReader::AddToODRListMap(
76 const std::string &key,
77 const TypeIR *value) {
78 auto map_it = odr_list_map_.find(key);
79 if (map_it == odr_list_map_.end()) {
80 odr_list_map_.emplace(key, std::list<const TypeIR *>({value}));
81 return;
82 }
83 odr_list_map_[key].emplace_back(value);
84 }
85
IsBuiltinTypeNodePresent(const BuiltinTypeIR * builtin_type,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)86 MergeStatus TextFormatToIRReader::IsBuiltinTypeNodePresent(
87 const BuiltinTypeIR *builtin_type, const TextFormatToIRReader &addend,
88 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
89
90 std::string builtin_linker_set_key = builtin_type->GetLinkerSetKey();
91 auto builtin_it = builtin_types_.find(builtin_linker_set_key);
92 if (builtin_it != builtin_types_.end()) {
93 return MergeStatus(false, builtin_it->second.GetSelfType());
94 }
95 // Add this builtin type to the parent graph's builtin_types_ map.
96 // Before that, correct the type id of the builtin-type.
97 const std::string &local_type_id = builtin_type->GetSelfType();
98 std::string builtin_global_type_id = AllocateNewTypeId();
99 auto it = builtin_types_.emplace(builtin_linker_set_key, *builtin_type);
100 it.first->second.SetSelfType(builtin_global_type_id);
101 it.first->second.SetReferencedType(builtin_global_type_id);
102 type_graph_.emplace(builtin_global_type_id, &((it.first)->second));
103
104 MergeStatus merge_status(true, builtin_global_type_id);
105 local_to_global_type_id_map->emplace(local_type_id, merge_status);
106 return merge_status;
107 }
108
DoesUDTypeODRViolationExist(const TypeIR * ud_type,const TextFormatToIRReader & addend,const std::string ud_type_unique_id_and_source,AbiElementMap<MergeStatus> * local_to_global_type_id_map_)109 MergeStatus TextFormatToIRReader::DoesUDTypeODRViolationExist(
110 const TypeIR *ud_type, const TextFormatToIRReader &addend,
111 const std::string ud_type_unique_id_and_source,
112 AbiElementMap<MergeStatus> *local_to_global_type_id_map_) {
113 // Per entry in the map :
114 // /-----------------------------------------------------------------------\
115 // | UDType->UniqueTagId + UdType->source File => list(const UDTypeIR *)|
116 // \-----------------------------------------------------------------------/
117 auto it = odr_list_map_.find(ud_type_unique_id_and_source);
118 if (it == odr_list_map_.end()) {
119 // Calling this an ODR violation even though it means no UD with the same
120 // name + source combination was seen in the parent graph. The type-id
121 // passed does not matter since was_newly_added_ is true, the type will get
122 // allocated a new type id.
123 return MergeStatus(true, "");
124 }
125 std::set<std::string> type_cache;
126 AbiDiffHelper diff_helper(type_graph_, addend.type_graph_, &type_cache,
127 nullptr, local_to_global_type_id_map_);
128 for (auto &contender_ud : it->second) {
129 if (diff_helper.CompareAndDumpTypeDiff(contender_ud->GetSelfType(),
130 ud_type->GetSelfType())
131 == DiffStatus::no_diff) {
132 local_to_global_type_id_map_->emplace(ud_type->GetSelfType(),
133 MergeStatus(
134 false,
135 contender_ud->GetSelfType()));
136 return MergeStatus(false, contender_ud->GetSelfType());
137 }
138 }
139 #ifdef DEBUG
140 llvm::errs() << "ODR violation detected for :" << ud_type->GetName() << "\n";
141 #endif
142 return MergeStatus(true, (*(it->second.begin()))->GetSelfType());
143 }
144
IsTypeNodePresent(const TypeIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)145 MergeStatus TextFormatToIRReader::IsTypeNodePresent(
146 const TypeIR *addend_node, const TextFormatToIRReader &addend,
147 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
148 std::string unique_type_id;
149 switch(addend_node->GetKind()) {
150 case RecordTypeKind:
151 unique_type_id =
152 GetODRListMapKey(static_cast<const RecordTypeIR *>(addend_node));
153 break;
154 case EnumTypeKind:
155 unique_type_id =
156 GetODRListMapKey(static_cast<const EnumTypeIR *>(addend_node));
157 break;
158 case FunctionTypeKind:
159 unique_type_id =
160 GetODRListMapKey(static_cast<const FunctionTypeIR *>(addend_node));
161 break;
162 default:
163 // We add the type proactively.
164 return MergeStatus(true, "type-hidden");
165 }
166 // Every other type is a referencing type / builtin type, so it is proactively
167 // added by returning MergeStatus with was_newly_added_ = true.
168 return DoesUDTypeODRViolationExist(
169 addend_node, addend, unique_type_id, local_to_global_type_id_map);
170 }
171
172 // This method merges the type referenced by 'references_type' into the parent
173 // graph. It also corrects the referenced_type field in the references_type
174 // object passed and returns the merge status of the *referenced type*.
MergeReferencingTypeInternal(const TextFormatToIRReader & addend,ReferencesOtherType * references_type,AbiElementMap<MergeStatus> * local_to_global_type_id_map)175 MergeStatus TextFormatToIRReader::MergeReferencingTypeInternal(
176 const TextFormatToIRReader &addend,
177 ReferencesOtherType *references_type,
178 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
179 // First look in the local_to_global_type_id_map for the referenced type's
180 // id.
181 const std::string &referenced_type_id =
182 references_type->GetReferencedType();
183 auto local_to_global_it = local_to_global_type_id_map->find(
184 referenced_type_id);
185 if (local_to_global_it != local_to_global_type_id_map->end()) {
186 // The type was already added to the parent graph. So change the
187 // referenced type to the global type id.
188 references_type->SetReferencedType(local_to_global_it->second.type_id_);
189 return local_to_global_it->second;
190 }
191 // If that did not go through, look at the addend's type_map_ and get the
192 // TypeIR* and call MergeType on it.
193 auto local_type_it = addend.type_graph_.find(referenced_type_id);
194 if (local_type_it != addend.type_graph_.end()) {
195 // We don't care about merge_status.was_newly_added since we wouldn't have
196 // gotten this far if we weren't adding this.
197 MergeStatus merge_status =
198 MergeType(local_type_it->second, addend,
199 local_to_global_type_id_map);
200 const std::string &global_type_id = merge_status.type_id_;
201 references_type->SetReferencedType(global_type_id);
202 return merge_status;
203 }
204 // The referenced type was hidden, so just set it to type-hidden.
205 const std::string &hidden_type_id = AllocateNewTypeId();
206 references_type->SetReferencedType(hidden_type_id);
207 return MergeStatus(true, hidden_type_id);
208 }
209
MergeRecordFields(const TextFormatToIRReader & addend,RecordTypeIR * added_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map)210 void TextFormatToIRReader::MergeRecordFields(
211 const TextFormatToIRReader &addend, RecordTypeIR *added_node,
212 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
213 for(auto &field : added_node->GetFields()) {
214 MergeReferencingTypeInternal(addend, &field, local_to_global_type_id_map);
215 }
216 }
217
MergeRecordCXXBases(const TextFormatToIRReader & addend,RecordTypeIR * added_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map)218 void TextFormatToIRReader::MergeRecordCXXBases(
219 const TextFormatToIRReader &addend, RecordTypeIR *added_node,
220 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
221 for(auto &base : added_node->GetBases()) {
222 MergeReferencingTypeInternal(addend, &base, local_to_global_type_id_map);
223 }
224 }
225
MergeRecordTemplateElements(const TextFormatToIRReader & addend,RecordTypeIR * added_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map)226 void TextFormatToIRReader::MergeRecordTemplateElements(
227 const TextFormatToIRReader &addend, RecordTypeIR *added_node,
228 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
229 for(auto &template_element : added_node->GetTemplateElements()) {
230 MergeReferencingTypeInternal(addend, &template_element,
231 local_to_global_type_id_map);
232 }
233 }
234
MergeRecordDependencies(const TextFormatToIRReader & addend,RecordTypeIR * added_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map)235 void TextFormatToIRReader::MergeRecordDependencies(
236 const TextFormatToIRReader &addend, RecordTypeIR *added_node,
237 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
238 // First call MergeType on all its fields.
239 MergeRecordFields(addend, added_node, local_to_global_type_id_map);
240
241 // Call MergeType on CXXBases of the record.
242 MergeRecordCXXBases(addend, added_node, local_to_global_type_id_map);
243
244 MergeRecordTemplateElements(addend, added_node, local_to_global_type_id_map);
245 }
246
247 template <typename T>
248 std::pair<MergeStatus, typename AbiElementMap<T>::iterator>
UpdateUDTypeAccounting(const T * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map,AbiElementMap<T> * specific_type_map)249 TextFormatToIRReader::UpdateUDTypeAccounting(
250 const T *addend_node, const TextFormatToIRReader &addend,
251 AbiElementMap<MergeStatus> *local_to_global_type_id_map,
252 AbiElementMap<T> *specific_type_map) {
253 std::string added_type_id = AllocateNewTypeId();
254 // Add the ud-type with type-id to the type_graph_, since if there are generic
255 // reference types which refer to the record being added, they'll need to find
256 // it's id in the map.
257 // Add ud-type to the parent graph.
258 T added_type_ir = *addend_node;
259 added_type_ir.SetSelfType(added_type_id);
260 added_type_ir.SetReferencedType(added_type_id);
261 auto it = AddToMapAndTypeGraph(std::move(added_type_ir), specific_type_map,
262 &type_graph_);
263 // Add to faciliate ODR checking.
264 const std::string &key = GetODRListMapKey(&(it->second));
265 MergeStatus type_merge_status = MergeStatus(true, added_type_id);
266 AddToODRListMap(key, &(it->second));
267 local_to_global_type_id_map->emplace(addend_node->GetSelfType(),
268 type_merge_status);
269 return {type_merge_status, it};
270 }
271 // This method is necessarily going to have a was_newly_merged_ = true in its
272 // MergeStatus return. So it necessarily merges a new RecordType.
MergeRecordAndDependencies(const RecordTypeIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)273 MergeStatus TextFormatToIRReader::MergeRecordAndDependencies(
274 const RecordTypeIR *addend_node, const TextFormatToIRReader &addend,
275 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
276 auto merge_status_and_it =
277 UpdateUDTypeAccounting(addend_node, addend, local_to_global_type_id_map,
278 &record_types_);
279 auto it = merge_status_and_it.second;
280 MergeRecordDependencies(addend, &(it->second), local_to_global_type_id_map);
281 return merge_status_and_it.first;
282 }
283
MergeEnumDependencies(const TextFormatToIRReader & addend,EnumTypeIR * added_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map)284 void TextFormatToIRReader::MergeEnumDependencies(
285 const TextFormatToIRReader &addend, EnumTypeIR *added_node,
286 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
287 const std::string underlying_type_id = added_node->GetUnderlyingType();
288 // Get the underlying type, it nessarily has to be present in the addend's
289 // type graph since builtin types can't be hidden. Call MergeType on it and
290 // change the underlying type to that.
291 auto it = addend.type_graph_.find(underlying_type_id);
292 if (it == addend.type_graph_.end()) {
293 llvm::errs() << "Enum underlying types should not be hidden\n";
294 ::exit(1);
295 }
296 MergeStatus merge_status = MergeType(it->second, addend,
297 local_to_global_type_id_map);
298 added_node->SetUnderlyingType(merge_status.type_id_);
299 }
300
301 // This method is necessarily going to have a was_newly_merged_ = true in its
302 // MergeStatus return. So it necessarily merges a new EnumType.
MergeEnumType(const EnumTypeIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)303 MergeStatus TextFormatToIRReader::MergeEnumType(
304 const EnumTypeIR *addend_node, const TextFormatToIRReader &addend,
305 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
306 auto merge_status_and_it =
307 UpdateUDTypeAccounting(addend_node, addend, local_to_global_type_id_map,
308 &enum_types_);
309 auto it = merge_status_and_it.second;
310 MergeEnumDependencies(addend, &(it->second), local_to_global_type_id_map);
311 return merge_status_and_it.first;
312 }
313
MergeFunctionType(const FunctionTypeIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)314 MergeStatus TextFormatToIRReader::MergeFunctionType(
315 const FunctionTypeIR *addend_node, const TextFormatToIRReader &addend,
316 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
317 auto merge_status_and_it =
318 UpdateUDTypeAccounting(addend_node, addend, local_to_global_type_id_map,
319 &function_types_);
320 auto it = merge_status_and_it.second;
321 MergeCFunctionLikeDeps(addend, &(it->second), local_to_global_type_id_map);
322 return merge_status_and_it.first;
323 }
324
325 template <typename T>
MergeReferencingTypeInternalAndUpdateParent(const TextFormatToIRReader & addend,const T * addend_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map,AbiElementMap<T> * parent_map,const std::string & updated_self_type_id)326 MergeStatus TextFormatToIRReader::MergeReferencingTypeInternalAndUpdateParent(
327 const TextFormatToIRReader &addend, const T *addend_node,
328 AbiElementMap<MergeStatus> *local_to_global_type_id_map,
329 AbiElementMap<T> *parent_map, const std::string &updated_self_type_id) {
330 MergeStatus merge_status;
331 uint64_t old_max_type_id = max_type_id_;
332 // Create copy of addend_node
333 T added_node =
334 *(addend_node);
335 added_node.SetSelfType(updated_self_type_id);
336 // The merge status returned is the merge status of the referenced type.
337 merge_status = MergeReferencingTypeInternal(addend, &added_node,
338 local_to_global_type_id_map);
339 if (merge_status.was_newly_added_) {
340 // Emplace to map (type-referenced -> Referencing type)
341 AddToMapAndTypeGraph(std::move(added_node), parent_map,
342 &type_graph_);
343 return MergeStatus(true, updated_self_type_id);
344 }
345 // The type that the added_node references was not newly added to the parent
346 // graph. However, we still might need to add the added_node to the parent
347 // graph, since for the particular 'Kind' of the added_node, it may not be
348 // present in the parent graph. This will be determined by looking at the
349 // appropriate 'type-referenced' -> TypeElement map in the parent for the
350 // type-id returned by the MergeStatus. If the map doesn't have an entry for
351 // the type-id returned by the MergeStatus, the added_type is not present in
352 // the parent graph and needs to be 'newly' added. We also need to modify the
353 // global type id in the local_to_global_type_id map. The added_node should
354 // already have it's self_type and referenced_type fields fixed up.
355 // We maintain a rollback id to have contiguous type ids.
356 max_type_id_ = old_max_type_id;
357 // Try finding the referenced_type is referred to by any referencing type
358 // of the same kind in the parent graph. It is safe to call this on the
359 // added_node, since the referenced_type in the added_node would have been
360 // modified by the MergeReferencingTypeInternal call.
361 auto it = parent_map->find(GetReferencedTypeMapKey(added_node));
362 if (it == parent_map->end()) {
363 // There was no counterpart found for the added_node's type Kind referencing
364 // the referenced type, so we added it to the parent and also updated the
365 // local_to_global_type_id_map's global_id value.
366 AddToMapAndTypeGraph(std::move(added_node), parent_map,
367 &type_graph_);
368
369 merge_status = MergeStatus(true, updated_self_type_id);
370 return merge_status;
371 }
372 // Update local_to_global_type_id map's MergeStatus.was_newly_added value for
373 // this key with false since this was node was not newly added.
374 // We never remove anything from the local_to_global_type_id_map, what's
375 // the point ? Since you store the decision of whether the type was newly
376 // added or not. It's global type id is the type-id of the element found
377 // in the parent map which refers to the added_node's modified
378 // referenced_type.
379 merge_status = MergeStatus(false, it->second.GetSelfType());
380 (*local_to_global_type_id_map)[addend_node->GetSelfType()] =
381 merge_status;
382 return merge_status;
383 }
384
MergeReferencingType(const TextFormatToIRReader & addend,const TypeIR * addend_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map,const std::string & updated_self_type_id)385 MergeStatus TextFormatToIRReader::MergeReferencingType(
386 const TextFormatToIRReader &addend, const TypeIR *addend_node,
387 AbiElementMap<MergeStatus> *local_to_global_type_id_map,
388 const std::string &updated_self_type_id) {
389 switch (addend_node->GetKind()) {
390 case PointerTypeKind:
391 return MergeReferencingTypeInternalAndUpdateParent(
392 addend, static_cast<const PointerTypeIR *>(addend_node),
393 local_to_global_type_id_map, &pointer_types_, updated_self_type_id);
394 case QualifiedTypeKind:
395 return MergeReferencingTypeInternalAndUpdateParent(
396 addend, static_cast<const QualifiedTypeIR *>(addend_node),
397 local_to_global_type_id_map, &qualified_types_, updated_self_type_id);
398 case ArrayTypeKind:
399 return MergeReferencingTypeInternalAndUpdateParent(
400 addend, static_cast<const ArrayTypeIR *>(addend_node),
401 local_to_global_type_id_map, &array_types_, updated_self_type_id);
402 case LvalueReferenceTypeKind:
403 return MergeReferencingTypeInternalAndUpdateParent(
404 addend, static_cast<const LvalueReferenceTypeIR *>(addend_node),
405 local_to_global_type_id_map, &lvalue_reference_types_,
406 updated_self_type_id);
407 case RvalueReferenceTypeKind:
408 return MergeReferencingTypeInternalAndUpdateParent(
409 addend, static_cast<const RvalueReferenceTypeIR *>(addend_node),
410 local_to_global_type_id_map, &rvalue_reference_types_,
411 updated_self_type_id);
412 default:
413 // Only referencing types
414 assert(0);
415 }
416 }
417
418 // This method creates a new node for the addend node in the graph if MergeType
419 // on the reference returned a MergeStatus with was_newly_added_ = true.
MergeGenericReferringType(const TextFormatToIRReader & addend,const TypeIR * addend_node,AbiElementMap<MergeStatus> * local_to_global_type_id_map)420 MergeStatus TextFormatToIRReader::MergeGenericReferringType(
421 const TextFormatToIRReader &addend, const TypeIR *addend_node,
422 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
423 // First add the type 'pro-actively'. We need to do this since we'll need to
424 // fill in 'referenced-type' fields in all this type's descendants and
425 // descendants which are compound types (records), can refer to this type.
426 std::string added_type_id = AllocateNewTypeId();
427 // Add the added record type to the local_to_global_type_id_map
428 local_to_global_type_id_map->emplace(addend_node->GetSelfType(),
429 MergeStatus(true, added_type_id));
430 return MergeReferencingType(addend, addend_node, local_to_global_type_id_map,
431 added_type_id);
432 }
433
MergeTypeInternal(const TypeIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)434 MergeStatus TextFormatToIRReader::MergeTypeInternal(
435 const TypeIR *addend_node, const TextFormatToIRReader &addend,
436 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
437 switch(addend_node->GetKind()) {
438 case BuiltinTypeKind:
439 return IsBuiltinTypeNodePresent(
440 static_cast<const BuiltinTypeIR *>(addend_node), addend,
441 local_to_global_type_id_map);
442 break;
443 case RecordTypeKind:
444 return MergeRecordAndDependencies(
445 static_cast<const RecordTypeIR *>(addend_node),
446 addend, local_to_global_type_id_map);
447 case EnumTypeKind:
448 return MergeEnumType(static_cast<const EnumTypeIR *>(
449 addend_node), addend, local_to_global_type_id_map);
450 case FunctionTypeKind:
451 return MergeFunctionType(static_cast<const FunctionTypeIR *>(
452 addend_node), addend, local_to_global_type_id_map);
453 default:
454 return MergeGenericReferringType(addend, addend_node,
455 local_to_global_type_id_map);
456 }
457 assert(0);
458 }
459
MergeType(const TypeIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)460 MergeStatus TextFormatToIRReader::MergeType(
461 const TypeIR *addend_node,
462 const TextFormatToIRReader &addend,
463 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
464 // Check if the addend type is already in the parent graph. Since we're
465 // going to traverse all the dependencies add whichever ones are not in the
466 // parent graph. This does not add the node itself though.
467 auto type_it =
468 local_to_global_type_id_map->find(addend_node->GetSelfType());
469 if (type_it != local_to_global_type_id_map->end()) {
470 return type_it->second;
471 }
472
473 MergeStatus merge_status = IsTypeNodePresent(addend_node, addend,
474 local_to_global_type_id_map);
475 if (!merge_status.was_newly_added_) {
476 return merge_status;
477 }
478 merge_status = MergeTypeInternal(addend_node, addend,
479 local_to_global_type_id_map);
480 return merge_status;
481 }
482
MergeCFunctionLikeDeps(const TextFormatToIRReader & addend,CFunctionLikeIR * cfunction_like_ir,AbiElementMap<MergeStatus> * local_to_global_type_id_map)483 void TextFormatToIRReader::MergeCFunctionLikeDeps(
484 const TextFormatToIRReader &addend, CFunctionLikeIR *cfunction_like_ir,
485 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
486 // Merge the return type first.
487 auto ret_type_it =
488 addend.type_graph_.find(cfunction_like_ir->GetReturnType());
489 if (ret_type_it == addend.type_graph_.end()) {
490 // Hidden types aren't officially added to the parent since there is
491 // nothing actually backing it. We assign a type-id.
492 cfunction_like_ir->SetReturnType(AllocateNewTypeId());
493 } else {
494 MergeStatus ret_merge_status = MergeType(ret_type_it->second, addend,
495 local_to_global_type_id_map);
496 cfunction_like_ir->SetReturnType(ret_merge_status.type_id_);
497 }
498 // Merge and fix parameters.
499 for (auto ¶m : cfunction_like_ir->GetParameters()) {
500 MergeReferencingTypeInternal(addend, ¶m, local_to_global_type_id_map);
501 }
502 }
503
MergeFunctionDeps(FunctionIR * added_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)504 void TextFormatToIRReader::MergeFunctionDeps(
505 FunctionIR *added_node, const TextFormatToIRReader &addend,
506 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
507 MergeCFunctionLikeDeps(addend, added_node, local_to_global_type_id_map);
508 // Merge and fix template parameters
509 for (auto &template_element : added_node->GetTemplateElements()) {
510 MergeReferencingTypeInternal(addend, &template_element,
511 local_to_global_type_id_map);
512 }
513 }
514
515 template <typename T>
IsLinkableMessagePresent(const LinkableMessageIR * lm,const AbiElementMap<T> & message_map)516 static bool IsLinkableMessagePresent(const LinkableMessageIR *lm,
517 const AbiElementMap<T> &message_map) {
518 return (message_map.find(lm->GetLinkerSetKey()) != message_map.end());
519 }
520
MergeFunction(const FunctionIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)521 void TextFormatToIRReader::MergeFunction(
522 const FunctionIR *addend_node, const TextFormatToIRReader &addend,
523 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
524 const std::string &function_linkage_name = addend_node->GetLinkerSetKey();
525 if (IsLinkableMessagePresent(addend_node, functions_)) {
526 // The functions and all of its dependencies have already been added.
527 // No two globally visible functions can have the same symbol name.
528 return;
529 }
530 FunctionIR function_ir = *addend_node;
531 MergeFunctionDeps(&function_ir, addend, local_to_global_type_id_map);
532 // Add it to the parent's function map.
533 functions_.emplace(function_linkage_name, std::move(function_ir));
534 }
535
AllocateNewTypeId()536 std::string TextFormatToIRReader::AllocateNewTypeId() {
537 return "type-" + std::to_string(++max_type_id_);
538 }
539
MergeGlobalVariable(const GlobalVarIR * addend_node,const TextFormatToIRReader & addend,AbiElementMap<MergeStatus> * local_to_global_type_id_map)540 void TextFormatToIRReader::MergeGlobalVariable(
541 const GlobalVarIR *addend_node, const TextFormatToIRReader &addend,
542 AbiElementMap<MergeStatus> *local_to_global_type_id_map) {
543 const std::string &global_variable_linkage_name =
544 addend_node->GetLinkerSetKey();
545 if (IsLinkableMessagePresent(addend_node, global_variables_)) {
546 // The global variable and all of its dependencies have already been added.
547 return;
548 }
549 GlobalVarIR global_variable_ir = *addend_node;
550 MergeReferencingTypeInternal(addend, &global_variable_ir,
551 local_to_global_type_id_map);
552 global_variables_.emplace(global_variable_linkage_name,
553 std::move(global_variable_ir));
554 }
555
MergeGraphs(const TextFormatToIRReader & addend)556 void TextFormatToIRReader::MergeGraphs(const TextFormatToIRReader &addend) {
557 // Iterate through nodes of addend reader and merge them.
558 // Keep a merged types cache since if a type is merged, so will all of its
559 // dependencies which weren't already merged.
560 AbiElementMap<MergeStatus> merged_types_cache;
561
562 for (auto &&type_ir : addend.type_graph_) {
563 MergeType(type_ir.second, addend, &merged_types_cache);
564 }
565
566 for (auto &&function_ir : addend.functions_) {
567 MergeFunction(&function_ir.second, addend, &merged_types_cache);
568 }
569
570 for (auto &&global_var_ir : addend.global_variables_) {
571 MergeGlobalVariable(&global_var_ir.second, addend, &merged_types_cache);
572 }
573 }
574 } // namespace abi_util
575
576