Skip to content

Commit 08a1c00

Browse files
authored
[Driver] Use clang-offload-deps tool for generating device dependencies (#2935)
This patch changes driver to use clang-offload-deps tool for generating dependence objects for SYCL and OpenMP programs when static fat libraries are used. Driver adds an additional link step on the host side to create host image which serves as an input for the clang-offload-deps tool. Dependence bitcode files produced by the tool are then compiled to objects where necessary and added to the device linker inputs. Signed-off-by: Sergey Dmitriev <[email protected]>
1 parent bfef111 commit 08a1c00

File tree

16 files changed

+415
-106
lines changed

16 files changed

+415
-106
lines changed

clang/include/clang/Driver/Action.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class Action {
7474
OffloadBundlingJobClass,
7575
OffloadUnbundlingJobClass,
7676
OffloadWrapperJobClass,
77+
OffloadDepsJobClass,
7778
SPIRVTranslatorJobClass,
7879
SPIRCheckJobClass,
7980
SYCLPostLinkJobClass,
@@ -648,6 +649,60 @@ class OffloadWrapperJobAction : public JobAction {
648649
}
649650
};
650651

652+
class OffloadDepsJobAction final : public JobAction {
653+
void anchor() override;
654+
655+
public:
656+
/// Type that provides information about the actions that depend on this
657+
/// offload deps action.
658+
struct DependentActionInfo final {
659+
/// The tool chain of the dependent action.
660+
const ToolChain *DependentToolChain = nullptr;
661+
662+
/// The bound architecture of the dependent action.
663+
StringRef DependentBoundArch;
664+
665+
/// The offload kind of the dependent action.
666+
const OffloadKind DependentOffloadKind = OFK_None;
667+
668+
DependentActionInfo(const ToolChain *DependentToolChain,
669+
StringRef DependentBoundArch,
670+
const OffloadKind DependentOffloadKind)
671+
: DependentToolChain(DependentToolChain),
672+
DependentBoundArch(DependentBoundArch),
673+
DependentOffloadKind(DependentOffloadKind) {}
674+
};
675+
676+
private:
677+
/// The host offloading toolchain that should be used with the action.
678+
const ToolChain *HostTC = nullptr;
679+
680+
/// Container that keeps information about each dependence of this deps
681+
/// action.
682+
SmallVector<DependentActionInfo, 6> DependentActionInfoArray;
683+
684+
public:
685+
OffloadDepsJobAction(const OffloadAction::HostDependence &HDep,
686+
types::ID Type);
687+
688+
/// Register information about a dependent action.
689+
void registerDependentActionInfo(const ToolChain *TC, StringRef BoundArch,
690+
OffloadKind Kind) {
691+
DependentActionInfoArray.push_back({TC, BoundArch, Kind});
692+
}
693+
694+
/// Return the information about all depending actions.
695+
ArrayRef<DependentActionInfo> getDependentActionsInfo() const {
696+
return DependentActionInfoArray;
697+
}
698+
699+
const ToolChain *getHostTC() const { return HostTC; }
700+
701+
static bool classof(const Action *A) {
702+
return A->getKind() == OffloadDepsJobClass;
703+
}
704+
};
705+
651706
class SPIRVTranslatorJobAction : public JobAction {
652707
void anchor() override;
653708

clang/include/clang/Driver/ToolChain.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class ToolChain {
144144
mutable std::unique_ptr<Tool> IfsMerge;
145145
mutable std::unique_ptr<Tool> OffloadBundler;
146146
mutable std::unique_ptr<Tool> OffloadWrapper;
147+
mutable std::unique_ptr<Tool> OffloadDeps;
147148
mutable std::unique_ptr<Tool> SPIRVTranslator;
148149
mutable std::unique_ptr<Tool> SPIRCheck;
149150
mutable std::unique_ptr<Tool> SYCLPostLink;
@@ -160,6 +161,7 @@ class ToolChain {
160161
Tool *getClangAs() const;
161162
Tool *getOffloadBundler() const;
162163
Tool *getOffloadWrapper() const;
164+
Tool *getOffloadDeps() const;
163165
Tool *getSPIRVTranslator() const;
164166
Tool *getSPIRCheck() const;
165167
Tool *getSYCLPostLink() const;

clang/lib/Driver/Action.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const char *Action::getClassName(ActionClass AC) {
4343
return "clang-offload-unbundler";
4444
case OffloadWrapperJobClass:
4545
return "clang-offload-wrapper";
46+
case OffloadDepsJobClass:
47+
return "clang-offload-deps";
4648
case SPIRVTranslatorJobClass:
4749
return "llvm-spirv";
4850
case SPIRCheckJobClass:
@@ -69,6 +71,9 @@ void Action::propagateDeviceOffloadInfo(OffloadKind OKind, const char *OArch) {
6971
// Unbundling actions use the host kinds.
7072
if (Kind == OffloadUnbundlingJobClass)
7173
return;
74+
// Deps job uses the host kinds.
75+
if (Kind == OffloadDepsJobClass)
76+
return;
7277

7378
assert((OffloadingDeviceKind == OKind || OffloadingDeviceKind == OFK_None) &&
7479
"Setting device kind to a different device??");
@@ -444,6 +449,18 @@ OffloadWrapperJobAction::OffloadWrapperJobAction(Action *Input,
444449
types::ID Type)
445450
: JobAction(OffloadWrapperJobClass, Input, Type) {}
446451

452+
void OffloadDepsJobAction::anchor() {}
453+
454+
OffloadDepsJobAction::OffloadDepsJobAction(
455+
const OffloadAction::HostDependence &HDep, types::ID Type)
456+
: JobAction(OffloadDepsJobClass, HDep.getAction(), Type),
457+
HostTC(HDep.getToolChain()) {
458+
OffloadingArch = HDep.getBoundArch();
459+
ActiveOffloadKindMask = HDep.getOffloadKinds();
460+
HDep.getAction()->propagateHostOffloadInfo(HDep.getOffloadKinds(),
461+
HDep.getBoundArch());
462+
}
463+
447464
void SPIRVTranslatorJobAction::anchor() {}
448465

449466
SPIRVTranslatorJobAction::SPIRVTranslatorJobAction(Action *Input,

clang/lib/Driver/Driver.cpp

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2883,6 +2883,9 @@ class OffloadingActionBuilder final {
28832883
/// Append linker actions generated by the builder.
28842884
virtual void appendLinkDependences(OffloadAction::DeviceDependences &DA) {}
28852885

2886+
/// Append linker actions generated by the builder.
2887+
virtual void addDeviceLinkDependencies(OffloadDepsJobAction *DA) {}
2888+
28862889
/// Initialize the builder. Return true if any initialization errors are
28872890
/// found.
28882891
virtual bool initialize() { return false; }
@@ -3605,6 +3608,24 @@ class OffloadingActionBuilder final {
36053608

36063609
void appendLinkDependences(OffloadAction::DeviceDependences &DA) override {}
36073610

3611+
void addDeviceLinkDependencies(OffloadDepsJobAction *DA) override {
3612+
for (unsigned I = 0; I < ToolChains.size(); ++I) {
3613+
// Register dependent toolchain.
3614+
DA->registerDependentActionInfo(
3615+
ToolChains[I], /*BoundArch=*/StringRef(), Action::OFK_OpenMP);
3616+
3617+
if (!ToolChains[I]->getTriple().isSPIR()) {
3618+
// Create object from the deps bitcode.
3619+
auto *BA = C.MakeAction<BackendJobAction>(DA, types::TY_PP_Asm);
3620+
auto *AA = C.MakeAction<AssembleJobAction>(BA, types::TY_Object);
3621+
3622+
// Add deps object to linker inputs.
3623+
DeviceLinkerInputs[I].push_back(AA);
3624+
} else
3625+
DeviceLinkerInputs[I].push_back(DA);
3626+
}
3627+
}
3628+
36083629
bool initialize() override {
36093630
// Get the OpenMP toolchains. If we don't get any, the action builder will
36103631
// know there is nothing to do related to OpenMP offloading.
@@ -4338,6 +4359,17 @@ class OffloadingActionBuilder final {
43384359
}
43394360
}
43404361

4362+
void addDeviceLinkDependencies(OffloadDepsJobAction *DA) override {
4363+
for (unsigned I = 0; I < ToolChains.size(); ++I) {
4364+
// Register dependent toolchain.
4365+
DA->registerDependentActionInfo(
4366+
ToolChains[I], /*BoundArch=*/StringRef(), Action::OFK_SYCL);
4367+
4368+
// Add deps output to linker inputs.
4369+
DeviceLinkerInputs[I].push_back(DA);
4370+
}
4371+
}
4372+
43414373
/// Initialize the GPU architecture list from arguments - this populates `GpuArchList` from
43424374
/// `--cuda-gpu-arch` flags. Only relevant if compiling to CUDA. Return true if any
43434375
/// initialization errors are found.
@@ -4741,6 +4773,32 @@ class OffloadingActionBuilder final {
47414773
return false;
47424774
}
47434775

4776+
/// Create link job from the given host inputs and feed the result to offload
4777+
/// deps job which fetches device dependencies from the linked host image.
4778+
/// Offload deps output is then forwarded to active device action builders so
4779+
/// they can add it to the device linker inputs.
4780+
void addDeviceLinkDependenciesFromHost(ActionList &LinkerInputs) {
4781+
// Link image for reading dependencies from it.
4782+
auto *LA = C.MakeAction<LinkJobAction>(LinkerInputs, types::TY_Image);
4783+
4784+
// Calculate all the offload kinds used in the current compilation.
4785+
unsigned ActiveOffloadKinds = 0u;
4786+
for (auto &I : InputArgToOffloadKindMap)
4787+
ActiveOffloadKinds |= I.second;
4788+
4789+
OffloadAction::HostDependence HDep(
4790+
*LA, *C.getSingleOffloadToolChain<Action::OFK_Host>(),
4791+
/*BoundArch*/ nullptr, ActiveOffloadKinds);
4792+
4793+
auto *DA = C.MakeAction<OffloadDepsJobAction>(HDep, types::TY_LLVM_BC);
4794+
4795+
for (auto *SB : SpecializedBuilders) {
4796+
if (!SB->isValid())
4797+
continue;
4798+
SB->addDeviceLinkDependencies(DA);
4799+
}
4800+
}
4801+
47444802
void makeHostLinkAction(ActionList &LinkerInputs) {
47454803
// Build a list of device linking actions.
47464804
ActionList DeviceAL;
@@ -5065,6 +5123,11 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
50655123

50665124
OffloadBuilder.appendTopLevelLinkAction(Actions);
50675125

5126+
// With static fat archives we need to create additional steps for
5127+
// generating dependence objects for device link actions.
5128+
if (!LinkerInputs.empty() && C.getDriver().getOffloadStaticLibSeen())
5129+
OffloadBuilder.addDeviceLinkDependenciesFromHost(LinkerInputs);
5130+
50685131
// Go through all of the args, and create a Linker specific argument list.
50695132
// When dealing with fat static archives each archive is individually
50705133
// unbundled.
@@ -5966,7 +6029,10 @@ InputInfo Driver::BuildJobsForActionNoCache(
59666029
const JobAction *JA = cast<JobAction>(A);
59676030
ActionList CollapsedOffloadActions;
59686031

5969-
ToolSelector TS(JA, *TC, C, isSaveTempsEnabled(),
6032+
auto *DA = dyn_cast<OffloadDepsJobAction>(JA);
6033+
const ToolChain *JATC = DA ? DA->getHostTC() : TC;
6034+
6035+
ToolSelector TS(JA, *JATC, C, isSaveTempsEnabled(),
59706036
embedBitcodeInObject() && !isUsingLTO());
59716037
const Tool *T = TS.getTool(Inputs, CollapsedOffloadActions);
59726038

@@ -5995,8 +6061,9 @@ InputInfo Driver::BuildJobsForActionNoCache(
59956061
bool SubJobAtTopLevel =
59966062
AtTopLevel && (isa<DsymutilJobAction>(A) || isa<VerifyJobAction>(A));
59976063
InputInfos.push_back(BuildJobsForAction(
5998-
C, Input, TC, BoundArch, SubJobAtTopLevel, MultipleArchs, LinkingOutput,
5999-
CachedResults, A->getOffloadingDeviceKind()));
6064+
C, Input, JATC, DA ? DA->getOffloadingArch() : BoundArch,
6065+
SubJobAtTopLevel, MultipleArchs, LinkingOutput, CachedResults,
6066+
A->getOffloadingDeviceKind()));
60006067
}
60016068
// Check if we are in sub-work for preprocessing for host side. If so we will
60026069
// add another job to print information to terminal later.
@@ -6165,6 +6232,46 @@ InputInfo Driver::BuildJobsForActionNoCache(
61656232
"Result does not exist??");
61666233
Result = CachedResults[ActionTC];
61676234
}
6235+
} else if (auto *DA = dyn_cast<OffloadDepsJobAction>(JA)) {
6236+
for (auto &DI : DA->getDependentActionsInfo()) {
6237+
assert(DI.DependentOffloadKind != Action::OFK_None &&
6238+
"Deps job with no offloading");
6239+
6240+
std::string OffloadingPrefix = Action::GetOffloadingFileNamePrefix(
6241+
DI.DependentOffloadKind,
6242+
DI.DependentToolChain->getTriple().normalize(),
6243+
/*CreatePrefixForHost=*/true);
6244+
auto CurI = InputInfo(
6245+
DA,
6246+
GetNamedOutputPath(C, *DA, BaseInput, DI.DependentBoundArch,
6247+
/*AtTopLevel=*/false,
6248+
MultipleArchs ||
6249+
DI.DependentOffloadKind == Action::OFK_HIP,
6250+
OffloadingPrefix),
6251+
BaseInput);
6252+
// Save the result.
6253+
UnbundlingResults.push_back(CurI);
6254+
6255+
// Get the unique string identifier for this dependence and cache the
6256+
// result.
6257+
StringRef Arch = TargetDeviceOffloadKind == Action::OFK_HIP
6258+
? DI.DependentOffloadKind == Action::OFK_Host
6259+
? StringRef()
6260+
: DI.DependentBoundArch
6261+
: BoundArch;
6262+
6263+
CachedResults[{A, GetTriplePlusArchString(DI.DependentToolChain, Arch,
6264+
DI.DependentOffloadKind)}] =
6265+
CurI;
6266+
}
6267+
6268+
// Now that we have all the results generated, select the one that should be
6269+
// returned for the current depending action.
6270+
std::pair<const Action *, std::string> ActionTC = {
6271+
A, GetTriplePlusArchString(TC, BoundArch, TargetDeviceOffloadKind)};
6272+
auto It = CachedResults.find(ActionTC);
6273+
assert(It != CachedResults.end() && "Result does not exist??");
6274+
Result = It->second;
61686275
} else if (JA->getType() == types::TY_Nothing)
61696276
Result = InputInfo(A, BaseInput);
61706277
else {

clang/lib/Driver/ToolChain.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,12 @@ Tool *ToolChain::getOffloadWrapper() const {
328328
return OffloadWrapper.get();
329329
}
330330

331+
Tool *ToolChain::getOffloadDeps() const {
332+
if (!OffloadDeps)
333+
OffloadDeps.reset(new tools::OffloadDeps(*this));
334+
return OffloadDeps.get();
335+
}
336+
331337
Tool *ToolChain::getSPIRVTranslator() const {
332338
if (!SPIRVTranslator)
333339
SPIRVTranslator.reset(new tools::SPIRVTranslator(*this));
@@ -403,6 +409,9 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
403409
case Action::OffloadWrapperJobClass:
404410
return getOffloadWrapper();
405411

412+
case Action::OffloadDepsJobClass:
413+
return getOffloadDeps();
414+
406415
case Action::SPIRVTranslatorJobClass:
407416
return getSPIRVTranslator();
408417

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7988,6 +7988,71 @@ void OffloadWrapper::ConstructJob(Compilation &C, const JobAction &JA,
79887988
CmdArgs, Inputs));
79897989
}
79907990

7991+
// Begin OffloadDeps
7992+
7993+
void OffloadDeps::constructJob(Compilation &C, const JobAction &JA,
7994+
ArrayRef<InputInfo> Outputs,
7995+
ArrayRef<InputInfo> Inputs,
7996+
const llvm::opt::ArgList &TCArgs,
7997+
const char *LinkingOutput) const {
7998+
auto &DA = cast<OffloadDepsJobAction>(JA);
7999+
8000+
ArgStringList CmdArgs;
8001+
8002+
// Get the targets.
8003+
SmallString<128> Targets{"-targets="};
8004+
auto DepInfo = DA.getDependentActionsInfo();
8005+
for (unsigned I = 0; I < DepInfo.size(); ++I) {
8006+
auto &Dep = DepInfo[I];
8007+
if (I)
8008+
Targets += ',';
8009+
Targets += Action::GetOffloadKindName(Dep.DependentOffloadKind);
8010+
Targets += '-';
8011+
Targets += Dep.DependentToolChain->getTriple().normalize();
8012+
if (Dep.DependentOffloadKind == Action::OFK_HIP &&
8013+
!Dep.DependentBoundArch.empty()) {
8014+
Targets += '-';
8015+
Targets += Dep.DependentBoundArch;
8016+
}
8017+
}
8018+
CmdArgs.push_back(TCArgs.MakeArgString(Targets));
8019+
8020+
// Prepare outputs.
8021+
SmallString<128> Outs{"-outputs="};
8022+
for (unsigned I = 0; I < Outputs.size(); ++I) {
8023+
if (I)
8024+
Outs += ',';
8025+
Outs += DepInfo[I].DependentToolChain->getInputFilename(Outputs[I]);
8026+
}
8027+
CmdArgs.push_back(TCArgs.MakeArgString(Outs));
8028+
8029+
// Add input file.
8030+
CmdArgs.push_back(Inputs.front().getFilename());
8031+
8032+
// All the inputs are encoded as commands.
8033+
C.addCommand(std::make_unique<Command>(
8034+
JA, *this, ResponseFileSupport::None(),
8035+
TCArgs.MakeArgString(getToolChain().GetProgramPath(getShortName())),
8036+
CmdArgs, None, Outputs));
8037+
}
8038+
8039+
void OffloadDeps::ConstructJob(Compilation &C, const JobAction &JA,
8040+
const InputInfo &Output,
8041+
const InputInfoList &Inputs,
8042+
const llvm::opt::ArgList &TCArgs,
8043+
const char *LinkingOutput) const {
8044+
constructJob(C, JA, Output, Inputs, TCArgs, LinkingOutput);
8045+
}
8046+
8047+
void OffloadDeps::ConstructJobMultipleOutputs(Compilation &C,
8048+
const JobAction &JA,
8049+
const InputInfoList &Outputs,
8050+
const InputInfoList &Inputs,
8051+
const llvm::opt::ArgList &TCArgs,
8052+
const char *LinkingOutput) const {
8053+
constructJob(C, JA, Outputs, Inputs, TCArgs, LinkingOutput);
8054+
}
8055+
79918056
// Begin SPIRVTranslator
79928057

79938058
void SPIRVTranslator::ConstructJob(Compilation &C, const JobAction &JA,

0 commit comments

Comments
 (0)