From be07751ed6e175076c27b6f3786825e3507b92fa Mon Sep 17 00:00:00 2001 From: mdtoguchi <47896532+mdtoguchi@users.noreply.github.com> Date: Thu, 19 Mar 2020 08:29:03 -0700 Subject: [PATCH] [SYCL][Driver] Improve fat static library support (#1319) When processing libraries on the command line, take into account any thusly named static archive (*.a) file and consider that for offloading. We will also scan the appropriate linker options passed in so we can determine if the library should be considered as whole-archive. The static libraries found on the command line will be 'sniffed' to determine if the static library is fat. This effectively negates the need to use -foffload-static-lib and -foffload-whole-static-lib which we should consider deprecated now. Add a deprecated diagnostic when -foffload-static-lib is used Refactor to bring along common code for the device check. Narrows the focus of what is considered to be processed from the linker only arguments. Pulls in objects from -Wl, instead of only archives, but is only part of a potential partial link step and is not fully processed. Signed-off-by: Michael D Toguchi --- .../clang/Basic/DiagnosticDriverKinds.td | 2 + clang/include/clang/Driver/Action.h | 13 + clang/include/clang/Driver/Driver.h | 14 + clang/include/clang/Driver/ToolChain.h | 2 + clang/lib/Driver/Action.cpp | 10 + clang/lib/Driver/Driver.cpp | 342 +++++++++++++----- clang/lib/Driver/ToolChain.cpp | 9 + clang/lib/Driver/ToolChains/Clang.cpp | 109 +++--- clang/lib/Driver/ToolChains/Clang.h | 13 + clang/test/Driver/sycl-offload-intelfpga.cpp | 78 ++-- .../test/Driver/sycl-offload-static-lib-2.cpp | 108 ++++++ clang/test/Driver/sycl-offload-static-lib.cpp | 32 +- clang/test/Driver/sycl-offload-win.c | 10 +- 13 files changed, 571 insertions(+), 171 deletions(-) create mode 100644 clang/test/Driver/sycl-offload-static-lib-2.cpp diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 6ac9e94f7c330..58f7c07fb18ad 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -340,6 +340,8 @@ def warn_drv_object_size_disabled_O0 : Warning< InGroup, DefaultWarnNoWerror; def err_invalid_branch_protection: Error < "invalid branch protection option '%0' in '%1'">; +def warn_drv_deprecated_option : Warning< + "option '%0' is deprecated, use '%1' directly instead">, InGroup; def note_drv_command_failed_diag_msg : Note< "diagnostic msg: %0">; diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h index fe991b971b73f..88e20a965c690 100644 --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -76,6 +76,7 @@ class Action { SPIRVTranslatorJobClass, SPIRCheckJobClass, SYCLPostLinkJobClass, + PartialLinkJobClass, BackendCompileJobClass, JobClassFirst = PreprocessJobClass, @@ -680,6 +681,18 @@ class SYCLPostLinkJobAction : public JobAction { } }; +class PartialLinkJobAction : public JobAction { + void anchor() override; + +public: + PartialLinkJobAction(Action *Input, types::ID OutputType); + PartialLinkJobAction(ActionList &Input, types::ID OutputType); + + static bool classof(const Action *A) { + return A->getKind() == PartialLinkJobClass; + } +}; + class BackendCompileJobAction : public JobAction { void anchor() override; diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 4867f48b53005..6679fae41f436 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -621,6 +621,15 @@ class Driver { &CachedResults, Action::OffloadKind TargetDeviceOffloadKind) const; + /// Static offload library seen. + bool OffloadStaticLibSeen = false; + + void setOffloadStaticLibSeen() { OffloadStaticLibSeen = true; } + + /// Returns true if an offload static library is found. + bool checkForOffloadStaticLib(Compilation &C, + llvm::opt::DerivedArgList &Args) const; + public: /// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and /// return the grouped values as integers. Numbers which are not @@ -642,6 +651,8 @@ class Driver { MutableArrayRef Digits); /// Compute the default -fmodule-cache-path. static void getDefaultModuleCachePath(SmallVectorImpl &Result); + + bool getOffloadStaticLibSeen() const { return OffloadStaticLibSeen; }; }; /// \return True if the last defined optimization level is -Ofast. @@ -651,6 +662,9 @@ bool isOptimizationLevelFast(const llvm::opt::ArgList &Args); /// \return True if the filename has a valid object file extension. bool isObjectFile(std::string FileName); +/// \return True if the filename has a static archive/lib extension. +bool isStaticArchiveFile(const StringRef &FileName); + /// \return True if the argument combination will end up generating remarks. bool willEmitRemarks(const llvm::opt::ArgList &Args); diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h index d7d134b32fded..65c849fe8398a 100644 --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -145,6 +145,7 @@ class ToolChain { mutable std::unique_ptr SPIRVTranslator; mutable std::unique_ptr SPIRCheck; mutable std::unique_ptr SYCLPostLink; + mutable std::unique_ptr PartialLink; mutable std::unique_ptr BackendCompiler; Tool *getClang() const; @@ -158,6 +159,7 @@ class ToolChain { Tool *getSPIRVTranslator() const; Tool *getSPIRCheck() const; Tool *getSYCLPostLink() const; + Tool *getPartialLink() const; Tool *getBackendCompiler() const; mutable std::unique_ptr SanitizerArguments; diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp index e83094374a04c..f2973da2322e1 100644 --- a/clang/lib/Driver/Action.cpp +++ b/clang/lib/Driver/Action.cpp @@ -49,6 +49,8 @@ const char *Action::getClassName(ActionClass AC) { return "llvm-no-spir-kernel"; case SYCLPostLinkJobClass: return "sycl-post-link"; + case PartialLinkJobClass: + return "partial-link"; case BackendCompileJobClass: return "backend-compiler"; } @@ -454,6 +456,14 @@ void SYCLPostLinkJobAction::anchor() {} SYCLPostLinkJobAction::SYCLPostLinkJobAction(Action *Input, types::ID Type) : JobAction(SYCLPostLinkJobClass, Input, Type) {} +void PartialLinkJobAction::anchor() {} + +PartialLinkJobAction::PartialLinkJobAction(Action *Input, types::ID Type) + : JobAction(PartialLinkJobClass, Input, Type) {} + +PartialLinkJobAction::PartialLinkJobAction(ActionList &Inputs, types::ID Type) + : JobAction(PartialLinkJobClass, Inputs, Type) {} + void BackendCompileJobAction::anchor() {} BackendCompileJobAction::BackendCompileJobAction(ActionList &Inputs, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index aab3dfe34e0ec..c08e54d5a54e5 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1302,6 +1302,10 @@ Compilation *Driver::BuildCompilation(ArrayRef ArgList) { InputList Inputs; BuildInputs(C->getDefaultToolChain(), *TranslatedArgs, Inputs); + // Determine if there are any offload static libraries. + if (checkForOffloadStaticLib(*C, *TranslatedArgs)) + setOffloadStaticLibSeen(); + // Populate the tool chains for the offloading devices, if any. CreateOffloadingDeviceToolChains(*C, Inputs); @@ -2473,6 +2477,7 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, Diag(diag::note_use_dashdash); } } + // TODO: remove when -foffload-static-lib support is dropped. else if (A->getOption().matches(options::OPT_offload_lib_Group)) { // Add the foffload-static-lib library to the command line to allow // processing when no source or object is supplied as well as proper @@ -2480,6 +2485,10 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, Arg *InputArg = MakeInputArg(Args, Opts, A->getValue()); Inputs.push_back(std::make_pair(types::TY_Object, InputArg)); A->claim(); + // Use of -foffload-static-lib and -foffload-whole-static-lib are + // deprecated with the updated functionality to scan the static libs. + Diag(clang::diag::warn_drv_deprecated_option) + << A->getAsString(Args) << A->getValue(); } } if (CCCIsCPP() && Inputs.empty()) { @@ -2490,6 +2499,188 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, } } +static bool runBundler(const SmallVectorImpl &BundlerArgs, + Compilation &C) { + // Find bundler. + StringRef ExecPath(C.getArgs().MakeArgString(C.getDriver().Dir)); + llvm::ErrorOr BundlerBinary = + llvm::sys::findProgramByName("clang-offload-bundler", ExecPath); + // Since this is run in real time and not in the toolchain, output the + // command line if requested. + bool OutputOnly = C.getArgs().hasArg(options::OPT__HASH_HASH_HASH); + if (C.getArgs().hasArg(options::OPT_v) || OutputOnly) { + for (StringRef A : BundlerArgs) + if (OutputOnly) + llvm::errs() << "\"" << A << "\" "; + else + llvm::errs() << A << " "; + llvm::errs() << '\n'; + } + if (BundlerBinary.getError()) + return false; + + return !llvm::sys::ExecuteAndWait(BundlerBinary.get(), BundlerArgs); +} + +bool hasFPGABinary(Compilation &C, std::string Object, types::ID Type) { + assert(types::isFPGA(Type) && "unexpected Type for FPGA binary check"); + // Temporary names for the output. + llvm::Triple TT; + TT.setArchName(types::getTypeName(Type)); + TT.setVendorName("intel"); + TT.setOS(llvm::Triple::UnknownOS); + TT.setEnvironment(llvm::Triple::SYCLDevice); + + // Checking uses -check-section option with the input file, no output + // file and the target triple being looked for. + const char *Targets = + C.getArgs().MakeArgString(Twine("-targets=sycl-") + TT.str()); + const char *Inputs = C.getArgs().MakeArgString(Twine("-inputs=") + Object); + // Always use -type=ao for aocx/aocr bundle checking. The 'bundles' are + // actually archives. + SmallVector BundlerArgs = {"clang-offload-bundler", "-type=ao", + Targets, Inputs, "-check-section"}; + return runBundler(BundlerArgs, C); +} + +static bool hasOffloadSections(Compilation &C, const StringRef &Archive, + DerivedArgList &Args) { + // Do not do the check if the file doesn't exist + if (!llvm::sys::fs::exists(Archive)) + return false; + + llvm::Triple TT(C.getDefaultToolChain().getTriple()); + // Checking uses -check-section option with the input file, no output + // file and the target triple being looked for. + // TODO - Improve checking to check for explicit offload target instead + // of the generic host availability. + const char *Targets = Args.MakeArgString(Twine("-targets=host-") + TT.str()); + const char *Inputs = Args.MakeArgString(Twine("-inputs=") + Archive.str()); + // Always use -type=ao for bundle checking. The 'bundles' are + // actually archives. + SmallVector BundlerArgs = {"clang-offload-bundler", "-type=ao", + Targets, Inputs, "-check-section"}; + return runBundler(BundlerArgs, C); +} + +// Simple helper function for Linker options, where the option is valid if +// it has '-' or '--' as the designator. +static bool optionMatches(const std::string &Option, + const std::string &OptCheck) { + return (Option == OptCheck || ("-" + Option) == OptCheck); +} + +// Process linker inputs for use with offload static libraries. We are only +// handling options and explicitly named static archives as these need to be +// partially linked. +static SmallVector getLinkerArgs(Compilation &C, + DerivedArgList &Args) { + SmallVector LibArgs; + for (const auto *A : Args) { + std::string FileName = A->getAsString(Args); + if (A->getOption().getKind() == Option::InputClass) { + StringRef Value(A->getValue()); + if (isStaticArchiveFile(Value)) { + LibArgs.push_back(Args.MakeArgString(FileName)); + continue; + } + } + if (A->getOption().hasFlag(options::LinkerInput)) { + // Do not add any libraries that are not fully named static libs + if (A->getOption().matches(options::OPT_l) || + A->getOption().matches(options::OPT_reserved_lib_Group) || + A->getOption().hasFlag(options::NoArgumentUnused)) + continue; + std::string PrevArg; + for (const std::string &Value : A->getValues()) { + auto addKnownValues = [&](const StringRef &V) { + // Only add named static libs objects and --whole-archive options. + if (optionMatches("-whole-archive", V.str()) || + optionMatches("-no-whole-archive", V.str()) || + isStaticArchiveFile(V)) { + LibArgs.push_back(Args.MakeArgString(V)); + return; + } + // Probably not the best way to handle this, but there are options + // that take arguments which we should not add to the known values. + // Handle -z and -rpath for now - can be expanded if/when usage shows + // the need. + if (PrevArg != "-z" && PrevArg != "-rpath" && V[0] != '-' && + isObjectFile(V.str())) { + LibArgs.push_back(Args.MakeArgString(V)); + return; + } + }; + if (Value[0] == '@') { + // Found a response file, we want to expand contents to try and + // discover more libraries and options. + SmallVector ExpandArgs; + ExpandArgs.push_back(Value.c_str()); + + llvm::BumpPtrAllocator A; + llvm::StringSaver S(A); + llvm::cl::ExpandResponseFiles( + S, + C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() + ? llvm::cl::TokenizeWindowsCommandLine + : llvm::cl::TokenizeGNUCommandLine, + ExpandArgs); + for (StringRef EA : ExpandArgs) + addKnownValues(EA); + } else + addKnownValues(Value); + PrevArg = Value; + } + continue; + } + // Use of -foffload-static-lib and -foffload-whole-static-lib is + // considered deprecated. Usage should move to passing in the static + // library name on the command line, encapsulating with + // -Wl,--whole-archive -Wl,--no-whole-archive as needed. + if (A->getOption().matches(options::OPT_foffload_static_lib_EQ)) { + LibArgs.push_back(Args.MakeArgString(A->getValue())); + continue; + } + if (A->getOption().matches(options::OPT_foffload_whole_static_lib_EQ)) { + // For -foffload-whole-static-lib, we add the --whole-archive wrap + // around the library which will be used during the partial link step. + LibArgs.push_back("--whole-archive"); + LibArgs.push_back(Args.MakeArgString(A->getValue())); + LibArgs.push_back("--no-whole-archive"); + continue; + } + } + return LibArgs; +} + +// Goes through all of the arguments, including inputs expected for the +// linker directly, to determine if we need to perform additional work for +// static offload libraries. +bool Driver::checkForOffloadStaticLib(Compilation &C, + DerivedArgList &Args) const { + // Check only if enabled with -fsycl or -fopenmp-targets + if (!Args.hasFlag(options::OPT_fsycl, options::OPT_fno_sycl, false) && + !Args.hasArg(options::OPT_fopenmp_targets_EQ)) + return false; + + // Right off the bat, assume the presense of -foffload-static-lib means + // the need to perform linking steps for fat static archive offloading. + // TODO: remove when -foffload-static-lib support is dropped. + if (Args.hasArg(options::OPT_offload_lib_Group)) + return true; + SmallVector OffloadLibArgs(getLinkerArgs(C, Args)); + for (const StringRef &OLArg : OffloadLibArgs) + if (isStaticArchiveFile(OLArg) && hasOffloadSections(C, OLArg, Args)) { + // FPGA binaries with AOCX or AOCR sections are not considered fat + // static archives. + if (Args.hasArg(options::OPT_fintelfpga)) + return !(hasFPGABinary(C, OLArg.str(), types::TY_FPGA_AOCR) || + hasFPGABinary(C, OLArg.str(), types::TY_FPGA_AOCX)); + return true; + } + return false; +} + namespace { /// Provides a convenient interface for different programming models to generate /// the required device actions. @@ -3393,8 +3584,9 @@ class OffloadingActionBuilder final { return ABRT_Inactive; std::string InputName = IA->getInputArg().getAsString(Args); - // Objects should already be consumed with -foffload-static-lib - if (Args.hasArg(options::OPT_offload_lib_Group) && + // Objects will be consumed as part of the partial link step when + // dealing with offload static libraries + if (C.getDriver().getOffloadStaticLibSeen() && IA->getType() == types::TY_Object && isObjectFile(InputName)) return ABRT_Inactive; @@ -3899,57 +4091,6 @@ class OffloadingActionBuilder final { return C.MakeAction(HDep, DDeps); } - bool hasFPGABinary(Compilation &C, std::string Object, types::ID Type) { - assert(types::isFPGA(Type) && "unexpected Type for FPGA binary check"); - // Temporary names for the output. - const ToolChain *OTC = C.getSingleOffloadToolChain(); - llvm::Triple TT; - TT.setArchName(types::getTypeName(Type)); - TT.setVendorName("intel"); - TT.setOS(llvm::Triple(OTC->getTriple()).getOS()); - TT.setEnvironment(llvm::Triple::SYCLDevice); - - // Checking uses -check-section option with the input file, no output - // file and the target triple being looked for. - const char *Targets = - C.getArgs().MakeArgString(Twine("-targets=sycl-") + TT.str()); - const char *Inputs = C.getArgs().MakeArgString(Twine("-inputs=") + - Object); - // Always use -type=ao for aocx/aocr bundle checking. The 'bundles' are - // actually archives. - std::vector BundlerArgs = { "clang-offload-bundler", - "-type=ao", - Targets, - Inputs, - "-check-section" }; - // Find bundler. - StringRef ExecPath(C.getArgs().MakeArgString(C.getDriver().Dir)); - auto BundlerBinary = llvm::sys::findProgramByName("clang-offload-bundler", - ExecPath); - if (C.getArgs().hasArg(options::OPT_ccc_print_phases, - options::OPT_ccc_print_bindings)) - return false; - // Since this is run in real time and not in the toolchain, output the - // command line if requested. - bool OutputOnly = C.getArgs().hasArg(options::OPT__HASH_HASH_HASH); - if (C.getArgs().hasArg(options::OPT_v) || OutputOnly) { - for (StringRef A : BundlerArgs) - if (OutputOnly) - llvm::errs() << "\"" << A << "\" "; - else - llvm::errs() << A << " "; - llvm::errs() << '\n'; - } - if (BundlerBinary.getError()) - return false; - - // Run the bundler. - bool Failed = llvm::sys::ExecuteAndWait(BundlerBinary.get(), BundlerArgs); - if (!Failed) - return true; - return false; - } - /// Generate an action that adds a host dependence to a device action. The /// results will be kept in this action builder. Return true if an error was /// found. @@ -3986,7 +4127,7 @@ class OffloadingActionBuilder final { if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() || !(HostAction->getType() == types::TY_Object && isObjectFile(InputName) && - Args.hasArg(options::OPT_offload_lib_Group))) { + C.getDriver().getOffloadStaticLibSeen())) { ActionList HostActionList; Action *A(HostAction); // Only check for FPGA device information when using fpga SubArch. @@ -4466,10 +4607,14 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, OffloadBuilder.appendTopLevelLinkAction(Actions); + // Go through all of the args, and create a Linker specific argument list. + // When dealing with fat static archives, this is fed into the partial link + // step on Linux or each archive is individually addressed on Windows. + SmallVector LinkArgs(getLinkerArgs(C, Args)); // When a static fat archive is provided, create a new unbundling step // for all of the objects. if (!C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() && - Args.hasArg(options::OPT_offload_lib_Group)) { + C.getDriver().getOffloadStaticLibSeen()) { ActionList UnbundlerInputs; for (const auto &LI : LinkerInputs) { // Unbundler only handles objects. @@ -4483,55 +4628,77 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args, UnbundlerInputs.push_back(LI); } const Arg *LastArg; - auto addUnbundlerInput = [&](types::ID T, const Arg *A) { + auto addUnbundlerInput = [&](types::ID T, const StringRef &A) { const llvm::opt::OptTable &Opts = getOpts(); - Arg *InputArg = MakeInputArg(Args, Opts, A->getValue()); + Arg *InputArg = MakeInputArg(Args, Opts, C.getArgs().MakeArgString(A)); LastArg = InputArg; Action *Current = C.MakeAction(*InputArg, T); UnbundlerInputs.push_back(Current); }; - for (const auto *A : Args.filtered(options::OPT_foffload_static_lib_EQ)) - addUnbundlerInput(types::TY_Archive, A); - for (const auto *A : - Args.filtered(options::OPT_foffload_whole_static_lib_EQ)) - addUnbundlerInput(types::TY_WholeArchive, A); + bool IsWholeArchive = false; + for (const StringRef &LA : LinkArgs) { + if (isStaticArchiveFile(LA)) { + addUnbundlerInput( + IsWholeArchive ? types::TY_WholeArchive : types::TY_Archive, LA); + continue; + } + if (optionMatches("-no-whole-archive", LA.str())) { + IsWholeArchive = false; + continue; + } + if (optionMatches("-whole-archive", LA.str())) { + IsWholeArchive = true; + continue; + } + if (isObjectFile(LA.str())) { + // Add any objects to the unbundler step. These objects are passed + // directly to the linker, so the driver does not know about them. + // FIXME - Better process objects passed to the linker. We are only + // adding these objects to the unbundler step, but these objects can + // potentially be fat objects that should be processed by the driver. + addUnbundlerInput(types::TY_Object, LA); + continue; + } + } + if (!UnbundlerInputs.empty()) { - Action *Current = C.MakeAction(*LastArg, types::TY_Archive); - OffloadBuilder.addHostDependenceToUnbundlingAction(Current, - UnbundlerInputs, LastArg); + Action *PartialLink = + C.MakeAction(UnbundlerInputs, types::TY_Object); + Action *Current = C.MakeAction(*LastArg, types::TY_Object); + ActionList AL; + AL.push_back(PartialLink); + OffloadBuilder.addHostDependenceToUnbundlingAction(Current, AL, LastArg); Current = OffloadBuilder.addDeviceDependencesToHostAction(Current, LastArg, phases::Link, PL.back(), PL); } } const llvm::opt::OptTable &Opts = getOpts(); - auto unbundleStaticLib = [&](types::ID T, const Arg *A) { - Arg *InputArg = MakeInputArg(Args, Opts, A->getValue()); + auto unbundleStaticLib = [&](types::ID T, const StringRef &A) { + Arg *InputArg = MakeInputArg(Args, Opts, Args.MakeArgString(A)); Action *Current = C.MakeAction(*InputArg, T); OffloadBuilder.addHostDependenceToDeviceActions(Current, InputArg, Args); OffloadBuilder.addDeviceDependencesToHostAction( Current, InputArg, phases::Link, PL.back(), PL); }; - for (const auto *A : Args.filtered(options::OPT_foffload_static_lib_EQ)) { + for (const StringRef &LA : LinkArgs) { + // At this point, we will process the archives for FPGA AOCO and individual + // archive unbundling for Windows. + if (!isStaticArchiveFile(LA)) + continue; // In MSVC environment offload-static-libs are handled slightly different // because of missing support for partial linking in the linker. We add an // unbundling action for each static archive which produces list files with // extracted objects. Device lists are then added to the appropriate device // link actions and host list is ignored since we are adding // offload-static-libs as normal libraries to the host link command. - if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment()) - unbundleStaticLib(types::TY_Archive, A); - // Pass along the -foffload-static-lib values to check if we need to - // add them for unbundling for FPGA AOT static lib usage. Uses FPGA - // aoco type to differentiate if aoco unbundling is needed. - if (Args.hasArg(options::OPT_fintelfpga)) - unbundleStaticLib(types::TY_FPGA_AOCO, A); - } - for (const auto *A : - Args.filtered(options::OPT_foffload_whole_static_lib_EQ)) { - if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment()) - unbundleStaticLib(types::TY_WholeArchive, A); + if (C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment() && + hasOffloadSections(C, LA, Args)) + unbundleStaticLib(types::TY_Archive, LA); + // Pass along the static libraries to check if we need to add them for + // unbundling for FPGA AOT static lib usage. Uses FPGA aoco type to + // differentiate if aoco unbundling is needed. if (Args.hasArg(options::OPT_fintelfpga)) - unbundleStaticLib(types::TY_FPGA_AOCO, A); + unbundleStaticLib(types::TY_FPGA_AOCO, LA); } // For an FPGA archive, we add the unbundling step above to take care of @@ -5411,8 +5578,8 @@ InputInfo Driver::BuildJobsForActionNoCache( bool IsFPGAObjLink = (JA->getType() == types::TY_Object && C.getInputArgs().hasArg(options::OPT_fintelfpga) && C.getInputArgs().hasArg(options::OPT_fsycl_link_EQ)); - if (C.getInputArgs().hasArg(options::OPT_offload_lib_Group) && - ((JA->getType() == types::TY_Archive && IsMSVCEnv) || + if (C.getDriver().getOffloadStaticLibSeen() && + (JA->getType() == types::TY_Archive || (JA->getType() == types::TY_Object && !IsMSVCEnv))) { // Host part of the unbundled static archive is not used. if (UI.DependentOffloadKind == Action::OFK_Host) @@ -6314,6 +6481,15 @@ bool clang::driver::isObjectFile(std::string FileName) { types::lookupTypeForExtension(Ext) == types::TY_Object); } +bool clang::driver::isStaticArchiveFile(const StringRef &FileName) { + if (!llvm::sys::path::has_extension(FileName)) + // Any file with no extension should not be considered an Archive. + return false; + StringRef Ext(llvm::sys::path::extension(FileName).drop_front()); + // Only .lib and .a files are to be considered. + return (Ext == "lib" || Ext == "a"); +} + bool clang::driver::willEmitRemarks(const ArgList &Args) { // -fsave-optimization-record enables it. if (Args.hasFlag(options::OPT_fsave_optimization_record, diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 51be68bbe1212..1abb81a8019c2 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -330,6 +330,12 @@ Tool *ToolChain::getSYCLPostLink() const { return SYCLPostLink.get(); } +Tool *ToolChain::getPartialLink() const { + if (!PartialLink) + PartialLink.reset(new tools::PartialLink(*this)); + return PartialLink.get(); +} + Tool *ToolChain::getBackendCompiler() const { if (!BackendCompiler) BackendCompiler.reset(buildBackendCompiler()); @@ -381,6 +387,9 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const { case Action::SYCLPostLinkJobClass: return getSYCLPostLink(); + case Action::PartialLinkJobClass: + return getPartialLink(); + case Action::BackendCompileJobClass: return getBackendCompiler(); } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 32557c38684ab..f1e167c12f89a 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7180,55 +7180,14 @@ void OffloadBundler::ConstructJobMultipleOutputs( C.getDefaultToolChain().getTriple().isWindowsMSVCEnvironment(); types::ID InputType(Input.getType()); bool IsFPGADepUnbundle = (JA.getType() == types::TY_FPGA_Dependencies); + bool IsArchiveUnbundle = + (!IsMSVCEnv && C.getDriver().getOffloadStaticLibSeen() && + (types::isArchive(InputType) || InputType == types::TY_Object)); - // For Linux, we have initial support for fat archives (archives which - // contain bundled objects). We will perform partial linking against the - // specific offload target archives which will be sent to the unbundler to - // produce a list of target objects. - // FIXME: This should be a separate job in the toolchain. - if (!IsMSVCEnv && TCArgs.hasArg(options::OPT_offload_lib_Group) && - (types::isArchive(InputType) || InputType == types::TY_Object)) { + if (IsArchiveUnbundle) TypeArg = "oo"; - ArgStringList LinkArgs; - LinkArgs.push_back("-r"); - LinkArgs.push_back("-o"); - std::string TmpName = - C.getDriver().GetTemporaryPath( - llvm::sys::path::stem(Input.getFilename()).str() + "-prelink", "o"); - InputFileName = C.addTempFile(C.getArgs().MakeArgString(TmpName)); - LinkArgs.push_back(InputFileName); - const ToolChain *HTC = C.getSingleOffloadToolChain(); - // Add crt objects - LinkArgs.push_back(TCArgs.MakeArgString(HTC->GetFilePath("crt1.o"))); - LinkArgs.push_back(TCArgs.MakeArgString(HTC->GetFilePath("crti.o"))); - // Add -L search directories. - TCArgs.AddAllArgs(LinkArgs, options::OPT_L); - - // TODO - We can potentially go through the args and add the known linker - // pass through args of --whole-archive and --no-whole-archive. This - // would allow to support user commands like: -Wl,--whole-archive - // -foffload-static-lib= -Wl,--no-whole-archive - // Input files consist of fat libraries and the object(s) to be unbundled. - bool IsWholeArchive = false; - for (const auto &I : Inputs) { - if (I.getType() == types::TY_WholeArchive && !IsWholeArchive) { - LinkArgs.push_back("--whole-archive"); - IsWholeArchive = true; - } else if (I.getType() == types::TY_Archive && IsWholeArchive) { - LinkArgs.push_back("--no-whole-archive"); - IsWholeArchive = false; - } - LinkArgs.push_back(I.getFilename()); - } - // Disable whole archive if it was enabled for the previous inputs. - if (IsWholeArchive) - LinkArgs.push_back("--no-whole-archive"); - // Add crt objects - LinkArgs.push_back(TCArgs.MakeArgString(HTC->GetFilePath("crtn.o"))); - const char *Exec = TCArgs.MakeArgString(getToolChain().GetLinkerPath()); - C.addCommand(std::make_unique(JA, *this, Exec, LinkArgs, Inputs)); - } else if (InputType == types::TY_FPGA_AOCX || - InputType == types::TY_FPGA_AOCR) { + else if (InputType == types::TY_FPGA_AOCX || + InputType == types::TY_FPGA_AOCR) { // Override type with archive object if (getToolChain().getTriple().getSubArch() == llvm::Triple::SPIRSubArch_fpga) @@ -7273,10 +7232,9 @@ void OffloadBundler::ConstructJobMultipleOutputs( Triples += Dep.DependentToolChain->getTriple().normalize(); } continue; - } else if (types::isArchive(InputType) || (InputType == types::TY_Object && - ((!IsMSVCEnv && TCArgs.hasArg(options::OPT_offload_lib_Group)) || - (TCArgs.hasArg(options::OPT_fintelfpga) && - TCArgs.hasArg(options::OPT_fsycl_link_EQ))))) { + } else if (InputType == types::TY_Archive || IsArchiveUnbundle || + (TCArgs.hasArg(options::OPT_fintelfpga) && + TCArgs.hasArg(options::OPT_fsycl_link_EQ))) { // Do not extract host part if we are unbundling archive on Windows // because it is not needed. Static offload libraries are added to the // host link command just as normal libraries. Do not extract the host @@ -7701,3 +7659,52 @@ void SYCLPostLink::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs, None)); } +// For Linux, we have initial support for fat archives (archives which +// contain bundled objects). We will perform partial linking against the +// specific offload target archives which will be sent to the unbundler to +// produce a list of target objects. +void PartialLink::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const { + // Construct simple partial link command. + assert(isa(JA) && "Expecting Partial Link job!"); + + // The partial linking command resembles this: + // ld -r -o + ArgStringList LinkArgs; + LinkArgs.push_back("-r"); + LinkArgs.push_back("-o"); + LinkArgs.push_back(Output.getFilename()); + + const ToolChain *HTC = C.getSingleOffloadToolChain(); + // Add crt objects + LinkArgs.push_back(TCArgs.MakeArgString(HTC->GetFilePath("crt1.o"))); + LinkArgs.push_back(TCArgs.MakeArgString(HTC->GetFilePath("crti.o"))); + // Add -L search directories. + TCArgs.AddAllArgs(LinkArgs, options::OPT_L); + HTC->AddFilePathLibArgs(TCArgs, LinkArgs); + + // Input files consist of fat libraries and the object(s) to be unbundled. + // We add the needed --whole-archive/--no-whole-archive when appropriate. + bool IsWholeArchive = false; + for (const auto &I : Inputs) { + if (I.getType() == types::TY_WholeArchive && !IsWholeArchive) { + LinkArgs.push_back("--whole-archive"); + IsWholeArchive = true; + } else if (I.getType() == types::TY_Archive && IsWholeArchive) { + LinkArgs.push_back("--no-whole-archive"); + IsWholeArchive = false; + } + LinkArgs.push_back(I.getFilename()); + } + // Disable whole archive if it was enabled for the previous inputs. + if (IsWholeArchive) + LinkArgs.push_back("--no-whole-archive"); + + // Add crt objects + LinkArgs.push_back(TCArgs.MakeArgString(HTC->GetFilePath("crtn.o"))); + const char *Exec = TCArgs.MakeArgString(getToolChain().GetLinkerPath()); + C.addCommand(std::make_unique(JA, *this, Exec, LinkArgs, Inputs)); +} diff --git a/clang/lib/Driver/ToolChains/Clang.h b/clang/lib/Driver/ToolChains/Clang.h index 0cd0e16ad7784..f1467e40746e1 100644 --- a/clang/lib/Driver/ToolChains/Clang.h +++ b/clang/lib/Driver/ToolChains/Clang.h @@ -205,6 +205,19 @@ class LLVM_LIBRARY_VISIBILITY SYCLPostLink final : public Tool { const llvm::opt::ArgList &TCArgs, const char *LinkingOutput) const override; }; + +/// Partially link objects and archives. +class LLVM_LIBRARY_VISIBILITY PartialLink final : public Tool { +public: + PartialLink(const ToolChain &TC) : Tool("partial link", "partial-link", TC) {} + + bool hasIntegratedCPP() const override { return false; } + bool hasGoodDiagnostics() const override { return true; } + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; } // end namespace tools } // end namespace driver diff --git a/clang/test/Driver/sycl-offload-intelfpga.cpp b/clang/test/Driver/sycl-offload-intelfpga.cpp index 33fd08f64091e..88ba93c94921f 100644 --- a/clang/test/Driver/sycl-offload-intelfpga.cpp +++ b/clang/test/Driver/sycl-offload-intelfpga.cpp @@ -59,7 +59,7 @@ // Create the dummy archive // RUN: echo "Dummy AOCR image" > %t.aocr // RUN: echo "void foo() {}" > %t.c -// RUN: %clang -c %t.c +// RUN: %clang -c -o %t.o %t.c // RUN: clang-offload-wrapper -o %t-aocr.bc -host=x86_64-unknown-linux-gnu -kind=sycl -target=fpga_aocr-intel-unknown-sycldevice %t.aocr // RUN: llc -filetype=obj -o %t-aocr.o %t-aocr.bc // RUN: llvm-ar crv %t.a %t.o %t-aocr.o @@ -115,7 +115,7 @@ // Create the dummy archive // RUN: echo "Dummy AOCX image" > %t.aocx // RUN: echo "void foo() {}" > %t.c -// RUN: %clang -c %t.c +// RUN: %clang -c -o %t.o %t.c // RUN: clang-offload-wrapper -o %t-aocx.bc -host=x86_64-unknown-linux-gnu -kind=sycl -target=fpga_aocx-intel-unknown-sycldevice %t.aocx // RUN: llc -filetype=obj -o %t-aocx.o %t-aocx.bc // RUN: llvm-ar crv %t_aocx.a %t.o %t-aocx.o @@ -123,8 +123,11 @@ // RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCX-PHASES %s // RUN: %clang_cl -fsycl -fintelfpga %t_aocx.a -ccc-print-phases 2>&1 \ // RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCX-PHASES %s -// CHK-FPGA-AOCX-PHASES: 0: input, "{{.*}}", object, (host-sycl) +// CHK-FPGA-AOCX-PHASES: 0: input, "{{.*}}", fpga_aocx, (host-sycl) // CHK-FPGA-AOCX-PHASES: 1: linker, {0}, image, (host-sycl) +// CHK-FPGA-AOCX-PHASES: 2: clang-offload-unbundler, {0}, fpga_aocx +// CHK-FPGA-AOCX-PHASES: 3: clang-offload-wrapper, {2}, object, (device-sycl) +// CHK-FPGA-AOCX-PHASES: 4: offload, "host-sycl ({{.*}}x86_64{{.*}})" {1}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice{{(-coff)?}})" {3}, image // RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -fintelfpga %t_aocx.a -### 2>&1 \ // RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCX,CHK-FPGA-AOCX-LIN %s @@ -255,47 +258,78 @@ /// -fintelfpga static lib (aoco) // RUN: echo "Dummy AOCO image" > %t.aoco // RUN: echo "void foo() {}" > %t.c -// RUN: %clang -c %t.c +// RUN: echo "void foo2() {}" > %t2.c +// RUN: %clang -c -o %t.o %t.c +// RUN: %clang -fsycl -c -o %t2.o %t2.c +// RUN: %clang_cl -fsycl -c -o %t2_cl.o %t2.c // RUN: clang-offload-wrapper -o %t-aoco.bc -host=x86_64-unknown-linux-gnu -kind=sycl -target=fpga_aoco-intel-unknown-sycldevice %t.aoco // RUN: llc -filetype=obj -o %t-aoco.o %t-aoco.bc -// RUN: llvm-ar crv %t_aoco.a %t.o %t-aoco.o -// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -fintelfpga -foffload-static-lib=%t_aocx.a %s -### -ccc-print-phases 2>&1 \ -// RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCO-PHASES,CHK-FPGA-AOCO-PHASES-LIN %s -// RUN: %clang_cl -fsycl -fintelfpga -foffload-static-lib=%t_aoco.a %s -### -ccc-print-phases 2>&1 \ -// RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCO-PHASES,CHK-FPGA-AOCO-PHASES-WIN %s +// RUN: llvm-ar crv %t_aoco.a %t.o %t2.o %t-aoco.o +// RUN: llvm-ar crv %t_aoco_cl.a %t.o %t2_cl.o %t-aoco.o +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -fintelfpga -foffload-static-lib=%t_aoco.a %s -### -ccc-print-phases 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-FPGA-AOCO-PHASES %s // CHK-FPGA-AOCO-PHASES: 0: input, "[[INPUTA:.+\.a]]", object, (host-sycl) // CHK-FPGA-AOCO-PHASES: 1: input, "[[INPUTCPP:.+\.cpp]]", c++, (host-sycl) // CHK-FPGA-AOCO-PHASES: 2: preprocessor, {1}, c++-cpp-output, (host-sycl) // CHK-FPGA-AOCO-PHASES: 3: input, "[[INPUTCPP]]", c++, (device-sycl) // CHK-FPGA-AOCO-PHASES: 4: preprocessor, {3}, c++-cpp-output, (device-sycl) // CHK-FPGA-AOCO-PHASES: 5: compiler, {4}, sycl-header, (device-sycl) -// CHK-FPGA-AOCO-PHASES-LIN: 6: offload, "host-sycl (x86_64-unknown-linux-gnu)" {2}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice)" {5}, c++-cpp-output -// CHK-FPGA-AOCO-PHASES-WIN: 6: offload, "host-sycl (x86_64-pc-windows-msvc)" {2}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice-coff)" {5}, c++-cpp-output +// CHK-FPGA-AOCO-PHASES: 6: offload, "host-sycl (x86_64-unknown-linux-gnu)" {2}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice)" {5}, c++-cpp-output // CHK-FPGA-AOCO-PHASES: 7: compiler, {6}, ir, (host-sycl) // CHK-FPGA-AOCO-PHASES: 8: backend, {7}, assembler, (host-sycl) // CHK-FPGA-AOCO-PHASES: 9: assembler, {8}, object, (host-sycl) // CHK-FPGA-AOCO-PHASES: 10: linker, {0, 9}, image, (host-sycl) // CHK-FPGA-AOCO-PHASES: 11: compiler, {4}, ir, (device-sycl) // CHK-FPGA-AOCO-PHASES: 12: input, "[[INPUTA]]", archive -// CHK-FPGA-AOCO-PHASES-LIN: 13: clang-offload-unbundler, {9, 12}, object +// CHK-FPGA-AOCO-PHASES: 13: partial-link, {9, 12}, object +// CHK-FPGA-AOCO-PHASES: 14: clang-offload-unbundler, {13}, object +// CHK-FPGA-AOCO-PHASES: 15: linker, {11, 14}, ir, (device-sycl) +// CHK-FPGA-AOCO-PHASES: 16: llvm-spirv, {15}, spirv, (device-sycl) +// CHK-FPGA-AOCO-PHASES: 17: input, "[[INPUTA]]", fpga_aoco +// CHK-FPGA-AOCO-PHASES: 18: clang-offload-unbundler, {17}, fpga_aoco +// CHK-FPGA-AOCO-PHASES: 19: backend-compiler, {16, 18}, fpga_aocx, (device-sycl) +// CHK-FPGA-AOCO-PHASES: 20: clang-offload-wrapper, {19}, object, (device-sycl) +// CHK-FPGA-AOCO-PHASES: 21: offload, "host-sycl (x86_64-unknown-linux-gnu)" {10}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice)" {20}, image + +/// FPGA AOCO Windows phases check +// RUN: %clang_cl -fsycl -fintelfpga -foffload-static-lib=%t_aoco_cl.a %s -### -ccc-print-phases 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCO-PHASES-WIN %s +// CHK-FPGA-AOCO-PHASES-WIN: 0: input, "{{.*}}", object, (host-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 1: input, "[[INPUTSRC:.+\.cpp]]", c++, (host-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 2: preprocessor, {1}, c++-cpp-output, (host-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 3: input, "[[INPUTSRC]]", c++, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 4: preprocessor, {3}, c++-cpp-output, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 5: compiler, {4}, sycl-header, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 6: offload, "host-sycl (x86_64-pc-windows-msvc)" {2}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice-coff)" {5}, c++-cpp-output +// CHK-FPGA-AOCO-PHASES-WIN: 7: compiler, {6}, ir, (host-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 8: backend, {7}, assembler, (host-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 9: assembler, {8}, object, (host-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 10: linker, {0, 9}, image, (host-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 11: compiler, {4}, ir, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 12: input, "[[INPUTA:.+\.a]]", archive // CHK-FPGA-AOCO-PHASES-WIN: 13: clang-offload-unbundler, {12}, archive -// CHK-FPGA-AOCO-PHASES: 14: linker, {11, 13}, ir, (device-sycl) -// CHK-FPGA-AOCO-PHASES: 15: llvm-spirv, {14}, spirv, (device-sycl) -// CHK-FPGA-AOCO-PHASES: 16: backend-compiler, {15}, fpga_aocx, (device-sycl) -// CHK-FPGA-AOCO-PHASES: 17: clang-offload-wrapper, {16}, object, (device-sycl) -// CHK-FPGA-AOCO-PHASES-LIN: 18: offload, "host-sycl (x86_64-unknown-linux-gnu)" {10}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice)" {17}, image -// CHK-FPGA-AOCO-PHASES-WIN: 18: offload, "host-sycl (x86_64-pc-windows-msvc)" {10}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice-coff)" {17}, image +// CHK-FPGA-AOCO-PHASES-WIN: 14: linker, {11, 13}, ir, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 15: llvm-spirv, {14}, spirv, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 16: input, "[[INPUTA]]", fpga_aoco +// CHK-FPGA-AOCO-PHASES-WIN: 17: clang-offload-unbundler, {16}, fpga_aoco +// CHK-FPGA-AOCO-PHASES-WIN: 18: backend-compiler, {15, 17}, fpga_aocx, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 19: clang-offload-wrapper, {18}, object, (device-sycl) +// CHK-FPGA-AOCO-PHASES-WIN: 20: offload, "host-sycl (x86_64-pc-windows-msvc)" {10}, "device-sycl (spir64_fpga-unknown-unknown-sycldevice-coff)" {19}, image /// aoco test, checking tools // RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -fintelfpga -foffload-static-lib=%t_aoco.a -### %s 2>&1 \ // RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCO,CHK-FPGA-AOCO-LIN %s -// RUN: %clang_cl -fsycl -fintelfpga -foffload-static-lib=%t_aoco.a -### %s 2>&1 \ +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -fintelfpga %t_aoco.a -### %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCO,CHK-FPGA-AOCO-LIN %s +// RUN: %clang_cl -fsycl -fintelfpga -foffload-static-lib=%t_aoco_cl.a -### %s 2>&1 \ +// RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCO,CHK-FPGA-AOCO-WIN %s +// RUN: %clang_cl -fsycl -fintelfpga %t_aoco_cl.a -### %s 2>&1 \ // RUN: | FileCheck -check-prefixes=CHK-FPGA-AOCO,CHK-FPGA-AOCO-WIN %s // CHK-FPGA-AOCO-LIN: clang-offload-bundler{{.*}} "-type=ao" "-targets=sycl-fpga_aoco-intel-unknown-sycldevice" "-inputs=[[INPUTLIB:.+\.a]]" "-check-section" // CHK-FPGA-AOCO-LIN: clang{{.*}} "-emit-obj" {{.*}} "-o" "[[HOSTOBJ:.+\.o]]" -// CHK-FPGA-AOCO-LIN: ld{{.*}} "-r" "-o" "[[PARTLINKOBJ:.+\.o]]" "{{.*}}crt1.o" "{{.*}}crti.o" "[[HOSTOBJ]]" "[[INPUTLIB]]" "{{.*}}crtn.o" +// CHK-FPGA-AOCO-LIN: ld{{.*}} "-r" "-o" "[[PARTLINKOBJ:.+\.o]]" "{{.*}}crt1.o" "{{.*}}crti.o" {{.*}} "[[HOSTOBJ]]" "[[INPUTLIB]]" "{{.*}}crtn.o" // CHK-FPGA-AOCO-LIN: clang-offload-bundler{{.*}} "-type=oo" "-targets=sycl-spir64_fpga-unknown-unknown-sycldevice" "-inputs=[[PARTLINKOBJ]]" "-outputs={{.*}}" "-unbundle" -// CHK-FPGA-AOCO-WIN: clang-offload-bundler{{.*}} "-type=aoo" "-targets=sycl-spir64_fpga-unknown-unknown-sycldevice-coff" "-inputs=[[INPUTLIB:.+\.a]]" "-outputs={{.*}}" "-unbundle" +// CHK-FPGA-AOCO-WIN: clang-offload-bundler{{.*}} "-type=aoo" "-targets=sycl-spir64_fpga-unknown-unknown-sycldevice{{(-coff)?}}" "-inputs=[[INPUTLIB:.+\.a]]" "-outputs={{.*}}" "-unbundle" // CHK-FPGA-AOCO: llvm-link{{.*}} "@{{.*}}" "-o" "[[LINKEDBC:.+\.bc]]" // CHK-FPGA-AOCO: llvm-spirv{{.*}} "-o" "[[TARGSPV:.+\.spv]]" {{.*}} "[[LINKEDBC]]" // CHK-FPGA-AOCO: clang-offload-bundler{{.*}} "-type=aoo" "-targets=sycl-fpga_aoco-intel-unknown-sycldevice" "-inputs=[[INPUTLIB]]" "-outputs=[[AOCOLIST:.+\.txt]]" "-unbundle" @@ -304,7 +338,7 @@ // CHK-FPGA-AOCO-LIN: llc{{.*}} "-filetype=obj" "-o" "[[FINALOBJL:.+\.o]]" "[[FINALBC]]" // CHK-FPGA-AOCO-WIN: llc{{.*}} "-filetype=obj" "-o" "[[FINALOBJW:.+\.obj]]" "[[FINALBC]]" // CHK-FPGA-AOCO-LIN: ld{{.*}} "[[INPUTLIB]]" {{.*}} "[[FINALOBJL]]" -// CHK-FPGA-AOCO-WIN: link.exe{{.*}} "-defaultlib:[[INPUTLIB]]" {{.*}} "[[FINALOBJW]]" +// CHK-FPGA-AOCO-WIN: link.exe{{.*}} "{{.*}}[[INPUTLIB]]" {{.*}} "[[FINALOBJW]]" // TODO: SYCL specific fail - analyze and enable // XFAIL: windows-msvc diff --git a/clang/test/Driver/sycl-offload-static-lib-2.cpp b/clang/test/Driver/sycl-offload-static-lib-2.cpp new file mode 100644 index 0000000000000..0fd43773dec6a --- /dev/null +++ b/clang/test/Driver/sycl-offload-static-lib-2.cpp @@ -0,0 +1,108 @@ +/// +/// Perform several driver tests for SYCL offloading involving static libs +/// +// REQUIRES: clang-driver +// REQUIRES: x86-registered-target + +/// ########################################################################### + +/// test behaviors of passing a fat static lib +// Build a fat static lib that will be used for all tests +// RUN: echo "void foo(void) {}" > %t1.cpp +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl %t1.cpp -c -o %t1_bundle.o +// RUN: llvm-ar crv %t.a %t1_bundle.o +// +// RUN: touch %t.a +// RUN: touch %t.o +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -L/dummy/dir %t.a -### %t.o 2>&1 \ +// RUN: | FileCheck %s -check-prefix=STATIC_LIB +// STATIC_LIB: ld{{(.exe)?}}" "-r" "-o" {{.*}} "[[INPUT:.+\.o]]" "-L/dummy/dir"{{.*}} "[[INPUT:.+\.a]]" +// STATIC_LIB: clang-offload-bundler{{.*}} "-type=oo" +// STATIC_LIB: llvm-link{{.*}} "@{{.*}}" + +/// ########################################################################### + +/// test behaviors of fat static lib with multiple objects +// RUN: touch %t.a +// RUN: touch %t-1.o +// RUN: touch %t-2.o +// RUN: touch %t-3.o +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl %t.a -### %t-1.o %t-2.o %t-3.o 2>&1 \ +// RUN: | FileCheck %s -check-prefix=STATIC_LIB_MULTI_O +// STATIC_LIB_MULTI_O: ld{{(.exe)?}}" "-r" "-o" {{.*}} "[[INPUT:.+\-1.o]]" "[[INPUT:.+\-2.o]]" "[[INPUT:.+\-3.o]]" "[[INPUT:.+\.a]]" +// STATIC_LIB_MULTI_O: clang-offload-bundler{{.*}} "-type=oo" +// STATIC_LIB_MULTI_O: llvm-link{{.*}} "@{{.*}}" + +/// ########################################################################### + +/// test behaviors of fat static lib from source +// RUN: touch %t.a +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl %t.a -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=STATIC_LIB_SRC +// STATIC_LIB_SRC: ld{{(.exe)?}}" "-r" "-o" {{.*}} "[[INPUT:.+\.a]]" +// STATIC_LIB_SRC: clang-offload-bundler{{.*}} "-type=oo" +// STATIC_LIB_SRC: llvm-link{{.*}} "@{{.*}}" + +/// ########################################################################### + +// RUN: touch %t.a +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl %t.a -o output_name -lOpenCL -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=STATIC_LIB_SRC2 +// STATIC_LIB_SRC2: ld{{(.exe)?}}" "-r" "-o" {{.*}} "[[INPUT:.+\.a]]" +// STATIC_LIB_SRC2: clang-offload-bundler{{.*}} "-type=oo" +// STATIC_LIB_SRC2: llvm-link{{.*}} "@{{.*}}" +// STATIC_LIB_SRC2: ld{{(.exe)?}}" {{.*}} "-o" "output_name" {{.*}} "-lOpenCL" + +/// ########################################################################### + +// RUN: touch %t.a +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl %t.a -o output_name -lstdc++ -z relro -### %s 2>&1 \ +// RUN: | FileCheck %s -check-prefix=STATIC_LIB_SRC3 +// STATIC_LIB_SRC3: ld{{(.exe)?}}" "-r" "-o" {{.*}} "[[INPUT:.+\.a]]" +// STATIC_LIB_SRC3: clang-offload-bundler{{.*}} "-type=oo" +// STATIC_LIB_SRC3: llvm-link{{.*}} "@{{.*}}" +// STATIC_LIB_SRC3: ld{{(.exe)?}}" {{.*}} "-o" "output_name" {{.*}} "-lstdc++" "-z" "relro" + +/// ########################################################################### + +/// test behaviors of -Wl,--whole-archive staticlib.a -Wl,--no-whole-archive +/// also test behaviors of -Wl,@arg with the above arguments +// RUN: touch %t.a +// RUN: touch %t_2.a +// RUN: touch %t.o +// RUN: echo "--whole-archive %t.a %t_2.a --no-whole-archive" > %t.arg +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -L/dummy/dir %t.o -Wl,--whole-archive %t.a %t_2.a -Wl,--no-whole-archive -### 2>&1 \ +// RUN: | FileCheck %s -check-prefixes=WHOLE_STATIC_LIB,WHOLE_STATIC_LIB_1 +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -L/dummy/dir %t.o -Wl,@%t.arg -### 2>&1 \ +// RUN: | FileCheck %s -check-prefixes=WHOLE_STATIC_LIB,WHOLE_STATIC_LIB_2 -DARGFILE=%t.arg +// WHOLE_STATIC_LIB: ld{{(.exe)?}}" "-r" "-o" "[[INPUT:.+\.o]]" "{{.*}}crt1.o" "{{.*}}crti.o" "-L/dummy/dir" {{.*}} "[[INPUTO:.+\.o]]" "--whole-archive" "[[INPUTA:.+\.a]]" "[[INPUTB:.+\.a]]" "--no-whole-archive" "{{.*}}crtn.o" +// WHOLE_STATIC_LIB: clang-offload-bundler{{.*}} "-type=oo" {{.*}} "-inputs=[[INPUT]]" +// WHOLE_STATIC_LIB: llvm-link{{.*}} "@{{.*}}" +// WHOLE_STATIC_LIB: llvm-spirv{{.*}} +// WHOLE_STATIC_LIB: clang-offload-wrapper{{.*}} +// WHOLE_STATIC_LIB: llc{{.*}} +// WHOLE_STATIC_LIB_1: ld{{.*}} "[[INPUTO]]" "--whole-archive" "[[INPUTA]]" "[[INPUTB]]" "--no-whole-archive" +// WHOLE_STATIC_LIB_2: ld{{.*}} "[[INPUTO]]" "@[[ARGFILE]]" + +/// test -Wl, behaviors for special case handling of -z and -rpath +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -L/dummy/dir %t.o -Wl,-rpath,nopass -Wl,-z,nopass %t.a %t_2.a -### 2>&1 \ +// RUN: | FileCheck %s -check-prefixes=WL_CHECK +// WL_CHECK-NOT: ld{{(.exe)?}}" "-r" {{.*}} "{{.*}}crt1.o" "{{.*}}crti.o" "-L/dummy/dir" {{.*}} "nopass" {{.*}} "{{.*}}crtn.o" +// WL_CHECK: ld{{.*}}" "-rpath" "nopass" "-z" "nopass" + +/// ########################################################################### + +/// test behaviors of static lib with no source/object +// RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -L/dummy/dir %t.a -### 2>&1 \ +// RUN: | FileCheck %s -check-prefixes=STATIC_LIB_NOSRC +// STATIC_LIB_NOSRC: clang-offload-bundler{{.*}} "-type=ao" "-targets=host-x86_64-unknown-linux-gnu" "-inputs=[[INPUTLIB:.+\.a]]" "-check-section" +// STATIC_LIB_NOSRC: ld{{.*}} "-r" "-o" "[[PARTIALOBJ:.+\.o]]" "{{.*}}crt1.o" {{.*}} "-L/dummy/dir" {{.*}} "[[INPUTLIB]]" +// STATIC_LIB_NOSRC: clang-offload-bundler{{.*}} "-type=oo" "-targets=sycl-spir64-unknown-unknown-sycldevice" "-inputs=[[PARTIALOBJ]]" "-outputs=[[DEVICELIST:.+\.txt]]" "-unbundle" +// STATIC_LIB_NOSRC: llvm-link{{.*}} "@[[DEVICELIST]]" "-o" "[[BCFILE:.+\.bc]]" +// STATIC_LIB_NOSRC: llvm-spirv{{.*}} "-o" "[[SPVFILE:.+\.spv]]" {{.*}} "[[BCFILE]]" +// STATIC_LIB_NOSRC: clang-offload-wrapper{{.*}} "-o=[[BCFILE2:.+\.bc]]" "-host=x86_64-unknown-linux-gnu" "-target=spir64" "-kind=sycl" "[[SPVFILE]]" +// STATIC_LIB_NOSRC: llc{{.*}} "-filetype=obj" "-o" "[[FINALOBJ:.+\.o]]" "[[BCFILE2]]" +// STATIC_LIB_NOSRC: ld{{.*}} "-L/dummy/dir" {{.*}} "[[INPUTLIB]]" "[[FINALOBJ]]" + +// fails on Windows due to the argument file to -Wl and directory separators +// XFAIL: windows-msvc diff --git a/clang/test/Driver/sycl-offload-static-lib.cpp b/clang/test/Driver/sycl-offload-static-lib.cpp index 897c34cf2bef2..3239170f9ab59 100644 --- a/clang/test/Driver/sycl-offload-static-lib.cpp +++ b/clang/test/Driver/sycl-offload-static-lib.cpp @@ -15,6 +15,14 @@ // FOFFLOAD_STATIC_LIB: clang-offload-bundler{{.*}} "-type=oo" // FOFFLOAD_STATIC_LIB: llvm-link{{.*}} "@{{.*}}" +/// Use of -foffload-static-lib and -foffload-whole-static-lib are deprecated +// RUN: touch dummy.a +// RUN: %clangxx -fsycl -foffload-static-lib=dummy.a -foffload-whole-static-lib=dummy.a -### 2>&1 \ +// RUN: | FileCheck %s -check-prefix=FOFFLOAD_STATIC_LIB_DEPRECATED +// RUN: %clang_cl -fsycl -foffload-static-lib=dummy.a -foffload-whole-static-lib=dummy.a -### 2>&1 \ +// RUN: | FileCheck %s -check-prefix=FOFFLOAD_STATIC_LIB_DEPRECATED +// FOFFLOAD_STATIC_LIB_DEPRECATED: option '-foffload-whole-static-lib=dummy.a' is deprecated, use 'dummy.a' directly instead + /// ########################################################################### /// test behaviors of -foffload-static-lib= with multiple objects @@ -48,11 +56,12 @@ // FOFFLOAD_STATIC_LIB_SRC: 10: linker, {0, 9}, image, (host-sycl) // FOFFLOAD_STATIC_LIB_SRC: 11: compiler, {4}, ir, (device-sycl) // FOFFLOAD_STATIC_LIB_SRC: 12: input, "[[INPUTA]]", archive -// FOFFLOAD_STATIC_LIB_SRC: 13: clang-offload-unbundler, {9, 12}, object -// FOFFLOAD_STATIC_LIB_SRC: 14: linker, {11, 13}, ir, (device-sycl) -// FOFFLOAD_STATIC_LIB_SRC: 15: llvm-spirv, {14}, spirv, (device-sycl) -// FOFFLOAD_STATIC_LIB_SRC: 16: clang-offload-wrapper, {15}, object, (device-sycl) -// FOFFLOAD_STATIC_LIB_SRC: 17: offload, "host-sycl (x86_64-unknown-linux-gnu)" {10}, "device-sycl (spir64-unknown-unknown-sycldevice)" {16}, image +// FOFFLOAD_STATIC_LIB_SRC: 13: partial-link, {9, 12}, object +// FOFFLOAD_STATIC_LIB_SRC: 14: clang-offload-unbundler, {13}, object +// FOFFLOAD_STATIC_LIB_SRC: 15: linker, {11, 14}, ir, (device-sycl) +// FOFFLOAD_STATIC_LIB_SRC: 16: llvm-spirv, {15}, spirv, (device-sycl) +// FOFFLOAD_STATIC_LIB_SRC: 17: clang-offload-wrapper, {16}, object, (device-sycl) +// FOFFLOAD_STATIC_LIB_SRC: 18: offload, "host-sycl (x86_64-unknown-linux-gnu)" {10}, "device-sycl (spir64-unknown-unknown-sycldevice)" {17}, image /// ########################################################################### @@ -92,7 +101,7 @@ // RUN: touch %t.o // RUN: %clangxx -target x86_64-unknown-linux-gnu -fsycl -L/dummy/dir -foffload-whole-static-lib=%t.a -foffload-whole-static-lib=%t_2.a -### %t.o 2>&1 \ // RUN: | FileCheck %s -check-prefix=FOFFLOAD_WHOLE_STATIC_LIB -// FOFFLOAD_WHOLE_STATIC_LIB: ld{{(.exe)?}}" "-r" "-o" "[[INPUT:.+\.o]]" "{{.*}}crt1.o" "{{.*}}crti.o" "-L/dummy/dir" "[[INPUTO:.+\.o]]" "--whole-archive" "[[INPUTA:.+\.a]]" "[[INPUTB:.+\.a]]" "--no-whole-archive" "{{.*}}crtn.o" +// FOFFLOAD_WHOLE_STATIC_LIB: ld{{(.exe)?}}" "-r" "-o" "[[INPUT:.+\.o]]" "{{.*}}crt1.o" "{{.*}}crti.o" "-L/dummy/dir" {{.*}} "[[INPUTO:.+\.o]]" "--whole-archive" "[[INPUTA:.+\.a]]" "[[INPUTB:.+\.a]]" "--no-whole-archive" "{{.*}}crtn.o" // FOFFLOAD_WHOLE_STATIC_LIB: clang-offload-bundler{{.*}} "-type=oo" {{.*}} "-inputs=[[INPUT]]" // FOFFLOAD_WHOLE_STATIC_LIB: llvm-link{{.*}} "@{{.*}}" // FOFFLOAD_WHOLE_STATIC_LIB: llvm-spirv{{.*}} @@ -111,8 +120,9 @@ // FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 1: linker, {0}, image, (host-sycl) // FOFFLOAD_STATIC_LIB_NOSRC_PHASES_1: 2: input, "[[INPUTA]]", archive // FOFFLOAD_STATIC_LIB_NOSRC_PHASES_2: 2: input, "[[INPUTA]]", wholearchive -// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 3: clang-offload-unbundler, {2}, object -// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 4: linker, {3}, ir, (device-sycl) -// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 5: llvm-spirv, {4}, spirv, (device-sycl) -// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 6: clang-offload-wrapper, {5}, object, (device-sycl) -// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 7: offload, "host-sycl (x86_64-unknown-linux-gnu)" {1}, "device-sycl (spir64-unknown-unknown-sycldevice)" {6}, image +// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 3: partial-link, {2}, object +// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 4: clang-offload-unbundler, {3}, object +// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 5: linker, {4}, ir, (device-sycl) +// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 6: llvm-spirv, {5}, spirv, (device-sycl) +// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 7: clang-offload-wrapper, {6}, object, (device-sycl) +// FOFFLOAD_STATIC_LIB_NOSRC_PHASES: 8: offload, "host-sycl (x86_64-unknown-linux-gnu)" {1}, "device-sycl (spir64-unknown-unknown-sycldevice)" {7}, image diff --git a/clang/test/Driver/sycl-offload-win.c b/clang/test/Driver/sycl-offload-win.c index 5192e12f45d1e..db65b5d70c113 100644 --- a/clang/test/Driver/sycl-offload-win.c +++ b/clang/test/Driver/sycl-offload-win.c @@ -6,8 +6,10 @@ // REQUIRES: x86-registered-target /// Test behaviors of -foffload-static-lib= with single object. -// RUN: touch %t.lib -// RUN: touch %t.obj +// Build the offload library that is used for the tests. +// RUN: echo "void foo() {}" > %t.c +// RUN: %clang_cl -fsycl -c -Fo%t.obj %t.c +// RUN: llvm-ar cr %t.lib %t.obj // RUN: %clang --target=x86_64-pc-windows-msvc -fsycl -foffload-static-lib=%t.lib %t.obj -### 2>&1 \ // RUN: | FileCheck -DOBJ=%t.obj -DLIB=%t.lib %s -check-prefixes=FOFFLOAD_STATIC_LIB,FOFFLOAD_STATIC_LIB_DEFAULT // RUN: %clang_cl --target=x86_64-pc-windows-msvc -fsycl -foffload-static-lib=%t.lib %t.obj -### 2>&1 \ @@ -40,8 +42,8 @@ /// ########################################################################### /// Test behaviors with multiple -foffload-static-lib= options. -// RUN: touch %t1.lib -// RUN: touch %t2.lib +// RUN: cp %t.lib %t1.lib +// RUN: cp %t.lib %t2.lib // RUN: touch %t.obj // RUN: %clang --target=x86_64-pc-windows-msvc -fsycl -foffload-static-lib=%t1.lib -foffload-static-lib=%t2.lib %t.obj -### 2>&1 \ // RUN: | FileCheck -DOBJ=%t.obj -DLIB1=%t1.lib -DLIB2=%t2.lib %s -check-prefixes=FOFFLOAD_STATIC_MULTI_LIB,FOFFLOAD_STATIC_MULTI_LIB_DEFAULT