From d50139d65c7debad5647bd95ec87f5904dae69ad Mon Sep 17 00:00:00 2001 From: Michael Toguchi Date: Thu, 28 Mar 2019 15:10:00 -0700 Subject: [PATCH] [SYCL] Add driver support for fat archives Signed-off-by: Michael Toguchi --- clang/include/clang/Driver/Options.td | 1 + clang/lib/Driver/Driver.cpp | 11 +++++++++ clang/lib/Driver/ToolChains/Clang.cpp | 31 +++++++++++++++++++++--- clang/lib/Driver/ToolChains/Gnu.cpp | 28 ++++++++++++++++++++- clang/lib/Driver/ToolChains/SYCL.cpp | 35 +++++++++++++++++++++------ clang/lib/Driver/ToolChains/SYCL.h | 1 + clang/test/Driver/openmp-offload.c | 11 +++++++++ clang/test/Driver/sycl-offload.c | 12 +++++++++ 8 files changed, 118 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index ccddb3d224c91..5409a2a290a15 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1554,6 +1554,7 @@ def fobjc_nonfragile_abi : Flag<["-"], "fobjc-nonfragile-abi">, Group; def fno_objc_nonfragile_abi : Flag<["-"], "fno-objc-nonfragile-abi">, Group; def fobjc_sender_dependent_dispatch : Flag<["-"], "fobjc-sender-dependent-dispatch">, Group; +def foffload_static_lib_EQ : CommaJoined<["-"], "foffload-static-lib=">, Flags<[DriverOption]>; def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group; def fopenmp : Flag<["-"], "fopenmp">, Group, Flags<[CC1Option, NoArgumentUnused]>, HelpText<"Parse OpenMP pragmas and generate parallel code.">; diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 87d76d58bf767..2305501199bcb 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -3131,6 +3131,17 @@ class OffloadingActionBuilder final { // If this is an unbundling action use it as is for each SYCL toolchain. if (auto *UA = dyn_cast(HostAction)) { SYCLDeviceActions.clear(); + auto *IA = cast(UA->getInputs().back()); + std::string FileName = IA->getInputArg().getAsString(Args); + // Check if the type of the file is the same as the action. Do not + // unbundle it if it is not. Do not unbundle .so files, for example, + // which are not object files. + if (IA->getType() == types::TY_Object && + (!llvm::sys::path::has_extension(FileName) || + types::lookupTypeForExtension( + llvm::sys::path::extension(FileName).drop_front()) != + types::TY_Object)) + return ABRT_Inactive; for (unsigned I = 0; I < ToolChains.size(); ++I) { SYCLDeviceActions.push_back(UA); UA->registerDependentActionInfo( diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index eb0ee7ecb8ee8..43018bbf72784 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6308,10 +6308,35 @@ void OffloadBundler::ConstructJobMultipleOutputs( assert(Inputs.size() == 1 && "Expecting to unbundle a single file!"); InputInfo Input = Inputs.front(); + const char *TypeArg = types::getTypeTempSuffix(Input.getType()); + const char *InputFileName = Input.getFilename(); + + // For objects, we have initial support for fat archives (archives which + // contain bundled objects). We will perform partial linking against the + // object and specific offload target archives which will be sent to the + // unbundler to produce a list of target objects. + if (Input.getType() == types::TY_Object && + TCArgs.hasArg(options::OPT_foffload_static_lib_EQ)) { + 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); + // Input files consist of fat libraries and the object to be unbundled. + LinkArgs.push_back(Input.getFilename()); + for (const auto& A : + TCArgs.getAllArgValues(options::OPT_foffload_static_lib_EQ)) + LinkArgs.push_back(TCArgs.MakeArgString(A)); + const char *Exec = TCArgs.MakeArgString(getToolChain().GetLinkerPath()); + C.addCommand(llvm::make_unique(JA, *this, Exec, LinkArgs, Inputs)); + } // Get the type. - CmdArgs.push_back(TCArgs.MakeArgString( - Twine("-type=") + types::getTypeTempSuffix(Input.getType()))); + CmdArgs.push_back(TCArgs.MakeArgString(Twine("-type=") + TypeArg)); // Get the targets. SmallString<128> Triples; @@ -6336,7 +6361,7 @@ void OffloadBundler::ConstructJobMultipleOutputs( // Get bundled file command. CmdArgs.push_back( - TCArgs.MakeArgString(Twine("-inputs=") + Input.getFilename())); + TCArgs.MakeArgString(Twine("-inputs=") + InputFileName)); // Get unbundled files command. SmallString<128> UB; diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp index 209a19b6d1e67..02799a4a27962 100644 --- a/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/clang/lib/Driver/ToolChains/Gnu.cpp @@ -476,7 +476,33 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA, bool NeedsSanitizerDeps = addSanitizerRuntimes(ToolChain, Args, CmdArgs); bool NeedsXRayDeps = addXRayRuntime(ToolChain, Args, CmdArgs); - AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); + // When offloading, the input file(s) could be from unbundled partially + // linked archives. The unbundled information is a list of files and not + // an actual object/archive. Take that list and pass those to the linker + // instead of the original object. + if (JA.isDeviceOffloading(Action::OFK_OpenMP) && + Args.hasArg(options::OPT_foffload_static_lib_EQ)) { + InputInfoList UpdatedInputs; + // Go through the Inputs to the link. When an object is encountered, we + // know it is an unbundled generated list. + // FIXME - properly add objects from list to be removed when compilation is + // complete. + for (const auto &II : Inputs) { + if (II.getType() == types::TY_Object) { + // Read each line of the generated unbundle file and add them to the + // link. + std::string FileName(II.getFilename()); + const char * ArgFile = C.getArgs().MakeArgString("@" + FileName); + auto CurInput = InputInfo(types::TY_Object, ArgFile, ArgFile); + UpdatedInputs.push_back(CurInput); + } else + UpdatedInputs.push_back(II); + } + AddLinkerInputs(ToolChain, UpdatedInputs, Args, CmdArgs, JA); + } + else + AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); + // The profile runtime also needs access to system libraries. getToolChain().addProfileRTLibs(Args, CmdArgs); diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp index e7f7b38551524..e3e357af4b4a5 100644 --- a/clang/lib/Driver/ToolChains/SYCL.cpp +++ b/clang/lib/Driver/ToolChains/SYCL.cpp @@ -53,13 +53,31 @@ const char *SYCL::Linker::constructLLVMSpirvCommand(Compilation &C, } const char *SYCL::Linker::constructLLVMLinkCommand( - Compilation &C, const JobAction &JA, StringRef SubArchName, - StringRef OutputFilePrefix, + Compilation &C, const JobAction &JA, const ArgList &Args, + StringRef SubArchName, StringRef OutputFilePrefix, const llvm::opt::ArgStringList &InputFiles) const { ArgStringList CmdArgs; // Add the input bc's created by compile step. - for (const auto &II : InputFiles) - CmdArgs.push_back(II); + // When offloading, the input file(s) could be from unbundled partially + // linked archives. The unbundled information is a list of files and not + // an actual object/archive. Take that list and pass those to the linker + // instead of the original object. + if (JA.isDeviceOffloading(Action::OFK_SYCL) && + Args.hasArg(options::OPT_foffload_static_lib_EQ)) { + // Go through the Inputs to the link. When an object is encountered, we + // know it is an unbundled generated list. + // FIXME - properly add objects from list to be removed when compilation is + // complete. + for (const auto &II : InputFiles) { + // Read each line of the generated unbundle file and add them to the link. + std::string FileName(II); + CmdArgs.push_back(C.getArgs().MakeArgString("@" + FileName)); + } + } + else + for (const auto &II : InputFiles) + CmdArgs.push_back(II); + // Add an intermediate output file. CmdArgs.push_back("-o"); SmallString<128> TmpName(C.getDriver().GetTemporaryPath( @@ -77,7 +95,7 @@ const char *SYCL::Linker::constructLLVMLinkCommand( void SYCL::Linker::constructLlcCommand(Compilation &C, const JobAction &JA, const InputInfo &Output, const char *InputFileName) const { // Construct llc command. - // The output is an object file + // The output is an object file. ArgStringList LlcArgs{"-filetype=obj", "-o", Output.getFilename(), InputFileName}; SmallString<128> LlcPath(C.getDriver().Dir); @@ -88,7 +106,7 @@ void SYCL::Linker::constructLlcCommand(Compilation &C, const JobAction &JA, // For SYCL the inputs of the linker job are SPIR-V binaries and output is // a single SPIR-V binary. Input can also be bitcode when specified by -// the user +// the user. void SYCL::Linker::ConstructJob(Compilation &C, const JobAction &JA, const InputInfo &Output, const InputInfoList &Inputs, @@ -112,7 +130,8 @@ void SYCL::Linker::ConstructJob(Compilation &C, const JobAction &JA, if (!II.isFilename()) continue; if (Args.hasFlag(options::OPT_fsycl_use_bitcode, - options::OPT_fno_sycl_use_bitcode, true)) + options::OPT_fno_sycl_use_bitcode, true) || + Args.hasArg(options::OPT_foffload_static_lib_EQ)) SpirvInputs.push_back(II.getFilename()); else { const char *LLVMSpirvOutputFile = @@ -122,7 +141,7 @@ void SYCL::Linker::ConstructJob(Compilation &C, const JobAction &JA, } } const char *LLVMLinkOutputFile = - constructLLVMLinkCommand(C, JA, SubArchName, Prefix, SpirvInputs); + constructLLVMLinkCommand(C, JA, Args, SubArchName, Prefix, SpirvInputs); constructLLVMSpirvCommand(C, JA, Output, Prefix, false, LLVMLinkOutputFile); } diff --git a/clang/lib/Driver/ToolChains/SYCL.h b/clang/lib/Driver/ToolChains/SYCL.h index a67c6ea2dbfa1..fb43b9358e01c 100644 --- a/clang/lib/Driver/ToolChains/SYCL.h +++ b/clang/lib/Driver/ToolChains/SYCL.h @@ -39,6 +39,7 @@ class LLVM_LIBRARY_VISIBILITY Linker : public Tool { bool isBc, const char *InputFile) const; /// \return llvm-link output file name. const char *constructLLVMLinkCommand(Compilation &C, const JobAction &JA, + const llvm::opt::ArgList &Args, llvm::StringRef SubArchName, llvm::StringRef OutputFilePrefix, const llvm::opt::ArgStringList &InputFiles) const; diff --git a/clang/test/Driver/openmp-offload.c b/clang/test/Driver/openmp-offload.c index aee7e0dbb1d18..17ce5c9b0bf97 100644 --- a/clang/test/Driver/openmp-offload.c +++ b/clang/test/Driver/openmp-offload.c @@ -655,3 +655,14 @@ // RUN: | FileCheck -check-prefix=CHK-FOPENMP-IS-DEVICE %s // CHK-FOPENMP-IS-DEVICE: clang{{.*}} "-aux-triple" "powerpc64le-unknown-linux" {{.*}}.c" "-fopenmp-is-device" "-fopenmp-host-ir-file-path" + +/// ########################################################################### + +/// test behaviors of -foffload-static-lib= +// RUN: touch %t.a +// RUN: touch %t.o +// RUN: %clang -fopenmp -fopenmp-targets=x86_64-pc-linux-gnu -foffload-static-lib=%t.a -### %t.o 2>&1 \ +// RUN: | FileCheck %s -check-prefix=FOFFLOAD_STATIC_LIB +// FOFFLOAD_STATIC_LIB: ld{{(.exe)?}}" "-r" "-o" {{.*}} "[[INPUT:.+\.o]]" +// FOFFLOAD_STATIC_LIB: clang-offload-bundler{{.*}} "-type=oo" +// FOFFLOAD_STATIC_LIB: ld{{.*}} "@{{.*}}" diff --git a/clang/test/Driver/sycl-offload.c b/clang/test/Driver/sycl-offload.c index 6d85f623ed90f..473aae2c12a0e 100644 --- a/clang/test/Driver/sycl-offload.c +++ b/clang/test/Driver/sycl-offload.c @@ -277,3 +277,15 @@ // RUN: %clang -fsycl -target x86_64-unknown-linux-gnu %s -o %t -### 2>&1 | FileCheck -check-prefix=CHECK-LD-SYCL %s // CHECK-LD-SYCL: "{{.*}}ld{{(.exe)?}}" // CHECK-LD-SYCL: "-lsycl" + +/// ########################################################################### + +/// test behaviors of -foffload-static-lib= +// RUN: touch %t.a +// RUN: touch %t.o +// RUN: %clang -fsycl -foffload-static-lib=%t.a -### %t.o 2>&1 \ +// RUN: | FileCheck %s -check-prefix=FOFFLOAD_STATIC_LIB +// FOFFLOAD_STATIC_LIB: ld{{(.exe)?}}" "-r" "-o" {{.*}} "[[INPUT:.+\.o]]" +// FOFFLOAD_STATIC_LIB: clang-offload-bundler{{.*}} "-type=oo" +// FOFFLOAD_STATIC_LIB: llvm-link{{.*}} "@{{.*}}" +