https://github.com/yxsamliu updated https://github.com/llvm/llvm-project/pull/211675
>From c2262ae93c337583a9780e05cb823eaae28c45f3 Mon Sep 17 00:00:00 2001 From: "Yaxun (Sam) Liu" <[email protected]> Date: Fri, 14 Aug 2026 10:25:03 -0400 Subject: [PATCH] [HIP] Use linker wrapper for device-only code object links HIP device-only non-RDC links should use the linker wrapper path so device libraries and offload profile options are handled consistently. When --no-gpu-bundle-output asks for raw images, the driver now runs the wrapper to produce a fat binary and then adds an unbundling job. This keeps raw image output out of clang-linker-wrapper and reuses the existing multiple-output naming path. --- clang/include/clang/Driver/Action.h | 3 +- clang/include/clang/Driver/Compilation.h | 15 +- clang/include/clang/Driver/Driver.h | 6 +- clang/include/clang/Driver/Util.h | 4 + clang/lib/Driver/Action.cpp | 4 + clang/lib/Driver/Compilation.cpp | 8 +- clang/lib/Driver/Driver.cpp | 158 +++++++++++------- clang/lib/Driver/ToolChains/AMDGPU.cpp | 1 + clang/lib/Driver/ToolChains/Clang.cpp | 57 +++++-- clang/test/Driver/hip-binding.hip | 35 +++- clang/test/Driver/hip-phases.hip | 18 +- .../test/Driver/hip-profile-rocm-runtime.hip | 65 ++++++- .../test/Driver/hip-toolchain-device-only.hip | 18 +- .../linker-wrapper-hip-no-rdc.c | 15 ++ 14 files changed, 296 insertions(+), 111 deletions(-) diff --git a/clang/include/clang/Driver/Action.h b/clang/include/clang/Driver/Action.h index 306db1be15910..018e267bec137 100644 --- a/clang/include/clang/Driver/Action.h +++ b/clang/include/clang/Driver/Action.h @@ -617,8 +617,9 @@ class OffloadUnbundlingJobAction final : public JobAction { SmallVector<DependentActionInfo, 6> DependentActionInfoArray; public: - // Offloading unbundling doesn't change the type of output. + // Offloading unbundling usually doesn't change the type of output. OffloadUnbundlingJobAction(Action *Input); + OffloadUnbundlingJobAction(Action *Input, types::ID OutputType); /// Register information about a dependent action. void registerDependentActionInfo(const ToolChain *TC, BoundArch BA, diff --git a/clang/include/clang/Driver/Compilation.h b/clang/include/clang/Driver/Compilation.h index 825806b6cfe33..01eff0af7852f 100644 --- a/clang/include/clang/Driver/Compilation.h +++ b/clang/include/clang/Driver/Compilation.h @@ -101,11 +101,11 @@ class Compilation { llvm::opt::ArgStringList TempFiles; /// Result files which should be removed on failure. - ArgStringMap ResultFiles; + ActionFileMap ResultFiles; /// Result files which are generated correctly on failure, and which should /// only be removed if we crash. - ArgStringMap FailureResultFiles; + ActionFileMap FailureResultFiles; /// -ftime-trace result files. ArgStringMap TimeTraceFiles; @@ -227,9 +227,9 @@ class Compilation { llvm::opt::ArgStringList &getTempFiles() { return TempFiles; } const llvm::opt::ArgStringList &getTempFiles() const { return TempFiles; } - const ArgStringMap &getResultFiles() const { return ResultFiles; } + const ActionFileMap &getResultFiles() const { return ResultFiles; } - const ArgStringMap &getFailureResultFiles() const { + const ActionFileMap &getFailureResultFiles() const { return FailureResultFiles; } @@ -266,14 +266,14 @@ class Compilation { /// addResultFile - Add a file to remove on failure, and returns its /// argument. const char *addResultFile(const char *Name, const JobAction *JA) { - ResultFiles[JA] = Name; + ResultFiles[JA].push_back(Name); return Name; } /// addFailureResultFile - Add a file to remove if we crash, and returns its /// argument. const char *addFailureResultFile(const char *Name, const JobAction *JA) { - FailureResultFiles[JA] = Name; + FailureResultFiles[JA].push_back(Name); return Name; } @@ -304,8 +304,7 @@ class Compilation { /// JobAction. Otherwise, delete all files in the map. /// \param IssueErrors - Report failures as errors. /// \return Whether all files were removed successfully. - bool CleanupFileMap(const ArgStringMap &Files, - const JobAction *JA, + bool CleanupFileMap(const ActionFileMap &Files, const JobAction *JA, bool IssueErrors = false) const; /// ExecuteCommand - Execute an actual command. diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index eece9ac5293f0..8db71b01195d9 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -693,11 +693,13 @@ class Driver { /// \param BA - The bound architecture. /// \param AtTopLevel - Whether this is a "top-level" action. /// \param MultipleArchs - Whether multiple -arch options were supplied. - /// \param NormalizedTriple - The normalized triple of the relevant target. + /// \param OffloadingPrefix - The filename prefix for an offload target. + /// \param UseFinalOutput - Whether to use the filename from -o. const char *GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, BoundArch BA, bool AtTopLevel, bool MultipleArchs, - StringRef NormalizedTriple) const; + StringRef OffloadingPrefix, + bool UseFinalOutput = true) const; /// GetTemporaryPath - Return the pathname of a temporary file to use /// as part of compilation; the file will have the given prefix and suffix. diff --git a/clang/include/clang/Driver/Util.h b/clang/include/clang/Driver/Util.h index 92d3d40433a32..d57a861833f08 100644 --- a/clang/include/clang/Driver/Util.h +++ b/clang/include/clang/Driver/Util.h @@ -21,6 +21,10 @@ namespace driver { /// ArgStringMap - Type used to map a JobAction to its result file. typedef llvm::DenseMap<const JobAction*, const char*> ArgStringMap; + /// ActionFileMap - Type used to map a JobAction to its result files. + typedef llvm::DenseMap<const JobAction *, SmallVector<const char *, 1>> + ActionFileMap; + /// ActionList - Type used for lists of actions. typedef SmallVector<Action*, 3> ActionList; diff --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp index b74ae925e922c..3b71bfb7b53b1 100644 --- a/clang/lib/Driver/Action.cpp +++ b/clang/lib/Driver/Action.cpp @@ -440,6 +440,10 @@ void OffloadUnbundlingJobAction::anchor() {} OffloadUnbundlingJobAction::OffloadUnbundlingJobAction(Action *Input) : JobAction(OffloadUnbundlingJobClass, Input, Input->getType()) {} +OffloadUnbundlingJobAction::OffloadUnbundlingJobAction(Action *Input, + types::ID OutputType) + : JobAction(OffloadUnbundlingJobClass, Input, OutputType) {} + void OffloadPackagerJobAction::anchor() {} OffloadPackagerJobAction::OffloadPackagerJobAction(ActionList &Inputs, diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp index c81c4445a29f9..f3fa01fa9e104 100644 --- a/clang/lib/Driver/Compilation.cpp +++ b/clang/lib/Driver/Compilation.cpp @@ -149,16 +149,16 @@ bool Compilation::CleanupFileList(const llvm::opt::ArgStringList &Files, return Success; } -bool Compilation::CleanupFileMap(const ArgStringMap &Files, - const JobAction *JA, - bool IssueErrors) const { +bool Compilation::CleanupFileMap(const ActionFileMap &Files, + const JobAction *JA, bool IssueErrors) const { bool Success = true; for (const auto &File : Files) { // If specified, only delete the files associated with the JobAction. // Otherwise, delete all files in the map. if (JA && File.first != JA) continue; - Success &= CleanupFile(File.second, IssueErrors); + for (const char *Filename : File.second) + Success &= CleanupFile(Filename, IssueErrors); } return Success; } diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 38795f7c2ae7a..9efaf2277d461 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -5053,6 +5053,11 @@ Driver::BuildOffloadingActions(Compilation &C, llvm::opt::DerivedArgList &Args, Args.hasFlag(options::OPT_fhip_emit_relocatable, options::OPT_fno_hip_emit_relocatable, false); + bool HasGPUOutputBundleOption = Args.hasArg( + options::OPT_gpu_bundle_output, options::OPT_no_gpu_bundle_output); + bool BundleGPUOutput = Args.hasFlag(options::OPT_gpu_bundle_output, + options::OPT_no_gpu_bundle_output, true); + if (!HIPNoRDC && HIPRelocatableObj) C.getDriver().Diag(diag::err_opt_not_valid_with_opt) << "-fhip-emit-relocatable" @@ -5071,6 +5076,7 @@ Driver::BuildOffloadingActions(Compilation &C, llvm::opt::DerivedArgList &Args, ActionList OffloadActions; OffloadAction::DeviceDependences DDeps; + bool HIPDeviceOnlyNeedsLink = false; const Action::OffloadKind OffloadKinds[] = { Action::OFK_OpenMP, Action::OFK_Cuda, Action::OFK_HIP, Action::OFK_SYCL}; @@ -5159,8 +5165,9 @@ Driver::BuildOffloadingActions(Compilation &C, llvm::opt::DerivedArgList &Args, } } - // Compiling HIP in device-only non-RDC mode requires linking each action - // individually. + // Compiling HIP in device-only non-RDC mode requires linking each action. + // Keep the inputs unlinked so the linker wrapper can link all device + // images in one invocation. for (Action *&A : DeviceActions) { auto *OffloadTriple = A->getOffloadingToolChain() ? &A->getOffloadingToolChain()->getTriple() @@ -5170,12 +5177,19 @@ Driver::BuildOffloadingActions(Compilation &C, llvm::opt::DerivedArgList &Args, (OffloadTriple->getOS() == llvm::Triple::OSType::AMDHSA || OffloadTriple->getOS() == llvm::Triple::OSType::ChipStar); - if ((A->getType() != types::TY_Object && !IsHIPSPV && - A->getType() != types::TY_LTO_BC) || - HIPRelocatableObj || !HIPNoRDC || !offloadDeviceOnly()) + if (HIPRelocatableObj || !HIPNoRDC || !offloadDeviceOnly()) + continue; + + if (IsHIPSPV) { + ActionList LinkerInput = {A}; + A = C.MakeAction<LinkJobAction>(LinkerInput, types::TY_Image); + continue; + } + + if (A->getType() != types::TY_Object && A->getType() != types::TY_LTO_BC) continue; - ActionList LinkerInput = {A}; - A = C.MakeAction<LinkJobAction>(LinkerInput, types::TY_Image); + + HIPDeviceOnlyNeedsLink = true; } auto *TCAndArch = TCAndArchs.begin(); @@ -5196,18 +5210,18 @@ Driver::BuildOffloadingActions(Compilation &C, llvm::opt::DerivedArgList &Args, } } - // HIP code in device-only non-RDC mode will bundle the output if it invoked - // the linker or if the user explicitly requested it. + // HIP code in device-only non-RDC mode will bundle linked output by default + // or bundle any output if the user explicitly requested it. bool ShouldBundleHIP = - Args.hasFlag(options::OPT_gpu_bundle_output, - options::OPT_no_gpu_bundle_output, false) || - (!Args.getLastArg(options::OPT_no_gpu_bundle_output) && HIPNoRDC && - offloadDeviceOnly() && llvm::none_of(OffloadActions, [](Action *A) { - return A->getType() != types::TY_Image; - })); + BundleGPUOutput && + (HasGPUOutputBundleOption || + (HIPNoRDC && offloadDeviceOnly() && + (HIPDeviceOnlyNeedsLink || llvm::none_of(OffloadActions, [](Action *A) { + return A->getType() != types::TY_Image; + })))); // All kinds exit now in device-only mode except for non-RDC mode HIP. - if (offloadDeviceOnly() && !ShouldBundleHIP) + if (offloadDeviceOnly() && !ShouldBundleHIP && !HIPDeviceOnlyNeedsLink) return C.MakeAction<OffloadAction>(DDeps, types::TY_Nothing); if (OffloadActions.empty()) @@ -5222,37 +5236,57 @@ Driver::BuildOffloadingActions(Compilation &C, llvm::opt::DerivedArgList &Args, C.MakeAction<LinkJobAction>(OffloadActions, types::TY_CUDA_FATBIN); DDep.add(*FatbinAction, *C.getSingleOffloadToolChain<Action::OFK_Cuda>(), /*BA=*/{}, Action::OFK_Cuda); - } else if (HIPNoRDC && offloadDeviceOnly()) { - // If we are in device-only non-RDC-mode we just emit the final HIP - // fatbinary for each translation unit, linking each input individually. - Action *FatbinAction = - C.MakeAction<LinkJobAction>(OffloadActions, types::TY_HIP_FATBIN); - DDep.add(*FatbinAction, - *C.getOffloadToolChains<Action::OFK_HIP>().first->second, - /*BA=*/{}, Action::OFK_HIP); } else if (HIPNoRDC) { // Host + device assembly: defer to clang-offload-bundler (see // BuildActions). - if (HIPAsmBundleDeviceOut && + if (!offloadDeviceOnly() && HIPAsmBundleDeviceOut && shouldBundleHIPAsmWithNewDriver(C, Args, C.getDriver())) { for (Action *OA : OffloadActions) HIPAsmBundleDeviceOut->push_back(OA); return HostAction; } - // Package all the offloading actions into a single output that can be - // embedded in the host and linked. - Action *PackagerAction = - C.MakeAction<OffloadPackagerJobAction>(OffloadActions, types::TY_Image); - // For HIP non-RDC compilation, wrap the device binary with linker wrapper - // before bundling with host code. Do not bind a specific GPU arch here, - // as the packaged image may contain entries for multiple GPUs. - ActionList AL{PackagerAction}; - PackagerAction = - C.MakeAction<LinkerWrapperJobAction>(AL, types::TY_HIP_FATBIN); - DDep.add(*PackagerAction, - *C.getOffloadToolChains<Action::OFK_HIP>().first->second, - /*BA=*/{}, Action::OFK_HIP); + Action *DeviceOutputAction; + if (offloadDeviceOnly() && !HIPDeviceOnlyNeedsLink) { + // Non-link outputs use clang-offload-bundler when explicitly requested. + DeviceOutputAction = + C.MakeAction<LinkJobAction>(OffloadActions, types::TY_HIP_FATBIN); + } else { + // Package all device inputs so the linker wrapper can link every GPU + // architecture in one invocation. + Action *Packager = C.MakeAction<OffloadPackagerJobAction>( + OffloadActions, types::TY_Image); + ActionList WrapperInputs = {Packager}; + DeviceOutputAction = C.MakeAction<LinkerWrapperJobAction>( + WrapperInputs, types::TY_HIP_FATBIN); + if (offloadDeviceOnly() && !ShouldBundleHIP) { + auto *Unbundler = C.MakeAction<OffloadUnbundlingJobAction>( + DeviceOutputAction, types::TY_Image); + for (Action *A : OffloadActions) { + const ToolChain *DeviceTC = nullptr; + BoundArch DeviceArch; + cast<OffloadAction>(A)->doOnEachDeviceDependence( + [&](Action *, const ToolChain *TC, BoundArch BA) { + assert(!DeviceTC && "expected one device dependence"); + DeviceTC = TC; + DeviceArch = BA; + }); + assert(DeviceTC && "expected a device toolchain"); + Unbundler->registerDependentActionInfo(DeviceTC, DeviceArch, + Action::OFK_HIP); + } + DeviceOutputAction = Unbundler; + } + } + if (auto *UA = dyn_cast<OffloadUnbundlingJobAction>(DeviceOutputAction)) { + for (const auto &Info : UA->getDependentActionsInfo()) + DDep.add(*DeviceOutputAction, *Info.DependentToolChain, + Info.DependentBoundArch, Action::OFK_HIP); + } else { + DDep.add(*DeviceOutputAction, + *C.getOffloadToolChains<Action::OFK_HIP>().first->second, + /*BA=*/{}, Action::OFK_HIP); + } } else { // Package all the offloading actions into a single output that can be // embedded in the host and linked. @@ -6336,32 +6370,35 @@ InputInfoList Driver::BuildJobsForActionNoCache( // Determine the place to write output to, if any. InputInfo Result; - InputInfoList UnbundlingResults; + InputInfoList MultipleResults; if (auto *UA = dyn_cast<OffloadUnbundlingJobAction>(JA)) { // If we have an unbundling job, we need to create results for all the // outputs. We also update the results cache so that other actions using // this unbundling action can get the right results. + bool ChangesType = UA->getInputs().front()->getType() != UA->getType(); for (auto &UI : UA->getDependentActionsInfo()) { assert(UI.DependentOffloadKind != Action::OFK_None && "Unbundling with no offloading??"); - // Unbundling actions are never at the top level. When we generate the - // offloading prefix, we also do that for the host file because the - // unbundling action does not change the type of the output which can - // cause a overwrite. + // Most unbundling actions are internal and keep the input type. The HIP + // device-only fatbin-to-image unbundling action is a final output action, + // so let a single output use -o while keeping generated names for + // multiple outputs. std::string OffloadingPrefix = Action::GetOffloadingFileNamePrefix( UI.DependentOffloadKind, UI.DependentToolChain->getTripleString(), /*CreatePrefixForHost=*/true); auto CurI = InputInfo( UA, GetNamedOutputPath(C, *UA, BaseInput, UI.DependentBoundArch, - /*AtTopLevel=*/false, + /*AtTopLevel=*/ChangesType, MultipleArchs || UI.DependentOffloadKind == Action::OFK_HIP, - OffloadingPrefix), + OffloadingPrefix, + /*UseFinalOutput=*/ChangesType && + UA->getDependentActionsInfo().size() == 1), BaseInput); // Save the unbundling result. - UnbundlingResults.push_back(CurI); + MultipleResults.push_back(CurI); // Get the unique string identifier for this dependence and cache the // result. @@ -6383,9 +6420,14 @@ InputInfoList Driver::BuildJobsForActionNoCache( // returned for the current depending action. std::pair<const Action *, std::string> ActionTC = { A, GetTriplePlusArchString(TC, BA, TargetDeviceOffloadKind)}; - assert(CachedResults.find(ActionTC) != CachedResults.end() && - "Result does not exist??"); - Result = CachedResults[ActionTC].front(); + auto ResultIt = CachedResults.find(ActionTC); + if (ResultIt != CachedResults.end()) { + Result = ResultIt->second.front(); + } else { + assert(ChangesType && + "unbundling result missing for current toolchain/arch"); + Result = MultipleResults.front(); + } } else if (JA->getType() == types::TY_Nothing) Result = {InputInfo(A, BaseInput)}; else { @@ -6412,23 +6454,23 @@ InputInfoList Driver::BuildJobsForActionNoCache( if (i + 1 != e) llvm::errs() << ", "; } - if (UnbundlingResults.empty()) + if (MultipleResults.empty()) llvm::errs() << "], output: " << Result.getAsString() << "\n"; else { llvm::errs() << "], outputs: ["; - for (unsigned i = 0, e = UnbundlingResults.size(); i != e; ++i) { - llvm::errs() << UnbundlingResults[i].getAsString(); + for (unsigned i = 0, e = MultipleResults.size(); i != e; ++i) { + llvm::errs() << MultipleResults[i].getAsString(); if (i + 1 != e) llvm::errs() << ", "; } llvm::errs() << "] \n"; } } else { - if (UnbundlingResults.empty()) + if (MultipleResults.empty()) T->ConstructJob(C, *JA, Result, InputInfos, Args, LinkingOutput); else - T->ConstructJobMultipleOutputs(C, *JA, UnbundlingResults, InputInfos, - Args, LinkingOutput); + T->ConstructJobMultipleOutputs(C, *JA, MultipleResults, InputInfos, Args, + LinkingOutput); } return {Result}; } @@ -6547,7 +6589,8 @@ static const char *GetModuleOutputPath(Compilation &C, const JobAction &JA, const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, BoundArch BA, bool AtTopLevel, bool MultipleArchs, - StringRef OffloadingPrefix) const { + StringRef OffloadingPrefix, + bool UseFinalOutput) const { std::string BoundArchStr = sanitizeTargetIDInFileName(BA.ArchName); llvm::PrettyStackTraceString CrashInfo("Computing output path"); @@ -6578,7 +6621,8 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, // Output to a user requested destination? if (AtTopLevel && !isa<DsymutilJobAction>(JA) && !isa<VerifyJobAction>(JA)) { - if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) + if (Arg *FinalOutput = + UseFinalOutput ? C.getArgs().getLastArg(options::OPT_o) : nullptr) return C.addResultFile(FinalOutput->getValue(), &JA); } diff --git a/clang/lib/Driver/ToolChains/AMDGPU.cpp b/clang/lib/Driver/ToolChains/AMDGPU.cpp index 5893f6f6b2915..b5ce9e0fda9a2 100644 --- a/clang/lib/Driver/ToolChains/AMDGPU.cpp +++ b/clang/lib/Driver/ToolChains/AMDGPU.cpp @@ -1282,6 +1282,7 @@ LTOKind AMDGPUToolChain::getLTOMode(const ArgList &Args, Action::OffloadKind Kind) const { if (getTriple().isAMDGCN() && getDriver().offloadDeviceOnly() && !Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false) && + !ToolChain::needsProfileRT(Args) && !Args.hasArg(options::OPT_foffload_lto, options::OPT_foffload_lto_EQ)) return LTOK_None; return ToolChain::getLTOMode(Args, Kind); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 94f9a26aac39f..79905d902374b 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -9750,8 +9750,13 @@ static bool requiresProfileRT(unsigned ID) { switch (ID) { case options::OPT_fprofile_generate: case options::OPT_fprofile_generate_EQ: + case options::OPT_fcs_profile_generate: + case options::OPT_fcs_profile_generate_EQ: case options::OPT_fprofile_instr_generate: case options::OPT_fprofile_instr_generate_EQ: + case options::OPT_fcreate_profile: + case options::OPT_fprofile_generate_cold_function_coverage: + case options::OPT_fprofile_generate_cold_function_coverage_EQ: case options::OPT_fcoverage_mapping: case options::OPT_fno_coverage_mapping: case options::OPT_fcoverage_compilation_dir_EQ: @@ -9822,8 +9827,16 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, OPT_fmultilib_flag, OPT_fprofile_generate, OPT_fprofile_generate_EQ, + OPT_fcs_profile_generate, + OPT_fcs_profile_generate_EQ, OPT_fprofile_instr_generate, OPT_fprofile_instr_generate_EQ, + OPT_fcreate_profile, + OPT_fprofile_generate_cold_function_coverage, + OPT_fprofile_generate_cold_function_coverage_EQ, + OPT_fno_profile_generate, + OPT_fno_profile_instr_generate, + OPT_noprofilelib, OPT_fcoverage_mapping, OPT_fno_coverage_mapping, OPT_fcoverage_compilation_dir_EQ, @@ -9848,13 +9861,15 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, return TC.getVFS().exists( TC.getCompilerRT(Args, Name, ToolChain::FT_Static)); }; - auto ShouldForwardForToolChain = [&](Arg *A, const ToolChain &TC) { + auto ShouldForwardForToolChain = [&](Arg *A, const ToolChain &TC, + const ArgList &TCArgs) { unsigned ID = A->getOption().getID(); // Don't forward profiling arguments if the toolchain doesn't support it. // Without this check using it on the host would result in linker errors. // Coverage mapping flags require -fprofile-instr-generate, so drop them // together to avoid a device cc1 diagnostic. - if (requiresProfileRT(ID) && !ToolChainHasRT(TC, "profile")) + if (requiresProfileRT(ID) && !TCArgs.hasArg(OPT_noprofilelib) && + !ToolChainHasRT(TC, "profile")) return false; // Don't forward sanitizer arguments if the toolchain doesn't support it. // Without this check using it on the host would result in linker errors. @@ -9864,13 +9879,13 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, return TC.HasNativeLLVMSupport() || ID != OPT_mllvm; }; auto ShouldForward = [&](const llvm::DenseSet<unsigned> &Set, Arg *A, - const ToolChain &TC) { + const ToolChain &TC, const ArgList &TCArgs) { if (A->getOption().matches(OPT_v) && SuppressHIPNoRDCVerbose) return false; return (Set.contains(A->getOption().getID()) || (A->getOption().getGroup().isValid() && Set.contains(A->getOption().getGroup().getID()))) && - ShouldForwardForToolChain(A, TC); + ShouldForwardForToolChain(A, TC, TCArgs); }; ArgStringList CmdArgs; @@ -9889,10 +9904,10 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, for (Arg *A : ToolChainArgs) { if (A->getOption().matches(OPT_Zlinker_input)) LinkerArgs.emplace_back(A->getValue()); - else if (ShouldForward(CompilerOptions, A, *TC)) { + else if (ShouldForward(CompilerOptions, A, *TC, ToolChainArgs)) { A->claim(); A->render(Args, CompilerArgs); - } else if (ShouldForward(LinkerOptions, A, *TC)) { + } else if (ShouldForward(LinkerOptions, A, *TC, ToolChainArgs)) { A->claim(); A->render(Args, LinkerArgs); } @@ -9996,9 +10011,12 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, Twine("--device-compiler=--rocm-path=") + A->getValue())); } - // Construct the link job so we can wrap around it. - Linker->ConstructJob(C, JA, Output, Inputs, Args, LinkingOutput); - const auto &LinkCommand = C.getJobs().getJobs().back(); + Command *LinkCommand = nullptr; + if (JA.getType() != types::TY_HIP_FATBIN) { + // Construct the link job so we can wrap around it. + Linker->ConstructJob(C, JA, Output, Inputs, Args, LinkingOutput); + LinkCommand = C.getJobs().getJobs().back().get(); + } // Forward -Xoffload-{compiler,linker}<-triple> arguments to the linker // wrapper. @@ -10066,8 +10084,10 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, } // Add the linker arguments to be forwarded by the wrapper. - CmdArgs.push_back(Args.MakeArgString(Twine("--linker-path=") + - LinkCommand->getExecutable())); + CmdArgs.push_back(Args.MakeArgString( + Twine("--linker-path=") + + (LinkCommand ? LinkCommand->getExecutable() + : Args.MakeArgString(getToolChain().GetLinkerPath())))); // We use action type to differentiate two use cases of the linker wrapper. // TY_Image for normal linker wrapper work. @@ -10080,6 +10100,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, for (auto Input : Inputs) CmdArgs.push_back(Input.getFilename()); } else { + assert(LinkCommand && "expected link command for normal wrapper job"); for (const char *LinkArg : LinkCommand->getArguments()) CmdArgs.push_back(LinkArg); } @@ -10106,8 +10127,14 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA, const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("clang-linker-wrapper")); - // Replace the executable and arguments of the link job with the - // wrapper. - LinkCommand->replaceExecutable(Exec); - LinkCommand->replaceArguments(CmdArgs); + if (LinkCommand) { + // Replace the executable and arguments of the link job with the + // wrapper. + LinkCommand->replaceExecutable(Exec); + LinkCommand->replaceArguments(CmdArgs); + } else { + C.addCommand(std::make_unique<Command>(JA, *this, + ResponseFileSupport::AtFileUTF8(), + Exec, CmdArgs, Inputs, Output)); + } } diff --git a/clang/test/Driver/hip-binding.hip b/clang/test/Driver/hip-binding.hip index b0839021aa1d9..ea782bdc51173 100644 --- a/clang/test/Driver/hip-binding.hip +++ b/clang/test/Driver/hip-binding.hip @@ -57,24 +57,45 @@ // RUN: --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c %s 2>&1 \ // RUN: | FileCheck -check-prefix=MULTI-D-ONLY %s // MULTI-D-ONLY: # "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[GFX908:.+]]" -// MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX908]]"], output: "[[GFX908_OUT:.+]]" // MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT]]"], output: "[[GFX90a:.+]]" -// MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX90a]]"], output: "[[GFX90a_OUT:.+]]" -// MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX908_OUT]]", "[[GFX90a_OUT]]"], output: "{{.+}}" +// MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "Offload::Packager", inputs: ["[[GFX908]]", "[[GFX90a]]"], output: "[[PACKAGE:.+]]" +// MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "Offload::Linker", inputs: ["[[PACKAGE]]"], output: "{{.+}}" // -// RUN: not %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc -emit-llvm \ +// RUN: %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc \ +// RUN: --no-gpu-bundle-output --gpu-bundle-output --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MULTI-D-ONLY %s +// +// RUN: %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc \ +// RUN: --no-gpu-bundle-output --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MULTI-D-ONLY-NO-BUNDLE %s +// MULTI-D-ONLY-NO-BUNDLE: # "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[GFX908:.+]]" +// MULTI-D-ONLY-NO-BUNDLE-NEXT: # "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT]]"], output: "[[GFX90A:.+]]" +// MULTI-D-ONLY-NO-BUNDLE-NEXT: # "amdgcn-amd-amdhsa" - "Offload::Packager", inputs: ["[[GFX908]]", "[[GFX90A]]"], output: "[[PACKAGE:.+]]" +// MULTI-D-ONLY-NO-BUNDLE-NEXT: # "amdgcn-amd-amdhsa" - "Offload::Linker", inputs: ["[[PACKAGE]]"], output: "[[FATBIN:.+]]" +// MULTI-D-ONLY-NO-BUNDLE-NEXT: # "amdgcn-amd-amdhsa" - "offload bundler", inputs: ["[[FATBIN]]"], outputs: ["a.out-hip-amdgcn-amd-amdhsa-gfx908", "a.out-hip-amdgcn-amd-amdhsa-gfx90a"] +// +// RUN: %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc \ +// RUN: --gpu-bundle-output --no-gpu-bundle-output --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=MULTI-D-ONLY-NO-BUNDLE %s +// +// RUN: not %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc \ // RUN: --no-gpu-bundle-output --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c -o %t %s 2>&1 \ // RUN: | FileCheck -check-prefix=MULTI-D-ONLY-NO-BUNDLE-O %s // MULTI-D-ONLY-NO-BUNDLE-O: error: cannot specify -o when generating multiple output files +// RUN: %clang -### --target=x86_64-linux-gnu -ccc-print-bindings -nogpulib -nogpuinc \ +// RUN: --no-gpu-bundle-output --offload-arch=gfx90a --offload-device-only -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix=SINGLE-D-ONLY-NO-BUNDLE %s +// SINGLE-D-ONLY-NO-BUNDLE: # "amdgcn-amd-amdhsa" - "Offload::Linker", inputs: ["[[PACKAGE:.+]]"], output: "[[FATBIN:.+]]" +// SINGLE-D-ONLY-NO-BUNDLE-NEXT: # "amdgcn-amd-amdhsa" - "offload bundler", inputs: ["[[FATBIN]]"], outputs: ["a.out-hip-amdgcn-amd-amdhsa-gfx90a"] + // RUN: %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc \ // RUN: --gpu-bundle-output --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c -o a.out %s 2>&1 \ // RUN: | FileCheck -check-prefix=MULTI-D-ONLY-O %s // MULTI-D-ONLY-O: "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[GFX908_OBJ:.+]]" -// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX908_OBJ]]"], output: "[[GFX908:.+]]" // MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT]]"], output: "[[GFX90A_OBJ:.+]]" -// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX90A_OBJ]]"], output: "[[GFX90A:.+]]" -// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX908]]", "[[GFX90A]]"], output: "a.out" +// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "Offload::Packager", inputs: ["[[GFX908_OBJ]]", "[[GFX90A_OBJ]]"], output: "[[PACKAGE:.+]]" +// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "Offload::Linker", inputs: ["[[PACKAGE]]"], output: "a.out" // RUN: %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc -emit-llvm \ // RUN: --gpu-bundle-output --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c -o a.out %s 2>&1 \ diff --git a/clang/test/Driver/hip-phases.hip b/clang/test/Driver/hip-phases.hip index 8fb58dc98cefc..9165a566c8366 100644 --- a/clang/test/Driver/hip-phases.hip +++ b/clang/test/Driver/hip-phases.hip @@ -701,13 +701,13 @@ // SPIRV-ONLY-NEXT: 2: compiler, {1}, ir, (device-hip, gfx1030) // SPIRV-ONLY-NEXT: 3: backend, {2}, assembler, (device-hip, gfx1030) // SPIRV-ONLY-NEXT: 4: assembler, {3}, object, (device-hip, gfx1030) -// SPIRV-ONLY-NEXT: 5: linker, {4}, image, (device-hip, gfx1030) -// SPIRV-ONLY-NEXT: 6: offload, "device-hip (amdgcn-amd-amdhsa:gfx1030)" {5}, image -// SPIRV-ONLY-NEXT: 7: input, "[[INPUT]]", hip, (device-hip, amdgcnspirv) -// SPIRV-ONLY-NEXT: 8: preprocessor, {7}, hip-cpp-output, (device-hip, amdgcnspirv) -// SPIRV-ONLY-NEXT: 9: compiler, {8}, ir, (device-hip, amdgcnspirv) -// SPIRV-ONLY-NEXT: 10: backend, {9}, lto-bc, (device-hip, amdgcnspirv) -// SPIRV-ONLY-NEXT: 11: linker, {10}, image, (device-hip, amdgcnspirv) -// SPIRV-ONLY-NEXT: 12: offload, "device-hip (spirv64-amd-amdhsa:amdgcnspirv)" {11}, image -// SPIRV-ONLY-NEXT: 13: linker, {6, 12}, hip-fatbin, (device-hip) +// SPIRV-ONLY-NEXT: 5: offload, "device-hip (amdgcn-amd-amdhsa:gfx1030)" {4}, object +// SPIRV-ONLY-NEXT: 6: input, "[[INPUT]]", hip, (device-hip, amdgcnspirv) +// SPIRV-ONLY-NEXT: 7: preprocessor, {6}, hip-cpp-output, (device-hip, amdgcnspirv) +// SPIRV-ONLY-NEXT: 8: compiler, {7}, ir, (device-hip, amdgcnspirv) +// SPIRV-ONLY-NEXT: 9: backend, {8}, lto-bc, (device-hip, amdgcnspirv) +// SPIRV-ONLY-NEXT: 10: linker, {9}, image, (device-hip, amdgcnspirv) +// SPIRV-ONLY-NEXT: 11: offload, "device-hip (spirv64-amd-amdhsa:amdgcnspirv)" {10}, image +// SPIRV-ONLY-NEXT: 12: llvm-offload-binary, {5, 11}, image, (device-hip) +// SPIRV-ONLY-NEXT: 13: clang-linker-wrapper, {12}, hip-fatbin, (device-hip) // SPIRV-ONLY-NEXT: 14: offload, "device-hip (amdgcn-amd-amdhsa)" {13}, none diff --git a/clang/test/Driver/hip-profile-rocm-runtime.hip b/clang/test/Driver/hip-profile-rocm-runtime.hip index fc82db4fc13c0..03190e8dca5d8 100644 --- a/clang/test/Driver/hip-profile-rocm-runtime.hip +++ b/clang/test/Driver/hip-profile-rocm-runtime.hip @@ -3,9 +3,10 @@ // Build a fake resource dir containing both the base profile runtime and the // ROCm device-profile runtime so the driver's existence check passes. -// RUN: rm -rf %t && mkdir -p %t/lib/x86_64-unknown-linux +// RUN: rm -rf %t && mkdir -p %t/lib/x86_64-unknown-linux %t/lib/amdgcn-amd-amdhsa // RUN: touch %t/lib/x86_64-unknown-linux/libclang_rt.profile.a // RUN: touch %t/lib/x86_64-unknown-linux/libclang_rt.profile_rocm.a +// RUN: touch %t/lib/amdgcn-amd-amdhsa/libclang_rt.profile.a // RUN: touch %t.o // HIP host link with PGO links clang_rt.profile_rocm. @@ -31,3 +32,65 @@ // RUN: | FileCheck -check-prefix=HOST-PGO %s // HOST-PGO: "{{.*}}libclang_rt.profile.a" // HOST-PGO-NOT: libclang_rt.profile_rocm.a + +// HIP device-only code object builds use the linker wrapper so its inner Clang +// invocation links the AMDGPU profile runtime. +// RUN: %clang -### -x hip --cuda-device-only \ +// RUN: --target=x86_64-unknown-linux --offload-arch=gfx906 \ +// RUN: -fprofile-generate -resource-dir=%t -nogpuinc -nogpulib %s 2>&1 \ +// RUN: | FileCheck -check-prefix=HIP-DEVICE-PGO %s +// HIP-DEVICE-PGO-DAG: "-emit-llvm-bc" +// HIP-DEVICE-PGO-DAG: "-flto=full" +// HIP-DEVICE-PGO: "{{.*}}llvm-offload-binary" +// HIP-DEVICE-PGO: "{{.*}}clang-linker-wrapper" +// HIP-DEVICE-PGO-SAME: "--device-compiler=amdgcn-amd-amdhsa=-fprofile-generate" +// HIP-DEVICE-PGO-SAME: "--emit-fatbin-only" + +// Unbundled code object output uses the linker wrapper's fat binary mode, then +// unbundles that fat binary into raw images. +// RUN: %clang -### -x hip --cuda-device-only --no-gpu-bundle-output \ +// RUN: --target=x86_64-unknown-linux --offload-arch=gfx906 \ +// RUN: -fprofile-generate -resource-dir=%t -nogpuinc -nogpulib %s 2>&1 \ +// RUN: | FileCheck -check-prefix=HIP-DEVICE-PGO-RAW %s +// HIP-DEVICE-PGO-RAW: "{{.*}}llvm-offload-binary" +// HIP-DEVICE-PGO-RAW: "{{.*}}clang-linker-wrapper" +// HIP-DEVICE-PGO-RAW-SAME: "--device-compiler=amdgcn-amd-amdhsa=-fprofile-generate" +// HIP-DEVICE-PGO-RAW-SAME: "--emit-fatbin-only" +// HIP-DEVICE-PGO-RAW: "{{.*}}clang-offload-bundler" +// HIP-DEVICE-PGO-RAW-SAME: "-type=hipfb" +// HIP-DEVICE-PGO-RAW-SAME: "-output=a.out-hip-amdgcn-amd-amdhsa-gfx906" +// HIP-DEVICE-PGO-RAW-SAME: "-unbundle" + +// Other profile generation modes are also forwarded to the linker wrapper. +// RUN: %clang -### -x hip --cuda-device-only \ +// RUN: --target=x86_64-unknown-linux --offload-arch=gfx906 \ +// RUN: -fcs-profile-generate -resource-dir=%t -nogpuinc -nogpulib %s 2>&1 \ +// RUN: | FileCheck -check-prefix=HIP-DEVICE-CSPGO %s +// HIP-DEVICE-CSPGO: "{{.*}}clang-linker-wrapper" +// HIP-DEVICE-CSPGO-SAME: "--device-compiler=amdgcn-amd-amdhsa=-fcs-profile-generate" + +// RUN: %clang -### -x hip --cuda-device-only \ +// RUN: --target=x86_64-unknown-linux --offload-arch=gfx906 \ +// RUN: -fcreate-profile -resource-dir=%t -nogpuinc -nogpulib %s 2>&1 \ +// RUN: | FileCheck -check-prefix=HIP-DEVICE-CREATE-PROFILE %s +// HIP-DEVICE-CREATE-PROFILE: "{{.*}}clang-linker-wrapper" +// HIP-DEVICE-CREATE-PROFILE-SAME: "--device-compiler=amdgcn-amd-amdhsa=-fcreate-profile" + +// RUN: %clang -### -x hip --cuda-device-only \ +// RUN: --target=x86_64-unknown-linux --offload-arch=gfx906 \ +// RUN: -fprofile-generate-cold-function-coverage -resource-dir=%t \ +// RUN: -nogpuinc -nogpulib %s 2>&1 \ +// RUN: | FileCheck -check-prefix=HIP-DEVICE-COLD-PROFILE %s +// HIP-DEVICE-COLD-PROFILE: "{{.*}}clang-linker-wrapper" +// HIP-DEVICE-COLD-PROFILE-SAME: "--device-compiler=amdgcn-amd-amdhsa=-fprofile-generate-cold-function-coverage" + +// -noprofilelib must reach the inner Clang together with the profile option. +// RUN: rm %t/lib/amdgcn-amd-amdhsa/libclang_rt.profile.a +// RUN: %clang -### -x hip --cuda-device-only \ +// RUN: --target=x86_64-unknown-linux --offload-arch=gfx906 \ +// RUN: -fprofile-generate -noprofilelib -resource-dir=%t \ +// RUN: -nogpuinc -nogpulib %s 2>&1 \ +// RUN: | FileCheck -check-prefix=HIP-DEVICE-NOPROFILELIB %s +// HIP-DEVICE-NOPROFILELIB: "{{.*}}clang-linker-wrapper" +// HIP-DEVICE-NOPROFILELIB-SAME: "--device-compiler=amdgcn-amd-amdhsa=-fprofile-generate" +// HIP-DEVICE-NOPROFILELIB-SAME: "--device-compiler=amdgcn-amd-amdhsa=-noprofilelib" diff --git a/clang/test/Driver/hip-toolchain-device-only.hip b/clang/test/Driver/hip-toolchain-device-only.hip index c0621854f17ce..279784e5305e5 100644 --- a/clang/test/Driver/hip-toolchain-device-only.hip +++ b/clang/test/Driver/hip-toolchain-device-only.hip @@ -6,18 +6,22 @@ // CHECK-NOT: error: // CHECK: [[CLANG:".*clang.*"]] "-cc1"{{.*}} "-triple" "amdgcn-amd-amdhsa" +// CHECK-SAME: "-emit-obj" // CHECK-SAME: "-fcuda-is-device" // CHECK-SAME: "-target-cpu" "gfx803" -// CHECK-SAME: {{.*}} "-o" [[OBJ_DEV_A_803:".*o"]] "-x" "hip" - -// CHECK: [[LLD: ".*lld.*"]] "-flavor" "gnu" "-m" "elf64_amdgpu" "--no-undefined" "-shared" -// CHECK-SAME: "-o" "[[IMG_DEV_A_803:.*out]]" [[OBJ_DEV_A_803]] +// CHECK-SAME: {{.*}} "-o" "[[OBJ_DEV_A_803:[^"]+\.o]]" "-x" "hip" // CHECK: [[CLANG:".*clang.*"]] "-cc1"{{.*}} "-triple" "amdgcn-amd-amdhsa" // CHECK-SAME: "-emit-obj" // CHECK-SAME: "-fcuda-is-device" // CHECK-SAME: "-target-cpu" "gfx900" -// CHECK-SAME: {{.*}} "-o" [[OBJ_DEV_A_900:".*o"]] "-x" "hip" +// CHECK-SAME: {{.*}} "-o" "[[OBJ_DEV_A_900:[^"]+\.o]]" "-x" "hip" + +// CHECK: [[PACKAGER:"[^"]*llvm-offload-binary[^"]*"]] "-o" "[[PACKAGE:[^"]+]]" +// CHECK-SAME: "--image=file=[[OBJ_DEV_A_803]],triple=amdgcn-amd-amdhsa,arch=gfx803,kind=hip" +// CHECK-SAME: "--image=file=[[OBJ_DEV_A_900]],triple=amdgcn-amd-amdhsa,arch=gfx900,kind=hip" -// CHECK: [[LLD]] "-flavor" "gnu" "-m" "elf64_amdgpu" "--no-undefined" "-shared" -// CHECK-SAME: "-o" "[[IMG_DEV_A_900:.*out]]" [[OBJ_DEV_A_900]] +// CHECK: [[WRAPPER:"[^"]*clang-linker-wrapper[^"]*"]] +// CHECK-SAME: "--should-extract=gfx803" "--should-extract=gfx900" +// CHECK-SAME: "--host-triple=x86_64-unknown-linux-gnu" +// CHECK-SAME: "--emit-fatbin-only" "-o" "{{[^"]+}}" "[[PACKAGE]]" diff --git a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-hip-no-rdc.c b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-hip-no-rdc.c index 44d0c1e2b7b51..f97a2bba5b31b 100644 --- a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-hip-no-rdc.c +++ b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper-hip-no-rdc.c @@ -55,3 +55,18 @@ __attribute__((visibility("protected"), used)) int x; // RUN: test -s %t.gfx9-4-generic-xnack+.co // RUN: test -f %t.gfx1200.co // RUN: test -s %t.gfx1200.co + +// Clang passes the exact output filename for each device image. +// RUN: rm -rf %t.dir && mkdir %t.dir +// RUN: cd %t.dir && %clang -x hip --cuda-device-only \ +// RUN: --no-gpu-bundle-output --offload-arch=gfx900 \ +// RUN: --offload-arch=gfx1200 -nogpuinc -nogpulib %s +// RUN: test -f %t.dir/linker-wrapper-hip-no-rdc-hip-amdgcn-amd-amdhsa-gfx900.out +// RUN: test -f %t.dir/linker-wrapper-hip-no-rdc-hip-amdgcn-amd-amdhsa-gfx1200.out + +// The implicit filename keeps its target suffix for one architecture too. +// RUN: rm -rf %t.single.dir && mkdir %t.single.dir +// RUN: cd %t.single.dir && %clang -x hip --cuda-device-only \ +// RUN: --no-gpu-bundle-output --offload-arch=gfx1200 \ +// RUN: -nogpuinc -nogpulib %s +// RUN: test -f %t.single.dir/linker-wrapper-hip-no-rdc-hip-amdgcn-amd-amdhsa-gfx1200.out _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
