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));


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to