[clang] 0e40e9e - [clang-sycl-linker][test] Improve dry-run mode and tighten test coverage (#200513)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 2 18:02:36 PDT 2026
Author: Yury Plyakhin
Date: 2026-06-02T18:02:30-07:00
New Revision: 0e40e9e341cf78662ab468fb8cdd6cac7e58ad1f
URL: https://github.com/llvm/llvm-project/commit/0e40e9e341cf78662ab468fb8cdd6cac7e58ad1f
DIFF: https://github.com/llvm/llvm-project/commit/0e40e9e341cf78662ab468fb8cdd6cac7e58ad1f.diff
LOG: [clang-sycl-linker][test] Improve dry-run mode and tighten test coverage (#200513)
- Rework `--dry-run` in `clang-sycl-linker` so it skips all real output
(writing bitcode, executing tools, etc.).
- The `link:`, `sycl-module-split:`, and a new `sycl-bundle:` summary
line are now gated on `-v` alone.
- Tighten `sycl-bundle:` checks in `basic.ll`, `split-mode.ll`, and
`triple.ll` to pin kind, triple, and arch (instead of just kind),
and add `-NOT: {{.+}}` after fully-covered dry-run check groups.
- replace the `clang-sycl-linker` + `llvm-objdump --offloading`
round-trip with a single `--dry-run -v` invocation.
- add dedicated `non-dry-run` mode test to verify code paths not exposed
in `dry-run`.
Assisted by Claude.
Added:
Modified:
clang/test/OffloadTools/clang-sycl-linker/basic.ll
clang/test/OffloadTools/clang-sycl-linker/split-mode.ll
clang/test/OffloadTools/clang-sycl-linker/triple.ll
clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
Removed:
################################################################################
diff --git a/clang/test/OffloadTools/clang-sycl-linker/basic.ll b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
index 3bd40eda90f71..bd65a35bd8384 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/basic.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/basic.ll
@@ -25,16 +25,14 @@
; MISSING: Input file '{{.*}}-missing.bc' does not exist
;
; Test the dry run of a simple case to link two input files.
+; Test that IMG_SPIRV image kind is set for non-AOT compilation.
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=none %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 libfiles: output: [[LLVMLINKOUT:.*]].bc
; 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: {{.+}}
;
-; Test that IMG_SPIRV image kind is set for non-AOT compilation.
-; RUN: llvm-objdump --offloading %t/spirv.out | FileCheck %s --check-prefix=IMAGE-KIND-SPIRV
-; IMAGE-KIND-SPIRV: kind spir-v
-;
; Test the dry run of a simple case with device library files specified.
; RUN: mkdir -p %t/libs
; RUN: touch %t/libs/lib1.bc
@@ -43,6 +41,8 @@
; RUN: | FileCheck %s --check-prefix=DEVLIBS
; DEVLIBS: link: inputs: {{.*}}.bc libfiles: {{.*}}lib1.bc, {{.*}}lib2.bc output: [[LLVMLINKOUT:.*]].bc
; DEVLIBS-NEXT: LLVM backend: input: [[LLVMLINKOUT]].bc, output: a_0.spv
+; DEVLIBS-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
+; DEVLIBS-NOT: {{.+}}
;
; Test -L short form (joined) and --bc-library= joined form.
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=none %t/input1.bc -L%t/libs --bc-library=lib1.bc -o a.spv 2>&1 \
@@ -87,27 +87,26 @@
; NO-DIR-AS-LIB: 'libs' library file not found
;
; Test AOT compilation for an Intel GPU.
+; Test that IMG_Object image kind is set for AOT compilation (Intel GPU).
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=none -arch=bmg_g21 %t/input1.bc %t/input2.bc -o %t/aot-gpu.out 2>&1 \
; RUN: --ocloc-options="-a -b" \
; RUN: | FileCheck %s --check-prefix=AOT-INTEL-GPU
; AOT-INTEL-GPU: link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: 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
-;
-; Test that IMG_Object image kind is set for AOT compilation (Intel GPU).
-; RUN: llvm-objdump --offloading %t/aot-gpu.out | FileCheck %s --check-prefix=IMAGE-KIND-OBJECT
-; IMAGE-KIND-OBJECT: kind elf
+; AOT-INTEL-GPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: bmg_g21
+; AOT-INTEL-GPU-NOT: {{.+}}
;
; Test AOT compilation for an Intel CPU.
+; Test that IMG_Object image kind is set for AOT compilation (Intel CPU).
; RUN: clang-sycl-linker --dry-run -v --module-split-mode=none -arch=graniterapids %t/input1.bc %t/input2.bc -o %t/aot-cpu.out 2>&1 \
; RUN: --opencl-aot-options="-a -b" \
; RUN: | FileCheck %s --check-prefix=AOT-INTEL-CPU
; AOT-INTEL-CPU: link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: 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
-;
-; Test that IMG_Object image kind is set for AOT compilation (Intel CPU).
-; RUN: llvm-objdump --offloading %t/aot-cpu.out | FileCheck %s --check-prefix=IMAGE-KIND-OBJECT
+; AOT-INTEL-CPU-NEXT: sycl-bundle: image kind: o, triple: spirv64, arch: graniterapids
+; AOT-INTEL-CPU-NOT: {{.+}}
;
; Check that the output file must be specified.
; RUN: not clang-sycl-linker --dry-run %t/input1.bc %t/input2.bc 2>&1 \
@@ -125,6 +124,18 @@
; RUN: llvm-objdump --offloading %t/no-entry-points.out | FileCheck %s --check-prefix=NO-ENTRY-POINTS
; NO-ENTRY-POINTS: OFFLOADING IMAGE [0]:
; NO-ENTRY-POINTS: producer sycl
+;
+; Real (non-dry-run) run: input1/input2 have
diff erent
+; sycl-module-id values, so two images are produced and packaged on disk.
+; Covers write on disk logic not reachable under --dry-run.
+; RUN: clang-sycl-linker %t/input1.bc %t/input2.bc -o %t/srcsplit.out
+; RUN: llvm-objdump --offloading %t/srcsplit.out | FileCheck %s --check-prefix=SRCSPLIT
+; SRCSPLIT: OFFLOADING IMAGE [0]:
+; SRCSPLIT: kind spir-v
+; SRCSPLIT: triple spirv64
+; SRCSPLIT: OFFLOADING IMAGE [1]:
+; SRCSPLIT: kind spir-v
+; SRCSPLIT: triple spirv64
;--- input1.ll
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
diff --git a/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll b/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll
index 56a6b3e082550..d10dbacf259fe 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/split-mode.ll
@@ -14,6 +14,7 @@
; RUN: | FileCheck %s --check-prefix=SPLIT-NONE
; SPLIT-NONE: link: inputs: {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc
; 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: {{.+}}
;
; Test the split mode ("kernel"): each SPIR_KERNEL function produces its own device image.
@@ -27,6 +28,9 @@
; 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: {{$}}
; SPLIT-KERNEL-NOT: {{.+}}
;
; Test default split mode ('source'): no --module-split-mode flag needed.
@@ -45,6 +49,8 @@
; 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: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
+; SPLIT-SRC-NEXT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
; SPLIT-SRC-NOT: {{.+}}
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1"
diff --git a/clang/test/OffloadTools/clang-sycl-linker/triple.ll b/clang/test/OffloadTools/clang-sycl-linker/triple.ll
index 01e88305138da..222930987ce16 100644
--- a/clang/test/OffloadTools/clang-sycl-linker/triple.ll
+++ b/clang/test/OffloadTools/clang-sycl-linker/triple.ll
@@ -6,16 +6,16 @@
;
; Test when explicit -triple= is used. Input does not supply a triple.
; RUN: llvm-as %t/no-triple.ll -o %t/no-triple.bc
-; RUN: clang-sycl-linker -triple=spirv64 %t/no-triple.bc -o %t/no-triple-input.out
-; RUN: llvm-objdump --offloading %t/no-triple-input.out | FileCheck %s --check-prefix=NO-TRIPLE-INPUT
-; NO-TRIPLE-INPUT: triple spirv64
+; RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t/no-triple.bc -o %t/no-triple-input.out 2>&1 \
+; RUN: | FileCheck %s --check-prefix=NO-TRIPLE-INPUT
+; NO-TRIPLE-INPUT: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
;
; Test that triple was inferred from inputs and recorded in the offload image.
; RUN: llvm-as %t/input1.ll -o %t/input1.bc
; RUN: llvm-as %t/input2.ll -o %t/input2.bc
-; RUN: clang-sycl-linker --module-split-mode=none %t/input1.bc %t/input2.bc -o %t/spirv.out
-; RUN: llvm-objdump --offloading %t/spirv.out | FileCheck %s --check-prefix=TRIPLE-INFERENCE
-; TRIPLE-INFERENCE: triple spirv64
+; RUN: clang-sycl-linker --dry-run -v --module-split-mode=none %t/input1.bc %t/input2.bc -o %t/spirv.out 2>&1 \
+; RUN: | FileCheck %s --check-prefix=TRIPLE-INFERENCE
+; TRIPLE-INFERENCE: sycl-bundle: image kind: spv, triple: spirv64, arch: {{$}}
;
; Test error on mismatched triple between inputs.
; RUN: llvm-as %t/input-mismatch.ll -o %t/input-mismatch.bc
diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
index b67d4902af173..e5e092c4737ec 100644
--- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
+++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp
@@ -56,7 +56,7 @@ using namespace llvm::opt;
using namespace llvm::object;
using namespace clang;
-/// Print commands/steps with arguments without executing.
+/// Print commands with arguments without executing.
static bool DryRun = false;
/// Print verbose output.
@@ -136,7 +136,7 @@ static std::string getMainExecutable(const char *Name) {
static Expected<StringRef>
createTempFile(const ArgList &Args, const Twine &Prefix, StringRef Extension) {
SmallString<128> Path;
- if (Args.hasArg(OPT_save_temps)) {
+ if (Args.hasArg(OPT_save_temps) || DryRun) {
// Generate a unique path name without creating a file
sys::fs::createUniquePath(Prefix + "-%%%%%%." + Extension, Path,
/*MakeAbsolute=*/false);
@@ -152,7 +152,7 @@ createTempFile(const ArgList &Args, const Twine &Prefix, StringRef Extension) {
static Expected<std::string> findProgram(const ArgList &Args, StringRef Name,
ArrayRef<StringRef> Paths) {
- if (Args.hasArg(OPT_dry_run))
+ if (DryRun)
return Name.str();
ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
if (!Path)
@@ -178,10 +178,12 @@ static Error executeCommands(StringRef ExecutablePath,
if (Verbose || DryRun)
printCommands(Args);
- if (!DryRun)
- if (sys::ExecuteAndWait(ExecutablePath, Args))
- return createStringError(
- "'%s' failed", sys::path::filename(ExecutablePath).str().c_str());
+ if (DryRun)
+ return Error::success();
+
+ if (sys::ExecuteAndWait(ExecutablePath, Args))
+ return createStringError("'%s' failed",
+ sys::path::filename(ExecutablePath).str().c_str());
return Error::success();
}
@@ -298,7 +300,7 @@ static Expected<LinkResult> linkInputs(ArrayRef<std::string> InputFiles,
if (!BitcodeOutput)
return BitcodeOutput.takeError();
- if (Verbose || DryRun) {
+ if (Verbose) {
std::string Inputs = llvm::join(InputFiles.begin(), InputFiles.end(), ", ");
std::string LibInputs =
llvm::join((*BCLibFiles).begin(), (*BCLibFiles).end(), ", ");
@@ -354,11 +356,13 @@ static Expected<LinkResult> linkInputs(ArrayRef<std::string> InputFiles,
outs() << *LinkerOutput;
// Write the final output into 'BitcodeOutput' file.
- int FD = -1;
- if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD))
- return errorCodeToError(EC);
- llvm::raw_fd_ostream OS(FD, true);
- WriteBitcodeToFile(*LinkerOutput, OS);
+ if (!DryRun) {
+ int FD = -1;
+ if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD))
+ return errorCodeToError(EC);
+ llvm::raw_fd_ostream OS(FD, true);
+ WriteBitcodeToFile(*LinkerOutput, OS);
+ }
return LinkResult{std::move(LinkerOutput), SmallString<256>(*BitcodeOutput),
std::move(TargetTriple)};
@@ -380,6 +384,9 @@ static Error runCodeGen(StringRef File, const llvm::Triple &TargetTriple,
errs() << formatv("LLVM backend: input: {0}, output: {1}\n", File,
OutputFile);
+ if (DryRun)
+ return Error::success();
+
// Parse input module.
SMDiagnostic Err;
std::unique_ptr<Module> M = parseIRFile(File, Err, C);
@@ -635,11 +642,13 @@ splitDeviceCode(std::unique_ptr<Module> M, StringRef LinkedBitcodeFile,
if (!BitcodeFileOrErr)
return BitcodeFileOrErr.takeError();
- int FD = -1;
- if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeFileOrErr, FD))
- return errorCodeToError(EC);
- raw_fd_ostream OS(FD, /*shouldClose=*/true);
- WriteBitcodeToFile(*Part, OS);
+ if (!DryRun) {
+ int FD = -1;
+ if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeFileOrErr, FD))
+ return errorCodeToError(EC);
+ raw_fd_ostream OS(FD, /*shouldClose=*/true);
+ WriteBitcodeToFile(*Part, OS);
+ }
SplitModules.push_back(
{SmallString<256>(*BitcodeFileOrErr),
@@ -651,7 +660,7 @@ splitDeviceCode(std::unique_ptr<Module> M, StringRef LinkedBitcodeFile,
std::move(M), Categorizer, SplitCallback))
return Err;
- if (Verbose || DryRun) {
+ if (Verbose) {
errs() << formatv("sycl-module-split: input: {0}, mode: {1}\n",
LinkedBitcodeFile, splitModeToString(Mode));
for (const SplitModule &SI : SplitModules) {
@@ -761,13 +770,11 @@ static Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
SmallVector<OffloadingImage> Images;
for (SplitModule &SI : SplitModules) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =
- llvm::MemoryBuffer::getFileOrSTDIN(SI.ModuleFilePath);
- if (std::error_code EC = FileOrErr.getError()) {
- if (DryRun)
- FileOrErr = MemoryBuffer::getMemBuffer("");
- else
- return createFileError(SI.ModuleFilePath, EC);
- }
+ DryRun ? llvm::MemoryBuffer::getMemBuffer("")
+ : llvm::MemoryBuffer::getFileOrSTDIN(SI.ModuleFilePath);
+ if (!FileOrErr)
+ return createFileError(SI.ModuleFilePath, FileOrErr.getError());
+
OffloadingImage TheImage{};
TheImage.TheImageKind = IsAOTCompileNeeded ? IMG_Object : IMG_SPIRV;
TheImage.TheOffloadKind = OFK_SYCL;
@@ -780,10 +787,21 @@ static Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
Images.emplace_back(std::move(TheImage));
}
+ if (Verbose) {
+ for (const OffloadingImage &Image : Images)
+ errs() << formatv(
+ "sycl-bundle: image kind: {0}, triple: {1}, arch: {2}\n",
+ getImageKindName(Image.TheImageKind),
+ Image.StringData.lookup("triple"), Image.StringData.lookup("arch"));
+ }
+
llvm::SmallString<0> Buffer = OffloadBinary::write(Images);
if (Buffer.size() % OffloadBinary::getAlignment() != 0)
return createStringError("Offload binary has invalid size alignment");
+ if (DryRun)
+ return Error::success();
+
auto OutputOrErr = FileOutputBuffer::create(OutputFile, Buffer.size());
if (!OutputOrErr)
return OutputOrErr.takeError();
@@ -857,7 +875,7 @@ int main(int argc, char **argv) {
reportError(std::move(Err));
// Remove the temporary files created.
- if (!Args.hasArg(OPT_save_temps))
+ if (!Args.hasArg(OPT_save_temps) && !DryRun)
for (const auto &TempFile : TempFiles)
if (std::error_code EC = sys::fs::remove(TempFile))
reportError(createFileError(TempFile, EC));
More information about the cfe-commits
mailing list