[clang] [clang-sycl-linker] Make AOT temp files land relative to cwd, not -o's dir (PR #224506)
Wenju He via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 17 21:49:58 PDT 2026
https://github.com/wenju-he updated https://github.com/llvm/llvm-project/pull/224506
>From f5d87ef6e00a7266be4e7a1e7d2a36ef32067c18 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 18 Sep 2026 05:32:51 +0200
Subject: [PATCH 1/2] [clang-sycl-linker] Make AOT temp files land relative to
cwd, not -o's dir
Derive the CodeGen/AOT Stem from -o's basename instead of its full
path. Previously the full path put .spv/.out in -o's directory while
.bc (via createTempFile's cwd-relative prefix) landed in cwd,
splitting a single run's -save-temps intermediates across two
locations. This aligns CodeGen/AOT with the cwd-relative convention
.bc already uses.
Co-Authored-By: Claude Sonnet 5 <noreply at anthropic.com>
---
clang/test/OffloadTools/clang-sycl-linker/basic.ll | 8 ++++++++
clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp | 2 +-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
index 00e3bba8d297e..09eced37fb894 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
@@ -135,6 +135,14 @@
; AOT-INTEL-CPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: graniterapids
; AOT-INTEL-CPU-NOT: {{.+}}
;
+; Test that AOT temp file names ("<stem>_<index>.spv"/".out") are derived from
+; -o's basename only, not its full path, so they land in the current directory.
+; RUN: mkdir -p %t/outdir
+; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc -o %t/outdir/nested.out 2>&1 \
+; RUN: | FileCheck %s --check-prefix=AOT-STEM-BASENAME
+; AOT-STEM-BASENAME: LLVM backend: input: {{.*}}.bc, output: nested_0.spv
+; AOT-STEM-BASENAME-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-output nested_0.out -file nested_0.spv
+;
; Check that the output file must be specified.
; RUN: not clang-sycl-linker --dry-run %t/input1.bc %t/input2.bc 2>&1 \
; RUN: | FileCheck %s --check-prefix=NOOUTPUT
diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
index 70afc6d6c4cea..a63c30b6262c3 100644
--- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
+++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
@@ -985,7 +985,7 @@ static Error runSYCLLink(ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs,
// Code generation step.
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
- StringRef Stem = OutputFile.rsplit('.').first;
+ StringRef Stem = sys::path::filename(OutputFile).rsplit('.').first;
std::string CodeGenFile = (Stem + "_" + Twine(I) + OutputFileNameExt).str();
if (Error Err = runCodeGen(SplitModules[I].ModuleFilePath,
>From 32e8db1af6dbb7945f1372998200f53b2b00ad52 Mon Sep 17 00:00:00 2001
From: Wenju He <wenju.he at intel.com>
Date: Fri, 18 Sep 2026 06:49:37 +0200
Subject: [PATCH 2/2] [clang-sycl-linker] Give AOT/SPV temp files
per-invocation uniqueness
Route the SPIR-V translation and AOT compile outputs through
createTempFile instead of building "<stem>_<index>.ext" directly, so
parallel invocations that share an output basename (but differ in
output directory) don't collide on the same cwd-relative intermediate
files. The --spirv-dump-device-code copy still uses the clean stem
name for readability.
Co-Authored-By: Claude Sonnet 5 <noreply at anthropic.com>
---
.../OffloadTools/clang-sycl-linker/basic.ll | 22 ++++++++++---------
.../clang-sycl-linker/split-mode.ll | 12 +++++-----
.../clang-sycl-linker/ClangSYCLLinker.cpp | 15 ++++++++++---
3 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
index 09eced37fb894..a9e62bbbd82bb 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
@@ -29,7 +29,7 @@
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit %t/input1.bc %t/input2.bc -o %t/spirv.out 2>&1 \
; RUN: | FileCheck %s --check-prefix=SIMPLE-FO
; SIMPLE-FO: link: inputs: {{.*}}.bc, {{.*}}.bc output: [[LLVMLINKOUT:.*]].bc
-; SIMPLE-FO-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: {{.*}}_0.spv
+; SIMPLE-FO-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: {{.*}}_0{{.*}}.spv
; SIMPLE-FO-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; SIMPLE-FO-NOT: {{.+}}
;
@@ -41,7 +41,7 @@
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit %t/input1.bc %t/input2.bc --library-path=%t/libs --whole-archive -l device -o /dev/null 2>&1 \
; RUN: | FileCheck %s --check-prefix=DEVLIBS
; DEVLIBS: link: inputs: {{.*}}.bc, {{.*}}.bc, {{.*}}libdevice.a(lib1.bc), {{.*}}libdevice.a(lib2.bc) output: [[LLVMLINKOUT:.*]].bc
-; DEVLIBS-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: {{.*}}_0.spv
+; DEVLIBS-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: {{.*}}_0{{.*}}.spv
; DEVLIBS-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; DEVLIBS-NOT: {{.+}}
;
@@ -113,8 +113,8 @@
; RUN: --ocloc-options="-a -b" \
; RUN: | FileCheck %s --check-prefix=AOT-INTEL-GPU
; AOT-INTEL-GPU: link: inputs: {{.*}}.bc, {{.*}}.bc output: [[LLVMLINKOUT:.*]].bc
-; AOT-INTEL-GPU-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: [[SPIRVTRANSLATIONOUT:.*]]_0.spv
-; AOT-INTEL-GPU-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-device bmg_g21 -a -b {{.*}}-output [[SPIRVTRANSLATIONOUT]]_0.out -file [[SPIRVTRANSLATIONOUT]]_0.spv
+; AOT-INTEL-GPU-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: [[SPIRVTRANSLATIONOUT:.*]]_0{{.*}}.spv
+; AOT-INTEL-GPU-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-device bmg_g21 -a -b {{.*}}-output [[SPIRVTRANSLATIONOUT]]_0{{.*}}.out -file [[SPIRVTRANSLATIONOUT]]_0{{.*}}.spv
; AOT-INTEL-GPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: bmg_g21
; AOT-INTEL-GPU-NOT: {{.+}}
;
@@ -130,18 +130,20 @@
; RUN: --opencl-aot-options="-a -b" \
; RUN: | FileCheck %s --check-prefix=AOT-INTEL-CPU
; AOT-INTEL-CPU: link: inputs: {{.*}}.bc, {{.*}}.bc output: [[LLVMLINKOUT:.*]].bc
-; AOT-INTEL-CPU-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: [[SPIRVTRANSLATIONOUT:.*]]_0.spv
-; AOT-INTEL-CPU-NEXT: "{{.*}}opencl-aot{{.*}}" {{.*}}--device=cpu -a -b {{.*}}-o [[SPIRVTRANSLATIONOUT]]_0.out [[SPIRVTRANSLATIONOUT]]_0.spv
+; AOT-INTEL-CPU-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: [[SPIRVTRANSLATIONOUT:.*]]_0{{.*}}.spv
+; AOT-INTEL-CPU-NEXT: "{{.*}}opencl-aot{{.*}}" {{.*}}--device=cpu -a -b {{.*}}-o [[SPIRVTRANSLATIONOUT]]_0{{.*}}.out [[SPIRVTRANSLATIONOUT]]_0{{.*}}.spv
; AOT-INTEL-CPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: graniterapids
; AOT-INTEL-CPU-NOT: {{.+}}
;
-; Test that AOT temp file names ("<stem>_<index>.spv"/".out") are derived from
-; -o's basename only, not its full path, so they land in the current directory.
+; Test that AOT temp file names ("<stem>_<index>-<unique>.spv"/".out") are
+; derived from -o's basename only, not its full path, so they land in the
+; current directory (with a unique suffix to avoid collisions between
+; concurrent invocations sharing an output basename).
; RUN: mkdir -p %t/outdir
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit -arch=bmg_g21 %t/input1.bc -o %t/outdir/nested.out 2>&1 \
; RUN: | FileCheck %s --check-prefix=AOT-STEM-BASENAME
-; AOT-STEM-BASENAME: LLVM backend: input: {{.*}}.bc, output: nested_0.spv
-; AOT-STEM-BASENAME-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-output nested_0.out -file nested_0.spv
+; AOT-STEM-BASENAME: LLVM backend: input: {{.*}}.bc, output: [[STEM:nested_0]]{{.*}}.spv
+; AOT-STEM-BASENAME-NEXT: "{{.*}}ocloc{{.*}}" {{.*}}-output [[STEM]]{{.*}}.out -file [[STEM]]{{.*}}.spv
;
; Check that the output file must be specified.
; RUN: not clang-sycl-linker --dry-run %t/input1.bc %t/input2.bc 2>&1 \
diff --git a/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll b/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll
index 6aa05fa9b8a50..f3af0e53fd3d5 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll
@@ -13,7 +13,7 @@
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=link_unit %t.bc -o %t-none.out 2>&1 \
; RUN: | FileCheck %s --check-prefix=SPLIT-NONE
; SPLIT-NONE: link: inputs: {{.*}}.bc output: [[LLVMLINKOUT:.*]].bc
-; SPLIT-NONE-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: {{.*}}_0.spv
+; SPLIT-NONE-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: {{.*}}_0{{.*}}.spv
; SPLIT-NONE-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; SPLIT-NONE-NOT: {{.+}}
;
@@ -25,9 +25,9 @@
; SPLIT-KERNEL-NEXT: [[SPLIT0:.*]].bc [kernel_c ]
; SPLIT-KERNEL-NEXT: [[SPLIT1:.*]].bc [kernel_b ]
; SPLIT-KERNEL-NEXT: [[SPLIT2:.*]].bc [kernel_a ]
-; SPLIT-KERNEL-NEXT: LLVM backend: input: [[SPLIT0]].bc, output: {{.*}}_0.spv
-; SPLIT-KERNEL-NEXT: LLVM backend: input: [[SPLIT1]].bc, output: {{.*}}_1.spv
-; SPLIT-KERNEL-NEXT: LLVM backend: input: [[SPLIT2]].bc, output: {{.*}}_2.spv
+; SPLIT-KERNEL-NEXT: LLVM backend: input: [[SPLIT0]].bc, output: {{.*}}_0{{.*}}.spv
+; SPLIT-KERNEL-NEXT: LLVM backend: input: [[SPLIT1]].bc, output: {{.*}}_1{{.*}}.spv
+; SPLIT-KERNEL-NEXT: LLVM backend: input: [[SPLIT2]].bc, output: {{.*}}_2{{.*}}.spv
; SPLIT-KERNEL-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; SPLIT-KERNEL-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; SPLIT-KERNEL-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
@@ -47,8 +47,8 @@
; SPLIT-SRC-NEXT: sycl-module-split: input: [[LLVMLINKOUT]].bc, mode: translation_unit
; SPLIT-SRC-NEXT: [[S0:.*]].bc [kernel_b kernel_c ]
; SPLIT-SRC-NEXT: [[S1:.*]].bc [kernel_a ]
-; SPLIT-SRC-NEXT: LLVM backend: input: [[S0]].bc, output: {{.*}}_0.spv
-; SPLIT-SRC-NEXT: LLVM backend: input: [[S1]].bc, output: {{.*}}_1.spv
+; SPLIT-SRC-NEXT: LLVM backend: input: [[S0]].bc, output: {{.*}}_0{{.*}}.spv
+; SPLIT-SRC-NEXT: LLVM backend: input: [[S1]].bc, output: {{.*}}_1{{.*}}.spv
; SPLIT-SRC-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; SPLIT-SRC-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; SPLIT-SRC-NOT: {{.+}}
diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
index a63c30b6262c3..8604c3c26d98c 100644
--- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
+++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
@@ -986,7 +986,13 @@ static Error runSYCLLink(ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs,
// Code generation step.
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
StringRef Stem = sys::path::filename(OutputFile).rsplit('.').first;
- std::string CodeGenFile = (Stem + "_" + Twine(I) + OutputFileNameExt).str();
+ SmallString<64> Prefix;
+ (Stem + "_" + Twine(I)).toVector(Prefix);
+ auto CodeGenFileOrErr =
+ createTempFile(Args, Prefix, OutputFileNameExt.drop_front());
+ if (!CodeGenFileOrErr)
+ return CodeGenFileOrErr.takeError();
+ StringRef CodeGenFile = *CodeGenFileOrErr;
if (Error Err = runCodeGen(SplitModules[I].ModuleFilePath,
Result.TargetTriple, Args, CodeGenFile, C))
@@ -994,14 +1000,17 @@ static Error runSYCLLink(ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs,
if (!SPIRVDumpDir.empty() && !DryRun) {
SmallString<128> DumpFile(SPIRVDumpDir);
- sys::path::append(DumpFile, sys::path::filename(CodeGenFile));
+ sys::path::append(DumpFile, Twine(Prefix) + OutputFileNameExt);
if (std::error_code EC = sys::fs::copy_file(CodeGenFile, DumpFile))
return createFileError(DumpFile, EC);
}
SplitModules[I].ModuleFilePath = CodeGenFile;
if (IsAOTCompileNeeded) {
- std::string AOTFile = (Stem + "_" + Twine(I) + ".out").str();
+ auto AOTFileOrErr = createTempFile(Args, Prefix, "out");
+ if (!AOTFileOrErr)
+ return AOTFileOrErr.takeError();
+ StringRef AOTFile = *AOTFileOrErr;
if (Error Err = runAOTCompile(CodeGenFile, AOTFile, Args))
return Err;
SplitModules[I].ModuleFilePath = AOTFile;
More information about the cfe-commits
mailing list