Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions clang/test/Driver/clang-linker-wrapper-zstd.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// REQUIRES: zstd && system-linux && x86-registered-target

// clang-linker-wrapper compression test: checks that the wrapper compresses
// SYCL device images when --compress is set, tags them with
// SYCLBinaryImageFormat::BIF_Compressed (int8 value 4) in the emitted wrapper
// module, honors --compression-level=, and reports invalid values.

// --- Prepare test data: build a bitcode module, pack it into an
// llvm-offload-binary, and embed it into an ELF object as the linker-wrapper
// input. The bitcode is large enough (well over the 512-byte compression
// threshold in wrapSYCLBinariesFromFile) that compression actually runs.

Comment on lines +8 to +12

@YuriPlyakhin YuriPlyakhin Jul 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. I think comments are unnecessarily verbose.

Suggested change
// --- Prepare test data: build a bitcode module, pack it into an
// llvm-offload-binary, and embed it into an ELF object as the linker-wrapper
// input. The bitcode is large enough (well over the 512-byte compression
// threshold in wrapSYCLBinariesFromFile) that compression actually runs.
// Prepare test data. The bitcode is over the 512-byte compression
// threshold, so the compression actually runs.

// RUN: %clang -cc1 -fsycl-is-device -disable-llvm-passes -triple=spir64-unknown-unknown %s -emit-llvm-bc -o %t.device.bc
// RUN: llvm-offload-binary -o %t.fat --image=file=%t.device.bc,kind=sycl,triple=spir64-unknown-unknown
// RUN: %clang -cc1 %s -triple=x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.fat

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we also need x86-registered-target in REQUIRES

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// RUN: touch %t.devicelib.bc

// With --compress and --wrapper-verbose the compression path fires and the
// wrapped image's Format slot in %__sycl.tgt_device_image is i8 4
// (BIF_Compressed).
// RUN: clang-linker-wrapper --print-wrapped-module --host-triple=x86_64-unknown-linux-gnu \
// RUN: --bitcode-library=spir64-unknown-unknown=%t.devicelib.bc \
// RUN: -sycl-post-link-options="-split=auto -symbols -properties" \
// RUN: --compress --compression-level=9 --wrapper-verbose \
// RUN: %t.o -o %t.out --linker-path="/usr/bin/ld" 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-COMPRESS

// Capture the original and compressed sizes.
// CHECK-COMPRESS: [Compression] Original image size: [[#ORIG:]]
// CHECK-COMPRESS: [Compression] Compressed image size: [[#COMP:]]
// CHECK-COMPRESS: [Compression] Compression level used: 9
//
// Assert that the compressed size is strictly smaller than the original size
// (i.e. ORIG - COMP > 0). FileCheck's numeric matching form only supports the
// `==` constraint — there is no `<` or `>` — but sub() rejects underflow at
// expression-evaluation time. We attach sub(ORIG, COMP) to a CHECK-NOT
// pattern that begins with a sentinel string never present in the output:
// * when ORIG > COMP, sub() succeeds; the substituted pattern is
// "COMPRESSION_SIZE_CHECK<some-number>", which doesn't appear, so the
// CHECK-NOT is satisfied vacuously.
// * when COMP >= ORIG, sub() underflows and FileCheck fails the pattern.
Comment on lines +33 to +41

@YuriPlyakhin YuriPlyakhin Jul 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. too verbose for me :)

Suggested change
// Assert that the compressed size is strictly smaller than the original size
// (i.e. ORIG - COMP > 0). FileCheck's numeric matching form only supports the
// `==` constraint — there is no `<` or `>` — but sub() rejects underflow at
// expression-evaluation time. We attach sub(ORIG, COMP) to a CHECK-NOT
// pattern that begins with a sentinel string never present in the output:
// * when ORIG > COMP, sub() succeeds; the substituted pattern is
// "COMPRESSION_SIZE_CHECK<some-number>", which doesn't appear, so the
// CHECK-NOT is satisfied vacuously.
// * when COMP >= ORIG, sub() underflows and FileCheck fails the pattern.
// Assert ORIG - COMP > 0.
// FileCheck's numeric matching form only supports the
// `==` constraint, but sub() rejects underflow at
// expression-evaluation time. We attach sub(ORIG, COMP) to a CHECK-NOT
// pattern that begins with a sentinel string never present in the output:
// * when ORIG > COMP, sub() succeeds; the substituted pattern is
// "COMPRESSION_SIZE_CHECK<some-number>", which doesn't appear, so the
// CHECK-NOT is satisfied.
// * when COMP >= ORIG, sub() underflows and FileCheck fails the pattern.

// CHECK-COMPRESS-NOT: COMPRESSION_SIZE_CHECK[[#sub(ORIG, COMP)]]
// The tgt_device_image struct is { i16 Version, i8 Kind, i8 Format, ptr ... };
// Kind for SYCL is 4, Format for BIF_Compressed is also 4.
Comment on lines +43 to +44

@YuriPlyakhin YuriPlyakhin Jul 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit. All we need is to check BIF_Compressed and it is already explained in the comments above that it is 4. The check is fine, but I don't think we need to provide verbose comments explaining each small detail not related to the functionality we test.

Suggested change
// The tgt_device_image struct is { i16 Version, i8 Kind, i8 Format, ptr ... };
// Kind for SYCL is 4, Format for BIF_Compressed is also 4.

// CHECK-COMPRESS: @.sycl_offloading.device_images = internal unnamed_addr constant [1 x %__sycl.tgt_device_image] [%__sycl.tgt_device_image { i16 {{[0-9]+}}, i8 4, i8 4,

// Without --compress the image is left untagged (Format = BIF_None = 0) and
// no [Compression] verbose lines are emitted.
// RUN: clang-linker-wrapper --print-wrapped-module --host-triple=x86_64-unknown-linux-gnu \
// RUN: --bitcode-library=spir64-unknown-unknown=%t.devicelib.bc \
// RUN: -sycl-post-link-options="-split=auto -symbols -properties" \
// RUN: --wrapper-verbose \
// RUN: %t.o -o %t.out --linker-path="/usr/bin/ld" 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-NO-COMPRESS

// CHECK-NO-COMPRESS-NOT: [Compression]
// CHECK-NO-COMPRESS: @.sycl_offloading.device_images = internal unnamed_addr constant [1 x %__sycl.tgt_device_image] [%__sycl.tgt_device_image { i16 {{[0-9]+}}, i8 4, i8 0,

// A non-integer --compression-level= is diagnosed.
// RUN: not clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu \
// RUN: --bitcode-library=spir64-unknown-unknown=%t.devicelib.bc \
// RUN: -sycl-post-link-options="-split=auto -symbols -properties" \
// RUN: --compress --compression-level=notanumber \
// RUN: %t.o -o %t.out --linker-path="/usr/bin/ld" 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-BAD-LEVEL

// CHECK-BAD-LEVEL: invalid value for --offload-compression-level=: 'notanumber'

template <typename t, typename Func>
__attribute__((sycl_kernel)) void kernel(const Func &func) {
func();
}

extern "C" {
// Symbols so the linker doesn't fail resolving them.
void __sycl_register_lib(void *) {}
void __sycl_unregister_lib(void *) {}
}

int main() {
kernel<class fake_kernel>([](){});
}
26 changes: 26 additions & 0 deletions clang/test/Driver/sycl-offload-new-driver-compression.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/// Check that '--offload-compress' and '--offload-compression-level=' are
/// forwarded to clang-linker-wrapper under the new offloading driver via the
/// existing addOffloadCompressArgs() plumbing (as '--compress' and
/// '--compression-level='). The wrapper consumes them in
/// wrapSYCLBinariesFromFile to zstd-compress each SYCL device image and tag
/// it with BIF_Compressed.

// RUN: %clangxx -### -fsycl --offload-new-driver --offload-compress \
// RUN: --offload-compression-level=3 %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-COMPRESS
// CHECK-COMPRESS: {{.*}}clang-linker-wrapper{{.*}}"--compress"{{.*}}"--compression-level=3"

// RUN: %clangxx -### -fsycl --offload-new-driver %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-NO-COMPRESS
// CHECK-NO-COMPRESS-NOT: {{.*}}clang-linker-wrapper{{.*}}"--compress"

// --no-offload-compress overrides an earlier --offload-compress.
// RUN: %clangxx -### -fsycl --offload-new-driver --offload-compress \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we check the opposite case, where we have --no-offload-compress --offload-compress

// RUN: --no-offload-compress %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-NO-COMPRESS

// ...and vice versa: a later --offload-compress wins over --no-offload-compress.
// RUN: %clangxx -### -fsycl --offload-new-driver --no-offload-compress \
// RUN: --offload-compress %s 2>&1 \
// RUN: | FileCheck %s --check-prefix=CHECK-COMPRESS-LAST
// CHECK-COMPRESS-LAST: {{.*}}clang-linker-wrapper{{.*}}"--compress"
38 changes: 38 additions & 0 deletions clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,41 @@ static Expected<StringRef> runAOTCompile(StringRef InputFile,
"Unsupported SYCL Triple and Arch");
}

/// Compress each SYCL device image in-place when --compress is set,
/// tagging its Format as BIF_Compressed. --compress and --compression-level=
/// are the same flags HIP forwards to clang-offload-bundler;
static Error compressImages(SmallVectorImpl<offloading::SYCLImage> &Images,
const ArgList &Args) {
if (!Args.hasArg(OPT_compress))
return Error::success();

int Level = 10;
if (auto *A = Args.getLastArg(OPT_compression_level_eq))
if (StringRef(A->getValue()).getAsInteger(10, Level))
return createStringError(
"invalid value for --offload-compression-level=: '%s'",
A->getValue());

for (auto &Image : Images) {
SmallVector<uint8_t, 0> CompressedBytes;
Expected<bool> DidCompressOrErr = offloading::compressSYCLDeviceImage(
ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *>(Image.Image->getBufferStart()),
Image.Image->getBufferSize()),
CompressedBytes, Level, /*Threshold=*/512, Verbose);
if (!DidCompressOrErr)
return DidCompressOrErr.takeError();
if (!*DidCompressOrErr)
continue;
Image.Image = MemoryBuffer::getMemBufferCopy(
StringRef(reinterpret_cast<const char *>(CompressedBytes.data()),
CompressedBytes.size()),
Image.Image->getBufferIdentifier());
Image.Format = offloading::SYCLBinaryImageFormat::BIF_Compressed;
}
return Error::success();
}

/// Reads device images from the given \p InputFile and wraps them
/// in one LLVM IR Module as a constant data.
///
Expand Down Expand Up @@ -1178,6 +1213,9 @@ wrapSYCLBinariesFromFile(ArrayRef<module_split::SplitModule> SplitModules,
ImageTarget, SI.CompileOptions, SI.LinkOptions);
}

if (Error E = compressImages(Images, Args))
return std::move(E);

LLVMContext C;
Module M("offload.wrapper.object", C);
M.setTargetTriple(Triple(
Expand Down
2 changes: 1 addition & 1 deletion clang/tools/clang-offload-wrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(LLVM_LINK_COMPONENTS BitWriter BitReader Core Object Support TransformUtils TargetParser)
set(LLVM_LINK_COMPONENTS BitWriter BitReader Core FrontendOffloading Object Support TransformUtils TargetParser)

add_clang_tool(clang-offload-wrapper
ClangOffloadWrapper.cpp
Expand Down
Loading
Loading