diff options
Diffstat (limited to 'llvm/tools/llvm-tapi-diff/DiffEngine.cpp')
-rw-r--r-- | llvm/tools/llvm-tapi-diff/DiffEngine.cpp | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/llvm/tools/llvm-tapi-diff/DiffEngine.cpp b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp new file mode 100644 index 000000000000..ff60d52f5c65 --- /dev/null +++ b/llvm/tools/llvm-tapi-diff/DiffEngine.cpp @@ -0,0 +1,556 @@ +//===-- DiffEngine.cpp - Structural file comparison -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the implementation of the llvm-tapi difference +// engine, which structurally compares two tbd files. +// +//===----------------------------------------------------------------------===/ +#include "DiffEngine.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/InterfaceFile.h" +#include "llvm/TextAPI/Symbol.h" +#include "llvm/TextAPI/Target.h" + +using namespace llvm; +using namespace MachO; +using namespace object; + +StringRef setOrderIndicator(InterfaceInputOrder Order) { + return ((Order == lhs) ? "< " : "> "); +} + +// The following template specialization implementations +// need to be explicitly placed into the llvm namespace +// to work around a GCC 4.8 bug. +namespace llvm { + +template <typename T, DiffAttrKind U> +inline void DiffScalarVal<T, U>::print(raw_ostream &OS, std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) << Val << "\n"; +} + +template <> +inline void +DiffScalarVal<StringRef, AD_Diff_Scalar_Str>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t\t" << setOrderIndicator(Order) << Val << "\n"; +} + +template <> +inline void +DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) << std::to_string(Val) + << "\n"; +} + +template <> +inline void +DiffScalarVal<bool, AD_Diff_Scalar_Bool>::print(raw_ostream &OS, + std::string Indent) { + OS << Indent << "\t" << setOrderIndicator(Order) + << ((Val == true) ? "true" : "false") << "\n"; +} + +} // end namespace llvm + +StringLiteral SymScalar::getSymbolNamePrefix(MachO::SymbolKind Kind) { + switch (Kind) { + case MachO::SymbolKind::GlobalSymbol: + return StringLiteral(""); + case MachO::SymbolKind::ObjectiveCClass: + return ObjC2MetaClassNamePrefix; + case MachO::SymbolKind ::ObjectiveCClassEHType: + return ObjC2EHTypePrefix; + case MachO::SymbolKind ::ObjectiveCInstanceVariable: + return ObjC2IVarPrefix; + } + llvm_unreachable("Unknown llvm::MachO::SymbolKind enum"); +} + +std::string SymScalar::stringifySymbolFlag(MachO::SymbolFlags Flag) { + switch (Flag) { + case MachO::SymbolFlags::None: + return ""; + case MachO::SymbolFlags::ThreadLocalValue: + return "Thread-Local"; + case MachO::SymbolFlags::WeakDefined: + return "Weak-Defined"; + case MachO::SymbolFlags::WeakReferenced: + return "Weak-Referenced"; + case MachO::SymbolFlags::Undefined: + return "Undefined"; + case MachO::SymbolFlags::Rexported: + return "Reexported"; + } + llvm_unreachable("Unknown llvm::MachO::SymbolFlags enum"); +} + +void SymScalar::print(raw_ostream &OS, std::string Indent, MachO::Target Targ) { + if (Val->getKind() == MachO::SymbolKind::ObjectiveCClass) { + if (Targ.Arch == MachO::AK_i386 && + Targ.Platform == MachO::PlatformKind::macOS) { + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << ObjC1ClassNamePrefix << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; + return; + } + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << ObjC2ClassNamePrefix << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; + } + OS << Indent << "\t\t" << ((Order == lhs) ? "< " : "> ") + << getSymbolNamePrefix(Val->getKind()) << Val->getName() + << getFlagString(Val->getFlags()) << "\n"; +} + +bool checkSymbolEquality(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS) { + return std::equal(LHS.begin(), LHS.end(), RHS.begin(), + [&](auto LHS, auto RHS) { return *LHS == *RHS; }); +} + +template <typename TargetVecT, typename ValTypeT, typename V> +void addDiffForTargSlice(V Val, Target Targ, DiffOutput &Diff, + InterfaceInputOrder Order) { + auto TargetVector = llvm::find_if( + Diff.Values, [&](const std::unique_ptr<AttributeDiff> &RawTVec) { + if (TargetVecT *TVec = dyn_cast<TargetVecT>(RawTVec.get())) + return TVec->Targ == Targ; + return false; + }); + if (TargetVector != Diff.Values.end()) { + ValTypeT NewVal(Order, Val); + cast<TargetVecT>(TargetVector->get())->TargValues.push_back(NewVal); + } else { + auto NewTargetVec = std::make_unique<TargetVecT>(Targ); + ValTypeT NewVal(Order, Val); + NewTargetVec->TargValues.push_back(NewVal); + Diff.Values.push_back(std::move(NewTargetVec)); + } +} + +DiffOutput getSingleAttrDiff(const std::vector<InterfaceFileRef> &IRefVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &IRef : IRefVec) + for (auto Targ : IRef.targets()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + IRef.getInstallName(), Targ, Diff, Order); + return Diff; +} + +DiffOutput +getSingleAttrDiff(const std::vector<std::pair<Target, std::string>> &PairVec, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Str_Vec; + for (const auto &Pair : PairVec) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + StringRef(Pair.second), Pair.first, Diff, Order); + return Diff; +} + +DiffOutput getSingleAttrDiff(InterfaceFile::const_symbol_range SymRange, + std::string Name, InterfaceInputOrder Order) { + DiffOutput Diff(Name); + Diff.Kind = AD_Sym_Vec; + for (const auto *Sym : SymRange) + for (auto Targ : Sym->targets()) + addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Diff, Order); + return Diff; +} + +template <typename T> +DiffOutput getSingleAttrDiff(T SingleAttr, std::string Attribute) { + DiffOutput Diff(Attribute); + Diff.Kind = SingleAttr.getKind(); + Diff.Values.push_back(std::make_unique<T>(SingleAttr)); + return Diff; +} + +template <typename T, DiffAttrKind U> +void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, + DiffScalarVal<T, U> Attr) { + Output.push_back(getSingleAttrDiff(Attr, Name)); +} + +template <typename T> +void diffAttribute(std::string Name, std::vector<DiffOutput> &Output, + const T &Val, InterfaceInputOrder Order) { + Output.push_back(getSingleAttrDiff(Val, Name, Order)); +} + +std::vector<DiffOutput> getSingleIF(InterfaceFile *Interface, + InterfaceInputOrder Order) { + std::vector<DiffOutput> Output; + diffAttribute("Install Name", Output, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>( + Order, Interface->getInstallName())); + diffAttribute("Current Version", Output, + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + Order, Interface->getCurrentVersion())); + diffAttribute("Compatibility Version", Output, + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + Order, Interface->getCompatibilityVersion())); + diffAttribute("Swift ABI Version", Output, + DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + Order, Interface->getSwiftABIVersion())); + diffAttribute("InstallAPI", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isInstallAPI())); + diffAttribute("Two Level Namespace", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isTwoLevelNamespace())); + diffAttribute("Application Extension Safe", Output, + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + Order, Interface->isApplicationExtensionSafe())); + diffAttribute("Reexported Libraries", Output, + Interface->reexportedLibraries(), Order); + diffAttribute("Allowable Clients", Output, Interface->allowableClients(), + Order); + diffAttribute("Parent Umbrellas", Output, Interface->umbrellas(), Order); + diffAttribute("Symbols", Output, Interface->symbols(), Order); + for (auto Doc : Interface->documents()) { + DiffOutput Documents("Inlined Reexported Frameworks/Libraries"); + Documents.Kind = AD_Inline_Doc; + Documents.Values.push_back(std::make_unique<InlineDoc>( + InlineDoc(Doc->getInstallName(), getSingleIF(Doc.get(), Order)))); + Output.push_back(std::move(Documents)); + } + return Output; +} + +void findAndAddDiff(const std::vector<InterfaceFileRef> &CollectedIRefVec, + const std::vector<InterfaceFileRef> &LookupIRefVec, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &IRef : CollectedIRefVec) + for (auto Targ : IRef.targets()) { + auto FoundIRef = llvm::find_if(LookupIRefVec, [&](const auto LIRef) { + auto FoundTarg = llvm::find(LIRef.targets(), Targ); + return (FoundTarg != LIRef.targets().end() && + IRef.getInstallName() == LIRef.getInstallName()); + }); + if (FoundIRef == LookupIRefVec.end()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + IRef.getInstallName(), Targ, Result, Order); + } +} + +void findAndAddDiff( + const std::vector<std::pair<Target, std::string>> &CollectedPairs, + const std::vector<std::pair<Target, std::string>> &LookupPairs, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Str_Vec; + for (const auto &Pair : CollectedPairs) { + auto FoundPair = llvm::find(LookupPairs, Pair); + if (FoundPair == LookupPairs.end()) + addDiffForTargSlice<DiffStrVec, + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + StringRef(Pair.second), Pair.first, Result, Order); + } +} + +void findAndAddDiff(InterfaceFile::const_symbol_range CollectedSyms, + InterfaceFile::const_symbol_range LookupSyms, + DiffOutput &Result, InterfaceInputOrder Order) { + Result.Kind = AD_Sym_Vec; + for (const auto *Sym : CollectedSyms) + for (const auto Targ : Sym->targets()) { + auto FoundSym = llvm::find_if(LookupSyms, [&](const auto LSym) { + auto FoundTarg = llvm::find(LSym->targets(), Targ); + return (Sym->getName() == LSym->getName() && + Sym->getKind() == LSym->getKind() && + Sym->getFlags() == LSym->getFlags() && + FoundTarg != LSym->targets().end()); + }); + if (FoundSym == LookupSyms.end()) + addDiffForTargSlice<DiffSymVec, SymScalar>(Sym, Targ, Result, Order); + } +} + +template <typename T> +DiffOutput recordDifferences(T LHS, T RHS, std::string Attr) { + DiffOutput Diff(Attr); + if (LHS.getKind() == RHS.getKind()) { + Diff.Kind = LHS.getKind(); + Diff.Values.push_back(std::make_unique<T>(LHS)); + Diff.Values.push_back(std::make_unique<T>(RHS)); + } + return Diff; +} + +template <typename T> +DiffOutput recordDifferences(const std::vector<T> &LHS, + const std::vector<T> &RHS, std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Str_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +DiffOutput recordDifferences(llvm::MachO::InterfaceFile::const_symbol_range LHS, + llvm::MachO::InterfaceFile::const_symbol_range RHS, + std::string Attr) { + DiffOutput Diff(Attr); + Diff.Kind = AD_Sym_Vec; + findAndAddDiff(LHS, RHS, Diff, lhs); + findAndAddDiff(RHS, LHS, Diff, rhs); + return Diff; +} + +std::vector<DiffOutput> +DiffEngine::findDifferences(const InterfaceFile *IFLHS, + const InterfaceFile *IFRHS) { + std::vector<DiffOutput> Output; + if (IFLHS->getInstallName() != IFRHS->getInstallName()) + Output.push_back(recordDifferences( + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(lhs, + IFLHS->getInstallName()), + DiffScalarVal<StringRef, AD_Diff_Scalar_Str>(rhs, + IFRHS->getInstallName()), + "Install Name")); + + if (IFLHS->getCurrentVersion() != IFRHS->getCurrentVersion()) + Output.push_back(recordDifferences( + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + lhs, IFLHS->getCurrentVersion()), + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + rhs, IFRHS->getCurrentVersion()), + "Current Version")); + if (IFLHS->getCompatibilityVersion() != IFRHS->getCompatibilityVersion()) + Output.push_back(recordDifferences( + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + lhs, IFLHS->getCompatibilityVersion()), + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>( + rhs, IFRHS->getCompatibilityVersion()), + "Compatibility Version")); + if (IFLHS->getSwiftABIVersion() != IFRHS->getSwiftABIVersion()) + Output.push_back( + recordDifferences(DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + lhs, IFLHS->getSwiftABIVersion()), + DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>( + rhs, IFRHS->getSwiftABIVersion()), + "Swift ABI Version")); + if (IFLHS->isInstallAPI() != IFRHS->isInstallAPI()) + Output.push_back(recordDifferences( + DiffScalarVal<bool, AD_Diff_Scalar_Bool>(lhs, IFLHS->isInstallAPI()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>(rhs, IFRHS->isInstallAPI()), + "InstallAPI")); + + if (IFLHS->isTwoLevelNamespace() != IFRHS->isTwoLevelNamespace()) + Output.push_back(recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + lhs, IFLHS->isTwoLevelNamespace()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + rhs, IFRHS->isTwoLevelNamespace()), + "Two Level Namespace")); + + if (IFLHS->isApplicationExtensionSafe() != + IFRHS->isApplicationExtensionSafe()) + Output.push_back( + recordDifferences(DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + lhs, IFLHS->isApplicationExtensionSafe()), + DiffScalarVal<bool, AD_Diff_Scalar_Bool>( + rhs, IFRHS->isApplicationExtensionSafe()), + "Application Extension Safe")); + + if (IFLHS->reexportedLibraries() != IFRHS->reexportedLibraries()) + Output.push_back(recordDifferences(IFLHS->reexportedLibraries(), + IFRHS->reexportedLibraries(), + "Reexported Libraries")); + + if (IFLHS->allowableClients() != IFRHS->allowableClients()) + Output.push_back(recordDifferences(IFLHS->allowableClients(), + IFRHS->allowableClients(), + "Allowable Clients")); + + if (IFLHS->umbrellas() != IFRHS->umbrellas()) + Output.push_back(recordDifferences(IFLHS->umbrellas(), IFRHS->umbrellas(), + "Parent Umbrellas")); + + if (!checkSymbolEquality(IFLHS->symbols(), IFRHS->symbols())) + Output.push_back( + recordDifferences(IFLHS->symbols(), IFRHS->symbols(), "Symbols")); + + if (IFLHS->documents() != IFRHS->documents()) { + DiffOutput Docs("Inlined Reexported Frameworks/Libraries"); + Docs.Kind = AD_Inline_Doc; + std::vector<StringRef> DocsInserted; + // Iterate through inline frameworks/libraries from interface file and find + // match based on install name. + for (auto DocLHS : IFLHS->documents()) { + auto Pair = llvm::find_if(IFRHS->documents(), [&](const auto &DocRHS) { + return (DocLHS->getInstallName() == DocRHS->getInstallName()); + }); + // If a match found, recursively get differences between the pair. + if (Pair != IFRHS->documents().end()) { + InlineDoc PairDiff = + InlineDoc(DocLHS->getInstallName(), + findDifferences(DocLHS.get(), Pair->get())); + if (!PairDiff.DocValues.empty()) + Docs.Values.push_back( + std::make_unique<InlineDoc>(std::move(PairDiff))); + } + // If a match is not found, get attributes from single item. + else + Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( + DocLHS->getInstallName(), getSingleIF(DocLHS.get(), lhs)))); + DocsInserted.push_back(DocLHS->getInstallName()); + } + for (auto DocRHS : IFRHS->documents()) { + auto WasGathered = + llvm::find_if(DocsInserted, [&](const auto &GatheredDoc) { + return (GatheredDoc == DocRHS->getInstallName()); + }); + if (WasGathered == DocsInserted.end()) + Docs.Values.push_back(std::make_unique<InlineDoc>(InlineDoc( + DocRHS->getInstallName(), getSingleIF(DocRHS.get(), rhs)))); + } + if (!Docs.Values.empty()) + Output.push_back(std::move(Docs)); + } + return Output; +} + +template <typename T> +void printSingleVal(std::string Indent, const DiffOutput &Attr, + raw_ostream &OS) { + if (Attr.Values.empty()) + return; + OS << Indent << Attr.Name << "\n"; + for (auto &RawItem : Attr.Values) + if (T *Item = dyn_cast<T>(RawItem.get())) + Item->print(OS, Indent); +} + +template <typename T> +T *castValues(const std::unique_ptr<AttributeDiff> &RawAttr) { + T *CastAttr = cast<T>(RawAttr.get()); + return CastAttr; +} + +template <typename T> void sortTargetValues(std::vector<T> &TargValues) { + llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { + return ValA.getOrder() < ValB.getOrder(); + }); + llvm::stable_sort(TargValues, [](const auto &ValA, const auto &ValB) { + return ValA.getOrder() == ValB.getOrder() && ValA.getVal() < ValB.getVal(); + }); +} + +template <typename T> +void printVecVal(std::string Indent, const DiffOutput &Attr, raw_ostream &OS) { + if (Attr.Values.empty()) + return; + + OS << Indent << Attr.Name << "\n"; + + std::vector<T *> SortedAttrs; + + llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), castValues<T>); + + llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { + return ValA->Targ < ValB->Targ; + }); + + for (auto *Vec : SortedAttrs) { + sortTargetValues<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>( + Vec->TargValues); + OS << Indent << "\t" << getTargetTripleName(Vec->Targ) << "\n"; + for (auto &Item : Vec->TargValues) + Item.print(OS, Indent); + } +} + +template <> +void printVecVal<DiffSymVec>(std::string Indent, const DiffOutput &Attr, + raw_ostream &OS) { + if (Attr.Values.empty()) + return; + + OS << Indent << Attr.Name << "\n"; + + std::vector<DiffSymVec *> SortedAttrs; + + llvm::transform(Attr.Values, std::back_inserter(SortedAttrs), + castValues<DiffSymVec>); + + llvm::sort(SortedAttrs, [&](const auto &ValA, const auto &ValB) { + return ValA->Targ < ValB->Targ; + }); + for (auto *SymVec : SortedAttrs) { + sortTargetValues<SymScalar>(SymVec->TargValues); + OS << Indent << "\t" << getTargetTripleName(SymVec->Targ) << "\n"; + for (auto &Item : SymVec->TargValues) + Item.print(OS, Indent, SymVec->Targ); + } +} + +void DiffEngine::printDifferences(raw_ostream &OS, + const std::vector<DiffOutput> &Diffs, + int IndentCounter) { + std::string Indent = std::string(IndentCounter, '\t'); + for (auto &Attr : Diffs) { + switch (Attr.Kind) { + case AD_Diff_Scalar_Str: + if (IndentCounter == 0) + printSingleVal<DiffScalarVal<StringRef, AD_Diff_Scalar_Str>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_PackedVersion: + printSingleVal< + DiffScalarVal<PackedVersion, AD_Diff_Scalar_PackedVersion>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Unsigned: + printSingleVal<DiffScalarVal<uint8_t, AD_Diff_Scalar_Unsigned>>(Indent, + Attr, OS); + break; + case AD_Diff_Scalar_Bool: + printSingleVal<DiffScalarVal<bool, AD_Diff_Scalar_Bool>>(Indent, Attr, + OS); + break; + case AD_Str_Vec: + printVecVal<DiffStrVec>(Indent, Attr, OS); + break; + case AD_Sym_Vec: + printVecVal<DiffSymVec>(Indent, Attr, OS); + break; + case AD_Inline_Doc: + if (!Attr.Values.empty()) { + OS << Indent << Attr.Name << "\n"; + for (auto &Item : Attr.Values) + if (InlineDoc *Doc = dyn_cast<InlineDoc>(Item.get())) + if (!Doc->DocValues.empty()) { + OS << Indent << "\t" << Doc->InstallName << "\n"; + printDifferences(OS, std::move(Doc->DocValues), 2); + } + } + break; + } + } +} + +bool DiffEngine::compareFiles(raw_ostream &OS) { + const auto *IFLHS = &(FileLHS->getInterfaceFile()); + const auto *IFRHS = &(FileRHS->getInterfaceFile()); + if (*IFLHS == *IFRHS) + return false; + OS << "< " << std::string(IFLHS->getPath().data()) << "\n> " + << std::string(IFRHS->getPath().data()) << "\n\n"; + std::vector<DiffOutput> Diffs = findDifferences(IFLHS, IFRHS); + printDifferences(OS, Diffs, 0); + return true; +} |