diff --git a/llvm/include/llvm/TextAPI/InterfaceFile.h b/llvm/include/llvm/TextAPI/InterfaceFile.h --- a/llvm/include/llvm/TextAPI/InterfaceFile.h +++ b/llvm/include/llvm/TextAPI/InterfaceFile.h @@ -24,6 +24,7 @@ #include "llvm/TextAPI/PackedVersion.h" #include "llvm/TextAPI/Platform.h" #include "llvm/TextAPI/Symbol.h" +#include "llvm/TextAPI/SymbolSet.h" #include "llvm/TextAPI/Target.h" namespace llvm { @@ -123,37 +124,15 @@ } // end namespace MachO. -struct SymbolsMapKey { - MachO::SymbolKind Kind; - StringRef Name; - - SymbolsMapKey(MachO::SymbolKind Kind, StringRef Name) - : Kind(Kind), Name(Name) {} -}; -template <> struct DenseMapInfo { - static inline SymbolsMapKey getEmptyKey() { - return SymbolsMapKey(MachO::SymbolKind::GlobalSymbol, StringRef{}); - } - - static inline SymbolsMapKey getTombstoneKey() { - return SymbolsMapKey(MachO::SymbolKind::ObjectiveCInstanceVariable, - StringRef{}); - } - - static unsigned getHashValue(const SymbolsMapKey &Key) { - return hash_combine(hash_value(Key.Kind), hash_value(Key.Name)); - } - - static bool isEqual(const SymbolsMapKey &LHS, const SymbolsMapKey &RHS) { - return std::tie(LHS.Kind, LHS.Name) == std::tie(RHS.Kind, RHS.Name); - } -}; - namespace MachO { /// Defines the interface file. class InterfaceFile { public: + InterfaceFile(std::unique_ptr &&InputSymbols) + : SymbolsSet(std::move(InputSymbols)) {} + + InterfaceFile() : SymbolsSet(std::make_unique()){}; /// Set the path from which this file was generated (if applicable). /// /// \param Path_ The path to the source file. @@ -360,66 +339,65 @@ return RPaths; } - /// Add a symbol to the symbols list or extend an existing one. - void addSymbol(SymbolKind Kind, StringRef Name, const TargetList &Targets, - SymbolFlags Flags = SymbolFlags::None); - - using SymbolMapType = DenseMap; - struct const_symbol_iterator - : public iterator_adaptor_base< - const_symbol_iterator, SymbolMapType::const_iterator, - std::forward_iterator_tag, const Symbol *, ptrdiff_t, - const Symbol *, const Symbol *> { - const_symbol_iterator() = default; - - template - const_symbol_iterator(U &&u) - : iterator_adaptor_base(std::forward(u)) {} - - reference operator*() const { return I->second; } - pointer operator->() const { return I->second; } - }; + /// Add Symbol with multiple targets. + /// + /// \param Kind The kind of global symbol to record. + /// \param Name The name of the symbol. + /// \param Targets The list of targets the symbol is defined in. + /// \param Flags The properties the symbol holds. + std::optional contains(SymbolKind Kind, + StringRef Name) const { + if (auto *Sym = SymbolsSet->findSymbol(Kind, Name)) + return Sym; + return std::nullopt; + } - using const_symbol_range = iterator_range; + /// Add a symbol to the symbols list or extend an existing one. + template ()))>::type> + void addSymbol(SymbolKind Kind, StringRef Name, RangeT &&Targets, + SymbolFlags Flags = SymbolFlags::None) { + SymbolsSet->addGlobal(Kind, Name, Flags, Targets); + } - using const_filtered_symbol_iterator = - filter_iterator>; - using const_filtered_symbol_range = - iterator_range; + /// Add Symbol with multiple targets. + /// + /// \param Kind The kind of global symbol to record. + /// \param Name The name of the symbol. + /// \param Targets The list of targets the symbol is defined in. + /// \param Flags The properties the symbol holds. + void addSymbol(SymbolKind Kind, StringRef Name, TargetList &&Targets, + SymbolFlags Flags = SymbolFlags::None) { + SymbolsSet->addGlobal(Kind, Name, Flags, Targets); + } - const_symbol_range symbols() const { - return {Symbols.begin(), Symbols.end()}; + /// Add Symbol with single target. + /// + /// \param Kind The kind of global symbol to record. + /// \param Name The name of the symbol. + /// \param Target The target the symbol is defined in. + /// \param Flags The properties the symbol holds. + void addSymbol(SymbolKind Kind, StringRef Name, Target &Target, + SymbolFlags Flags = SymbolFlags::None) { + SymbolsSet->addGlobal(Kind, Name, Flags, Target); } - size_t symbolsCount() const { return Symbols.size(); } + /// Get size of symbol set. + /// \return The number of symbols the file holds. + size_t symbolsCount() const { return SymbolsSet->size(); } - const_filtered_symbol_range exports() const { - std::function fn = [](const Symbol *Symbol) { - return !Symbol->isUndefined() && !Symbol->isReexported(); - }; - return make_filter_range( - make_range({Symbols.begin()}, {Symbols.end()}), - fn); - } + using const_symbol_range = SymbolSet::const_symbol_range; + using const_filtered_symbol_range = SymbolSet::const_filtered_symbol_range; + const_symbol_range symbols() const { return SymbolsSet->symbols(); }; + const_filtered_symbol_range exports() const { return SymbolsSet->exports(); }; const_filtered_symbol_range reexports() const { - std::function fn = [](const Symbol *Symbol) { - return Symbol->isReexported(); - }; - return make_filter_range( - make_range({Symbols.begin()}, {Symbols.end()}), - fn); - } - + return SymbolsSet->reexports(); + }; const_filtered_symbol_range undefineds() const { - std::function fn = [](const Symbol *Symbol) { - return Symbol->isUndefined(); - }; - return make_filter_range( - make_range({Symbols.begin()}, {Symbols.end()}), - fn); - } + return SymbolsSet->undefineds(); + }; /// The equality is determined by attributes that impact linking /// compatibilities. UUIDs, Path, & FileKind are irrelevant since these by @@ -457,23 +435,20 @@ std::vector> Documents; std::vector> UUIDs; std::vector> RPaths; - SymbolMapType Symbols; + std::unique_ptr SymbolsSet; InterfaceFile *Parent = nullptr; }; -template -bool operator==(const DenseMapBase &LHS, - const DenseMapBase &RHS) { - if (LHS.size() != RHS.size()) - return false; - for (const auto &KV : LHS) { - auto I = RHS.find(KV.first); - if (I == RHS.end() || *I->second != *KV.second) - return false; - } - return true; +// Keep containers that hold InterfaceFileRefs in sorted order and uniqued. +template +typename C::iterator addEntry(C &Container, StringRef InstallName) { + auto I = partition_point(Container, [=](const InterfaceFileRef &O) { + return O.getInstallName() < InstallName; + }); + if (I != Container.end() && I->getInstallName() == InstallName) + return I; + + return Container.emplace(I, InstallName); } } // end namespace MachO. diff --git a/llvm/include/llvm/TextAPI/Symbol.h b/llvm/include/llvm/TextAPI/Symbol.h --- a/llvm/include/llvm/TextAPI/Symbol.h +++ b/llvm/include/llvm/TextAPI/Symbol.h @@ -65,12 +65,26 @@ constexpr StringLiteral ObjC2IVarPrefix = "_OBJC_IVAR_$_"; using TargetList = SmallVector; + +// Keep containers that hold Targets in sorted order and uniqued. +template +typename C::iterator addEntry(C &Container, const Target &Targ) { + auto Iter = + lower_bound(Container, Targ, [](const Target &LHS, const Target &RHS) { + return LHS < RHS; + }); + if ((Iter != std::end(Container)) && !(Targ < *Iter)) + return Iter; + + return Container.insert(Iter, Targ); +} + class Symbol { public: Symbol(SymbolKind Kind, StringRef Name, TargetList Targets, SymbolFlags Flags) : Name(Name), Targets(std::move(Targets)), Kind(Kind), Flags(Flags) {} - void addTarget(Target target) { Targets.emplace_back(target); } + void addTarget(Target InputTarget) { addEntry(Targets, InputTarget); } SymbolKind getKind() const { return Kind; } StringRef getName() const { return Name; } ArchitectureSet getArchitectures() const { diff --git a/llvm/include/llvm/TextAPI/SymbolSet.h b/llvm/include/llvm/TextAPI/SymbolSet.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/TextAPI/SymbolSet.h @@ -0,0 +1,182 @@ +//===- llvm/TextAPI/SymbolSet.h - TAPI Symbol Set --------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_SYMBOLSET_H +#define LLVM_TEXTAPI_SYMBOLSET_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Allocator.h" +#include "llvm/TextAPI/Architecture.h" +#include "llvm/TextAPI/ArchitectureSet.h" +#include "llvm/TextAPI/Symbol.h" +#include + +namespace llvm { + +struct SymbolsMapKey { + MachO::SymbolKind Kind; + StringRef Name; + + SymbolsMapKey(MachO::SymbolKind Kind, StringRef Name) + : Kind(Kind), Name(Name) {} +}; +template <> struct DenseMapInfo { + static inline SymbolsMapKey getEmptyKey() { + return SymbolsMapKey(MachO::SymbolKind::GlobalSymbol, StringRef{}); + } + + static inline SymbolsMapKey getTombstoneKey() { + return SymbolsMapKey(MachO::SymbolKind::ObjectiveCInstanceVariable, + StringRef{}); + } + + static unsigned getHashValue(const SymbolsMapKey &Key) { + return hash_combine(hash_value(Key.Kind), hash_value(Key.Name)); + } + + static bool isEqual(const SymbolsMapKey &LHS, const SymbolsMapKey &RHS) { + return std::tie(LHS.Kind, LHS.Name) == std::tie(RHS.Kind, RHS.Name); + } +}; + +template +bool operator==(const DenseMapBase &LHS, + const DenseMapBase &RHS) { + if (LHS.size() != RHS.size()) + return false; + for (const auto &KV : LHS) { + auto I = RHS.find(KV.first); + if (I == RHS.end() || *I->second != *KV.second) + return false; + } + return true; +} + +template +bool operator!=(const DenseMapBase &LHS, + const DenseMapBase &RHS) { + return !(LHS == RHS); +} + +namespace MachO { + +class SymbolSet { +private: + llvm::BumpPtrAllocator Allocator; + StringRef copyString(StringRef String) { + if (String.empty()) + return {}; + void *Ptr = Allocator.Allocate(String.size(), 1); + memcpy(Ptr, String.data(), String.size()); + return StringRef(reinterpret_cast(Ptr), String.size()); + } + + using SymbolsMapType = llvm::DenseMap; + SymbolsMapType Symbols; + + Symbol *addGlobalImpl(SymbolKind, StringRef Name, SymbolFlags Flags); + +public: + SymbolSet() = default; + Symbol *addGlobal(SymbolKind Kind, StringRef Name, SymbolFlags Flags, + const Target &Targ); + size_t size() const { return Symbols.size(); } + + template ()))>::type> + Symbol *addGlobal(SymbolKind Kind, StringRef Name, SymbolFlags Flags, + RangeT &&Targets) { + auto *Global = addGlobalImpl(Kind, Name, Flags); + for (const auto &Targ : Targets) + Global->addTarget(Targ); + if (Kind == SymbolKind::ObjectiveCClassEHType) + addGlobal(SymbolKind::ObjectiveCClass, Name, Flags, Targets); + return Global; + } + + const Symbol *findSymbol(SymbolKind Kind, StringRef Name) const; + + struct const_symbol_iterator + : public iterator_adaptor_base< + const_symbol_iterator, SymbolsMapType::const_iterator, + std::forward_iterator_tag, const Symbol *, ptrdiff_t, + const Symbol *, const Symbol *> { + const_symbol_iterator() = default; + + template + const_symbol_iterator(U &&u) + : iterator_adaptor_base(std::forward(u)) {} + + reference operator*() const { return I->second; } + pointer operator->() const { return I->second; } + }; + + using const_symbol_range = iterator_range; + + using const_filtered_symbol_iterator = + filter_iterator>; + using const_filtered_symbol_range = + iterator_range; + + // Range that contains all symbols. + const_symbol_range symbols() const { + return {Symbols.begin(), Symbols.end()}; + } + + // Range that contains all defined and exported symbols. + const_filtered_symbol_range exports() const { + std::function fn = [](const Symbol *Symbol) { + return !Symbol->isUndefined() && !Symbol->isReexported(); + }; + return make_filter_range( + make_range({Symbols.begin()}, {Symbols.end()}), + fn); + } + + // Range that contains all reexported symbols. + const_filtered_symbol_range reexports() const { + std::function fn = [](const Symbol *Symbol) { + return Symbol->isReexported(); + }; + return make_filter_range( + make_range({Symbols.begin()}, {Symbols.end()}), + fn); + } + + // Range that contains all undefined and exported symbols. + const_filtered_symbol_range undefineds() const { + std::function fn = [](const Symbol *Symbol) { + return Symbol->isUndefined(); + }; + return make_filter_range( + make_range({Symbols.begin()}, {Symbols.end()}), + fn); + } + + bool operator==(const SymbolSet &O) const; + + bool operator!=(const SymbolSet &O) const { return !(Symbols == O.Symbols); } + + void *allocate(size_t Size, unsigned Align = 8) { + return Allocator.Allocate(Size, Align); + } +}; + +} // namespace MachO +} // namespace llvm +#endif // LLVM_TEXTAPI_SYMBOLSET_H diff --git a/llvm/lib/TextAPI/CMakeLists.txt b/llvm/lib/TextAPI/CMakeLists.txt --- a/llvm/lib/TextAPI/CMakeLists.txt +++ b/llvm/lib/TextAPI/CMakeLists.txt @@ -6,6 +6,7 @@ PackedVersion.cpp Platform.cpp Symbol.cpp + SymbolSet.cpp Target.cpp TextStub.cpp TextStubCommon.cpp diff --git a/llvm/lib/TextAPI/InterfaceFile.cpp b/llvm/lib/TextAPI/InterfaceFile.cpp --- a/llvm/lib/TextAPI/InterfaceFile.cpp +++ b/llvm/lib/TextAPI/InterfaceFile.cpp @@ -120,17 +120,6 @@ return make_filter_range(Targets, fn); } -void InterfaceFile::addSymbol(SymbolKind Kind, StringRef Name, - const TargetList &Targets, SymbolFlags Flags) { - Name = copyString(Name); - auto result = Symbols.try_emplace(SymbolsMapKey{Kind, Name}, nullptr); - if (result.second) - result.first->second = new (Allocator) Symbol{Kind, Name, Targets, Flags}; - else - for (const auto &Target : Targets) - result.first->second->addTarget(Target); -} - void InterfaceFile::addDocument(std::shared_ptr &&Document) { auto Pos = llvm::lower_bound(Documents, Document, [](const std::shared_ptr &LHS, @@ -167,7 +156,7 @@ return false; if (ReexportedLibraries != O.ReexportedLibraries) return false; - if (Symbols != O.Symbols) + if (*SymbolsSet != *O.SymbolsSet) return false; // Don't compare run search paths for older filetypes that cannot express // them. diff --git a/llvm/lib/TextAPI/Symbol.cpp b/llvm/lib/TextAPI/Symbol.cpp --- a/llvm/lib/TextAPI/Symbol.cpp +++ b/llvm/lib/TextAPI/Symbol.cpp @@ -65,10 +65,9 @@ }; SymbolFlags LHSFlags = Flags; SymbolFlags RHSFlags = O.Flags; - if ((!O.isData() && !O.isText()) || (!isData() && !isText())) { - RemoveFlag(*this, LHSFlags); - RemoveFlag(O, RHSFlags); - } + // Ignore Text and Data for now. + RemoveFlag(*this, LHSFlags); + RemoveFlag(O, RHSFlags); return std::tie(Name, Kind, Targets, LHSFlags) == std::tie(O.Name, O.Kind, O.Targets, RHSFlags); } diff --git a/llvm/lib/TextAPI/SymbolSet.cpp b/llvm/lib/TextAPI/SymbolSet.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/TextAPI/SymbolSet.cpp @@ -0,0 +1,38 @@ +//===- lib/TextAPI/SymbolSet.cpp - TAPI Symbol Set ------------*- C++-*----===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/SymbolSet.h" + +using namespace llvm; +using namespace llvm::MachO; + +Symbol *SymbolSet::addGlobalImpl(SymbolKind Kind, StringRef Name, + SymbolFlags Flags) { + Name = copyString(Name); + auto Result = Symbols.try_emplace(SymbolsMapKey{Kind, Name}, nullptr); + if (Result.second) + Result.first->second = + new (Allocator) Symbol{Kind, Name, TargetList(), Flags}; + return Result.first->second; +} + +Symbol *SymbolSet::addGlobal(SymbolKind Kind, StringRef Name, SymbolFlags Flags, + const Target &Targ) { + auto *Sym = addGlobalImpl(Kind, Name, Flags); + Sym->addTarget(Targ); + if (Kind == SymbolKind::ObjectiveCClassEHType) + addGlobal(SymbolKind::ObjectiveCClass, Name, Flags, Targ); + return Sym; +} + +const Symbol *SymbolSet::findSymbol(SymbolKind Kind, StringRef Name) const { + auto It = Symbols.find({Kind, Name}); + if (It != Symbols.end()) + return It->second; + return nullptr; +} diff --git a/llvm/unittests/TextAPI/TextStubV5Tests.cpp b/llvm/unittests/TextAPI/TextStubV5Tests.cpp --- a/llvm/unittests/TextAPI/TextStubV5Tests.cpp +++ b/llvm/unittests/TextAPI/TextStubV5Tests.cpp @@ -124,6 +124,8 @@ "_globalVar" ], "objc_class": [ + "ClassA", + "ClassB", "ClassData" ], "objc_eh_type": [ @@ -264,7 +266,18 @@ false, true, {Target(AK_x86_64, PLATFORM_MACOS)}}, - {SymbolKind::ObjectiveCClass, "ClassA", false, false, true, MacOSTargets}, + {SymbolKind::ObjectiveCClass, + "ClassA", + false, + false, + true, + {Target(AK_x86_64, PLATFORM_MACOS)}}, + {SymbolKind::ObjectiveCClass, + "ClassB", + false, + false, + true, + {Target(AK_x86_64, PLATFORM_MACOS)}}, {SymbolKind::ObjectiveCClass, "ClassData", false, @@ -440,7 +453,7 @@ { "data": { "thread_local": [ "_globalVar" ], - "objc_class": [ "ClassData" ], + "objc_class": [ "ClassData", "ClassA", "ClassB"], "objc_eh_type": [ "ClassA", "ClassB" ] }, "text": { @@ -495,6 +508,8 @@ ExportedSymbolSeq ExpectedExports = { {SymbolKind::GlobalSymbol, "_funcFoo", false, false, false, {iOSTarget}}, {SymbolKind::GlobalSymbol, "_globalVar", false, true, true, {iOSTarget}}, + {SymbolKind::ObjectiveCClass, "ClassA", false, false, true, {iOSTarget}}, + {SymbolKind::ObjectiveCClass, "ClassB", false, false, true, {iOSTarget}}, {SymbolKind::ObjectiveCClass, "ClassData", false, @@ -622,6 +637,8 @@ "_globalVar" ], "objc_class": [ + "ClassA", + "ClassB", "ClassData" ], "objc_eh_type": [