Index: include/clang/Driver/Action.h =================================================================== --- include/clang/Driver/Action.h +++ include/clang/Driver/Action.h @@ -66,9 +66,10 @@ DsymutilJobClass, VerifyDebugInfoJobClass, VerifyPCHJobClass, + OffloadBundlingJobClass, JobClassFirst = PreprocessJobClass, - JobClassLast = VerifyPCHJobClass + JobClassLast = OffloadBundlingJobClass }; // The offloading kind determines if this action is binded to a particular @@ -481,6 +482,18 @@ } }; +class OffloadBundlingJobAction : public JobAction { + void anchor() override; + +public: + // Offloading bundling doesn't change the type of output. + OffloadBundlingJobAction(ActionList &Inputs); + + static bool classof(const Action *A) { + return A->getKind() == OffloadBundlingJobClass; + } +}; + } // end namespace driver } // end namespace clang Index: lib/Driver/Action.cpp =================================================================== --- lib/Driver/Action.cpp +++ lib/Driver/Action.cpp @@ -36,6 +36,8 @@ case DsymutilJobClass: return "dsymutil"; case VerifyDebugInfoJobClass: return "verify-debug-info"; case VerifyPCHJobClass: return "verify-pch"; + case OffloadBundlingJobClass: + return "clang-offload-bundler"; } llvm_unreachable("invalid class"); @@ -346,3 +348,8 @@ VerifyPCHJobAction::VerifyPCHJobAction(Action *Input, types::ID Type) : VerifyJobAction(VerifyPCHJobClass, Input, Type) {} + +void OffloadBundlingJobAction::anchor() {} + +OffloadBundlingJobAction::OffloadBundlingJobAction(ActionList &Inputs) + : JobAction(OffloadBundlingJobClass, Inputs, Inputs.front()->getType()) {} Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -1550,6 +1550,9 @@ /// found. virtual bool initialize() { return false; } + /// Return true if the builder can use bundling/unbundling. + virtual bool canUseBundlerUnbundler() const { return false; } + /// Return true if this builder is valid. We have a valid builder if we have /// associated device tool chains. bool isValid() { return !ToolChains.empty(); } @@ -1874,6 +1877,26 @@ return ABRT_Success; } + void appendTopLevelActions(ActionList &AL) override { + if (OpenMPDeviceActions.empty()) + return; + + // We should always have an action for each input. + assert(OpenMPDeviceActions.size() == ToolChains.size() && + "Number of OpenMP actions and toolchains do not match."); + + // Append all device actions followed by the proper offload action. + auto TI = ToolChains.begin(); + for (auto *A : OpenMPDeviceActions) { + OffloadAction::DeviceDependences Dep; + Dep.add(*A, **TI, /*BoundArch=*/nullptr, Action::OFK_OpenMP); + AL.push_back(C.MakeAction(Dep, A->getType())); + ++TI; + } + // We no longer need the action stored in this builder. + OpenMPDeviceActions.clear(); + } + void appendLinkDependences(OffloadAction::DeviceDependences &DA) override { assert(ToolChains.size() == DeviceLinkerInputs.size() && "Toolchains and linker inputs sizes do not match."); @@ -1900,6 +1923,11 @@ DeviceLinkerInputs.resize(ToolChains.size()); return false; } + + bool canUseBundlerUnbundler() const override { + // OpenMP should use bundled files whenever possible. + return true; + } }; /// @@ -1909,6 +1937,9 @@ /// Specialized builders being used by this offloading action builder. SmallVector SpecializedBuilders; + /// Flag set to true if all valid builders allow file bundling/unbundling. + bool CanUseBundler; + public: OffloadingActionBuilder(Compilation &C, DerivedArgList &Args, const Driver::InputList &Inputs) @@ -1927,9 +1958,22 @@ // TODO: Build other specialized builders here. // - // Initialize all the builders, keeping track of errors. - for (auto *SB : SpecializedBuilders) + // Initialize all the builders, keeping track of errors. If all valid + // builders agree that we can use bundling, set the flag to true. + unsigned ValidBuilders = 0u; + unsigned ValidBuildersSupportingBundling = 0u; + for (auto *SB : SpecializedBuilders) { IsValid = IsValid && !SB->initialize(); + + // Update the counters if the builder is valid. + if (SB->isValid()) { + ++ValidBuilders; + if (SB->canUseBundlerUnbundler()) + ++ValidBuildersSupportingBundling; + } + } + CanUseBundler = + ValidBuilders && ValidBuilders == ValidBuildersSupportingBundling; } ~OffloadingActionBuilder() { @@ -2028,18 +2072,32 @@ return false; } - /// Add the offloading top level actions to the provided action list. + /// Add the offloading top level actions to the provided action list. This + /// function can replace the host action by a bundling action if the + /// programming models allow it. bool appendTopLevelActions(ActionList &AL, Action *HostAction, const Arg *InputArg) { - auto NumActions = AL.size(); - + // Get the device actions to be appended. + ActionList OffloadAL; for (auto *SB : SpecializedBuilders) { if (!SB->isValid()) continue; - SB->appendTopLevelActions(AL); + SB->appendTopLevelActions(OffloadAL); } - assert(NumActions <= AL.size() && "Expecting more actions, not less!"); + // If we can use the bundler, replace the host action by the bundling one in + // the resulting list. Otherwise, just append the device actions. + if (CanUseBundler && !OffloadAL.empty()) { + // Add the host action to the list in order to create the bundling action. + OffloadAL.push_back(HostAction); + + // We expect that the host action was just appended to the action list + // before this method was called. + assert(HostAction == AL.back() && "Host action not in the list??"); + HostAction = C.MakeAction(OffloadAL); + AL.back() = HostAction; + } else + AL.append(OffloadAL.begin(), OffloadAL.end()); // Propagate to the current host action (if any) the offload information // associated with the current input. @@ -2048,8 +2106,8 @@ /*BoundArch=*/nullptr); // If any action is added by the builders, -o is ambiguous if we have more - // than one top-level action. - if (NumActions < AL.size() && Args.hasArg(options::OPT_o) && + // than one top-level action. If a bundler is used, there is no ambiguity. + if (!CanUseBundler && !OffloadAL.empty() && Args.hasArg(options::OPT_o) && AL.size() > 1) { C.getDriver().Diag( clang::diag::err_drv_output_argument_with_multiple_files); Index: lib/Driver/ToolChain.cpp =================================================================== --- lib/Driver/ToolChain.cpp +++ lib/Driver/ToolChain.cpp @@ -263,6 +263,10 @@ case Action::VerifyPCHJobClass: case Action::BackendJobClass: return getClang(); + + case Action::OffloadBundlingJobClass: + // FIXME: Add a tool for the bundling actions. + return nullptr; } llvm_unreachable("Invalid tool kind."); Index: test/Driver/openmp-offload.c =================================================================== --- test/Driver/openmp-offload.c +++ test/Driver/openmp-offload.c @@ -239,3 +239,31 @@ // CHK-COMMANDS-ST: clang{{.*}}" "-cc1" "-triple" "powerpc64le--linux" "-S" {{.*}}"-fopenmp" {{.*}}"-o" "[[HOSTASM:.+\.s]]" "-x" "ir" "[[HOSTBC]]" // CHK-COMMANDS-ST: clang{{.*}}" "-cc1as" "-triple" "powerpc64le--linux" "-filetype" "obj" {{.*}}"-o" [[HOSTOBJ:.+\.o]]" [[HOSTASM:.+\.s]] // CHK-COMMANDS-ST: ld" {{.*}}"-o" "[[HOSTBIN:.+\.out]]" {{.*}}"-lomptarget" {{.*}}"-T" "[[HOSTLK:.+\.lk]]" + + +/// ########################################################################### + +/// Check separate compilation with offloading - bundling actions +// RUN: %clang -### -ccc-print-phases -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-BUACTIONS %s + +// CHK-BUACTIONS: 0: input, "[[INPUT:.+\.c]]", c, (host-openmp) +// CHK-BUACTIONS: 1: preprocessor, {0}, cpp-output, (host-openmp) +// CHK-BUACTIONS: 2: compiler, {1}, ir, (host-openmp) +// CHK-BUACTIONS: 3: input, "[[INPUT]]", c, (device-openmp) +// CHK-BUACTIONS: 4: preprocessor, {3}, cpp-output, (device-openmp) +// CHK-BUACTIONS: 5: compiler, {4}, ir, (device-openmp) +// CHK-BUACTIONS: 6: offload, "host-openmp (powerpc64le--linux)" {2}, "device-openmp (powerpc64le-ibm-linux-gnu)" {5}, ir +// CHK-BUACTIONS: 7: backend, {6}, assembler, (device-openmp) +// CHK-BUACTIONS: 8: assembler, {7}, object, (device-openmp) +// CHK-BUACTIONS: 9: offload, "device-openmp (powerpc64le-ibm-linux-gnu)" {8}, object +// CHK-BUACTIONS: 10: input, "[[INPUT]]", c, (device-openmp) +// CHK-BUACTIONS: 11: preprocessor, {10}, cpp-output, (device-openmp) +// CHK-BUACTIONS: 12: compiler, {11}, ir, (device-openmp) +// CHK-BUACTIONS: 13: offload, "host-openmp (powerpc64le--linux)" {2}, "device-openmp (x86_64-pc-linux-gnu)" {12}, ir +// CHK-BUACTIONS: 14: backend, {13}, assembler, (device-openmp) +// CHK-BUACTIONS: 15: assembler, {14}, object, (device-openmp) +// CHK-BUACTIONS: 16: offload, "device-openmp (x86_64-pc-linux-gnu)" {15}, object +// CHK-BUACTIONS: 17: backend, {2}, assembler, (host-openmp) +// CHK-BUACTIONS: 18: assembler, {17}, object, (host-openmp) +// CHK-BUACTIONS: 19: clang-offload-bundler, {9, 16, 18}, object, (host-openmp)