https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/224446
>From a217f89d53671c82d6aff719d1812497b4f1ad70 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Thu, 17 Sep 2026 16:41:01 -0500 Subject: [PATCH 1/3] [ClangLinkerWrapper] Emit extra job to generate -save-temps assembly Summary: This is an oft-requested feature and unfortunately cannot be done easily. The approach is just to make a redundant job that does the same handling, but emits an `--lto-emit-asm` job in the background. Ideally these get enough parallelism that the total compile time doesn't change much. I cannot find a cleaner way to return the 'old driver behavior' for emitting assembly as was requested. THe LTO pipeline is not built to generate intermediate files like this, and running this *then* consuming the assembly on output would break thinLTO, partitioned LTO, or future object linking. --- .../clang-linker-wrapper/linker-wrapper.c | 6 +++ .../ClangLinkerWrapper.cpp | 45 +++++++++++++++---- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c index 550cab4b0604d..11f03e663edbf 100644 --- a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c +++ b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c @@ -51,6 +51,12 @@ __attribute__((visibility("protected"), used)) int x; // AMDGPU-LTO-TEMPS: clang{{.*}} --target=amdgpu10.30-amd-amdhsa -mcpu=gfx1030 {{.*}}-save-temps +// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run --save-temps \ +// RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=AMDGPU-SAVE-TEMPS-ASM + +// AMDGPU-SAVE-TEMPS-ASM-DAG: clang{{.*}} --target=amdgpu10.30-amd-amdhsa -mcpu=gfx1030 {{.*}}-Wl,--save-temps{{$}} +// AMDGPU-SAVE-TEMPS-ASM-DAG: clang{{.*}} --target=amdgpu10.30-amd-amdhsa -mcpu=gfx1030 {{.*}}-Xlinker --lto-emit-asm + // RUN: llvm-offload-binary -o %t.out \ // RUN: --image=file=%t.spirv.bc,kind=sycl,triple=spirv64-unknown-unknown,arch=foo // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 59faf38dda140..1ac27a75e990b 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -241,20 +241,34 @@ Expected<StringRef> createOutputFile(const Twine &Prefix, StringRef Extension) { } /// Execute the command \p ExecutablePath with the arguments \p Args. -Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) { +/// If \p Proc is set, launch without waiting and store the process handle. +Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args, + sys::ProcessInfo *Proc = nullptr) { if (Verbose || DryRun) printCommands(Args); if (DryRun) return Error::success(); - // If the command line fits within system limits, execute directly. - if (sys::commandLineFitsWithinSystemLimits(ExecutablePath, Args)) { - if (sys::ExecuteAndWait(ExecutablePath, Args)) + auto Launch = [&](ArrayRef<StringRef> ExecArgs) -> Error { + if (Proc) { + bool ExecutionFailed = false; + *Proc = sys::ExecuteNoWait(ExecutablePath, ExecArgs, std::nullopt, {}, 0, + nullptr, &ExecutionFailed); + if (ExecutionFailed) + return createStringError( + "'%s' failed", sys::path::filename(ExecutablePath).str().c_str()); + return Error::success(); + } + if (sys::ExecuteAndWait(ExecutablePath, ExecArgs)) return createStringError( "'%s' failed", sys::path::filename(ExecutablePath).str().c_str()); return Error::success(); - } + }; + + // If the command line fits within system limits, execute directly. + if (sys::commandLineFitsWithinSystemLimits(ExecutablePath, Args)) + return Launch(Args); // Write the arguments to a response file and pass that instead. auto TempFileOrErr = createOutputFile("response", "rsp"); @@ -274,10 +288,7 @@ Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) { std::string ResponseFile = ("@" + *TempFileOrErr).str(); SmallVector<StringRef, 2> NewArgs = {Args.front(), ResponseFile}; - if (sys::ExecuteAndWait(ExecutablePath, NewArgs)) - return createStringError("'%s' failed", - sys::path::filename(ExecutablePath).str().c_str()); - return Error::success(); + return Launch(NewArgs); } Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) { @@ -624,9 +635,25 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args, for (StringRef Arg : Args.getAllArgValues(OPT_compiler_arg_EQ)) CmdArgs.push_back(Args.MakeArgString(Arg)); + // We cannot generate temporary assembly files to satisfy `--save-temps` + // through the LTO pipeline. Launch a separate clang job to emit the file. + sys::ProcessInfo AsmProc; + if (SaveTemps && Triple.isAMDGPU()) { + SmallVector<StringRef, 16> AsmArgs(CmdArgs); + AsmArgs.append({"-Xlinker", "--lto-emit-asm"}); + if (Error Err = executeCommands(*ClangPath, AsmArgs, &AsmProc)) + return std::move(Err); + } + if (Error Err = executeCommands(*ClangPath, CmdArgs)) return std::move(Err); + if (SaveTemps && Triple.isAMDGPU() && !DryRun) { + if (sys::Wait(AsmProc, std::nullopt).ReturnCode) + return createStringError("'%s' failed", + sys::path::filename(*ClangPath).str().c_str()); + } + return *TempFileOrErr; } } // namespace generic >From 00956d20b1268d68b0194c03534436c71fbe9b45 Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Thu, 17 Sep 2026 22:47:51 -0500 Subject: [PATCH 2/3] remove --- clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c | 2 +- clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c index 11f03e663edbf..b092052cabb9e 100644 --- a/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c +++ b/clang/test/OffloadTools/clang-linker-wrapper/linker-wrapper.c @@ -55,7 +55,7 @@ __attribute__((visibility("protected"), used)) int x; // RUN: --linker-path=/usr/bin/ld %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=AMDGPU-SAVE-TEMPS-ASM // AMDGPU-SAVE-TEMPS-ASM-DAG: clang{{.*}} --target=amdgpu10.30-amd-amdhsa -mcpu=gfx1030 {{.*}}-Wl,--save-temps{{$}} -// AMDGPU-SAVE-TEMPS-ASM-DAG: clang{{.*}} --target=amdgpu10.30-amd-amdhsa -mcpu=gfx1030 {{.*}}-Xlinker --lto-emit-asm +// AMDGPU-SAVE-TEMPS-ASM-DAG: clang{{.*}} --target=amdgpu10.30-amd-amdhsa -mcpu=gfx1030 {{.*}}-Xlinker --lto-emit-asm{{$}} // RUN: llvm-offload-binary -o %t.out \ // RUN: --image=file=%t.spirv.bc,kind=sycl,triple=spirv64-unknown-unknown,arch=foo diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 1ac27a75e990b..d2f277f180de7 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -640,6 +640,7 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args, sys::ProcessInfo AsmProc; if (SaveTemps && Triple.isAMDGPU()) { SmallVector<StringRef, 16> AsmArgs(CmdArgs); + llvm::erase(AsmArgs, "-Wl,--save-temps"); AsmArgs.append({"-Xlinker", "--lto-emit-asm"}); if (Error Err = executeCommands(*ClangPath, AsmArgs, &AsmProc)) return std::move(Err); >From deb561cf571c321aa8a4dfe160fed08acd74148c Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Fri, 18 Sep 2026 09:01:08 -0500 Subject: [PATCH 3/3] address comment --- .../clang-linker-wrapper/ClangLinkerWrapper.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index d2f277f180de7..a0eee34df6c22 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -646,14 +646,14 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args, return std::move(Err); } - if (Error Err = executeCommands(*ClangPath, CmdArgs)) - return std::move(Err); - + Error Err = executeCommands(*ClangPath, CmdArgs); if (SaveTemps && Triple.isAMDGPU() && !DryRun) { - if (sys::Wait(AsmProc, std::nullopt).ReturnCode) - return createStringError("'%s' failed", - sys::path::filename(*ClangPath).str().c_str()); + if (sys::Wait(AsmProc, std::nullopt).ReturnCode && !Err) + Err = createStringError("'%s' failed", + sys::path::filename(*ClangPath).str().c_str()); } + if (Err) + return std::move(Err); return *TempFileOrErr; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
