Home | History | Annotate | Download | only in repr
      1 // Copyright (C) 2018 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 "repr/abi_diff_helpers.h"
     16 
     17 #include "utils/header_abi_util.h"
     18 
     19 #include <llvm/Support/raw_ostream.h>
     20 
     21 
     22 namespace header_checker {
     23 namespace repr {
     24 
     25 
     26 std::string Unwind(const std::deque<std::string> *type_queue) {
     27   if (!type_queue) {
     28     return "";
     29   }
     30   std::string stack_str;
     31   std::deque<std::string> type_queue_copy = *type_queue;
     32   while (!type_queue_copy.empty()) {
     33     stack_str += type_queue_copy.front() + "-> ";
     34     type_queue_copy.pop_front();
     35   }
     36   return stack_str;
     37 }
     38 
     39 static void TypeQueueCheckAndPushBack(std::deque<std::string> *type_queue,
     40                                       const std::string &str) {
     41   if (type_queue) {
     42     type_queue->push_back(str);
     43   }
     44 }
     45 
     46 static void TypeQueueCheckAndPop(std::deque<std::string> *type_queue) {
     47   if (type_queue && !type_queue->empty()) {
     48     type_queue->pop_back();
     49   }
     50 }
     51 
     52 static bool IsAccessDownGraded(AccessSpecifierIR old_access,
     53                                AccessSpecifierIR new_access) {
     54   bool access_downgraded = false;
     55   switch (old_access) {
     56     case AccessSpecifierIR::ProtectedAccess:
     57       if (new_access == AccessSpecifierIR::PrivateAccess) {
     58         access_downgraded = true;
     59       }
     60       break;
     61     case AccessSpecifierIR::PublicAccess:
     62       if (new_access != AccessSpecifierIR::PublicAccess) {
     63         access_downgraded = true;
     64       }
     65       break;
     66     default:
     67       break;
     68   }
     69   return access_downgraded;
     70 }
     71 
     72 static std::string ConvertTypeIdToString(
     73     const AbiElementMap<const TypeIR *> &type_graph,
     74     const std::string &type_id) {
     75   auto it = type_graph.find(type_id);
     76   if (it != type_graph.end()) {
     77     return it->second->GetName();
     78   }
     79   return "type-unexported";
     80 }
     81 
     82 template <typename Container>
     83 static void ReplaceReferencesOtherTypeIdWithName(
     84     const AbiElementMap<const TypeIR *> &type_graph,
     85     Container &to_fix_elements) {
     86   for (auto &element : to_fix_elements) {
     87     element.SetReferencedType(
     88         ConvertTypeIdToString(type_graph, element.GetReferencedType()));
     89   }
     90 }
     91 
     92 static void ReplaceEnumTypeIRTypeIdsWithTypeNames(
     93     const AbiElementMap<const TypeIR *> &type_graph, EnumTypeIR *enum_type_ir) {
     94   // Replace underlying type.
     95   enum_type_ir->SetUnderlyingType(
     96       ConvertTypeIdToString(type_graph, enum_type_ir->GetUnderlyingType()));
     97 }
     98 
     99 static void ReplaceRecordTypeIRTypeIdsWithTypeNames(
    100     const AbiElementMap<const TypeIR *> &type_graph,
    101     RecordTypeIR *record_type_ir) {
    102   // Replace Fields
    103   ReplaceReferencesOtherTypeIdWithName(type_graph,
    104                                        record_type_ir->GetFields());
    105   // Replace template parameters
    106   ReplaceReferencesOtherTypeIdWithName(type_graph,
    107                                        record_type_ir->GetTemplateElements());
    108   // Replace bases
    109   ReplaceReferencesOtherTypeIdWithName(type_graph,
    110                                        record_type_ir->GetBases());
    111 }
    112 
    113 static void ReplaceGlobalVarTypeIdsWithTypeNames(
    114     const AbiElementMap<const TypeIR *> &type_graph,
    115     GlobalVarIR *global_var_ir) {
    116   // Replace referenced type id.
    117   global_var_ir->SetReferencedType(
    118       ConvertTypeIdToString(type_graph, global_var_ir->GetReferencedType()));
    119 }
    120 
    121 static void ReplaceFunctionTypeIdsWithTypeNames(
    122     const AbiElementMap<const TypeIR *> &type_graph, FunctionIR *function_ir) {
    123   // Replace return type
    124   function_ir->SetReturnType(
    125       ConvertTypeIdToString(type_graph, function_ir->GetReturnType()));
    126   // Replace function parameters
    127   ReplaceReferencesOtherTypeIdWithName(type_graph,
    128                                        function_ir->GetParameters());
    129   // Replace function template parameters
    130   ReplaceReferencesOtherTypeIdWithName(type_graph,
    131                                        function_ir->GetTemplateElements());
    132 }
    133 
    134 void ReplaceTypeIdsWithTypeNames(
    135     const AbiElementMap<const TypeIR *> &type_graph,
    136     LinkableMessageIR *lm) {
    137   switch (lm->GetKind()) {
    138     case FunctionKind:
    139       ReplaceFunctionTypeIdsWithTypeNames(
    140           type_graph, static_cast<FunctionIR *>(lm));
    141       break;
    142     case GlobalVarKind:
    143       ReplaceGlobalVarTypeIdsWithTypeNames(
    144           type_graph, static_cast<GlobalVarIR *>(lm));
    145       break;
    146     case RecordTypeKind:
    147       ReplaceRecordTypeIRTypeIdsWithTypeNames(
    148           type_graph, static_cast<RecordTypeIR *>(lm));
    149       break;
    150     case EnumTypeKind:
    151       ReplaceEnumTypeIRTypeIdsWithTypeNames(
    152           type_graph, static_cast<EnumTypeIR *>(lm));
    153       break;
    154     default:
    155       // This method should not be called on any other LinkableMessage
    156       assert(0);
    157   }
    158 }
    159 
    160 void AbiDiffHelper::CompareEnumFields(
    161     const std::vector<EnumFieldIR> &old_fields,
    162     const std::vector<EnumFieldIR> &new_fields,
    163     EnumTypeDiffIR *enum_type_diff_ir) {
    164   AbiElementMap<const EnumFieldIR *> old_fields_map;
    165   AbiElementMap<const EnumFieldIR *> new_fields_map;
    166   utils::AddToMap(&old_fields_map, old_fields,
    167                   [](const EnumFieldIR *f) {return f->GetName();},
    168                   [](const EnumFieldIR *f) {return f;});
    169 
    170   utils::AddToMap(&new_fields_map, new_fields,
    171                   [](const EnumFieldIR *f) {return f->GetName();},
    172                   [](const EnumFieldIR *f) {return f;});
    173 
    174   std::vector<const EnumFieldIR *> removed_fields =
    175       utils::FindRemovedElements(old_fields_map, new_fields_map);
    176 
    177   std::vector<const EnumFieldIR *> added_fields =
    178       utils::FindRemovedElements(new_fields_map, old_fields_map);
    179 
    180   enum_type_diff_ir->SetFieldsAdded(std::move(added_fields));
    181 
    182   enum_type_diff_ir->SetFieldsRemoved(std::move(removed_fields));
    183 
    184   std::vector<std::pair<const EnumFieldIR *,
    185                         const EnumFieldIR *>> cf =
    186       utils::FindCommonElements(old_fields_map, new_fields_map);
    187   std::vector<EnumFieldDiffIR> enum_field_diffs;
    188   for (auto &&common_fields : cf) {
    189     if (common_fields.first->GetValue() != common_fields.second->GetValue()) {
    190       EnumFieldDiffIR enum_field_diff_ir(common_fields.first,
    191                                                    common_fields.second);
    192       enum_field_diffs.emplace_back(std::move(enum_field_diff_ir));
    193     }
    194   }
    195   enum_type_diff_ir->SetFieldsDiff(std::move(enum_field_diffs));
    196 }
    197 
    198 DiffStatus AbiDiffHelper::CompareEnumTypes(
    199     const EnumTypeIR *old_type, const EnumTypeIR *new_type,
    200     std::deque<std::string> *type_queue,
    201     DiffMessageIR::DiffKind diff_kind) {
    202   if (old_type->GetUniqueId() != new_type->GetUniqueId()) {
    203     return DiffStatus::direct_diff;
    204   }
    205   auto enum_type_diff_ir = std::make_unique<EnumTypeDiffIR>();
    206   enum_type_diff_ir->SetName(old_type->GetName());
    207   const std::string &old_underlying_type =
    208       ConvertTypeIdToString(old_types_, old_type->GetUnderlyingType());
    209   const std::string &new_underlying_type =
    210       ConvertTypeIdToString(new_types_, new_type->GetUnderlyingType());
    211   if (old_underlying_type != new_underlying_type) {
    212     enum_type_diff_ir->SetUnderlyingTypeDiff(
    213         std::make_unique<std::pair<std::string, std::string>>(
    214             old_underlying_type, new_underlying_type));
    215   }
    216   CompareEnumFields(old_type->GetFields(), new_type->GetFields(),
    217                     enum_type_diff_ir.get());
    218   if ((enum_type_diff_ir->IsExtended() ||
    219        enum_type_diff_ir->IsIncompatible()) &&
    220       (ir_diff_dumper_ && !ir_diff_dumper_->AddDiffMessageIR(
    221           enum_type_diff_ir.get(), Unwind(type_queue), diff_kind))) {
    222     llvm::errs() << "AddDiffMessage on EnumTypeDiffIR failed\n";
    223     ::exit(1);
    224   }
    225   return DiffStatus::no_diff;
    226 }
    227 
    228 static std::string RemoveThunkInfoFromMangledName(const std::string &name) {
    229   if (name.find("_ZTv") != 0 && name.find("_ZTh") != 0 &&
    230       name.find("_ZTc") != 0) {
    231     return name;
    232   }
    233   size_t base_name_pos = name.find("N");
    234   if (base_name_pos == std::string::npos) {
    235     return name;
    236   }
    237   return "_Z" + name.substr(base_name_pos);
    238 }
    239 
    240 bool AbiDiffHelper::CompareVTableComponents(
    241     const VTableComponentIR &old_component,
    242     const VTableComponentIR &new_component) {
    243   // Vtable components in prebuilts/abi-dumps/vndk/28 don't have thunk info.
    244   if (old_component.GetName() != new_component.GetName()) {
    245     if (RemoveThunkInfoFromMangledName(old_component.GetName()) ==
    246         RemoveThunkInfoFromMangledName(new_component.GetName())) {
    247       llvm::errs() << "WARNING: Ignore difference between "
    248                    << old_component.GetName() << " and "
    249                    << new_component.GetName() << "\n";
    250     } else {
    251       return false;
    252     }
    253   }
    254   return old_component.GetValue() == new_component.GetValue() &&
    255          old_component.GetKind() == new_component.GetKind();
    256 }
    257 
    258 bool AbiDiffHelper::CompareVTables(
    259     const RecordTypeIR *old_record,
    260     const RecordTypeIR *new_record) {
    261 
    262   const std::vector<VTableComponentIR> &old_components =
    263       old_record->GetVTableLayout().GetVTableComponents();
    264   const std::vector<VTableComponentIR> &new_components =
    265       new_record->GetVTableLayout().GetVTableComponents();
    266   if (old_components.size() > new_components.size()) {
    267     // Something in the vtable got deleted.
    268     return false;
    269   }
    270   uint32_t i = 0;
    271   while (i < old_components.size()) {
    272     auto &old_element = old_components.at(i);
    273     auto &new_element = new_components.at(i);
    274     if (!CompareVTableComponents(old_element, new_element)) {
    275       return false;
    276     }
    277     i++;
    278   }
    279   return true;
    280 }
    281 
    282 bool AbiDiffHelper::CompareSizeAndAlignment(
    283     const TypeIR *old_type,
    284     const TypeIR *new_type) {
    285   return old_type->GetSize() == new_type->GetSize() &&
    286       old_type->GetAlignment() == new_type->GetAlignment();
    287 }
    288 
    289 DiffStatusPair<std::unique_ptr<RecordFieldDiffIR>>
    290 AbiDiffHelper::CompareCommonRecordFields(
    291     const RecordFieldIR *old_field,
    292     const RecordFieldIR *new_field,
    293     std::deque<std::string> *type_queue,
    294     DiffMessageIR::DiffKind diff_kind) {
    295 
    296   DiffStatus field_diff_status =
    297       CompareAndDumpTypeDiff(old_field->GetReferencedType(),
    298                              new_field->GetReferencedType(),
    299                              type_queue, diff_kind);
    300 
    301   if (old_field->GetOffset() != new_field->GetOffset() ||
    302       // TODO: Should this be an inquality check instead ? Some compilers can
    303       // make signatures dependant on absolute values of access specifiers.
    304       IsAccessDownGraded(old_field->GetAccess(), new_field->GetAccess()) ||
    305       (field_diff_status == DiffStatus::direct_diff)) {
    306     return std::make_pair(
    307         DiffStatus::direct_diff,
    308         std::make_unique<RecordFieldDiffIR>(old_field, new_field)
    309         );
    310   }
    311   return std::make_pair(field_diff_status, nullptr);
    312 }
    313 
    314 
    315 GenericFieldDiffInfo<RecordFieldIR, RecordFieldDiffIR>
    316 AbiDiffHelper::CompareRecordFields(
    317     const std::vector<RecordFieldIR> &old_fields,
    318     const std::vector<RecordFieldIR> &new_fields,
    319     std::deque<std::string> *type_queue,
    320     DiffMessageIR::DiffKind diff_kind) {
    321   GenericFieldDiffInfo<RecordFieldIR, RecordFieldDiffIR>
    322       diffed_removed_added_fields;
    323   AbiElementMap<const RecordFieldIR *> old_fields_map;
    324   AbiElementMap<const RecordFieldIR *> new_fields_map;
    325   std::map<uint64_t, const RecordFieldIR *> old_fields_offset_map;
    326   std::map<uint64_t, const RecordFieldIR *> new_fields_offset_map;
    327 
    328   utils::AddToMap(
    329       &old_fields_map, old_fields,
    330       [](const RecordFieldIR *f) {return f->GetName();},
    331       [](const RecordFieldIR *f) {return f;});
    332   utils::AddToMap(
    333       &new_fields_map, new_fields,
    334       [](const RecordFieldIR *f) {return f->GetName();},
    335       [](const RecordFieldIR *f) {return f;});
    336   utils::AddToMap(
    337       &old_fields_offset_map, old_fields,
    338       [](const RecordFieldIR *f) {return f->GetOffset();},
    339       [](const RecordFieldIR *f) {return f;});
    340   utils::AddToMap(
    341       &new_fields_offset_map, new_fields,
    342       [](const RecordFieldIR *f) {return f->GetOffset();},
    343       [](const RecordFieldIR *f) {return f;});
    344   // If a field is removed from the map field_name -> offset see if another
    345   // field is present at the same offset and compare the size and type etc,
    346   // remove it from the removed fields if they're compatible.
    347   DiffStatus final_diff_status = DiffStatus::no_diff;
    348   std::vector<const RecordFieldIR *> removed_fields =
    349       utils::FindRemovedElements(old_fields_map, new_fields_map);
    350 
    351   std::vector<const RecordFieldIR *> added_fields =
    352       utils::FindRemovedElements(new_fields_map, old_fields_map);
    353 
    354   auto predicate =
    355       [&](const RecordFieldIR *removed_field,
    356           std::map<uint64_t, const RecordFieldIR *> &field_off_map) {
    357         uint64_t old_field_offset = removed_field->GetOffset();
    358         auto corresponding_field_at_same_offset =
    359             field_off_map.find(old_field_offset);
    360         // Correctly reported as removed, so do not remove.
    361         if (corresponding_field_at_same_offset == field_off_map.end()) {
    362           return false;
    363         }
    364 
    365         auto comparison_result = CompareCommonRecordFields(
    366             removed_field, corresponding_field_at_same_offset->second,
    367             type_queue, diff_kind);
    368         // No actual diff, so remove it.
    369         return (comparison_result.second == nullptr);
    370       };
    371 
    372   removed_fields.erase(
    373       std::remove_if(
    374           removed_fields.begin(), removed_fields.end(),
    375           std::bind(predicate, std::placeholders::_1, new_fields_offset_map)),
    376       removed_fields.end());
    377   added_fields.erase(
    378       std::remove_if(
    379           added_fields.begin(), added_fields.end(),
    380           std::bind(predicate, std::placeholders::_1, old_fields_offset_map)),
    381       added_fields.end());
    382 
    383   diffed_removed_added_fields.removed_fields_ = std::move(removed_fields);
    384   diffed_removed_added_fields.added_fields_ = std::move(added_fields);
    385 
    386   std::vector<std::pair<
    387       const RecordFieldIR *, const RecordFieldIR *>> cf =
    388       utils::FindCommonElements(old_fields_map, new_fields_map);
    389   bool common_field_diff_exists = false;
    390   for (auto &&common_fields : cf) {
    391     auto diffed_field_ptr = CompareCommonRecordFields(
    392         common_fields.first, common_fields.second, type_queue, diff_kind);
    393     if (!common_field_diff_exists &&
    394         (diffed_field_ptr.first &
    395         (DiffStatus::direct_diff | DiffStatus::indirect_diff))) {
    396         common_field_diff_exists = true;
    397     }
    398     if (diffed_field_ptr.second != nullptr) {
    399       diffed_removed_added_fields.diffed_fields_.emplace_back(
    400           std::move(*(diffed_field_ptr.second.release())));
    401     }
    402   }
    403   if (diffed_removed_added_fields.diffed_fields_.size() != 0 ||
    404       diffed_removed_added_fields.removed_fields_.size() != 0) {
    405     final_diff_status = DiffStatus::direct_diff;
    406   } else if (common_field_diff_exists) {
    407     final_diff_status = DiffStatus::indirect_diff;
    408   }
    409   diffed_removed_added_fields.diff_status_ = final_diff_status;
    410   return diffed_removed_added_fields;
    411 }
    412 
    413 bool AbiDiffHelper::CompareBaseSpecifiers(
    414     const std::vector<CXXBaseSpecifierIR> &old_base_specifiers,
    415     const std::vector<CXXBaseSpecifierIR> &new_base_specifiers,
    416     std::deque<std::string> *type_queue,
    417     DiffMessageIR::DiffKind diff_kind) {
    418   if (old_base_specifiers.size() != new_base_specifiers.size()) {
    419     return false;
    420   }
    421   int i = 0;
    422   while (i < old_base_specifiers.size()) {
    423     if (CompareAndDumpTypeDiff(old_base_specifiers.at(i).GetReferencedType(),
    424                                new_base_specifiers.at(i).GetReferencedType(),
    425                                type_queue, diff_kind) ==
    426         DiffStatus::direct_diff ||
    427         (old_base_specifiers.at(i).GetAccess() !=
    428          new_base_specifiers.at(i).GetAccess())) {
    429       return false;
    430     }
    431     i++;
    432   }
    433   return true;
    434 }
    435 
    436 DiffStatus AbiDiffHelper::CompareTemplateInfo(
    437     const std::vector<TemplateElementIR> &old_template_elements,
    438     const std::vector<TemplateElementIR> &new_template_elements,
    439     std::deque<std::string> *type_queue,
    440     DiffMessageIR::DiffKind diff_kind) {
    441   uint32_t old_template_size = old_template_elements.size();
    442   uint32_t i = 0;
    443   if (old_template_size != new_template_elements.size()) {
    444     return DiffStatus::direct_diff;
    445   }
    446   DiffStatus final_diff_status = DiffStatus::no_diff;
    447   while (i < old_template_size) {
    448     const TemplateElementIR &old_template_element =
    449         old_template_elements[i];
    450     const TemplateElementIR &new_template_element =
    451         new_template_elements[i];
    452     auto template_element_diff =
    453         CompareAndDumpTypeDiff(old_template_element.GetReferencedType(),
    454                                new_template_element.GetReferencedType(),
    455                                type_queue, diff_kind);
    456     if (template_element_diff &
    457         (DiffStatus::direct_diff | DiffStatus::indirect_diff)) {
    458       final_diff_status = template_element_diff;
    459     }
    460     i++;
    461   }
    462   return final_diff_status;
    463 }
    464 
    465 template <typename DiffContainer, typename T>
    466 static std::vector<DiffContainer> ConvertToDiffContainerVector(
    467     std::vector<std::pair<T, T>> &nc_vector) {
    468   std::vector<DiffContainer> cptr_vec;
    469   for (auto &e : nc_vector) {
    470     cptr_vec.emplace_back(&e.first, &e.second);
    471   }
    472   return cptr_vec;
    473 }
    474 
    475 template <typename T>
    476 static std::vector<const T*> ConvertToConstPtrVector(
    477     std::vector<T> &nc_vector) {
    478   std::vector<const T*> cptr_vec;
    479   for (auto &e : nc_vector) {
    480     cptr_vec.emplace_back(&e);
    481   }
    482   return cptr_vec;
    483 }
    484 
    485 static std::vector<RecordFieldIR> FixupRemovedFieldTypeIds(
    486     const std::vector<const RecordFieldIR *> &removed_fields,
    487     const AbiElementMap<const TypeIR *> &old_types) {
    488   std::vector<RecordFieldIR> removed_fields_dup;
    489   for (auto &removed_field : removed_fields) {
    490     removed_fields_dup.emplace_back(*removed_field);
    491     RecordFieldIR &it = removed_fields_dup[removed_fields_dup.size() -1];
    492     it.SetReferencedType(
    493         ConvertTypeIdToString(old_types, it.GetReferencedType()));
    494   }
    495   return removed_fields_dup;
    496 }
    497 
    498 std::vector<std::pair<RecordFieldIR, RecordFieldIR>>
    499 AbiDiffHelper::FixupDiffedFieldTypeIds(
    500     const std::vector<RecordFieldDiffIR> &field_diffs) {
    501   std::vector<std::pair<RecordFieldIR, RecordFieldIR>>
    502       diffed_fields_dup;
    503   for (auto &field_diff : field_diffs) {
    504     diffed_fields_dup.emplace_back(*(field_diff.old_field_),
    505                                    *(field_diff.new_field_));
    506     auto &it = diffed_fields_dup[diffed_fields_dup.size() - 1];
    507     RecordFieldIR &old_field = it.first;
    508     RecordFieldIR &new_field = it.second;
    509     old_field.SetReferencedType(
    510         ConvertTypeIdToString(old_types_, old_field.GetReferencedType()));
    511     new_field.SetReferencedType(
    512         ConvertTypeIdToString(new_types_, new_field.GetReferencedType()));
    513   }
    514   return diffed_fields_dup;
    515 }
    516 
    517 DiffStatus AbiDiffHelper::CompareFunctionTypes(
    518     const FunctionTypeIR *old_type,
    519     const FunctionTypeIR *new_type,
    520     std::deque<std::string> *type_queue,
    521     DiffMessageIR::DiffKind diff_kind) {
    522   DiffStatus param_diffs = CompareFunctionParameters(old_type->GetParameters(),
    523                                                      new_type->GetParameters(),
    524                                                      type_queue, diff_kind);
    525   DiffStatus return_type_diff =
    526       CompareAndDumpTypeDiff(old_type->GetReturnType(),
    527                              new_type->GetReturnType(),
    528                              type_queue, diff_kind);
    529 
    530   if (param_diffs == DiffStatus::direct_diff ||
    531       return_type_diff == DiffStatus::direct_diff) {
    532     return DiffStatus::direct_diff;
    533   }
    534 
    535   if (param_diffs == DiffStatus::indirect_diff ||
    536       return_type_diff == DiffStatus::indirect_diff) {
    537     return DiffStatus::indirect_diff;
    538   }
    539 
    540   return DiffStatus::no_diff;
    541 }
    542 
    543 DiffStatus AbiDiffHelper::CompareRecordTypes(
    544     const RecordTypeIR *old_type,
    545     const RecordTypeIR *new_type,
    546     std::deque<std::string> *type_queue,
    547     DiffMessageIR::DiffKind diff_kind) {
    548   auto record_type_diff_ir = std::make_unique<RecordTypeDiffIR>();
    549   // Compare names.
    550   if (!old_type->IsAnonymous() && !new_type->IsAnonymous() &&
    551       old_type->GetUniqueId() != new_type->GetUniqueId()) {
    552     // Do not dump anything since the record types themselves are fundamentally
    553     // different.
    554     return DiffStatus::direct_diff;
    555   }
    556   DiffStatus final_diff_status = DiffStatus::no_diff;
    557   record_type_diff_ir->SetName(old_type->GetName());
    558   if (IsAccessDownGraded(old_type->GetAccess(), new_type->GetAccess())) {
    559     final_diff_status = DiffStatus::indirect_diff;
    560     record_type_diff_ir->SetAccessDiff(
    561         std::make_unique<AccessSpecifierDiffIR>(
    562             old_type->GetAccess(), new_type->GetAccess()));
    563   }
    564 
    565   if (!CompareSizeAndAlignment(old_type, new_type)) {
    566     final_diff_status = DiffStatus::indirect_diff;
    567     record_type_diff_ir->SetTypeDiff(
    568         std::make_unique<TypeDiffIR>(
    569             std::make_pair(old_type->GetSize(), new_type->GetSize()),
    570             std::make_pair(old_type->GetAlignment(),
    571                            new_type->GetAlignment())));
    572   }
    573   if (!CompareVTables(old_type, new_type)) {
    574     final_diff_status = DiffStatus::indirect_diff;
    575     record_type_diff_ir->SetVTableLayoutDiff(
    576         std::make_unique<VTableLayoutDiffIR>(
    577             old_type->GetVTableLayout(), new_type->GetVTableLayout()));
    578   }
    579   auto &old_fields_dup = old_type->GetFields();
    580   auto &new_fields_dup = new_type->GetFields();
    581   auto field_status_and_diffs = CompareRecordFields(
    582       old_fields_dup, new_fields_dup, type_queue, diff_kind);
    583   // TODO: Combine this with base class diffs as well.
    584   final_diff_status = final_diff_status | field_status_and_diffs.diff_status_;
    585 
    586   std::vector<CXXBaseSpecifierIR> old_bases = old_type->GetBases();
    587   std::vector<CXXBaseSpecifierIR> new_bases = new_type->GetBases();
    588 
    589   if (!CompareBaseSpecifiers(old_bases, new_bases, type_queue, diff_kind) &&
    590       ir_diff_dumper_) {
    591     ReplaceReferencesOtherTypeIdWithName(old_types_, old_bases);
    592     ReplaceReferencesOtherTypeIdWithName(new_types_, new_bases);
    593     record_type_diff_ir->SetBaseSpecifierDiffs (
    594         std::make_unique<CXXBaseSpecifierDiffIR>(old_bases,
    595                                                            new_bases));
    596   }
    597   if (ir_diff_dumper_) {
    598     // Make copies of the fields removed and diffed, since we have to change
    599     // type ids -> type strings.
    600     std::vector<std::pair<RecordFieldIR, RecordFieldIR>> field_diff_dups =
    601         FixupDiffedFieldTypeIds(field_status_and_diffs.diffed_fields_);
    602     std::vector<RecordFieldDiffIR> field_diffs_fixed =
    603         ConvertToDiffContainerVector<RecordFieldDiffIR,
    604                                      RecordFieldIR>(field_diff_dups);
    605 
    606     std::vector<RecordFieldIR> field_removed_dups =
    607         FixupRemovedFieldTypeIds(field_status_and_diffs.removed_fields_,
    608                                  old_types_);
    609     std::vector<const RecordFieldIR *> fields_removed_fixed =
    610         ConvertToConstPtrVector(field_removed_dups);
    611 
    612     std::vector<RecordFieldIR> field_added_dups =
    613         FixupRemovedFieldTypeIds(field_status_and_diffs.added_fields_,
    614                                  new_types_);
    615     std::vector<const RecordFieldIR *> fields_added_fixed =
    616         ConvertToConstPtrVector(field_added_dups);
    617 
    618     record_type_diff_ir->SetFieldDiffs(std::move(field_diffs_fixed));
    619     record_type_diff_ir->SetFieldsRemoved(std::move(fields_removed_fixed));
    620     record_type_diff_ir->SetFieldsAdded(std::move(fields_added_fixed));
    621 
    622     if (record_type_diff_ir->DiffExists() &&
    623         !ir_diff_dumper_->AddDiffMessageIR(record_type_diff_ir.get(),
    624                                            Unwind(type_queue), diff_kind)) {
    625       llvm::errs() << "AddDiffMessage on record type failed\n";
    626       ::exit(1);
    627     }
    628   }
    629 
    630   final_diff_status = final_diff_status |
    631       CompareTemplateInfo(old_type->GetTemplateElements(),
    632                           new_type->GetTemplateElements(),
    633                           type_queue, diff_kind);
    634 
    635   // Records cannot be 'extended' compatibly, without a certain amount of risk.
    636   return ((final_diff_status &
    637            (DiffStatus::direct_diff | DiffStatus::indirect_diff)) ?
    638           DiffStatus::indirect_diff : DiffStatus::no_diff);
    639 }
    640 
    641 DiffStatus AbiDiffHelper::CompareLvalueReferenceTypes(
    642     const LvalueReferenceTypeIR *old_type,
    643     const LvalueReferenceTypeIR *new_type,
    644     std::deque<std::string> *type_queue,
    645     DiffMessageIR::DiffKind diff_kind) {
    646   return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
    647                                 new_type->GetReferencedType(),
    648                                 type_queue, diff_kind);
    649 }
    650 
    651 DiffStatus AbiDiffHelper::CompareRvalueReferenceTypes(
    652     const RvalueReferenceTypeIR *old_type,
    653     const RvalueReferenceTypeIR *new_type,
    654     std::deque<std::string> *type_queue,
    655     DiffMessageIR::DiffKind diff_kind) {
    656   return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
    657                                 new_type->GetReferencedType(),
    658                                 type_queue, diff_kind);
    659 }
    660 
    661 DiffStatus AbiDiffHelper::CompareQualifiedTypes(
    662     const QualifiedTypeIR *old_type,
    663     const QualifiedTypeIR *new_type,
    664     std::deque<std::string> *type_queue,
    665     DiffMessageIR::DiffKind diff_kind) {
    666   // If all the qualifiers are not the same, return direct_diff, else
    667   // recursively compare the unqualified types.
    668   if (old_type->IsConst() != new_type->IsConst() ||
    669       old_type->IsVolatile() != new_type->IsVolatile() ||
    670       old_type->IsRestricted() != new_type->IsRestricted()) {
    671     return DiffStatus::direct_diff;
    672   }
    673   return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
    674                                 new_type->GetReferencedType(),
    675                                 type_queue, diff_kind);
    676 }
    677 
    678 DiffStatus AbiDiffHelper::ComparePointerTypes(
    679     const PointerTypeIR *old_type,
    680     const PointerTypeIR *new_type,
    681     std::deque<std::string> *type_queue,
    682     DiffMessageIR::DiffKind diff_kind) {
    683   // The following need to be the same for two pointer types to be considered
    684   // equivalent:
    685   // 1) Number of pointer indirections are the same.
    686   // 2) The ultimate pointee is the same.
    687   assert(CompareSizeAndAlignment(old_type, new_type));
    688   return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
    689                                 new_type->GetReferencedType(),
    690                                 type_queue, diff_kind);
    691 }
    692 
    693 DiffStatus AbiDiffHelper::CompareBuiltinTypes(
    694     const BuiltinTypeIR *old_type,
    695     const BuiltinTypeIR *new_type) {
    696   // If the size, alignment and is_unsigned are the same, return no_diff
    697   // else return direct_diff.
    698   if (!CompareSizeAndAlignment(old_type, new_type) ||
    699       old_type->IsUnsigned() != new_type->IsUnsigned() ||
    700       old_type->IsIntegralType() != new_type->IsIntegralType()) {
    701     return DiffStatus::direct_diff;
    702   }
    703   return DiffStatus::no_diff;
    704 }
    705 
    706 DiffStatus AbiDiffHelper::CompareFunctionParameters(
    707     const std::vector<ParamIR> &old_parameters,
    708     const std::vector<ParamIR> &new_parameters,
    709     std::deque<std::string> *type_queue,
    710     DiffMessageIR::DiffKind diff_kind) {
    711   size_t old_parameters_size = old_parameters.size();
    712   if (old_parameters_size != new_parameters.size()) {
    713     return DiffStatus::direct_diff;
    714   }
    715   uint64_t i = 0;
    716   while (i < old_parameters_size) {
    717     const ParamIR &old_parameter = old_parameters.at(i);
    718     const ParamIR &new_parameter = new_parameters.at(i);
    719     if ((CompareAndDumpTypeDiff(old_parameter.GetReferencedType(),
    720                                new_parameter.GetReferencedType(),
    721                                type_queue, diff_kind) ==
    722         DiffStatus::direct_diff) ||
    723         (old_parameter.GetIsDefault() != new_parameter.GetIsDefault())) {
    724       return DiffStatus::direct_diff;
    725     }
    726     i++;
    727   }
    728   return DiffStatus::no_diff;
    729 }
    730 
    731 DiffStatus AbiDiffHelper::CompareAndDumpTypeDiff(
    732     const TypeIR *old_type, const TypeIR *new_type,
    733     LinkableMessageKind kind, std::deque<std::string> *type_queue,
    734     DiffMessageIR::DiffKind diff_kind) {
    735   if (kind == LinkableMessageKind::BuiltinTypeKind) {
    736     return CompareBuiltinTypes(
    737         static_cast<const BuiltinTypeIR *>(old_type),
    738         static_cast<const BuiltinTypeIR *>(new_type));
    739   }
    740 
    741   if (kind == LinkableMessageKind::QualifiedTypeKind) {
    742     return CompareQualifiedTypes(
    743         static_cast<const QualifiedTypeIR *>(old_type),
    744         static_cast<const QualifiedTypeIR *>(new_type),
    745         type_queue, diff_kind);
    746   }
    747 
    748   if (kind == LinkableMessageKind::EnumTypeKind) {
    749     return CompareEnumTypes(
    750         static_cast<const EnumTypeIR *>(old_type),
    751         static_cast<const EnumTypeIR *>(new_type),
    752         type_queue, diff_kind);
    753   }
    754 
    755   if (kind == LinkableMessageKind::LvalueReferenceTypeKind) {
    756     return CompareLvalueReferenceTypes(
    757         static_cast<const LvalueReferenceTypeIR *>(old_type),
    758         static_cast<const LvalueReferenceTypeIR *>(new_type),
    759         type_queue, diff_kind);
    760   }
    761 
    762   if (kind == LinkableMessageKind::RvalueReferenceTypeKind) {
    763     return CompareRvalueReferenceTypes(
    764         static_cast<const RvalueReferenceTypeIR *>(old_type),
    765         static_cast<const RvalueReferenceTypeIR *>(new_type),
    766         type_queue, diff_kind);
    767   }
    768 
    769   if (kind == LinkableMessageKind::PointerTypeKind) {
    770     return ComparePointerTypes(
    771         static_cast<const PointerTypeIR *>(old_type),
    772         static_cast<const PointerTypeIR *>(new_type),
    773         type_queue, diff_kind);
    774   }
    775 
    776   if (kind == LinkableMessageKind::RecordTypeKind) {
    777     return CompareRecordTypes(
    778         static_cast<const RecordTypeIR *>(old_type),
    779         static_cast<const RecordTypeIR *>(new_type),
    780         type_queue, diff_kind);
    781   }
    782 
    783   if (kind == LinkableMessageKind::FunctionTypeKind) {
    784     return CompareFunctionTypes(
    785         static_cast<const FunctionTypeIR *>(old_type),
    786         static_cast<const FunctionTypeIR *>(new_type),
    787         type_queue, diff_kind);
    788   }
    789   return DiffStatus::no_diff;
    790 }
    791 
    792 static DiffStatus CompareDistinctKindMessages(
    793     const TypeIR *old_type, const TypeIR *new_type) {
    794   // For these types to be considered ABI compatible, the very least requirement
    795   // is that their sizes and alignments should be equal.
    796   // TODO: Fill in
    797   return DiffStatus::direct_diff;
    798 }
    799 
    800 DiffStatus AbiDiffHelper::CompareAndDumpTypeDiff(
    801     const std::string &old_type_id, const std::string &new_type_id,
    802     std::deque<std::string> *type_queue,
    803     DiffMessageIR::DiffKind diff_kind) {
    804   // Check the map for type ids which have already been compared
    805   // These types have already been diffed, return without further comparison.
    806   if (!type_cache_->insert(old_type_id + new_type_id).second) {
    807     return DiffStatus::no_diff;
    808   }
    809 
    810   TypeQueueCheckAndPushBack(
    811       type_queue, ConvertTypeIdToString(old_types_,old_type_id));
    812 
    813   AbiElementMap<const TypeIR *>::const_iterator old_it =
    814       old_types_.find(old_type_id);
    815   AbiElementMap<const TypeIR *>::const_iterator new_it =
    816       new_types_.find(new_type_id);
    817 
    818   if (old_it == old_types_.end() || new_it == new_types_.end()) {
    819     TypeQueueCheckAndPop(type_queue);
    820     // One of the types were hidden, we cannot compare further.
    821     if (diff_policy_options_.consider_opaque_types_different_) {
    822       return DiffStatus::opaque_diff;
    823     }
    824     return DiffStatus::no_diff;
    825   }
    826 
    827   LinkableMessageKind old_kind = old_it->second->GetKind();
    828   LinkableMessageKind new_kind = new_it->second->GetKind();
    829   DiffStatus diff_status = DiffStatus::no_diff;
    830   if (old_kind != new_kind) {
    831     diff_status = CompareDistinctKindMessages(old_it->second, new_it->second);
    832   } else {
    833     diff_status = CompareAndDumpTypeDiff(old_it->second , new_it->second ,
    834                                          old_kind, type_queue, diff_kind);
    835   }
    836 
    837   TypeQueueCheckAndPop(type_queue);
    838 
    839   if (diff_policy_options_.consider_opaque_types_different_ &&
    840       diff_status == DiffStatus::opaque_diff) {
    841     // If `-considered-opaque-types-different` is specified and the comparison
    842     // of `referenced_type` results in `opaque_diff`, then check the type name
    843     // at this level.
    844     return (old_it->second->GetName() == new_it->second->GetName() ?
    845             DiffStatus::no_diff : DiffStatus::direct_diff);
    846   }
    847 
    848   return diff_status;
    849 }
    850 
    851 
    852 }  // namespace repr
    853 }  // namespace header_checker
    854