https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/215349
>From b921782104b80ccb9b5c33a4ee70aa0d11e11b87 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Tue, 11 Aug 2026 11:13:42 -0700 Subject: [PATCH] [clang][ssaf] Link static libraries and multi-arch static libraries This change introduces support for linking static libraries and multi-arch static libraries. To implement this, we have added two `link` overloads to the `EntityLinker`: a static library folds in as a bundle of TU summaries, and a multi-arch static library contributes only the static library with the matching target triple. The target triple is supplied through a new optional flag, `--target-triple`. If unspecified, it is inferred from the first input, if possible. --- .../Core/EntityLinker/EntityLinker.h | 56 +++- .../EntityLinker/MultiArchStaticLibrary.h | 3 + .../Core/EntityLinker/StaticLibrary.h | 12 +- .../Core/Support/FormatProviders.h | 8 + .../clang/ScalableStaticAnalysis/Tool/Utils.h | 14 + .../Core/EntityLinker/EntityLinker.cpp | 78 +++++ .../lib/ScalableStaticAnalysis/Tool/Utils.cpp | 21 ++ .../ssaf-linker/Inputs/libord-reversed.json | 62 ++++ .../ssaf-linker/Inputs/libtwo-2arch.json | 39 +++ .../Scalable/ssaf-linker/Inputs/tu-linux.json | 11 + .../ssaf-linker/Inputs/tu-x86_64.json | 11 + .../Analysis/Scalable/ssaf-linker/help.test | 23 +- .../Analysis/Scalable/ssaf-linker/io.test | 12 +- .../Scalable/ssaf-linker/linking-errors.test | 125 +++++++- .../Scalable/ssaf-linker/linking.test | 137 ++++++++ .../Analysis/Scalable/ssaf-linker/time.test | 6 +- .../Scalable/ssaf-linker/verbose.test | 53 +++- clang/tools/clang-ssaf-linker/CMakeLists.txt | 1 + clang/tools/clang-ssaf-linker/LinkCLI.cpp | 295 ++++++++++++++++++ clang/tools/clang-ssaf-linker/LinkCLI.h | 111 +++++++ clang/tools/clang-ssaf-linker/SSAFLinker.cpp | 134 +------- 21 files changed, 1058 insertions(+), 154 deletions(-) create mode 100644 clang/test/Analysis/Scalable/ssaf-linker/Inputs/libord-reversed.json create mode 100644 clang/test/Analysis/Scalable/ssaf-linker/Inputs/libtwo-2arch.json create mode 100644 clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-linux.json create mode 100644 clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-x86_64.json create mode 100644 clang/tools/clang-ssaf-linker/LinkCLI.cpp create mode 100644 clang/tools/clang-ssaf-linker/LinkCLI.h diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h index f07def1a9b344..2df3ad1c9cf3c 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h @@ -8,6 +8,8 @@ // // This file defines the EntityLinker class that combines multiple TU summaries // into a unified LU summary by deduplicating entities and patching summaries. +// TU summaries may be supplied individually, bundled in a static library, or +// bundled in one architecture member of a multi-architecture static library. // //===----------------------------------------------------------------------===// @@ -17,6 +19,7 @@ #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h" #include "llvm/Support/Error.h" #include "llvm/TargetParser/Triple.h" +#include <cstddef> #include <map> #include <memory> #include <set> @@ -24,17 +27,22 @@ namespace clang::ssaf { +class MultiArchStaticLibrary; +class StaticLibrary; class TUSummaryEncoding; class EntityLinker { LUSummaryEncoding Output; + + // Namespaces of the TU summaries folded in, supplied directly or as members + // of a library. std::set<BuildNamespace> ProcessedTUNamespaces; public: /// Constructs an EntityLinker to link TU summaries into a LU summary. /// - /// \param TargetTriple The target triple of the link unit. Every linked TU - /// must report the same triple. + /// \param TargetTriple The target triple of the link unit. Every linked + /// input must report the same triple. /// \param LUNamespace The namespace identifying this link unit. EntityLinker(llvm::Triple TargetTriple, NestedBuildNamespace LUNamespace) : Output(std::move(TargetTriple), std::move(LUNamespace)) {} @@ -45,11 +53,40 @@ class EntityLinker { /// and merges them into a single data store. /// /// \param Summary The TU summary to link. Ownership is transferred. - /// \returns Error if the TU namespace has already been linked or if patching - /// fails, success otherwise. Corrupted summary data (missing linkage - /// information, duplicate entity IDs, etc.) triggers a fatal error. + /// \returns Error if \p Summary reports a different target triple than this + /// link unit, if its TU namespace has already been linked, or if + /// patching fails; success otherwise. Corrupted summary data + /// (missing linkage information, duplicate entity IDs, etc.) + /// triggers a fatal error. llvm::Error link(std::unique_ptr<TUSummaryEncoding> Summary); + /// Links every member of a static library into the LU summary. + /// + /// Members are folded in unconditionally, in an unspecified order, exactly as + /// if each had been passed as an individual TU summary. + /// + /// \param Library The static library to link. Ownership is transferred. + /// \returns Error if \p Library reports a different target triple than this + /// link unit or if any member fails to link, success otherwise. + llvm::Error link(std::unique_ptr<StaticLibrary> Library); + + /// Links the architecture member matching this link unit into the LU summary. + /// + /// Members for other architectures are discarded. + /// + /// \param Library The multi-arch static library to link. Ownership is + /// transferred. + /// \returns Error if \p Library has no member whose target triple equals this + /// link unit's, or if the selected member fails to link; success + /// otherwise. + llvm::Error link(std::unique_ptr<MultiArchStaticLibrary> Library); + + /// Returns the number of TU summaries folded in so far. + /// + /// Counts members expanded from libraries as well as TU summaries linked + /// directly, so it is not the number of link() calls. + size_t getLinkedTUCount() const { return ProcessedTUNamespaces.size(); } + /// Returns the accumulated LU summary. /// /// \returns LU summary containing all the deduplicated and patched entity @@ -57,6 +94,15 @@ class EntityLinker { LUSummaryEncoding takeOutput() && { return std::move(Output); } private: + /// Checks that an input belongs to this link unit's target. + /// + /// \param TargetTriple The triple of the input being linked. + /// \param InputNamespace The namespace naming that input in the diagnostic. + /// \returns Error if \p TargetTriple differs from this link unit's, success + /// otherwise. + llvm::Error checkTargetTriple(const llvm::Triple &TargetTriple, + const BuildNamespace &InputNamespace) const; + /// Resolves a TU entity name to an LU entity name and ID. /// /// \param OldName The entity name in the TU namespace. diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h index f5ddafbb9d577..b900d5458e9b5 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h @@ -23,6 +23,7 @@ namespace clang::ssaf { +class LinkCLI; class MultiArchCreateCLI; /// Represents a multi-architecture static library. @@ -32,6 +33,8 @@ class MultiArchCreateCLI; /// architectures; the wrapper's \c Namespace identifies that shared library and /// every member's namespace must agree on its name. class MultiArchStaticLibrary { + friend class EntityLinker; + friend class LinkCLI; friend class MultiArchCreateCLI; friend class SerializationFormat; friend class TestFixture; diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h index c74fc4b16e0b2..db1477a42e9b5 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h @@ -23,6 +23,7 @@ namespace clang::ssaf { +class LinkCLI; class MultiArchCreateCLI; class StaticLibraryCreateCLI; @@ -30,8 +31,8 @@ class StaticLibraryCreateCLI; /// /// A StaticLibrary bundles member translation units without performing /// entity resolution, mirroring the role of ar / libtool -static / lib.exe -/// in native build pipelines. It is consumed by the EntityLinker for -/// selective inclusion when passed as a command line argument. +/// in native build pipelines. It is consumed by the EntityLinker when passed +/// as a command line argument. /// /// Static libraries are single-architecture: every member's target triple /// must equal the library's. Multi-architecture static libraries are @@ -40,8 +41,13 @@ class StaticLibraryCreateCLI; /// /// Members are stored as encoded TUSummaryEncoding objects: the /// static-library tool never decodes per-entity payloads, and the linker -/// consumes them as-is during its selective inclusion pass. +/// consumes them as-is while folding them into its link unit. +/// +/// TODO: The linker currently folds in every member. Restrict inclusion to +/// the members a link unit actually references, as native linkers do. class StaticLibrary { + friend class EntityLinker; + friend class LinkCLI; friend class MultiArchCreateCLI; friend class MultiArchStaticLibrary; friend class SerializationFormat; diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h b/clang/include/clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h index e4a7b54033924..e671a7c2d088a 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h @@ -22,6 +22,7 @@ #include "clang/ScalableStaticAnalysis/Core/WholeProgramAnalysis/AnalysisName.h" #include "llvm/Support/FormatProviders.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/Triple.h" namespace llvm { @@ -88,6 +89,13 @@ template <> struct format_provider<clang::ssaf::AnalysisName> { } }; +template <> struct format_provider<llvm::Triple> { + static void format(const llvm::Triple &Val, raw_ostream &OS, + StringRef Style) { + OS << llvm::Triple::normalize(Val.str()); + } +}; + } // namespace llvm #endif // LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_SUPPORT_FORMATPROVIDERS_H diff --git a/clang/include/clang/ScalableStaticAnalysis/Tool/Utils.h b/clang/include/clang/ScalableStaticAnalysis/Tool/Utils.h index 8ceb539a055de..9c82a2de0608c 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Tool/Utils.h +++ b/clang/include/clang/ScalableStaticAnalysis/Tool/Utils.h @@ -23,6 +23,7 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/WithColor.h" +#include "llvm/TargetParser/Triple.h" #include <string> namespace clang::ssaf { @@ -76,6 +77,19 @@ void loadPlugins(llvm::ArrayRef<std::string> Paths); void initTool(int argc, const char **argv, llvm::StringRef Version, llvm::cl::OptionCategory &Category, llvm::StringRef ToolHeading); +//===----------------------------------------------------------------------===// +// Target Triples +//===----------------------------------------------------------------------===// + +/// Parses and validates a target triple supplied on the command line. +/// +/// \param FlagName The option supplying \p Value, named in the diagnostic. +/// \param Value The triple as spelled by the user. Must not be empty. +/// \returns The parsed triple. Calls fail() and exits if the architecture is +/// unrecognized. +llvm::Triple parseTargetTripleOrFail(llvm::StringRef FlagName, + llvm::StringRef Value); + //===----------------------------------------------------------------------===// // Data Structures //===----------------------------------------------------------------------===// diff --git a/clang/lib/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.cpp b/clang/lib/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.cpp index 462978932a53d..b703efd8324fb 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.cpp +++ b/clang/lib/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.cpp @@ -8,11 +8,15 @@ #include "clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/EntitySummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Model/EntityLinkage.h" #include "clang/ScalableStaticAnalysis/Core/Model/EntityName.h" #include "clang/ScalableStaticAnalysis/Core/Support/ErrorBuilder.h" #include "clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" #include <cassert> using namespace clang::ssaf; @@ -44,6 +48,16 @@ static constexpr const char *FailedToInsertEntityIntoOutputSummary = static constexpr const char *DuplicateTUNamespace = "failed to link TU summary: duplicate {0}"; +static constexpr const char *LinkingStaticLibraryMember = + "failed to link member {0} of static library {1}"; + +static constexpr const char *MismatchedTargetTriple = + "target triple '{0}' of {1} does not match link unit target triple '{2}'"; + +static constexpr const char *NoMemberForTargetTriple = + "multi-arch static library {0} has no member for target triple '{1}' " + "(available: {2})"; + } // namespace ErrorMessages static NestedBuildNamespace @@ -180,7 +194,25 @@ EntityLinker::patch(const std::vector<EntitySummaryEncoding *> &PatchTargets, return llvm::Error::success(); } +llvm::Error +EntityLinker::checkTargetTriple(const llvm::Triple &TargetTriple, + const BuildNamespace &InputNamespace) const { + if (TargetTriple != Output.TargetTriple) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::MismatchedTargetTriple, + TargetTriple, InputNamespace, + Output.TargetTriple) + .build(); + } + return llvm::Error::success(); +} + llvm::Error EntityLinker::link(std::unique_ptr<TUSummaryEncoding> Summary) { + if (auto Err = + checkTargetTriple(Summary->TargetTriple, Summary->TUNamespace)) { + return Err; + } + auto [_, Inserted] = ProcessedTUNamespaces.insert(Summary->TUNamespace); if (!Inserted) { return ErrorBuilder::create(std::errc::invalid_argument, @@ -195,3 +227,49 @@ llvm::Error EntityLinker::link(std::unique_ptr<TUSummaryEncoding> Summary) { auto PatchTargets = merge(SummaryRef, EntityResolutionTable); return patch(PatchTargets, EntityResolutionTable); } + +llvm::Error EntityLinker::link(std::unique_ptr<StaticLibrary> Library) { + if (auto Err = checkTargetTriple(Library->TargetTriple, Library->Namespace)) { + return Err; + } + + while (!Library->Members.empty()) { + auto Node = Library->Members.extract(Library->Members.begin()); + const BuildNamespace MemberNamespace = Node.value()->TUNamespace; + + if (auto Err = link(std::move(Node.value()))) { + return ErrorBuilder::wrap(std::move(Err)) + .context(ErrorMessages::LinkingStaticLibraryMember, MemberNamespace, + Library->Namespace) + .build(); + } + } + + return llvm::Error::success(); +} + +llvm::Error +EntityLinker::link(std::unique_ptr<MultiArchStaticLibrary> Library) { + auto MatchingMember = llvm::find_if( + Library->Members, [this](const std::unique_ptr<StaticLibrary> &Member) { + return Member->TargetTriple == Output.TargetTriple; + }); + + if (MatchingMember == Library->Members.end()) { + auto TargetTriples = llvm::map_range( + Library->Members, [](const std::unique_ptr<StaticLibrary> &Member) { + return llvm::Triple::normalize(Member->TargetTriple.str()); + }); + std::string Available = Library->Members.empty() + ? std::string("none") + : llvm::join(TargetTriples, ", "); + + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::NoMemberForTargetTriple, + Library->Namespace, Output.TargetTriple, + Available) + .build(); + } + + return link(std::move(Library->Members.extract(MatchingMember).value())); +} diff --git a/clang/lib/ScalableStaticAnalysis/Tool/Utils.cpp b/clang/lib/ScalableStaticAnalysis/Tool/Utils.cpp index 6a740f57741d7..03139f378186f 100644 --- a/clang/lib/ScalableStaticAnalysis/Tool/Utils.cpp +++ b/clang/lib/ScalableStaticAnalysis/Tool/Utils.cpp @@ -56,6 +56,9 @@ constexpr const char *FileAlreadyExists = "File already exists"; constexpr const char *FailedToLoadPlugin = "failed to load plugin '{0}': {1}"; +constexpr const char *InvalidTargetTriple = + "invalid {0} '{1}': unrecognized architecture"; + } // namespace ErrorMessages llvm::StringRef ToolName; @@ -148,6 +151,24 @@ void clang::ssaf::loadPlugins(llvm::ArrayRef<std::string> Paths) { } } +llvm::Triple clang::ssaf::parseTargetTripleOrFail(llvm::StringRef FlagName, + llvm::StringRef Value) { + assert(!Value.empty() && + "parseTargetTripleOrFail: triple value cannot be empty"); + + // Normalize so the components are moved to their proper places. + llvm::Triple T(llvm::Triple::normalize(Value)); + + // Only the architecture is validated. Validating vendor or OS rejects real + // targets like x86_64-unknown-linux-gnu. A misspelled vendor or OS is instead + // caught as a triple mismatch during linking or library creation. + if (T.getArch() == llvm::Triple::UnknownArch) { + fail(ErrorMessages::InvalidTargetTriple, FlagName, Value); + } + + return T; +} + void clang::ssaf::initTool(int argc, const char **argv, llvm::StringRef Version, llvm::cl::OptionCategory &Category, llvm::StringRef ToolHeading) { diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Inputs/libord-reversed.json b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/libord-reversed.json new file mode 100644 index 0000000000000..0356200cb9aa5 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/libord-reversed.json @@ -0,0 +1,62 @@ +{ + "members": [ + { + "data": [], + "id_table": [ + { + "id": 0, + "name": { + "suffix": "", + "usr": "c:@F@only_in_b#" + } + } + ], + "linkage_table": [ + { + "id": 0, + "linkage": { + "type": "Internal" + } + } + ], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "tu-b.cpp" + }, + "type": "TUSummary" + }, + { + "data": [], + "id_table": [ + { + "id": 0, + "name": { + "suffix": "", + "usr": "c:@F@only_in_a#" + } + } + ], + "linkage_table": [ + { + "id": 0, + "linkage": { + "type": "Internal" + } + } + ], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "tu-a.cpp" + }, + "type": "TUSummary" + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libord" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Inputs/libtwo-2arch.json b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/libtwo-2arch.json new file mode 100644 index 0000000000000..c52597151dae3 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/libtwo-2arch.json @@ -0,0 +1,39 @@ +{ + "members": [ + { + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libtwo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" + }, + { + "members": [ + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "x86_64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "x86.cpp" + }, + "type": "TUSummary" + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libtwo" + }, + "target_triple": "x86_64-apple-macosx", + "type": "StaticLibrary" + } + ], + "namespace": { + "kind": "MultiArchStaticLibrary", + "name": "libtwo" + }, + "type": "MultiArchStaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-linux.json b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-linux.json new file mode 100644 index 0000000000000..9bf5495c9e2f1 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-linux.json @@ -0,0 +1,11 @@ +{ + "tu_namespace": { + "kind": "CompilationUnit", + "name": "linux.cpp" + }, + "id_table": [], + "linkage_table": [], + "data": [], + "target_triple": "x86_64-unknown-linux-gnu", + "type": "TUSummary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-x86_64.json b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-x86_64.json new file mode 100644 index 0000000000000..a482c204f77c7 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-linker/Inputs/tu-x86_64.json @@ -0,0 +1,11 @@ +{ + "tu_namespace": { + "kind": "CompilationUnit", + "name": "x86.cpp" + }, + "id_table": [], + "linkage_table": [], + "data": [], + "target_triple": "x86_64-apple-macosx", + "type": "TUSummary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-linker/help.test b/clang/test/Analysis/Scalable/ssaf-linker/help.test index 7b0c21f066016..45d8e11a15e30 100644 --- a/clang/test/Analysis/Scalable/ssaf-linker/help.test +++ b/clang/test/Analysis/Scalable/ssaf-linker/help.test @@ -15,14 +15,15 @@ // CHECK-NEXT: Type "clang-ssaf-linker{{(\.exe)?}} <subcommand> --help" to get more help on a specific subcommand // CHECK-EMPTY: // CHECK-NEXT: OPTIONS: -// CHECK-NEXT: -h - Alias for --help -// CHECK-NEXT: --help - Display available options (--help-hidden for more) -// CHECK-NEXT: --help-hidden - Display all available options -// CHECK-NEXT: --help-list - Display list of available options (--help-list-hidden for more) -// CHECK-NEXT: --help-list-hidden - Display list of all available options -// CHECK-NEXT: -o <path> - Output file path -// CHECK-NEXT: --print-all-options - Print all option values after command line parsing -// CHECK-NEXT: --print-options - Print non-default options after command line parsing -// CHECK-NEXT: --time - Enable timing -// CHECK-NEXT: --verbose - Enable verbose output -// CHECK-NEXT: --version - Display the version of this program +// CHECK-NEXT: -h - Alias for --help +// CHECK-NEXT: --help - Display available options (--help-hidden for more) +// CHECK-NEXT: --help-hidden - Display all available options +// CHECK-NEXT: --help-list - Display list of available options (--help-list-hidden for more) +// CHECK-NEXT: --help-list-hidden - Display list of all available options +// CHECK-NEXT: -o <path> - Output file path +// CHECK-NEXT: --print-all-options - Print all option values after command line parsing +// CHECK-NEXT: --print-options - Print non-default options after command line parsing +// CHECK-NEXT: --target-triple=<triple> - Target triple of the link unit (defaults to the first input's; required when the first input is a multi-arch static library with several members) +// CHECK-NEXT: --time - Enable timing +// CHECK-NEXT: --verbose - Enable verbose output +// CHECK-NEXT: --version - Display the version of this program diff --git a/clang/test/Analysis/Scalable/ssaf-linker/io.test b/clang/test/Analysis/Scalable/ssaf-linker/io.test index 304f97db656f6..b2b17c9f094ea 100644 --- a/clang/test/Analysis/Scalable/ssaf-linker/io.test +++ b/clang/test/Analysis/Scalable/ssaf-linker/io.test @@ -6,15 +6,23 @@ // Malformed JSON input. // RUN: not clang-ssaf-linker %S/Inputs/tu-malformed.json -o %t/out.json 2>&1 \ // RUN: | FileCheck %s --match-full-lines --check-prefix=BAD-JSON -// BAD-JSON: clang-ssaf-linker: error: reading TUSummary from file '{{.*}}tu-malformed.json' +// BAD-JSON: clang-ssaf-linker: error: Reading artifact '{{.*}}tu-malformed.json' +// BAD-JSON-NEXT: reading ArtifactEncoding from file '{{.*}}tu-malformed.json' // BAD-JSON-NEXT: {{.*}}: Invalid JSON value{{.*}} // Missing required fields in otherwise valid JSON. // RUN: not clang-ssaf-linker %S/Inputs/tu-missing-fields.json -o %t/out.json 2>&1 \ // RUN: | FileCheck %s --match-full-lines --check-prefix=MISSING-FIELDS -// MISSING-FIELDS: clang-ssaf-linker: error: reading TUSummary from file '{{.*}}tu-missing-fields.json' +// MISSING-FIELDS: clang-ssaf-linker: error: Reading artifact '{{.*}}tu-missing-fields.json' +// MISSING-FIELDS-NEXT: reading ArtifactEncoding from file '{{.*}}tu-missing-fields.json' // MISSING-FIELDS-NEXT: failed to read IdTable from field 'id_table': expected JSON array +// An unrecognized artifact kind names every kind the linker can read. +// RUN: not clang-ssaf-linker %S/Inputs/bad-artifact.json -o %t/out.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=BAD-ARTIFACT +// BAD-ARTIFACT: clang-ssaf-linker: error: Reading artifact '{{.*}}bad-artifact.json' +// BAD-ARTIFACT: unknown value 'BogusKind' for field 'type' + // Output file already exists. // RUN: touch %t/out.json // RUN: not clang-ssaf-linker %S/Inputs/tu-empty.json -o %t/out.json 2>&1 \ diff --git a/clang/test/Analysis/Scalable/ssaf-linker/linking-errors.test b/clang/test/Analysis/Scalable/ssaf-linker/linking-errors.test index 5bda8128ac0e1..c3f5894ec86a6 100644 --- a/clang/test/Analysis/Scalable/ssaf-linker/linking-errors.test +++ b/clang/test/Analysis/Scalable/ssaf-linker/linking-errors.test @@ -5,24 +5,137 @@ // Linking the same TU namespace twice produces an error. // RUN: not clang-ssaf-linker %S/Inputs/tu-empty.json %S/Inputs/tu-empty.json -o %t/lu.json 2>&1 \ -// RUN: | FileCheck %s --match-full-lines --check-prefix=DUP-NS -// DUP-NS: clang-ssaf-linker: error: Linking summary '{{.*}}tu-empty.json' -// DUP-NS-NEXT: failed to link TU summary: duplicate BuildNamespace(CompilationUnit, empty.cpp) +// RUN: | FileCheck %s --match-full-lines --check-prefix=DUP-NS-TU +// DUP-NS-TU: clang-ssaf-linker: error: Linking artifact '{{.*}}tu-empty.json' +// DUP-NS-TU-NEXT: failed to link TU summary: duplicate BuildNamespace(CompilationUnit, empty.cpp) + +// A static library member colliding with an already linked TU summary names the +// member and the library it came from, on top of the input file context. +// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-1.json %S/Inputs/tu-2.json --namespace libtwo -o %t/libtwo.json +// RUN: not clang-ssaf-linker %S/Inputs/tu-1.json %t/libtwo.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=DUP-MEMBER +// DUP-MEMBER: clang-ssaf-linker: error: Linking artifact '{{.*}}libtwo.json' +// DUP-MEMBER-NEXT: failed to link member BuildNamespace(CompilationUnit, tu1.cpp) of static library BuildNamespace(StaticLibrary, libtwo) +// DUP-MEMBER-NEXT: failed to link TU summary: duplicate BuildNamespace(CompilationUnit, tu1.cpp) // Entity ID object in summary data blob with '@' key alongside extra keys is a fatal error. // RUN: not clang-ssaf-linker %S/Inputs/tu-invalid-entity-id-multikey.json -o %t/lu.json 2>&1 \ // RUN: | FileCheck %s --match-full-lines --check-prefix=INVALID-ID-MULTIKEY -// INVALID-ID-MULTIKEY: clang-ssaf-linker: error: Linking summary '{{.*}}tu-invalid-entity-id-multikey.json' +// INVALID-ID-MULTIKEY: clang-ssaf-linker: error: Linking artifact '{{.*}}tu-invalid-entity-id-multikey.json' // INVALID-ID-MULTIKEY-NEXT: failed to read EntityId: expected JSON object with a single '@' key mapped to a number (unsigned 64-bit integer) // Entity ID object in summary data blob with a non-uint64 '@' value is a fatal error. // RUN: not clang-ssaf-linker %S/Inputs/tu-invalid-entity-id-value.json -o %t/lu.json 2>&1 \ // RUN: | FileCheck %s --match-full-lines --check-prefix=INVALID-ID-VALUE -// INVALID-ID-VALUE: clang-ssaf-linker: error: Linking summary '{{.*}}tu-invalid-entity-id-value.json' +// INVALID-ID-VALUE: clang-ssaf-linker: error: Linking artifact '{{.*}}tu-invalid-entity-id-value.json' // INVALID-ID-VALUE-NEXT: failed to read EntityId: expected JSON object with a single '@' key mapped to a number (unsigned 64-bit integer) // Entity ID reference in summary data blob pointing to an ID absent from the resolution table // RUN: not clang-ssaf-linker %S/Inputs/tu-invalid-entity-id-ref.json -o %t/lu.json 2>&1 \ // RUN: | FileCheck %s --match-full-lines --check-prefix=INVALID-ID-REF -// INVALID-ID-REF: clang-ssaf-linker: error: Linking summary '{{.*}}tu-invalid-entity-id-ref.json' +// INVALID-ID-REF: clang-ssaf-linker: error: Linking artifact '{{.*}}tu-invalid-entity-id-ref.json' // INVALID-ID-REF-NEXT: failed to patch EntityId: 'EntityId(99)' not found in entity resolution table + +// ============================================================================ +// Target triple mismatches +// +// The link unit's triple comes from the first input (or --target-triple), and +// every later input must agree with it. EntityLinker reports the disagreement +// and the command line adds the input's path as context. +// ============================================================================ + +// A TU summary disagreeing with a triple inferred from a preceding TU summary, +// with no --target-triple involved at all. +// RUN: not clang-ssaf-linker %S/Inputs/tu-1.json %S/Inputs/tu-x86_64.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=TRIPLE-INFERRED-TU +// TRIPLE-INFERRED-TU: target triple 'x86_64-apple-macosx' of BuildNamespace(CompilationUnit, x86.cpp) does not match link unit target triple 'arm64-apple-macosx' + +// A static library disagreeing with a preceding input. +// RUN: not clang-ssaf-linker %S/Inputs/tu-1.json %S/Inputs/lib-x86_64.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=TRIPLE-MISMATCH-LIB +// TRIPLE-MISMATCH-LIB: clang-ssaf-linker: error: Linking artifact '{{.*}}lib-x86_64.json' +// TRIPLE-MISMATCH-LIB-NEXT: target triple 'x86_64-apple-macosx' of BuildNamespace(StaticLibrary, libmulti) does not match link unit target triple 'arm64-apple-macosx' + +// A TU summary disagreeing with a triple inferred from a preceding library, +// rather than from a preceding TU summary or --target-triple. +// RUN: not clang-ssaf-linker %S/Inputs/lib-x86_64.json %S/Inputs/tu-1.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=TRIPLE-FROM-LIB +// TRIPLE-FROM-LIB: target triple 'arm64-apple-macosx' of BuildNamespace(CompilationUnit, tu1.cpp) does not match link unit target triple 'x86_64-apple-macosx' + +// A multi-arch static library carrying no member for the link unit's triple +// reports the members it does carry. +// RUN: not clang-ssaf-linker --target-triple arm64-apple-ios %S/Inputs/libtwo-2arch.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=MEMBER-MISSING +// MEMBER-MISSING: clang-ssaf-linker: error: Linking artifact '{{.*}}libtwo-2arch.json' +// MEMBER-MISSING-NEXT: multi-arch static library BuildNamespace(MultiArchStaticLibrary, libtwo) has no member for target triple 'arm64-apple-ios' (available: arm64-apple-macosx, x86_64-apple-macosx) + +// A bundle with no members at all still reports which triple was wanted. +// RUN: not clang-ssaf-linker --target-triple arm64-apple-macosx %S/Inputs/libmulti-empty-wrapper.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=EMPTY-BUNDLE +// EMPTY-BUNDLE: multi-arch static library BuildNamespace(MultiArchStaticLibrary, libmulti) has no member for target triple 'arm64-apple-macosx' (available: none) + +// ============================================================================ +// Target triple inference failures +// ============================================================================ + +// More than one member in the first input: the architecture has to be chosen. +// RUN: not clang-ssaf-linker %S/Inputs/libtwo-2arch.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=AMBIGUOUS-MEMBERS +// AMBIGUOUS-MEMBERS: cannot infer target triple from '{{.*}}libtwo-2arch.json': multi-arch static library has 2 members; pass --target-triple to select one + +// No members in the first input: there is nothing to infer from. +// RUN: not clang-ssaf-linker %S/Inputs/libmulti-empty-wrapper.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=INFER-NO-MEMBERS +// INFER-NO-MEMBERS: cannot infer target triple from '{{.*}}libmulti-empty-wrapper.json': multi-arch static library has no members; pass --target-triple + +// ============================================================================ +// Shared-library inputs are not linkable yet +// ============================================================================ + +// RUN: not clang-ssaf-linker %S/Inputs/lu-arm64.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=LU-FIRST +// LU-FIRST: '{{.*}}lu-arm64.json' is a link unit summary: linking against shared libraries is not yet supported + +// RUN: not clang-ssaf-linker %S/Inputs/libfoo-2arch.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=MULTI-ARCH-SHARED-FIRST +// MULTI-ARCH-SHARED-FIRST: '{{.*}}libfoo-2arch.json' is a multi-arch shared library: linking against shared libraries is not yet supported + +// Rejected wherever they appear, not just in first position: a later input is +// turned away by linkInput rather than by target triple resolution. +// RUN: not clang-ssaf-linker %S/Inputs/tu-1.json %S/Inputs/lu-arm64.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=LU-SECOND +// LU-SECOND: '{{.*}}lu-arm64.json' is a link unit summary: linking against shared libraries is not yet supported + +// RUN: not clang-ssaf-linker %S/Inputs/tu-1.json %S/Inputs/libfoo-2arch.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=MULTI-ARCH-SHARED-SECOND +// MULTI-ARCH-SHARED-SECOND: '{{.*}}libfoo-2arch.json' is a multi-arch shared library: linking against shared libraries is not yet supported + +// ============================================================================ +// Malformed --target-triple +// +// Only the architecture is validated; a vendor or OS spelled oddly is accepted +// and simply means "unspecified", so linking.test covers those as acceptances +// rather than rejections. +// ============================================================================ + +// RUN: not clang-ssaf-linker --target-triple bogus %S/Inputs/tu-1.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=BAD-TRIPLE-ARCH +// BAD-TRIPLE-ARCH: clang-ssaf-linker: error: invalid --target-triple 'bogus': unrecognized architecture + +// ============================================================================ +// Error ordering: inputs are folded in as they are read, so the first input +// that cannot be accepted is the one reported. These two runs share the same +// three inputs in different orders and must report different errors. +// ============================================================================ + +// The triple mismatch at input 2 is reported; input 3's duplicate namespace is +// never reached. +// RUN: not clang-ssaf-linker %S/Inputs/tu-1.json %S/Inputs/lib-x86_64.json %S/Inputs/tu-1.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=ORDER-TRIPLE-FIRST --implicit-check-not="duplicate BuildNamespace" +// ORDER-TRIPLE-FIRST: target triple 'x86_64-apple-macosx' of BuildNamespace(StaticLibrary, libmulti) does not match link unit target triple 'arm64-apple-macosx' + +// The duplicate namespace at input 2 is reported; input 3's triple mismatch is +// never reached. +// RUN: not clang-ssaf-linker %S/Inputs/tu-1.json %S/Inputs/tu-1.json %S/Inputs/lib-x86_64.json -o %t/lu.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=ORDER-DUP-FIRST --implicit-check-not="does not match link unit target triple" +// ORDER-DUP-FIRST: failed to link TU summary: duplicate BuildNamespace(CompilationUnit, tu1.cpp) diff --git a/clang/test/Analysis/Scalable/ssaf-linker/linking.test b/clang/test/Analysis/Scalable/ssaf-linker/linking.test index a6bd0c8df22c8..030dfb56841a3 100644 --- a/clang/test/Analysis/Scalable/ssaf-linker/linking.test +++ b/clang/test/Analysis/Scalable/ssaf-linker/linking.test @@ -40,3 +40,140 @@ // RUN: cd %t && clang-ssaf-linker %S/Inputs/tu-1.json -o lu-1.json // RUN: diff %S/Outputs/lu-1.json %t/lu-1.json // RUN: rm %t/lu-1.json + +// ============================================================================ +// Static libraries are folded in as bundles of TU summaries +// +// The library below bundles tu-1 and tu-2, so linking it is equivalent to +// passing those two TU summaries on the command line. Members are folded in TU +// namespace order, which here matches the command line order above. +// ============================================================================ + +// Both libraries are derived from tu-1 and tu-2 rather than stored as fixtures: +// static-library-create.test and multi-arch-create.test already pin what these +// two commands produce, so a checked-in copy would duplicate them. +// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-1.json %S/Inputs/tu-2.json --namespace libtwo -o %t/libtwo.json +// RUN: clang-ssaf-linker multi-arch create %t/libtwo.json -o %t/libtwo-1arch.json + +// RUN: clang-ssaf-linker %t/libtwo.json -o %t/lu-1+2.json +// RUN: diff %S/Outputs/lu-1+2.json %t/lu-1+2.json +// RUN: rm %t/lu-1+2.json + +// A static library with no members contributes nothing, exactly as an empty TU +// summary does. +// RUN: clang-ssaf-linker %S/Inputs/lib-arm64.json -o %t/lu-empty.json +// RUN: diff %S/Outputs/lu-empty.json %t/lu-empty.json +// RUN: rm %t/lu-empty.json + +// Members are folded in TU namespace order, not in the order they happen to +// appear in the library file. Inputs/libord-reversed.json lists tu-b.cpp before +// tu-a.cpp, yet tu-a.cpp's entity is assigned the first entity ID. +// RUN: clang-ssaf-linker %S/Inputs/libord-reversed.json -o %t/lu-ord.json +// RUN: FileCheck %s --check-prefix=FOLD-ORDER --input-file=%t/lu-ord.json +// FOLD-ORDER: "id_table": [ +// FOLD-ORDER: "id": 0, +// FOLD-ORDER: "name": "tu-a.cpp" +// FOLD-ORDER: "id": 1, +// FOLD-ORDER: "name": "tu-b.cpp" +// RUN: rm %t/lu-ord.json + +// Libraries and bare TU summaries mix freely in one invocation. +// RUN: clang-ssaf-linker %S/Inputs/tu-empty.json %t/libtwo.json -o %t/lu-1+2.json +// RUN: diff %S/Outputs/lu-1+2.json %t/lu-1+2.json +// RUN: rm %t/lu-1+2.json + +// ============================================================================ +// Multi-arch static libraries contribute the member matching the link unit +// ============================================================================ + +// A single-member bundle names its target unambiguously, so no --target-triple +// is needed and the result matches linking the member directly. +// RUN: clang-ssaf-linker %t/libtwo-1arch.json -o %t/lu-1+2.json +// RUN: diff %S/Outputs/lu-1+2.json %t/lu-1+2.json +// RUN: rm %t/lu-1+2.json + +// A bundle in a later position is resolved against the triple the first input +// already fixed, rather than contributing one of its own. +// RUN: clang-ssaf-linker %S/Inputs/tu-empty.json %t/libtwo-1arch.json -o %t/lu-1+2.json +// RUN: diff %S/Outputs/lu-1+2.json %t/lu-1+2.json +// RUN: rm %t/lu-1+2.json + +// Only the selected member is folded in. Inputs/libtwo-2arch.json pairs an +// empty arm64 member with an x86_64 member that has a member, so selecting arm64 +// must produce an empty link unit -- folding the wrong member, or both, would +// pull x86.cpp in. +// RUN: clang-ssaf-linker --target-triple arm64-apple-macosx %S/Inputs/libtwo-2arch.json -o %t/lu-empty.json +// RUN: diff %S/Outputs/lu-empty.json %t/lu-empty.json +// RUN: rm %t/lu-empty.json + +// Members are matched on the triple's canonical components, not its spelling: +// "aarch64" selects the "arm64" member. The link unit records the spelling that +// was requested rather than the member's own. +// RUN: clang-ssaf-linker --target-triple aarch64-apple-macosx %S/Inputs/libtwo-2arch.json -o %t/lu-aarch64.json +// RUN: FileCheck %s --check-prefix=ALIAS-MEMBER --input-file=%t/lu-aarch64.json +// ALIAS-MEMBER: "target_triple": "aarch64-apple-macosx", +// RUN: rm %t/lu-aarch64.json + +// An OS version does not distinguish a member either, and the link unit again +// records the requested spelling. +// RUN: clang-ssaf-linker --target-triple arm64-apple-macosx15.0 %S/Inputs/libtwo-2arch.json -o %t/lu-versioned.json +// RUN: FileCheck %s --check-prefix=VERSIONED-MEMBER --input-file=%t/lu-versioned.json +// VERSIONED-MEMBER: "target_triple": "arm64-apple-macosx15.0", +// RUN: rm %t/lu-versioned.json + +// An explicit --target-triple agreeing with the inputs is accepted and does not +// change the result. +// RUN: clang-ssaf-linker --target-triple arm64-apple-macosx %S/Inputs/tu-1.json %S/Inputs/tu-2.json -o %t/lu-1+2.json +// RUN: diff %S/Outputs/lu-1+2.json %t/lu-1+2.json +// RUN: rm %t/lu-1+2.json + +// ============================================================================ +// Target triples are not restricted to Apple platforms +// +// "unknown" and "none" are the conventional spellings for an unspecified +// vendor or OS, so a triple that uses them is well formed and takes part in +// triple resolution like any other. +// ============================================================================ + +// Inference needs no flag off-Apple either: the link unit adopts the input's +// own triple and records it. +// RUN: clang-ssaf-linker %S/Inputs/tu-linux.json -o %t/lu-linux.json +// RUN: FileCheck %s --check-prefix=LINUX-INFERRED --input-file=%t/lu-linux.json +// LINUX-INFERRED: "target_triple": "x86_64-unknown-linux-gnu", +// RUN: rm %t/lu-linux.json + +// RUN: not clang-ssaf-linker --target-triple wasm32-unknown-unknown %S/Inputs/libtwo-2arch.json -o %t/lu-wasm.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WASM-TRIPLE +// WASM-TRIPLE: has no member for target triple 'wasm32-unknown-unknown' (available: arm64-apple-macosx, x86_64-apple-macosx) + +// A component that belongs in a later field is not mistaken for an +// unrecognized OS: "elf" here names an object format, and the triple is +// reported in the normalized spelling the link unit would record. +// RUN: not clang-ssaf-linker --target-triple riscv64-unknown-elf %S/Inputs/lib-arm64.json -o %t/lu-riscv.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=RISCV-TRIPLE +// RISCV-TRIPLE: does not match link unit target triple 'riscv64-unknown-unknown-elf' + +// A component dropped off the end of the triple is absent rather than +// malformed: arm64-apple names an architecture and a vendor but no OS. +// RUN: not clang-ssaf-linker --target-triple arm64-apple %S/Inputs/lib-arm64.json -o %t/lu-no-os.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NO-OS-TRIPLE +// NO-OS-TRIPLE: does not match link unit target triple 'arm64-apple' + +// Only the architecture is validated, so a vendor or OS spelled as something +// llvm::Triple does not recognize is accepted too: it parses to Unknown, which +// means "unspecified" rather than "invalid". These two guard against +// re-introducing the component checks that once rejected every non-Apple +// target -- the acceptances below use legitimate spellings a re-added check +// would exempt, so they would not catch it. +// RUN: not clang-ssaf-linker --target-triple arm64-bogus-macosx %S/Inputs/lib-arm64.json -o %t/lu-bogus-vendor.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=ODD-VENDOR +// ODD-VENDOR: does not match link unit target triple 'arm64-bogus-macosx' + +// RUN: not clang-ssaf-linker --target-triple arm64-apple-bogus %S/Inputs/lib-arm64.json -o %t/lu-bogus-os.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=ODD-OS +// ODD-OS: does not match link unit target triple 'arm64-apple-bogus' + +// "none" is likewise a conventional spelling for an unspecified component. +// RUN: not clang-ssaf-linker --target-triple armv7-none-eabi %S/Inputs/lib-arm64.json -o %t/lu-eabi.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=EABI-TRIPLE +// EABI-TRIPLE: does not match link unit target triple 'armv7-unknown-none-eabi' diff --git a/clang/test/Analysis/Scalable/ssaf-linker/time.test b/clang/test/Analysis/Scalable/ssaf-linker/time.test index 3e5809692f049..665198da4c4b6 100644 --- a/clang/test/Analysis/Scalable/ssaf-linker/time.test +++ b/clang/test/Analysis/Scalable/ssaf-linker/time.test @@ -10,8 +10,8 @@ // CHECK-NEXT: ===-------------------------------------------------------------------------=== // CHECK-NEXT: Total Execution Time: {{[0-9.]+}} seconds ({{[0-9.]+}} wall clock) // CHECK: {{.*}}---Wall Time---{{.*}} -// CHECK-DAG: {{.*}}Write Summary -// CHECK-DAG: {{.*}}Read Summaries -// CHECK-DAG: {{.*}}Link Summaries +// CHECK-DAG: {{.*}}Write Link Unit Summary +// CHECK-DAG: {{.*}}Read Artifacts +// CHECK-DAG: {{.*}}Link Artifacts // CHECK-DAG: {{.*}}Validate Input // CHECK: {{.*}}Total diff --git a/clang/test/Analysis/Scalable/ssaf-linker/verbose.test b/clang/test/Analysis/Scalable/ssaf-linker/verbose.test index d731748ad7547..57578792746da 100644 --- a/clang/test/Analysis/Scalable/ssaf-linker/verbose.test +++ b/clang/test/Analysis/Scalable/ssaf-linker/verbose.test @@ -7,14 +7,55 @@ // RUN: | FileCheck %s --match-full-lines // CHECK: note: - Linking started. // CHECK-NEXT: note: - Validating input. -// CHECK-NEXT: note: - Validated output summary path '{{.*}}lu-1+2.json'. -// CHECK-NEXT: note: - Validated 2 input summary paths. -// CHECK-NEXT: note: - Linking input. -// CHECK-NEXT: note: - Constructing linker. -// CHECK-NEXT: note: - Linking summaries. +// CHECK-NEXT: note: - Validated output path '{{.*}}lu-1+2.json'. +// CHECK-NEXT: note: - Validated 2 input artifact path(s). +// CHECK-NEXT: note: - Creating link unit. +// CHECK-NEXT: note: - Linking artifacts. // CHECK-NEXT: note: - [1/2] Reading '{{.*}}tu-1.json'. +// CHECK-NEXT: note: - Target triple: 'arm64-apple-macosx' (inferred from '{{.*}}tu-1.json'). // CHECK-NEXT: note: - [1/2] Linking '{{.*}}tu-1.json'. // CHECK-NEXT: note: - [2/2] Reading '{{.*}}tu-2.json'. // CHECK-NEXT: note: - [2/2] Linking '{{.*}}tu-2.json'. -// CHECK-NEXT: note: - Writing output summary to '{{.*}}lu-1+2.json'. +// CHECK-NEXT: note: - Linked 2 translation unit(s). +// CHECK-NEXT: note: - Target namespace: 'NestedBuildNamespace([BuildNamespace(LinkUnit, lu-1+2)])'. +// CHECK-NEXT: note: - Writing link unit summary to '{{.*}}lu-1+2.json'. // CHECK-NEXT: note: - Linking finished. + +// A library input reports how many members it contributes, and the closing +// count is of translation units folded in, not of inputs on the command line. +// Only the lines that differ from the run above are checked. + +// RUN: clang-ssaf-linker static-library create %S/Inputs/tu-1.json %S/Inputs/tu-2.json --namespace libtwo -o %t/libtwo.json +// RUN: clang-ssaf-linker --verbose %t/libtwo.json %S/Inputs/lib-arm64.json -o %t/lu-libs.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=LIBS +// LIBS: note: - [1/2] Linking '{{.*}}libtwo.json' (static library, 2 member(s)). +// LIBS-NEXT: note: - [2/2] Reading '{{.*}}lib-arm64.json'. +// LIBS-NEXT: note: - [2/2] Linking '{{.*}}lib-arm64.json' (static library, 0 member(s)). +// LIBS-NEXT: note: - Linked 2 translation unit(s). + +// A multi-arch input reports how many members it carries. + +// RUN: clang-ssaf-linker multi-arch create %t/libtwo.json -o %t/libtwo-1arch.json +// RUN: clang-ssaf-linker --verbose %t/libtwo-1arch.json -o %t/lu-member.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER +// MEMBER: note: - [1/1] Reading '{{.*}}libtwo-1arch.json'. +// MEMBER-NEXT: note: - Target triple: 'arm64-apple-macosx' (inferred from '{{.*}}libtwo-1arch.json'). +// MEMBER-NEXT: note: - [1/1] Linking '{{.*}}libtwo-1arch.json' (multi-arch static library, 1 member(s)). +// MEMBER-NEXT: note: - Linked 2 translation unit(s). + +// An explicit --target-triple is reported as such rather than as inferred. +// Which member it selects is asserted by content in linking.test, not here; the +// selected member of Inputs/libtwo-2arch.json is empty, hence no linked units. + +// RUN: clang-ssaf-linker --verbose --target-triple arm64-apple-macosx %S/Inputs/libtwo-2arch.json -o %t/lu-explicit.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=EXPLICIT +// EXPLICIT: note: - Target triple: 'arm64-apple-macosx' (from --target-triple). +// EXPLICIT-NEXT: note: - [1/1] Linking '{{.*}}libtwo-2arch.json' (multi-arch static library, 2 member(s)). +// EXPLICIT-NEXT: note: - Linked 0 translation unit(s). + +// A run that cannot fix a target triple reports no triple and links nothing. + +// RUN: not clang-ssaf-linker --verbose %S/Inputs/libtwo-2arch.json -o %t/lu-none.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NO-TRIPLE --implicit-check-not="Target triple" +// NO-TRIPLE: note: - [1/1] Reading '{{.*}}libtwo-2arch.json'. +// NO-TRIPLE-NEXT: error: cannot infer target triple from '{{.*}}libtwo-2arch.json': multi-arch static library has 2 members; pass --target-triple to select one diff --git a/clang/tools/clang-ssaf-linker/CMakeLists.txt b/clang/tools/clang-ssaf-linker/CMakeLists.txt index c51c1f25ff9cc..4527a9a27ee24 100644 --- a/clang/tools/clang-ssaf-linker/CMakeLists.txt +++ b/clang/tools/clang-ssaf-linker/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_tool(clang-ssaf-linker + LinkCLI.cpp MultiArchCreateCLI.cpp StaticLibraryCreateCLI.cpp SSAFLinker.cpp diff --git a/clang/tools/clang-ssaf-linker/LinkCLI.cpp b/clang/tools/clang-ssaf-linker/LinkCLI.cpp new file mode 100644 index 0000000000000..dd15bce157bfe --- /dev/null +++ b/clang/tools/clang-ssaf-linker/LinkCLI.cpp @@ -0,0 +1,295 @@ +//===- LinkCLI.cpp --------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implements the default (no subcommand) linking action. Inputs are read one +// at a time and folded into the link unit as they are read, so the first +// input that cannot be accepted is the one reported. +// +// The target triple is fixed from the first input (or from --target-triple) +// before the linker is constructed. Validating every later input against it +// happens here rather than in EntityLinker: choosing which inputs belong to +// a target is a command line concern, and EntityLinker treats a mismatch as +// a fatal precondition violation. +// +//===----------------------------------------------------------------------===// + +#include "LinkCLI.h" + +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchSharedLibrary.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" +#include "clang/ScalableStaticAnalysis/Core/Support/ErrorBuilder.h" +#include "clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/Support/Path.h" +#include <cassert> +#include <memory> +#include <utility> +#include <variant> + +using namespace llvm; +using namespace clang::ssaf; + +namespace path = llvm::sys::path; + +namespace { + +//===----------------------------------------------------------------------===// +// Error Messages +//===----------------------------------------------------------------------===// + +constexpr const char *ReadingArtifact = "Reading artifact '{0}'"; + +constexpr const char *LinkingArtifact = "Linking artifact '{0}'"; + +constexpr const char *NoInputs = + "no input artifacts: at least one input is required"; + +constexpr const char *NoMembersToInferFrom = + "cannot infer target triple from '{0}': multi-arch static library has no " + "members; pass --target-triple"; + +constexpr const char *AmbiguousMembersToInferFrom = + "cannot infer target triple from '{0}': multi-arch static library has {1} " + "members; pass --target-triple to select one"; + +constexpr const char *UnsupportedSharedInput = + "'{0}' is a {1}: linking against shared libraries is not yet supported"; + +constexpr const char *LinkUnitSummaryName = "link unit summary"; +constexpr const char *MultiArchSharedLibraryName = "multi-arch shared library"; + +//===----------------------------------------------------------------------===// +// ArtifactEncoding Helpers +//===----------------------------------------------------------------------===// + +/// Returns the human readable kind of an artifact the linker cannot consume. +/// +/// Only the shared-library family reaches this: every linkable alternative is +/// handled before it is called. The static_assert makes a new alternative a +/// compile error here rather than an unhandled case at runtime. +llvm::StringRef unsupportedInputKindName(const ArtifactEncoding &E) { + static_assert(std::variant_size_v<ArtifactEncoding> == 5, + "unsupportedInputKindName must cover every ArtifactEncoding " + "alternative the linker cannot consume"); + + if (std::holds_alternative<LUSummaryEncoding>(E)) { + return LinkUnitSummaryName; + } + + assert( + std::holds_alternative<MultiArchSharedLibrary>(E) && + "linkable ArtifactEncoding alternatives must be handled by the caller"); + return MultiArchSharedLibraryName; +} + +} // namespace + +namespace clang::ssaf { + +void LinkCLI::run(llvm::TimerGroup &TG, llvm::ArrayRef<std::string> InputPaths, + llvm::StringRef OutputPath, llvm::StringRef TargetTriple, + bool Verbose, bool Time) { + this->InputPaths = InputPaths; + this->OutputPath = OutputPath; + this->TargetTriple = TargetTriple; + this->Verbose = Verbose; + this->Time = Time; + + llvm::Timer TValidate("validate", "Validate Input", TG); + llvm::Timer TRead("read", "Read Artifacts", TG); + llvm::Timer TLink("link", "Link Artifacts", TG); + llvm::Timer TWrite("write", "Write Link Unit Summary", TG); + + // Nesting depth for indenting verbose notes. + const unsigned Level = 0; + + info(Verbose, Level, "Linking started."); + + validate(Level + 1, TValidate); + + LUSummaryEncoding Output = link(Level + 1, TRead, TLink); + + write(Output, Level + 1, TWrite); + + info(Verbose, Level, "Linking finished."); + + // A second run() should start from a clean slate. + InputFiles.clear(); + ExplicitTriple.reset(); +} + +void LinkCLI::validate(unsigned Level, llvm::Timer &TValidate) { + info(Verbose, Level, "Validating input."); + + llvm::TimeRegion _(Time ? &TValidate : nullptr); + + OutputFile = FormatFile::fromOutputPath(OutputPath); + LinkUnitName = path::stem(OutputFile.Path).str(); + info(Verbose, Level + 1, "Validated output path '{0}'.", OutputFile.Path); + + if (InputPaths.empty()) { + fail(NoInputs); + } + for (const auto &InputPath : InputPaths) { + InputFiles.push_back(FormatFile::fromInputPath(InputPath)); + } + info(Verbose, Level + 1, "Validated {0} input artifact path(s).", + InputFiles.size()); + + if (!TargetTriple.empty()) { + ExplicitTriple = parseTargetTripleOrFail("--target-triple", TargetTriple); + } +} + +LUSummaryEncoding LinkCLI::link(unsigned Level, llvm::Timer &TRead, + llvm::Timer &TLink) { + info(Verbose, Level, "Creating link unit."); + + const unsigned InputLevel = Level + 1; + info(Verbose, InputLevel, "Linking artifacts."); + + // The target triple comes from the first input, so it has to be read before + // the linker can be constructed. + constexpr size_t FirstIndex = 0; + ArtifactEncoding First = + readInput(InputFiles[FirstIndex], FirstIndex, InputLevel + 1, TRead); + + llvm::Triple LinkUnitTriple = + resolveTargetTriple(First, InputFiles[FirstIndex].Path, InputLevel + 1); + + NestedBuildNamespace LUNamespace( + BuildNamespace(BuildNamespaceKind::LinkUnit, LinkUnitName)); + EntityLinker EL(LinkUnitTriple, LUNamespace); + + linkInput(EL, std::move(First), InputFiles[FirstIndex].Path, FirstIndex, + InputLevel + 1, TLink); + for (size_t Index : llvm::seq<size_t>(FirstIndex + 1, InputFiles.size())) { + linkInput(EL, readInput(InputFiles[Index], Index, InputLevel + 1, TRead), + InputFiles[Index].Path, Index, InputLevel + 1, TLink); + } + + info(Verbose, InputLevel, "Linked {0} translation unit(s).", + EL.getLinkedTUCount()); + info(Verbose, InputLevel, "Target namespace: '{0}'.", LUNamespace); + + return std::move(EL).takeOutput(); +} + +ArtifactEncoding LinkCLI::readInput(const FormatFile &Input, size_t Index, + unsigned Level, llvm::Timer &TRead) { + info(Verbose, Level, "[{0}/{1}] Reading '{2}'.", Index + 1, InputFiles.size(), + Input.Path); + + llvm::TimeRegion _(Time ? &TRead : nullptr); + + auto ExpectedEncoding = Input.Format->readArtifactEncoding(Input.Path); + if (!ExpectedEncoding) { + fail(ErrorBuilder::wrap(ExpectedEncoding.takeError()) + .context(ReadingArtifact, Input.Path) + .build()); + } + return std::move(*ExpectedEncoding); +} + +llvm::Triple LinkCLI::resolveTargetTriple(const ArtifactEncoding &First, + llvm::StringRef SourceFile, + unsigned Level) { + if (ExplicitTriple) { + info(Verbose, Level, "Target triple: '{0}' (from --target-triple).", + *ExplicitTriple); + return *ExplicitTriple; + } + + auto Inferred = [&]() -> llvm::Triple { + if (const auto *TU = std::get_if<TUSummaryEncoding>(&First)) { + return TU->getTargetTriple(); + } + + if (const auto *SL = std::get_if<StaticLibrary>(&First)) { + return SL->TargetTriple; + } + + if (const auto *MASL = std::get_if<MultiArchStaticLibrary>(&First)) { + // A single member names the target unambiguously; anything else needs the + // architecture to be chosen on the command line. + if (MASL->Members.empty()) { + fail(NoMembersToInferFrom, SourceFile); + } + if (MASL->Members.size() > 1) { + fail(AmbiguousMembersToInferFrom, SourceFile, MASL->Members.size()); + } + return (*MASL->Members.begin())->TargetTriple; + } + + fail(UnsupportedSharedInput, SourceFile, unsupportedInputKindName(First)); + }(); + + info(Verbose, Level, "Target triple: '{0}' (inferred from '{1}').", Inferred, + SourceFile); + + return Inferred; +} + +void LinkCLI::linkInput(EntityLinker &EL, ArtifactEncoding Encoding, + llvm::StringRef SourceFile, size_t Index, + unsigned Level, llvm::Timer &TLink) { + auto failOnError = [&](llvm::Error Err) { + if (Err) { + fail(ErrorBuilder::wrap(std::move(Err)) + .context(LinkingArtifact, SourceFile) + .build()); + } + }; + + if (auto *TU = std::get_if<TUSummaryEncoding>(&Encoding)) { + info(Verbose, Level, "[{0}/{1}] Linking '{2}'.", Index + 1, + InputFiles.size(), SourceFile); + llvm::TimeRegion _(Time ? &TLink : nullptr); + + failOnError(EL.link(std::make_unique<TUSummaryEncoding>(std::move(*TU)))); + return; + } + + if (auto *SL = std::get_if<StaticLibrary>(&Encoding)) { + info(Verbose, Level, + "[{0}/{1}] Linking '{2}' (static library, {3} member(s)).", Index + 1, + InputFiles.size(), SourceFile, SL->Members.size()); + llvm::TimeRegion _(Time ? &TLink : nullptr); + + failOnError(EL.link(std::make_unique<StaticLibrary>(std::move(*SL)))); + return; + } + + if (auto *MASL = std::get_if<MultiArchStaticLibrary>(&Encoding)) { + info(Verbose, Level, + "[{0}/{1}] Linking '{2}' (multi-arch static library, {3} member(s)).", + Index + 1, InputFiles.size(), SourceFile, MASL->Members.size()); + llvm::TimeRegion _(Time ? &TLink : nullptr); + + failOnError( + EL.link(std::make_unique<MultiArchStaticLibrary>(std::move(*MASL)))); + return; + } + + fail(UnsupportedSharedInput, SourceFile, unsupportedInputKindName(Encoding)); +} + +void LinkCLI::write(const LUSummaryEncoding &Output, unsigned Level, + llvm::Timer &TWrite) { + info(Verbose, Level, "Writing link unit summary to '{0}'.", OutputFile.Path); + + llvm::TimeRegion _(Time ? &TWrite : nullptr); + + if (auto Err = + OutputFile.Format->writeLUSummaryEncoding(Output, OutputFile.Path)) { + fail(std::move(Err)); + } +} + +} // namespace clang::ssaf diff --git a/clang/tools/clang-ssaf-linker/LinkCLI.h b/clang/tools/clang-ssaf-linker/LinkCLI.h new file mode 100644 index 0000000000000..fe38788e11676 --- /dev/null +++ b/clang/tools/clang-ssaf-linker/LinkCLI.h @@ -0,0 +1,111 @@ +//===- LinkCLI.h ------------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Declares the CLI action class for the link action of `clang-ssaf-linker`. +// Links TU summaries, static libraries, and members of multi-arch static +// libraries into one LU summary. +// +// The class is intentionally independent of the tool's cl::opt globals. +// Every input it needs is passed to run(), so the class can be reused or +// unit-tested outside the driver. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_LINKCLI_H +#define LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_LINKCLI_H + +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h" +#include "clang/ScalableStaticAnalysis/Tool/Utils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Timer.h" +#include "llvm/TargetParser/Triple.h" +#include <cstddef> +#include <optional> +#include <string> +#include <vector> + +namespace clang::ssaf { + +/// Runs the default linking action for `clang-ssaf-linker`. +class LinkCLI { +public: + /// Orchestrates validation, linking, and serialization of the LU summary. + /// Non-recoverable errors call fail() from Tool/Utils.h and terminate the + /// process. + void run(llvm::TimerGroup &TG, llvm::ArrayRef<std::string> InputPaths, + llvm::StringRef OutputPath, llvm::StringRef TargetTriple, + bool Verbose, bool Time); + +private: + /// Validates the output path and every input path, derives the link unit + /// name, and validates TargetTriple if it is set. + void validate(unsigned Level, llvm::Timer &TValidate); + + /// Reads the inputs and folds each into one link unit, in command line + /// order. + /// + /// \returns The accumulated LU summary. + LUSummaryEncoding link(unsigned Level, llvm::Timer &TRead, + llvm::Timer &TLink); + + /// Reads the artifact from \p Input. + /// + /// \param Index The input's position, reported as the note's [i/N] counter. + ArtifactEncoding readInput(const FormatFile &Input, size_t Index, + unsigned Level, llvm::Timer &TRead); + + /// Determines the link unit's target triple. + /// + /// An explicit --target-triple wins. Otherwise the triple is inferred from + /// \p First: its own for a TU summary or a static library, and its sole + /// member's for a single-member multi-arch static library. Any other shape + /// cannot be inferred from and requires --target-triple. + /// + /// \param SourceFile The path \p First was read from, named in diagnostics. + llvm::Triple resolveTargetTriple(const ArtifactEncoding &First, + llvm::StringRef SourceFile, unsigned Level); + + /// Folds one input into \p EL, reporting whatever EntityLinker rejects -- + /// including an input that does not belong to the resolved target -- with the + /// input's path as context. + /// + /// \param SourceFile The input's path, named in diagnostics and notes. + /// \param Index The input's position, reported as the note's [i/N] counter. + void linkInput(EntityLinker &EL, ArtifactEncoding Encoding, + llvm::StringRef SourceFile, size_t Index, unsigned Level, + llvm::Timer &TLink); + + /// Serializes the LU summary to the validated output path. + void write(const LUSummaryEncoding &Output, unsigned Level, + llvm::Timer &TWrite); + + // Arguments captured by run() before dispatching to linking methods. + // InputPaths, OutputPath, and TargetTriple are non-owning: they alias the + // driver's cl::opt storage, which outlives the call. + llvm::ArrayRef<std::string> InputPaths; + llvm::StringRef OutputPath; + llvm::StringRef TargetTriple; + bool Verbose = false; + bool Time = false; + + // State populated during validate() and consumed by later phases. + FormatFile OutputFile; + std::vector<FormatFile> InputFiles; + std::string LinkUnitName; + + // The triple from --target-triple, parsed and validated by validate(), or + // nullopt when the flag is not supplied. + std::optional<llvm::Triple> ExplicitTriple; +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_TOOLS_CLANG_SSAF_LINKER_LINKCLI_H diff --git a/clang/tools/clang-ssaf-linker/SSAFLinker.cpp b/clang/tools/clang-ssaf-linker/SSAFLinker.cpp index 9d72e9f338328..7236fc65c6767 100644 --- a/clang/tools/clang-ssaf-linker/SSAFLinker.cpp +++ b/clang/tools/clang-ssaf-linker/SSAFLinker.cpp @@ -7,40 +7,30 @@ //===----------------------------------------------------------------------===// // // This file implements the SSAF entity linker tool. Its default behavior is to -// link N TU summaries into one LU summary via the EntityLinker framework. It -// also provides the `static-library` subcommand for bundling TU summaries into -// a StaticLibrary, and the `multi-arch` subcommand for bundling StaticLibrary +// link N inputs (TU summaries, static libraries, and multi-arch static +// libraries) into one LU summary via the EntityLinker framework. It also +// provides the `static-library` subcommand for bundling TU summaries into a +// StaticLibrary, and the `multi-arch` subcommand for bundling StaticLibrary // and SharedLibrary members (or existing multi-arch bundles) into // MultiArchStaticLibrary or MultiArchSharedLibrary. // //===----------------------------------------------------------------------===// +#include "LinkCLI.h" #include "MultiArchCreateCLI.h" #include "StaticLibraryCreateCLI.h" -#include "clang/ScalableStaticAnalysis/Core/EntityLinker/EntityLinker.h" -#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" -#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" -#include "clang/ScalableStaticAnalysis/Core/Support/ErrorBuilder.h" #include "clang/ScalableStaticAnalysis/SSAFForceLinker.h" // IWYU pragma: keep #include "clang/ScalableStaticAnalysis/Tool/Utils.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/InitLLVM.h" -#include "llvm/Support/Path.h" #include "llvm/Support/Timer.h" -#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" -#include <memory> #include <string> using namespace llvm; using namespace clang::ssaf; -namespace path = llvm::sys::path; - namespace { //===----------------------------------------------------------------------===// @@ -66,6 +56,14 @@ cl::opt<std::string> OutputPath("o", cl::desc("Output file path"), cl::value_desc("path"), cl::Required, cl::cat(SsafLinkerCategory)); +cl::opt<std::string> TargetTriple( + "target-triple", + cl::desc( + "Target triple of the link unit (defaults to the first input's; " + "required when the first input is a multi-arch static library with " + "several members)"), + cl::value_desc("triple"), cl::cat(SsafLinkerCategory)); + // --verbose and --time apply to every subcommand. cl::opt<bool> Verbose("verbose", cl::desc("Enable verbose output"), cl::init(false), cl::cat(SsafLinkerCategory), @@ -152,8 +150,6 @@ constexpr const char *MultiArchCreateVerb = "create"; namespace LocalErrorMessages { -constexpr const char *LinkingSummary = "Linking summary '{0}'"; - constexpr const char *UnknownStaticLibraryVerb = "unknown static-library verb '{0}': expected 'create'"; @@ -163,110 +159,12 @@ constexpr const char *UnknownMultiArchVerb = } // namespace LocalErrorMessages //===----------------------------------------------------------------------===// -// link action +// default (no subcommand) link action //===----------------------------------------------------------------------===// -struct LinkerInput { - std::vector<FormatFile> InputFiles; - FormatFile OutputFile; - std::string LinkUnitName; -}; - -LinkerInput validateLinkInput(llvm::TimerGroup &TG) { - llvm::Timer TValidate("validate", "Validate Input", TG); - LinkerInput LI; - - { - llvm::TimeRegion _(Time ? &TValidate : nullptr); - - LI.OutputFile = FormatFile::fromOutputPath(OutputPath); - LI.LinkUnitName = path::stem(LI.OutputFile.Path).str(); - } - - info(Verbose, 2, "Validated output summary path '{0}'.", LI.OutputFile.Path); - - { - llvm::TimeRegion _(Time ? &TValidate : nullptr); - for (const auto &InputPath : InputPaths) { - LI.InputFiles.push_back(FormatFile::fromInputPath(InputPath)); - } - } - - info(Verbose, 2, "Validated {0} input summary paths.", LI.InputFiles.size()); - - return LI; -} - void runLink(llvm::TimerGroup &TG) { - info(Verbose, 0, "Linking started."); - - LinkerInput LI; - { - info(Verbose, 1, "Validating input."); - LI = validateLinkInput(TG); - } - - info(Verbose, 1, "Linking input."); - info(Verbose, 2, "Constructing linker."); - - // TODO: The linker currently uses a hardcoded target triple. Architecture - // tracking in the linker will be handled properly in a separate PR. - EntityLinker EL(llvm::Triple("arm64-apple-macosx"), - NestedBuildNamespace(BuildNamespace( - BuildNamespaceKind::LinkUnit, LI.LinkUnitName))); - - llvm::Timer TRead("read", "Read Summaries", TG); - llvm::Timer TLink("link", "Link Summaries", TG); - llvm::Timer TWrite("write", "Write Summary", TG); - - info(Verbose, 2, "Linking summaries."); - - for (auto [Index, InputFile] : llvm::enumerate(LI.InputFiles)) { - std::unique_ptr<TUSummaryEncoding> Summary; - - { - info(Verbose, 3, "[{0}/{1}] Reading '{2}'.", (Index + 1), - LI.InputFiles.size(), InputFile.Path); - - llvm::TimeRegion _(Time ? &TRead : nullptr); - - auto ExpectedSummaryEncoding = - InputFile.Format->readTUSummaryEncoding(InputFile.Path); - if (!ExpectedSummaryEncoding) { - fail(ExpectedSummaryEncoding.takeError()); - } - - Summary = std::make_unique<TUSummaryEncoding>( - std::move(*ExpectedSummaryEncoding)); - } - - { - info(Verbose, 3, "[{0}/{1}] Linking '{2}'.", (Index + 1), - LI.InputFiles.size(), InputFile.Path); - - llvm::TimeRegion _(Time ? &TLink : nullptr); - - if (auto Err = EL.link(std::move(Summary))) { - fail(ErrorBuilder::wrap(std::move(Err)) - .context(LocalErrorMessages::LinkingSummary, InputFile.Path) - .build()); - } - } - } - - { - info(Verbose, 2, "Writing output summary to '{0}'.", LI.OutputFile.Path); - - llvm::TimeRegion _(Time ? &TWrite : nullptr); - - auto Output = std::move(EL).takeOutput(); - if (auto Err = LI.OutputFile.Format->writeLUSummaryEncoding( - Output, LI.OutputFile.Path)) { - fail(std::move(Err)); - } - } - - info(Verbose, 0, "Linking finished."); + LinkCLI LC; + LC.run(TG, InputPaths, OutputPath, TargetTriple, Verbose, Time); } //===----------------------------------------------------------------------===// _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
