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
