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 &param : cfunction_like_ir->GetParameters()) {
500     MergeReferencingTypeInternal(addend, &param, 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