Index: include/clang/Driver/Action.h =================================================================== --- include/clang/Driver/Action.h +++ include/clang/Driver/Action.h @@ -160,6 +160,8 @@ /// files for each offloading kind. std::string getOffloadingFileNamePrefix(llvm::StringRef NormalizedTriple) const; + /// Return a string containing a offload kind name. + static StringRef GetOffloadKindName(OffloadKind Kind); /// Set the device offload info of this action and propagate it to its /// dependences. Index: include/clang/Driver/ToolChain.h =================================================================== --- include/clang/Driver/ToolChain.h +++ include/clang/Driver/ToolChain.h @@ -85,10 +85,12 @@ mutable std::unique_ptr Clang; mutable std::unique_ptr Assemble; mutable std::unique_ptr Link; + mutable std::unique_ptr OffloadBundler; Tool *getClang() const; Tool *getAssemble() const; Tool *getLink() const; Tool *getClangAs() const; + Tool *getOffloadBundler() const; mutable std::unique_ptr SanitizerArguments; Index: lib/Driver/Action.cpp =================================================================== --- lib/Driver/Action.cpp +++ lib/Driver/Action.cpp @@ -9,6 +9,7 @@ #include "clang/Driver/Action.h" #include "clang/Driver/ToolChain.h" +#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Regex.h" @@ -128,6 +129,22 @@ return Res; } +/// Return a string with the offload kind name. If that is not defined, we +/// assume 'host'. +llvm::StringRef Action::GetOffloadKindName(OffloadKind Kind) { + switch (Kind) { + case OFK_None: + case OFK_Host: + return "host"; + case OFK_Cuda: + return "cuda"; + case OFK_OpenMP: + return "openmp"; + + // TODO: Add other programming models here. + } +} + void InputAction::anchor() {} InputAction::InputAction(const Arg &_Input, types::ID _Type) Index: lib/Driver/ToolChain.cpp =================================================================== --- lib/Driver/ToolChain.cpp +++ lib/Driver/ToolChain.cpp @@ -239,6 +239,12 @@ return Link.get(); } +Tool *ToolChain::getOffloadBundler() const { + if (!OffloadBundler) + OffloadBundler.reset(new tools::OffloadBundler(*this)); + return OffloadBundler.get(); +} + Tool *ToolChain::getTool(Action::ActionClass AC) const { switch (AC) { case Action::AssembleJobClass: @@ -266,8 +272,7 @@ case Action::OffloadBundlingJobClass: case Action::OffloadUnbundlingJobClass: - // FIXME: Add a tool for the bundling actions. - return nullptr; + return getOffloadBundler(); } llvm_unreachable("Invalid tool kind."); Index: lib/Driver/Tools.h =================================================================== --- lib/Driver/Tools.h +++ lib/Driver/Tools.h @@ -137,6 +137,19 @@ const char *LinkingOutput) const override; }; +/// Offload bundler tool. +class LLVM_LIBRARY_VISIBILITY OffloadBundler final : public Tool { +public: + OffloadBundler(const ToolChain &TC) + : Tool("offload bundler", "clang-offload-bundler", TC) {} + + bool hasIntegratedCPP() const override { return false; } + void ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const override; +}; + /// \brief Base class for all GNU tools that provide the same behavior when /// it comes to response files support class LLVM_LIBRARY_VISIBILITY GnuTool : public Tool { Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -6768,6 +6768,72 @@ SplitDebugName(Args, Input)); } +void OffloadBundler::ConstructJob(Compilation &C, const JobAction &JA, + const InputInfo &Output, + const InputInfoList &Inputs, + const llvm::opt::ArgList &TCArgs, + const char *LinkingOutput) const { + assert(isa(JA) && "Expecting bundling job!"); + + // The bundling command looks like this: + // clang-offload-bundler -type=bc + // -targets=host-triple,openmp-triple1,openmp-triple2 + // -outputs=input_file + // -inputs=unbundle_file_host,unbundle_file_tgt1,unbundle_file_tgt2" + + ArgStringList CmdArgs; + + // Get the type. + CmdArgs.push_back(TCArgs.MakeArgString( + Twine("-type=") + types::getTypeTempSuffix(Output.getType()))); + + assert(JA.getInputs().size() == Inputs.size() && + "Not have inputs for all dependence actions??"); + + // Get the targets. + SmallString<128> Triples; + Triples += "-targets="; + for (unsigned I = 0; I < Inputs.size(); ++I) { + if (I) + Triples += ','; + + Action::OffloadKind CurKind = Action::OFK_Host; + const ToolChain *CurTC = &getToolChain(); + const Action *CurDep = JA.getInputs()[I]; + + if (const auto *OA = dyn_cast(CurDep)) { + OA->doOnEachDependence([&](Action *A, const ToolChain *TC, const char *) { + CurKind = A->getOffloadingDeviceKind(); + CurTC = TC; + }); + } + Triples += Action::GetOffloadKindName(CurKind); + Triples += '-'; + Triples += CurTC->getTriple().normalize(); + } + CmdArgs.push_back(TCArgs.MakeArgString(Triples)); + + // Get bundled file command. + CmdArgs.push_back( + TCArgs.MakeArgString(Twine("-outputs=") + Output.getFilename())); + + // Get unbundled files command. + SmallString<128> UB; + UB += "-inputs="; + for (unsigned I = 0; I < Inputs.size(); ++I) { + if (I) + UB += ','; + UB += Inputs[I].getFilename(); + } + CmdArgs.push_back(TCArgs.MakeArgString(UB)); + + // All the inputs are encoded as commands. + C.addCommand(llvm::make_unique( + JA, *this, + TCArgs.MakeArgString(getToolChain().GetProgramPath(getShortName())), + CmdArgs, None)); +} + void GnuTool::anchor() {} void gcc::Common::ConstructJob(Compilation &C, const JobAction &JA, Index: test/Driver/openmp-offload.c =================================================================== --- test/Driver/openmp-offload.c +++ test/Driver/openmp-offload.c @@ -320,3 +320,37 @@ // CHK-UBUACTIONS: 14: assembler, {13}, object, (host-openmp) // CHK-UBUACTIONS: 15: clang-offload-bundler, {7, 12, 14}, object, (host-openmp) +/// ########################################################################### + +/// Check separate compilation with offloading - bundling jobs construct +// RUN: %clang -### -fopenmp -c -o %t.o -lsomelib -target powerpc64le-linux -fopenmp-targets=powerpc64le-ibm-linux-gnu,x86_64-pc-linux-gnu %s 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-BUJOBS %s +// RUN: %clang -### -fopenmp -c -o %t.o -lsomelib -target powerpc64le-linux -fopenmp-targets=powerpc64le-ibm-linux-gnu,x86_64-pc-linux-gnu %s -save-temps 2>&1 \ +// RUN: | FileCheck -check-prefix=CHK-BUJOBS-ST %s + +// Create host BC. +// CHK-BUJOBS: clang{{.*}}" "-cc1" "-triple" "powerpc64le--linux" "-emit-llvm-bc" {{.*}}"-fopenmp" {{.*}}"-o" "[[HOSTBC:.+\.bc]]" "-x" "c" "[[INPUT:.+\.c]]" "-fopenmp-targets=powerpc64le-ibm-linux-gnu,x86_64-pc-linux-gnu" + +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "powerpc64le--linux" "-E" {{.*}}"-fopenmp" {{.*}}"-o" "[[HOSTPP:.+\.i]]" "-x" "c" "[[INPUT:.+\.c]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "powerpc64le--linux" "-emit-llvm-bc" {{.*}}"-fopenmp" {{.*}}"-o" "[[HOSTBC:.+\.bc]]" "-x" "cpp-output" "[[HOSTPP]]" "-fopenmp-targets=powerpc64le-ibm-linux-gnu,x86_64-pc-linux-gnu" + +// Create target 1 object. +// CHK-BUJOBS: clang{{.*}}" "-cc1" "-triple" "powerpc64le-ibm-linux-gnu" "-emit-obj" {{.*}}"-fopenmp" {{.*}}"-o" "[[T1OBJ:.+\.o]]" "-x" "c" "[[INPUT]]" "-fopenmp-is-device" "-fopenmp-host-ir-file-path" "[[HOSTBC]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "powerpc64le-ibm-linux-gnu" "-E" {{.*}}"-fopenmp" {{.*}}"-o" "[[T1PP:.+\.i]]" "-x" "c" "[[INPUT]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "powerpc64le-ibm-linux-gnu" "-emit-llvm-bc" {{.*}}"-fopenmp" {{.*}}"-o" "[[T1BC:.+\.bc]]" "-x" "cpp-output" "[[T1PP]]" "-fopenmp-is-device" "-fopenmp-host-ir-file-path" "[[HOSTBC]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "powerpc64le-ibm-linux-gnu" "-S" {{.*}}"-fopenmp" {{.*}}"-o" "[[T1ASM:.+\.s]]" "-x" "ir" "[[T1BC]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1as" "-triple" "powerpc64le-ibm-linux-gnu" "-filetype" "obj" {{.*}}"-o" "[[T1OBJ:.+\.o]]" "[[T1ASM]]" + +// Create target 2 object. +// CHK-BUJOBS: clang{{.*}}" "-cc1" "-triple" "x86_64-pc-linux-gnu" "-emit-obj" {{.*}}"-fopenmp" {{.*}}"-o" "[[T2OBJ:.+\.o]]" "-x" "c" "[[INPUT]]" "-fopenmp-is-device" "-fopenmp-host-ir-file-path" "[[HOSTBC]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "x86_64-pc-linux-gnu" "-E" {{.*}}"-fopenmp" {{.*}}"-o" "[[T2PP:.+\.i]]" "-x" "c" "[[INPUT]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "x86_64-pc-linux-gnu" "-emit-llvm-bc" {{.*}}"-fopenmp" {{.*}}"-o" "[[T2BC:.+\.bc]]" "-x" "cpp-output" "[[T2PP]]" "-fopenmp-is-device" "-fopenmp-host-ir-file-path" "[[HOSTBC]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "x86_64-pc-linux-gnu" "-S" {{.*}}"-fopenmp" {{.*}}"-o" "[[T2ASM:.+\.s]]" "-x" "ir" "[[T2BC]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1as" "-triple" "x86_64-pc-linux-gnu" "-filetype" "obj" {{.*}}"-o" "[[T2OBJ:.+\.o]]" "[[T2ASM]]" + +// Create host object and bundle. +// CHK-BUJOBS: clang{{.*}}" "-cc1" "-triple" "powerpc64le--linux" "-emit-obj" {{.*}}"-fopenmp" {{.*}}"-o" "[[HOSTOBJ:.+\.o]]" "-x" "ir" "[[HOSTBC]]" +// CHK-BUJOBS: clang-offload-bundler" "-type=o" "-targets=openmp-powerpc64le-ibm-linux-gnu,openmp-x86_64-pc-linux-gnu,host-powerpc64le--linux" "-outputs=[[RES:.+\.o]]" "-inputs=[[T1OBJ]],[[T2OBJ]],[[HOSTOBJ]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1" "-triple" "powerpc64le--linux" "-S" {{.*}}"-fopenmp" {{.*}}"-o" "[[HOSTASM:.+\.s]]" "-x" "ir" "[[HOSTBC]]" +// CHK-BUJOBS-ST: clang{{.*}}" "-cc1as" "-triple" "powerpc64le--linux" "-filetype" "obj" {{.*}}"-o" "[[HOSTOBJ:.+\.o]]" "[[HOSTASM]]" +// CHK-BUJOBS-ST: clang-offload-bundler" "-type=o" "-targets=openmp-powerpc64le-ibm-linux-gnu,openmp-x86_64-pc-linux-gnu,host-powerpc64le--linux" "-outputs=[[RES:.+\.o]]" "-inputs=[[T1OBJ]],[[T2OBJ]],[[HOSTOBJ]]"