[clang] [ClangLinkerWrapper] Emit extra job to generate -save-temps assembly (PR #224446)
Joseph Huber via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 18 07:01:29 PDT 2026
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 <huberjn at outlook.com>
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 <huberjn at outlook.com>
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 <huberjn at outlook.com>
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;
}
More information about the cfe-commits
mailing list