Skip to content

Commit 823b1a5

Browse files
authored
[clang-installapi] Store dylib attributes in the order they are passed on the command line. (#139087)
With the introduction of tbd-v5 holding rpaths, the order in which those attributes are passed to `clang-installapi` must be represented in tbd files. Previously, all dylib attributes were stored in a non-deterministic `StringMap`. Instead, hold them in a custom collection with an underlying vector to continue supporting searching by attribute. This makes the order of all diagnostics related to load command comparisons stable. This approach resolves errors when building with reverse-iteration.
1 parent d926ec3 commit 823b1a5

File tree

6 files changed

+87
-46
lines changed

6 files changed

+87
-46
lines changed

clang/include/clang/InstallAPI/DylibVerifier.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,31 @@ enum class VerificationMode {
2525
Pedantic,
2626
};
2727

28-
using LibAttrs = llvm::StringMap<ArchitectureSet>;
2928
using ReexportedInterfaces = llvm::SmallVector<llvm::MachO::InterfaceFile, 8>;
3029

30+
/// Represents dynamic library specific attributes that are tied to
31+
/// architecture slices. It is commonly used for comparing options
32+
/// passed on the command line to installapi and what exists in dylib load
33+
/// commands.
34+
class LibAttrs {
35+
public:
36+
using Entry = std::pair<std::string, ArchitectureSet>;
37+
using AttrsToArchs = llvm::SmallVector<Entry, 10>;
38+
39+
// Mutable access to architecture set tied to the input attribute.
40+
ArchitectureSet &getArchSet(StringRef Attr);
41+
// Get entry based on the attribute.
42+
std::optional<Entry> find(StringRef Attr) const;
43+
// Immutable access to underlying container.
44+
const AttrsToArchs &get() const { return LibraryAttributes; };
45+
// Mutable access to underlying container.
46+
AttrsToArchs &get() { return LibraryAttributes; };
47+
bool operator==(const LibAttrs &Other) const { return Other.get() == get(); };
48+
49+
private:
50+
AttrsToArchs LibraryAttributes;
51+
};
52+
3153
// Pointers to information about a zippered declaration used for
3254
// querying and reporting violations against different
3355
// declarations that all map to the same symbol.

clang/lib/InstallAPI/DiagnosticBuilderWrappers.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
9797

9898
const clang::DiagnosticBuilder &
9999
operator<<(const clang::DiagnosticBuilder &DB,
100-
const StringMapEntry<ArchitectureSet> &LibAttr) {
101-
std::string IFAsString;
102-
raw_string_ostream OS(IFAsString);
100+
const clang::installapi::LibAttrs::Entry &LibAttr) {
101+
std::string Entry;
102+
raw_string_ostream OS(Entry);
103103

104-
OS << LibAttr.getKey() << " [ " << LibAttr.getValue() << " ]";
105-
DB.AddString(IFAsString);
104+
OS << LibAttr.first << " [ " << LibAttr.second << " ]";
105+
DB.AddString(Entry);
106106
return DB;
107107
}
108108

clang/lib/InstallAPI/DiagnosticBuilderWrappers.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define LLVM_CLANG_INSTALLAPI_DIAGNOSTICBUILDER_WRAPPER_H
1515

1616
#include "clang/Basic/Diagnostic.h"
17+
#include "clang/InstallAPI/DylibVerifier.h"
1718
#include "llvm/TextAPI/Architecture.h"
1819
#include "llvm/TextAPI/ArchitectureSet.h"
1920
#include "llvm/TextAPI/InterfaceFile.h"
@@ -42,7 +43,7 @@ const clang::DiagnosticBuilder &operator<<(const clang::DiagnosticBuilder &DB,
4243

4344
const clang::DiagnosticBuilder &
4445
operator<<(const clang::DiagnosticBuilder &DB,
45-
const StringMapEntry<ArchitectureSet> &LibAttr);
46+
const clang::installapi::LibAttrs::Entry &LibAttr);
4647

4748
} // namespace MachO
4849
} // namespace llvm

clang/lib/InstallAPI/DylibVerifier.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@ using namespace llvm::MachO;
1818
namespace clang {
1919
namespace installapi {
2020

21+
ArchitectureSet &LibAttrs::getArchSet(StringRef Attr) {
22+
auto *It = llvm::find_if(LibraryAttributes, [&Attr](const auto &Input) {
23+
return Attr == Input.first;
24+
});
25+
if (It != LibraryAttributes.end())
26+
return It->second;
27+
LibraryAttributes.push_back({Attr.str(), ArchitectureSet()});
28+
return LibraryAttributes.back().second;
29+
}
30+
31+
std::optional<LibAttrs::Entry> LibAttrs::find(StringRef Attr) const {
32+
auto *It = llvm::find_if(LibraryAttributes, [&Attr](const auto &Input) {
33+
return Attr == Input.first;
34+
});
35+
if (It == LibraryAttributes.end())
36+
return std::nullopt;
37+
return *It;
38+
}
39+
2140
/// Metadata stored about a mapping of a declaration to a symbol.
2241
struct DylibVerifier::SymbolContext {
2342
// Name to use for all querying and verification
@@ -825,13 +844,13 @@ bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
825844
DylibTargets.push_back(RS->getTarget());
826845
const BinaryAttrs &BinInfo = RS->getBinaryAttrs();
827846
for (const StringRef LibName : BinInfo.RexportedLibraries)
828-
DylibReexports[LibName].set(DylibTargets.back().Arch);
847+
DylibReexports.getArchSet(LibName).set(DylibTargets.back().Arch);
829848
for (const StringRef LibName : BinInfo.AllowableClients)
830-
DylibClients[LibName].set(DylibTargets.back().Arch);
849+
DylibClients.getArchSet(LibName).set(DylibTargets.back().Arch);
831850
// Compare attributes that are only representable in >= TBD_V5.
832851
if (FT >= FileType::TBD_V5)
833852
for (const StringRef Name : BinInfo.RPaths)
834-
DylibRPaths[Name].set(DylibTargets.back().Arch);
853+
DylibRPaths.getArchSet(Name).set(DylibTargets.back().Arch);
835854
}
836855

837856
// Check targets first.
@@ -923,31 +942,33 @@ bool DylibVerifier::verifyBinaryAttrs(const ArrayRef<Target> ProvidedTargets,
923942
if (Provided == Dylib)
924943
return true;
925944

926-
for (const llvm::StringMapEntry<ArchitectureSet> &PAttr : Provided) {
927-
const auto DAttrIt = Dylib.find(PAttr.getKey());
928-
if (DAttrIt == Dylib.end()) {
929-
Ctx.Diag->Report(DiagID_missing) << "binary file" << PAttr;
945+
for (const LibAttrs::Entry &PEntry : Provided.get()) {
946+
const auto &[PAttr, PArchSet] = PEntry;
947+
auto DAttrEntry = Dylib.find(PAttr);
948+
if (!DAttrEntry) {
949+
Ctx.Diag->Report(DiagID_missing) << "binary file" << PEntry;
930950
if (Fatal)
931951
return false;
932952
}
933953

934-
if (PAttr.getValue() != DAttrIt->getValue()) {
935-
Ctx.Diag->Report(DiagID_mismatch) << PAttr << *DAttrIt;
954+
if (PArchSet != DAttrEntry->second) {
955+
Ctx.Diag->Report(DiagID_mismatch) << PEntry << *DAttrEntry;
936956
if (Fatal)
937957
return false;
938958
}
939959
}
940960

941-
for (const llvm::StringMapEntry<ArchitectureSet> &DAttr : Dylib) {
942-
const auto PAttrIt = Provided.find(DAttr.getKey());
943-
if (PAttrIt == Provided.end()) {
944-
Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DAttr;
961+
for (const LibAttrs::Entry &DEntry : Dylib.get()) {
962+
const auto &[DAttr, DArchSet] = DEntry;
963+
const auto &PAttrEntry = Provided.find(DAttr);
964+
if (!PAttrEntry) {
965+
Ctx.Diag->Report(DiagID_missing) << "installAPI option" << DEntry;
945966
if (!Fatal)
946967
continue;
947968
return false;
948969
}
949970

950-
if (PAttrIt->getValue() != DAttr.getValue()) {
971+
if (PAttrEntry->second != DArchSet) {
951972
if (Fatal)
952973
llvm_unreachable("this case was already covered above.");
953974
}

clang/tools/clang-installapi/ClangInstallAPI.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ static bool run(ArrayRef<const char *> Args, const char *ProgName) {
170170
[&IF](
171171
const auto &Attrs,
172172
std::function<void(InterfaceFile *, StringRef, const Target &)> Add) {
173-
for (const auto &Lib : Attrs)
174-
for (const auto &T : IF.targets(Lib.getValue()))
175-
Add(&IF, Lib.getKey(), T);
173+
for (const auto &[Attr, ArchSet] : Attrs.get())
174+
for (const auto &T : IF.targets(ArchSet))
175+
Add(&IF, Attr, T);
176176
};
177177

178178
assignLibAttrs(Opts.LinkerOpts.AllowableClients,

clang/tools/clang-installapi/Options.cpp

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -610,35 +610,35 @@ Options::processAndFilterOutInstallAPIOptions(ArrayRef<const char *> Args) {
610610

611611
for (const Arg *A : ParsedArgs.filtered(OPT_allowable_client)) {
612612
auto It = ArgToArchMap.find(A);
613-
LinkerOpts.AllowableClients[A->getValue()] =
613+
LinkerOpts.AllowableClients.getArchSet(A->getValue()) =
614614
It != ArgToArchMap.end() ? It->second : ArchitectureSet();
615615
A->claim();
616616
}
617617

618618
for (const Arg *A : ParsedArgs.filtered(OPT_reexport_l)) {
619619
auto It = ArgToArchMap.find(A);
620-
LinkerOpts.ReexportedLibraries[A->getValue()] =
620+
LinkerOpts.ReexportedLibraries.getArchSet(A->getValue()) =
621621
It != ArgToArchMap.end() ? It->second : ArchitectureSet();
622622
A->claim();
623623
}
624624

625625
for (const Arg *A : ParsedArgs.filtered(OPT_reexport_library)) {
626626
auto It = ArgToArchMap.find(A);
627-
LinkerOpts.ReexportedLibraryPaths[A->getValue()] =
627+
LinkerOpts.ReexportedLibraryPaths.getArchSet(A->getValue()) =
628628
It != ArgToArchMap.end() ? It->second : ArchitectureSet();
629629
A->claim();
630630
}
631631

632632
for (const Arg *A : ParsedArgs.filtered(OPT_reexport_framework)) {
633633
auto It = ArgToArchMap.find(A);
634-
LinkerOpts.ReexportedFrameworks[A->getValue()] =
634+
LinkerOpts.ReexportedFrameworks.getArchSet(A->getValue()) =
635635
It != ArgToArchMap.end() ? It->second : ArchitectureSet();
636636
A->claim();
637637
}
638638

639639
for (const Arg *A : ParsedArgs.filtered(OPT_rpath)) {
640640
auto It = ArgToArchMap.find(A);
641-
LinkerOpts.RPaths[A->getValue()] =
641+
LinkerOpts.RPaths.getArchSet(A->getValue()) =
642642
It != ArgToArchMap.end() ? It->second : ArchitectureSet();
643643
A->claim();
644644
}
@@ -733,9 +733,9 @@ Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
733733
llvm::for_each(DriverOpts.Targets,
734734
[&AllArchs](const auto &T) { AllArchs.set(T.first.Arch); });
735735
auto assignDefaultLibAttrs = [&AllArchs](LibAttrs &Attrs) {
736-
for (StringMapEntry<ArchitectureSet> &Entry : Attrs)
737-
if (Entry.getValue().empty())
738-
Entry.setValue(AllArchs);
736+
for (auto &[_, Archs] : Attrs.get())
737+
if (Archs.empty())
738+
Archs = AllArchs;
739739
};
740740
assignDefaultLibAttrs(LinkerOpts.AllowableClients);
741741
assignDefaultLibAttrs(LinkerOpts.ReexportedFrameworks);
@@ -789,7 +789,7 @@ std::pair<LibAttrs, ReexportedInterfaces> Options::getReexportedLibraries() {
789789
std::unique_ptr<InterfaceFile> Reexport = std::move(*ReexportIFOrErr);
790790
StringRef InstallName = Reexport->getInstallName();
791791
assert(!InstallName.empty() && "Parse error for install name");
792-
Reexports.insert({InstallName, Archs});
792+
Reexports.getArchSet(InstallName) = Archs;
793793
ReexportIFs.emplace_back(std::move(*Reexport));
794794
return true;
795795
};
@@ -802,39 +802,36 @@ std::pair<LibAttrs, ReexportedInterfaces> Options::getReexportedLibraries() {
802802
for (const PlatformType P : Platforms) {
803803
PathSeq PlatformSearchPaths = getPathsForPlatform(FEOpts.SystemFwkPaths, P);
804804
llvm::append_range(FwkSearchPaths, PlatformSearchPaths);
805-
for (const StringMapEntry<ArchitectureSet> &Lib :
806-
LinkerOpts.ReexportedFrameworks) {
807-
std::string Name = (Lib.getKey() + ".framework/" + Lib.getKey()).str();
805+
for (const auto &[Lib, Archs] : LinkerOpts.ReexportedFrameworks.get()) {
806+
std::string Name = (Lib + ".framework/" + Lib);
808807
std::string Path = findLibrary(Name, *FM, FwkSearchPaths, {}, {});
809808
if (Path.empty()) {
810-
Diags->Report(diag::err_cannot_find_reexport) << false << Lib.getKey();
809+
Diags->Report(diag::err_cannot_find_reexport) << false << Lib;
811810
return {};
812811
}
813812
if (DriverOpts.TraceLibraryLocation)
814813
errs() << Path << "\n";
815814

816-
AccumulateReexports(Path, Lib.getValue());
815+
AccumulateReexports(Path, Archs);
817816
}
818817
FwkSearchPaths.resize(FwkSearchPaths.size() - PlatformSearchPaths.size());
819818
}
820819

821-
for (const StringMapEntry<ArchitectureSet> &Lib :
822-
LinkerOpts.ReexportedLibraries) {
823-
std::string Name = "lib" + Lib.getKey().str() + ".dylib";
820+
for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraries.get()) {
821+
std::string Name = "lib" + Lib + ".dylib";
824822
std::string Path = findLibrary(Name, *FM, {}, LinkerOpts.LibPaths, {});
825823
if (Path.empty()) {
826-
Diags->Report(diag::err_cannot_find_reexport) << true << Lib.getKey();
824+
Diags->Report(diag::err_cannot_find_reexport) << true << Lib;
827825
return {};
828826
}
829827
if (DriverOpts.TraceLibraryLocation)
830828
errs() << Path << "\n";
831829

832-
AccumulateReexports(Path, Lib.getValue());
830+
AccumulateReexports(Path, Archs);
833831
}
834832

835-
for (const StringMapEntry<ArchitectureSet> &Lib :
836-
LinkerOpts.ReexportedLibraryPaths)
837-
AccumulateReexports(Lib.getKey(), Lib.getValue());
833+
for (const auto &[Lib, Archs] : LinkerOpts.ReexportedLibraryPaths.get())
834+
AccumulateReexports(Lib, Archs);
838835

839836
return {std::move(Reexports), std::move(ReexportIFs)};
840837
}

0 commit comments

Comments
 (0)