| 1 | //===- Action.cpp - Abstract compilation steps ----------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "clang/Driver/Action.h" |
| 10 | #include "llvm/Support/ErrorHandling.h" |
| 11 | #include <cassert> |
| 12 | #include <string> |
| 13 | |
| 14 | using namespace clang; |
| 15 | using namespace driver; |
| 16 | using namespace llvm::opt; |
| 17 | |
| 18 | Action::~Action() = default; |
| 19 | |
| 20 | const char *Action::getClassName(ActionClass AC) { |
| 21 | switch (AC) { |
| 22 | case InputClass: return "input" ; |
| 23 | case BindArchClass: return "bind-arch" ; |
| 24 | case OffloadClass: |
| 25 | return "offload" ; |
| 26 | case PreprocessJobClass: return "preprocessor" ; |
| 27 | case PrecompileJobClass: return "precompiler" ; |
| 28 | case ExtractAPIJobClass: |
| 29 | return "api-extractor" ; |
| 30 | case AnalyzeJobClass: |
| 31 | return "analyzer" ; |
| 32 | case CompileJobClass: return "compiler" ; |
| 33 | case BackendJobClass: return "backend" ; |
| 34 | case AssembleJobClass: return "assembler" ; |
| 35 | case IfsMergeJobClass: return "interface-stub-merger" ; |
| 36 | case LinkJobClass: return "linker" ; |
| 37 | case LipoJobClass: return "lipo" ; |
| 38 | case DsymutilJobClass: return "dsymutil" ; |
| 39 | case VerifyDebugInfoJobClass: return "verify-debug-info" ; |
| 40 | case VerifyPCHJobClass: return "verify-pch" ; |
| 41 | case OffloadBundlingJobClass: |
| 42 | return "clang-offload-bundler" ; |
| 43 | case OffloadUnbundlingJobClass: |
| 44 | return "clang-offload-unbundler" ; |
| 45 | case OffloadPackagerJobClass: |
| 46 | return "llvm-offload-binary" ; |
| 47 | case LinkerWrapperJobClass: |
| 48 | return "clang-linker-wrapper" ; |
| 49 | case StaticLibJobClass: |
| 50 | return "static-lib-linker" ; |
| 51 | case BinaryAnalyzeJobClass: |
| 52 | return "binary-analyzer" ; |
| 53 | case BinaryTranslatorJobClass: |
| 54 | return "binary-translator" ; |
| 55 | case ObjcopyJobClass: |
| 56 | return "objcopy" ; |
| 57 | } |
| 58 | |
| 59 | llvm_unreachable("invalid class" ); |
| 60 | } |
| 61 | |
| 62 | void Action::propagateDeviceOffloadInfo(OffloadKind OKind, const char *OArch, |
| 63 | const ToolChain *OToolChain) { |
| 64 | // Offload action set its own kinds on their dependences. |
| 65 | if (Kind == OffloadClass) |
| 66 | return; |
| 67 | // Unbundling actions use the host kinds. |
| 68 | if (Kind == OffloadUnbundlingJobClass) |
| 69 | return; |
| 70 | |
| 71 | assert((OffloadingDeviceKind == OKind || OffloadingDeviceKind == OFK_None) && |
| 72 | "Setting device kind to a different device??" ); |
| 73 | assert(!ActiveOffloadKindMask && "Setting a device kind in a host action??" ); |
| 74 | OffloadingDeviceKind = OKind; |
| 75 | OffloadingArch = OArch; |
| 76 | OffloadingToolChain = OToolChain; |
| 77 | |
| 78 | for (auto *A : Inputs) |
| 79 | A->propagateDeviceOffloadInfo(OKind: OffloadingDeviceKind, OArch, OToolChain); |
| 80 | } |
| 81 | |
| 82 | void Action::propagateHostOffloadInfo(unsigned OKinds, const char *OArch) { |
| 83 | // Offload action set its own kinds on their dependences. |
| 84 | if (Kind == OffloadClass) |
| 85 | return; |
| 86 | |
| 87 | assert(OffloadingDeviceKind == OFK_None && |
| 88 | "Setting a host kind in a device action." ); |
| 89 | ActiveOffloadKindMask |= OKinds; |
| 90 | OffloadingArch = OArch; |
| 91 | |
| 92 | for (auto *A : Inputs) |
| 93 | A->propagateHostOffloadInfo(OKinds: ActiveOffloadKindMask, OArch); |
| 94 | } |
| 95 | |
| 96 | void Action::propagateOffloadInfo(const Action *A) { |
| 97 | if (unsigned HK = A->getOffloadingHostActiveKinds()) |
| 98 | propagateHostOffloadInfo(OKinds: HK, OArch: A->getOffloadingArch()); |
| 99 | else |
| 100 | propagateDeviceOffloadInfo(OKind: A->getOffloadingDeviceKind(), |
| 101 | OArch: A->getOffloadingArch(), |
| 102 | OToolChain: A->getOffloadingToolChain()); |
| 103 | } |
| 104 | |
| 105 | std::string Action::getOffloadingKindPrefix() const { |
| 106 | switch (OffloadingDeviceKind) { |
| 107 | case OFK_None: |
| 108 | break; |
| 109 | case OFK_Host: |
| 110 | llvm_unreachable("Host kind is not an offloading device kind." ); |
| 111 | break; |
| 112 | case OFK_Cuda: |
| 113 | return "device-cuda" ; |
| 114 | case OFK_OpenMP: |
| 115 | return "device-openmp" ; |
| 116 | case OFK_HIP: |
| 117 | return "device-hip" ; |
| 118 | case OFK_SYCL: |
| 119 | return "device-sycl" ; |
| 120 | |
| 121 | // TODO: Add other programming models here. |
| 122 | } |
| 123 | |
| 124 | if (!ActiveOffloadKindMask) |
| 125 | return {}; |
| 126 | |
| 127 | std::string Res("host" ); |
| 128 | assert(!((ActiveOffloadKindMask & OFK_Cuda) && |
| 129 | (ActiveOffloadKindMask & OFK_HIP)) && |
| 130 | "Cannot offload CUDA and HIP at the same time" ); |
| 131 | if (ActiveOffloadKindMask & OFK_Cuda) |
| 132 | Res += "-cuda" ; |
| 133 | if (ActiveOffloadKindMask & OFK_HIP) |
| 134 | Res += "-hip" ; |
| 135 | if (ActiveOffloadKindMask & OFK_OpenMP) |
| 136 | Res += "-openmp" ; |
| 137 | if (ActiveOffloadKindMask & OFK_SYCL) |
| 138 | Res += "-sycl" ; |
| 139 | |
| 140 | // TODO: Add other programming models here. |
| 141 | |
| 142 | return Res; |
| 143 | } |
| 144 | |
| 145 | /// Return a string that can be used as prefix in order to generate unique files |
| 146 | /// for each offloading kind. |
| 147 | std::string |
| 148 | Action::GetOffloadingFileNamePrefix(OffloadKind Kind, |
| 149 | StringRef NormalizedTriple, |
| 150 | bool CreatePrefixForHost) { |
| 151 | // Don't generate prefix for host actions unless required. |
| 152 | if (!CreatePrefixForHost && (Kind == OFK_None || Kind == OFK_Host)) |
| 153 | return {}; |
| 154 | |
| 155 | std::string Res("-" ); |
| 156 | Res += GetOffloadKindName(Kind); |
| 157 | Res += "-" ; |
| 158 | Res += NormalizedTriple; |
| 159 | return Res; |
| 160 | } |
| 161 | |
| 162 | /// Return a string with the offload kind name. If that is not defined, we |
| 163 | /// assume 'host'. |
| 164 | StringRef Action::GetOffloadKindName(OffloadKind Kind) { |
| 165 | switch (Kind) { |
| 166 | case OFK_None: |
| 167 | case OFK_Host: |
| 168 | return "host" ; |
| 169 | case OFK_Cuda: |
| 170 | return "cuda" ; |
| 171 | case OFK_OpenMP: |
| 172 | return "openmp" ; |
| 173 | case OFK_HIP: |
| 174 | return "hip" ; |
| 175 | case OFK_SYCL: |
| 176 | return "sycl" ; |
| 177 | |
| 178 | // TODO: Add other programming models here. |
| 179 | } |
| 180 | |
| 181 | llvm_unreachable("invalid offload kind" ); |
| 182 | } |
| 183 | |
| 184 | void InputAction::anchor() {} |
| 185 | |
| 186 | InputAction::InputAction(const Arg &_Input, types::ID _Type, StringRef _Id) |
| 187 | : Action(InputClass, _Type), Input(_Input), Id(_Id.str()) {} |
| 188 | |
| 189 | void BindArchAction::anchor() {} |
| 190 | |
| 191 | BindArchAction::BindArchAction(Action *Input, StringRef ArchName) |
| 192 | : Action(BindArchClass, Input), ArchName(ArchName) {} |
| 193 | |
| 194 | void OffloadAction::anchor() {} |
| 195 | |
| 196 | OffloadAction::OffloadAction(const HostDependence &HDep) |
| 197 | : Action(OffloadClass, HDep.getAction()), HostTC(HDep.getToolChain()) { |
| 198 | OffloadingArch = HDep.getBoundArch(); |
| 199 | ActiveOffloadKindMask = HDep.getOffloadKinds(); |
| 200 | HDep.getAction()->propagateHostOffloadInfo(OKinds: HDep.getOffloadKinds(), |
| 201 | OArch: HDep.getBoundArch()); |
| 202 | } |
| 203 | |
| 204 | OffloadAction::OffloadAction(const DeviceDependences &DDeps, types::ID Ty) |
| 205 | : Action(OffloadClass, DDeps.getActions(), Ty), |
| 206 | DevToolChains(DDeps.getToolChains()) { |
| 207 | auto &OKinds = DDeps.getOffloadKinds(); |
| 208 | auto &BArchs = DDeps.getBoundArchs(); |
| 209 | auto &OTCs = DDeps.getToolChains(); |
| 210 | |
| 211 | // If all inputs agree on the same kind, use it also for this action. |
| 212 | if (llvm::all_equal(Range: OKinds)) |
| 213 | OffloadingDeviceKind = OKinds.front(); |
| 214 | |
| 215 | // If we have a single dependency, inherit the architecture from it. |
| 216 | if (OKinds.size() == 1) |
| 217 | OffloadingArch = BArchs.front(); |
| 218 | |
| 219 | // Propagate info to the dependencies. |
| 220 | for (unsigned i = 0, e = getInputs().size(); i != e; ++i) |
| 221 | getInputs()[i]->propagateDeviceOffloadInfo(OKind: OKinds[i], OArch: BArchs[i], OToolChain: OTCs[i]); |
| 222 | } |
| 223 | |
| 224 | OffloadAction::OffloadAction(const HostDependence &HDep, |
| 225 | const DeviceDependences &DDeps) |
| 226 | : Action(OffloadClass, HDep.getAction()), HostTC(HDep.getToolChain()), |
| 227 | DevToolChains(DDeps.getToolChains()) { |
| 228 | // We use the kinds of the host dependence for this action. |
| 229 | OffloadingArch = HDep.getBoundArch(); |
| 230 | ActiveOffloadKindMask = HDep.getOffloadKinds(); |
| 231 | HDep.getAction()->propagateHostOffloadInfo(OKinds: HDep.getOffloadKinds(), |
| 232 | OArch: HDep.getBoundArch()); |
| 233 | |
| 234 | // Add device inputs and propagate info to the device actions. Do work only if |
| 235 | // we have dependencies. |
| 236 | for (unsigned i = 0, e = DDeps.getActions().size(); i != e; ++i) { |
| 237 | if (auto *A = DDeps.getActions()[i]) { |
| 238 | getInputs().push_back(Elt: A); |
| 239 | A->propagateDeviceOffloadInfo(OKind: DDeps.getOffloadKinds()[i], |
| 240 | OArch: DDeps.getBoundArchs()[i], |
| 241 | OToolChain: DDeps.getToolChains()[i]); |
| 242 | // If this action is used to forward single dependency, set the toolchain. |
| 243 | if (DDeps.getActions().size() == 1) |
| 244 | OffloadingToolChain = DDeps.getToolChains()[i]; |
| 245 | } |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | void OffloadAction::doOnHostDependence(const OffloadActionWorkTy &Work) const { |
| 250 | if (!HostTC) |
| 251 | return; |
| 252 | assert(!getInputs().empty() && "No dependencies for offload action??" ); |
| 253 | auto *A = getInputs().front(); |
| 254 | Work(A, HostTC, A->getOffloadingArch()); |
| 255 | } |
| 256 | |
| 257 | void OffloadAction::doOnEachDeviceDependence( |
| 258 | const OffloadActionWorkTy &Work) const { |
| 259 | auto I = getInputs().begin(); |
| 260 | auto E = getInputs().end(); |
| 261 | if (I == E) |
| 262 | return; |
| 263 | |
| 264 | // We expect to have the same number of input dependences and device tool |
| 265 | // chains, except if we also have a host dependence. In that case we have one |
| 266 | // more dependence than we have device tool chains. |
| 267 | assert(getInputs().size() == DevToolChains.size() + (HostTC ? 1 : 0) && |
| 268 | "Sizes of action dependences and toolchains are not consistent!" ); |
| 269 | |
| 270 | // Skip host action |
| 271 | if (HostTC) |
| 272 | ++I; |
| 273 | |
| 274 | auto TI = DevToolChains.begin(); |
| 275 | for (; I != E; ++I, ++TI) |
| 276 | Work(*I, *TI, (*I)->getOffloadingArch()); |
| 277 | } |
| 278 | |
| 279 | void OffloadAction::doOnEachDependence(const OffloadActionWorkTy &Work) const { |
| 280 | doOnHostDependence(Work); |
| 281 | doOnEachDeviceDependence(Work); |
| 282 | } |
| 283 | |
| 284 | void OffloadAction::doOnEachDependence(bool IsHostDependence, |
| 285 | const OffloadActionWorkTy &Work) const { |
| 286 | if (IsHostDependence) |
| 287 | doOnHostDependence(Work); |
| 288 | else |
| 289 | doOnEachDeviceDependence(Work); |
| 290 | } |
| 291 | |
| 292 | bool OffloadAction::hasHostDependence() const { return HostTC != nullptr; } |
| 293 | |
| 294 | Action *OffloadAction::getHostDependence() const { |
| 295 | assert(hasHostDependence() && "Host dependence does not exist!" ); |
| 296 | assert(!getInputs().empty() && "No dependencies for offload action??" ); |
| 297 | return HostTC ? getInputs().front() : nullptr; |
| 298 | } |
| 299 | |
| 300 | bool OffloadAction::hasSingleDeviceDependence( |
| 301 | bool DoNotConsiderHostActions) const { |
| 302 | if (DoNotConsiderHostActions) |
| 303 | return getInputs().size() == (HostTC ? 2 : 1); |
| 304 | return !HostTC && getInputs().size() == 1; |
| 305 | } |
| 306 | |
| 307 | Action * |
| 308 | OffloadAction::getSingleDeviceDependence(bool DoNotConsiderHostActions) const { |
| 309 | assert(hasSingleDeviceDependence(DoNotConsiderHostActions) && |
| 310 | "Single device dependence does not exist!" ); |
| 311 | // The previous assert ensures the number of entries in getInputs() is |
| 312 | // consistent with what we are doing here. |
| 313 | return HostTC ? getInputs()[1] : getInputs().front(); |
| 314 | } |
| 315 | |
| 316 | void OffloadAction::DeviceDependences::add(Action &A, const ToolChain &TC, |
| 317 | const char *BoundArch, |
| 318 | OffloadKind OKind) { |
| 319 | DeviceActions.push_back(Elt: &A); |
| 320 | DeviceToolChains.push_back(Elt: &TC); |
| 321 | DeviceBoundArchs.push_back(Elt: BoundArch); |
| 322 | DeviceOffloadKinds.push_back(Elt: OKind); |
| 323 | } |
| 324 | |
| 325 | void OffloadAction::DeviceDependences::add(Action &A, const ToolChain &TC, |
| 326 | const char *BoundArch, |
| 327 | unsigned OffloadKindMask) { |
| 328 | DeviceActions.push_back(Elt: &A); |
| 329 | DeviceToolChains.push_back(Elt: &TC); |
| 330 | DeviceBoundArchs.push_back(Elt: BoundArch); |
| 331 | |
| 332 | // Add each active offloading kind from a mask. |
| 333 | for (OffloadKind OKind : {OFK_OpenMP, OFK_Cuda, OFK_HIP, OFK_SYCL}) |
| 334 | if (OKind & OffloadKindMask) |
| 335 | DeviceOffloadKinds.push_back(Elt: OKind); |
| 336 | } |
| 337 | |
| 338 | OffloadAction::HostDependence::HostDependence(Action &A, const ToolChain &TC, |
| 339 | const char *BoundArch, |
| 340 | const DeviceDependences &DDeps) |
| 341 | : HostAction(A), HostToolChain(TC), HostBoundArch(BoundArch) { |
| 342 | for (auto K : DDeps.getOffloadKinds()) |
| 343 | HostOffloadKinds |= K; |
| 344 | } |
| 345 | |
| 346 | void JobAction::anchor() {} |
| 347 | |
| 348 | JobAction::JobAction(ActionClass Kind, Action *Input, types::ID Type) |
| 349 | : Action(Kind, Input, Type) {} |
| 350 | |
| 351 | JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type) |
| 352 | : Action(Kind, Inputs, Type) {} |
| 353 | |
| 354 | void PreprocessJobAction::anchor() {} |
| 355 | |
| 356 | PreprocessJobAction::PreprocessJobAction(Action *Input, types::ID OutputType) |
| 357 | : JobAction(PreprocessJobClass, Input, OutputType) {} |
| 358 | |
| 359 | void PrecompileJobAction::anchor() {} |
| 360 | |
| 361 | PrecompileJobAction::PrecompileJobAction(Action *Input, types::ID OutputType) |
| 362 | : JobAction(PrecompileJobClass, Input, OutputType) {} |
| 363 | |
| 364 | PrecompileJobAction::PrecompileJobAction(ActionClass Kind, Action *Input, |
| 365 | types::ID OutputType) |
| 366 | : JobAction(Kind, Input, OutputType) { |
| 367 | assert(isa<PrecompileJobAction>((Action*)this) && "invalid action kind" ); |
| 368 | } |
| 369 | |
| 370 | void ExtractAPIJobAction::() {} |
| 371 | |
| 372 | ExtractAPIJobAction::(Action *Inputs, types::ID OutputType) |
| 373 | : JobAction(ExtractAPIJobClass, Inputs, OutputType) {} |
| 374 | |
| 375 | void AnalyzeJobAction::anchor() {} |
| 376 | |
| 377 | AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType) |
| 378 | : JobAction(AnalyzeJobClass, Input, OutputType) {} |
| 379 | |
| 380 | void CompileJobAction::anchor() {} |
| 381 | |
| 382 | CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType) |
| 383 | : JobAction(CompileJobClass, Input, OutputType) {} |
| 384 | |
| 385 | void BackendJobAction::anchor() {} |
| 386 | |
| 387 | BackendJobAction::BackendJobAction(Action *Input, types::ID OutputType) |
| 388 | : JobAction(BackendJobClass, Input, OutputType) {} |
| 389 | |
| 390 | void AssembleJobAction::anchor() {} |
| 391 | |
| 392 | AssembleJobAction::AssembleJobAction(Action *Input, types::ID OutputType) |
| 393 | : JobAction(AssembleJobClass, Input, OutputType) {} |
| 394 | |
| 395 | void IfsMergeJobAction::anchor() {} |
| 396 | |
| 397 | IfsMergeJobAction::IfsMergeJobAction(ActionList &Inputs, types::ID Type) |
| 398 | : JobAction(IfsMergeJobClass, Inputs, Type) {} |
| 399 | |
| 400 | void LinkJobAction::anchor() {} |
| 401 | |
| 402 | LinkJobAction::LinkJobAction(ActionList &Inputs, types::ID Type) |
| 403 | : JobAction(LinkJobClass, Inputs, Type) {} |
| 404 | |
| 405 | void LipoJobAction::anchor() {} |
| 406 | |
| 407 | LipoJobAction::LipoJobAction(ActionList &Inputs, types::ID Type) |
| 408 | : JobAction(LipoJobClass, Inputs, Type) {} |
| 409 | |
| 410 | void DsymutilJobAction::anchor() {} |
| 411 | |
| 412 | DsymutilJobAction::DsymutilJobAction(ActionList &Inputs, types::ID Type) |
| 413 | : JobAction(DsymutilJobClass, Inputs, Type) {} |
| 414 | |
| 415 | void VerifyJobAction::anchor() {} |
| 416 | |
| 417 | VerifyJobAction::VerifyJobAction(ActionClass Kind, Action *Input, |
| 418 | types::ID Type) |
| 419 | : JobAction(Kind, Input, Type) { |
| 420 | assert((Kind == VerifyDebugInfoJobClass || Kind == VerifyPCHJobClass) && |
| 421 | "ActionClass is not a valid VerifyJobAction" ); |
| 422 | } |
| 423 | |
| 424 | void VerifyDebugInfoJobAction::anchor() {} |
| 425 | |
| 426 | VerifyDebugInfoJobAction::VerifyDebugInfoJobAction(Action *Input, |
| 427 | types::ID Type) |
| 428 | : VerifyJobAction(VerifyDebugInfoJobClass, Input, Type) {} |
| 429 | |
| 430 | void VerifyPCHJobAction::anchor() {} |
| 431 | |
| 432 | VerifyPCHJobAction::VerifyPCHJobAction(Action *Input, types::ID Type) |
| 433 | : VerifyJobAction(VerifyPCHJobClass, Input, Type) {} |
| 434 | |
| 435 | void OffloadBundlingJobAction::anchor() {} |
| 436 | |
| 437 | OffloadBundlingJobAction::OffloadBundlingJobAction(ActionList &Inputs) |
| 438 | : JobAction(OffloadBundlingJobClass, Inputs, Inputs.back()->getType()) {} |
| 439 | |
| 440 | void OffloadUnbundlingJobAction::anchor() {} |
| 441 | |
| 442 | OffloadUnbundlingJobAction::OffloadUnbundlingJobAction(Action *Input) |
| 443 | : JobAction(OffloadUnbundlingJobClass, Input, Input->getType()) {} |
| 444 | |
| 445 | void OffloadPackagerJobAction::anchor() {} |
| 446 | |
| 447 | OffloadPackagerJobAction::OffloadPackagerJobAction(ActionList &Inputs, |
| 448 | types::ID Type) |
| 449 | : JobAction(OffloadPackagerJobClass, Inputs, Type) {} |
| 450 | |
| 451 | void LinkerWrapperJobAction::anchor() {} |
| 452 | |
| 453 | LinkerWrapperJobAction::LinkerWrapperJobAction(ActionList &Inputs, |
| 454 | types::ID Type) |
| 455 | : JobAction(LinkerWrapperJobClass, Inputs, Type) {} |
| 456 | |
| 457 | void StaticLibJobAction::anchor() {} |
| 458 | |
| 459 | StaticLibJobAction::StaticLibJobAction(ActionList &Inputs, types::ID Type) |
| 460 | : JobAction(StaticLibJobClass, Inputs, Type) {} |
| 461 | |
| 462 | void BinaryAnalyzeJobAction::anchor() {} |
| 463 | |
| 464 | BinaryAnalyzeJobAction::BinaryAnalyzeJobAction(Action *Input, types::ID Type) |
| 465 | : JobAction(BinaryAnalyzeJobClass, Input, Type) {} |
| 466 | |
| 467 | void BinaryTranslatorJobAction::anchor() {} |
| 468 | |
| 469 | BinaryTranslatorJobAction::BinaryTranslatorJobAction(Action *Input, |
| 470 | types::ID Type) |
| 471 | : JobAction(BinaryTranslatorJobClass, Input, Type) {} |
| 472 | |
| 473 | void ObjcopyJobAction::anchor() {} |
| 474 | |
| 475 | ObjcopyJobAction::ObjcopyJobAction(Action *Input, types::ID Type) |
| 476 | : JobAction(ObjcopyJobClass, Input, Type) {} |
| 477 | |