[SYCL] Support --offload-compress in clang-linker-wrapper#22605
[SYCL] Support --offload-compress in clang-linker-wrapper#22605bviyer wants to merge 21 commits into
Conversation
| // The --compress flag above is a different mechanism: for HIP only, it is | ||
| // forwarded verbatim to a clang-offload-bundler subprocess which does the | ||
| // compression itself. | ||
| def offload_compress : Flag<["--"], "offload-compress">, |
There was a problem hiding this comment.
Instead of adding new flags could we just re-use the existing ones? Then I don't think we need any driver changes, it seems addOffloadCompressArgs should do what we need if we make clang-linker-wrapper do the right thing
There was a problem hiding this comment.
Fixed. I made it use --compress and --compress-level=
| BIF_Native, // Native Image kind | ||
| BIF_SPIRV, // SPIR-V | ||
| BIF_LLVMBC, // LLVM bitcode | ||
| BIF_CompressedNone // zstd-compressed image; format-of-original unknown |
There was a problem hiding this comment.
Maybe we could just call it BIF_Compressed unless we need different BIFCompressed versions for different original formats, which it seems like we don't
| case SYCLBinaryImageFormat::BIF_LLVMBC: | ||
| return 3; | ||
| case SYCLBinaryImageFormat::BIF_CompressedNone: | ||
| return 4; |
There was a problem hiding this comment.
I assume the SYCL runtime already knows how to handle this and the format matches the old offload model?
| "'--offload-compress' is specified but zstd is not available"); | ||
|
|
||
| int Level = 10; // default zstd level, matches clang-offload-wrapper | ||
| int Threshold = 512; // skip compression below this many bytes |
There was a problem hiding this comment.
I wonder if we can h ave a shared function that both clang-offload-wrapper and clang-linker-wrapper use until we remove old offload model support. Maybe we could put it in llvm/lib/Frontend/Offloading/Utility.cpp?
There was a problem hiding this comment.
This is fixed here ec31530. Its a bit messy.
2361b30 to
ec31530
Compare
|
@bviyer, please, remove |
Sorry. Fixed. |
sarnex
left a comment
There was a problem hiding this comment.
nice cleanup! some comments below
| // CHECK-NO-COMPRESS-NOT: {{.*}}clang-linker-wrapper{{.*}}"--compress" | ||
|
|
||
| // --no-offload-compress overrides an earlier --offload-compress. | ||
| // RUN: %clangxx -### -fsycl --offload-new-driver --offload-compress \ |
There was a problem hiding this comment.
should we check the opposite case, where we have --no-offload-compress --offload-compress
| // consume them here and zstd-compress each image payload in place, flipping | ||
| // its Format to BIF_Compressed. The SYCL runtime recognizes that tag and | ||
| // decompresses lazily via CompressedRTDeviceBinaryImage. | ||
| if (Args.hasArg(OPT_compress)) { |
There was a problem hiding this comment.
could we move this all into a separate function like compressSYCLImages or something?
| // Forward-declared instead of #include "llvm/Frontend/Offloading/Utility.h" | ||
| // because pulling in that header (transitively llvm/Object/OffloadBinary.h) | ||
| // makes llvm::object::OffloadKind visible via `using namespace llvm::object` | ||
| // above, which collides with the local anonymous-namespace `enum OffloadKind` |
There was a problem hiding this comment.
IMO instead of this workaround we should fix the local anonymous namespace issue, maybe rename it to something that shows how it's different from the OffloadBinary.h one if really we need a separate enum
There was a problem hiding this comment.
Renamed to OffloadModelKind (didn't want to remove the kind since it was used in several places and looks like it means something).
| // pre-refactor behavior which errored regardless of image kind or | ||
| // format. The guarded branch below would otherwise skip the check for | ||
| // non-SYCL / explicit-format images. | ||
| if (OffloadCompressDevImgs && !llvm::compression::zstd::isAvailable()) |
There was a problem hiding this comment.
IMO changing the error behavior is fine, if we don't actually try to compress anything it's fine to not error, having this extra check is confusing
|
|
||
| for (auto &Image : Images) { | ||
| SmallVector<uint8_t, 0> CompressedBytes; | ||
| Expected<bool> Compressed = offloading::compressSYCLDeviceImage( |
There was a problem hiding this comment.
can we make it a bit clearer on what the return type is with something like
Expected<bool> DidCompressOrErr = ...
if (!DidCompressOrErr)
return DidCompressOrErr.takeError();
bool DidCompres = *DidCompressOrErr;
/// use DidCompress
At first it was unclear to me what the bool represented so I think making it explicit would help readability
There was a problem hiding this comment.
Fixed. However, I didn't create a new variable called DidCompress I just used the check on (*DidCompressOrErr). Generally, I do not create new variables unless the item is used in multiple places.
There was a problem hiding this comment.
i think we lost the LLVM_ENABLE_EXCEPTIONS handling which we probably need
sarnex
left a comment
There was a problem hiding this comment.
did you forget to push your changes to the PR? i'm not seeing any new commits with fixes
sarnex
left a comment
There was a problem hiding this comment.
lgtm thanks! just some final nits
| /// clang-offload-wrapper also uses; the surrounding loop isn't shared | ||
| /// because that tool operates on one image at a time and emits an LLVM | ||
| /// Constant directly, not a SYCLImage list. | ||
| static Error compressSYCLImages(SmallVectorImpl<offloading::SYCLImage> &Images, |
There was a problem hiding this comment.
sorry instead can we put this in the namespace sycl starting at line ~515 and call it compressImages? Thanks
| /// tagging its Format as BIF_Compressed. --compress and --compression-level= | ||
| /// are the same flags HIP forwards to clang-offload-bundler; for SYCL we | ||
| /// consume them here. The per-image work delegates to | ||
| /// offloading::compressSYCLDeviceImage, which the old-driver |
There was a problem hiding this comment.
im not sure we need to mention the old offloading model here or anywhere in code comments in new offload model code
| // enum. | ||
| enum OffloadKind { | ||
| // enum. Distinct from llvm::object::OffloadKind (in Object/OffloadBinary.h), | ||
| // which is the 16-bit bitmask on the OffloadBinary wire format |
There was a problem hiding this comment.
im not sure what wire format means
There was a problem hiding this comment.
Yep, it was an artifact of something I was typing. I reworded to "...in the OffloadBinary header."
| // command-line-parsed tag with an extra Host slot and First/Last sentinels, | ||
| // serialized as a uint8_t into the SYCL runtime's sycl_device_binary_struct | ||
| // Kind field. | ||
| enum OffloadModelKind { |
Forward --offload-compress (and --offload-compression-level=) from the driver to clang-linker-wrapper under the new offloading driver. In wrapSYCLBinariesFromFile, zstd-compress each SYCL device image whose payload is at or above --offload-compression-threshold= (default 512 bytes) and tag it with the new BIF_CompressedNone image format so the SYCL runtime decompresses lazily via CompressedRTDeviceBinaryImage.
ClangOffLoadWrappper and ClangLinkerWrapper.
e45280d to
e79b396
Compare
YuriPlyakhin
left a comment
There was a problem hiding this comment.
Please update PR description: BIF_CompressedNone -> BIF_Compressed
YuriPlyakhin
left a comment
There was a problem hiding this comment.
Could you please add a functional test for the new compression path to test changes in clang-linker-wrapper.cpp?
Test would run clang-linker-wrapper --compress and would verify the emitted image is tagged with BIF_Compressed.
Also it would check invalid value for --offload-compression-level= error message.
FYI: The old wrapper is tested by clang-offload-wrapper-zstd.c
Co-authored-by: Yury Plyakhin <yury.plyakhin@intel.com>
Fixed. |
path in ClangLinkerWrapper.
Added test here: 48bf119 |
|
|
||
| // 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 |
There was a problem hiding this comment.
i think we also need x86-registered-target in REQUIRES
| // CHECK-COMPRESS: [Compression] Original image size: | ||
| // CHECK-COMPRESS: [Compression] Compressed image size: |
There was a problem hiding this comment.
i think we should be able to do basic math operations in Filecheck https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-numeric-substitution-blocks
could we at least verify the compressed size is smaller than the original?
|
@intel/llvm-gatekeepers please consider merging |
YuriPlyakhin
left a comment
There was a problem hiding this comment.
LGTM. Few nits - all related to too verbose comments. Feel free to ignore. Thanks! :)
Co-authored-by: Yury Plyakhin <yury.plyakhin@intel.com>
Co-authored-by: Yury Plyakhin <yury.plyakhin@intel.com>
There was a problem hiding this comment.
Pull request overview
This PR adds SYCL device-image compression support to the new offloading driver path by forwarding --offload-compress (and --offload-compression-level=) to clang-linker-wrapper, then zstd-compressing SYCL images in wrapSYCLBinariesFromFile and tagging them as BIF_Compressed so the SYCL runtime can lazily decompress them.
Changes:
- Introduces
offloading::compressSYCLDeviceImage()as a shared helper for zstd compression with threshold/verbosity support. - Extends SYCL wrapper image-format handling with a new
SYCLBinaryImageFormat::BIF_Compressed(mapped to int8 value4). - Adds
clang-linker-wrapperin-place compression of SYCL device images when--compressis present, plus new driver and wrapper tests.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| llvm/lib/Frontend/Offloading/Utility.cpp | Implements compressSYCLDeviceImage() using LLVM zstd support and verbose size reporting. |
| llvm/include/llvm/Frontend/Offloading/Utility.h | Declares compressSYCLDeviceImage() API and documents parameters/defaults. |
| llvm/lib/Frontend/Offloading/SYCLOffloadWrapper.cpp | Maps new BIF_Compressed to serialized int8 format value. |
| llvm/include/llvm/Frontend/Offloading/SYCLOffloadWrapper.h | Adds BIF_Compressed to the SYCL image-format enum. |
| clang/tools/clang-offload-wrapper/CMakeLists.txt | Links clang-offload-wrapper against FrontendOffloading to use the shared helper. |
| clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp | Switches compression logic to call the shared helper; renames kind enum to OldOffloadKind. |
| clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp | Adds --compress path that compresses SYCL images and tags them BIF_Compressed. |
| clang/test/Driver/sycl-offload-new-driver-compression.cpp | Verifies driver forwarding of compression flags to clang-linker-wrapper. |
| clang/test/Driver/clang-linker-wrapper-zstd.cpp | End-to-end wrapper test: compression happens, format tagged, level parsing and diagnostics. |
| 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) |
| // Offload models supported by this tool. The support basically means mapping | ||
| // a string representation given at the command line to a value from this | ||
| // enum. | ||
| enum OffloadKind { | ||
| // enum. Distinct from llvm::object::OffloadKind (in Object/OffloadBinary.h), | ||
| // which is the 16-bit bitmask stored in the OffloadBinary header | ||
| // (OFK_None/OFK_OpenMP/... = powers of two). This enum is a sequential | ||
| // command-line-parsed tag with an extra Host slot and First/Last sentinels, | ||
| // serialized as a uint8_t into the SYCL runtime's sycl_device_binary_struct | ||
| // Kind field. | ||
| enum OldOffloadKind { | ||
| Unknown = 0, |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Yury Plyakhin <yury.plyakhin@intel.com>
| if (!Args.hasArg(OPT_compress)) | ||
| return Error::success(); | ||
|
|
||
| int Level = 10; |
There was a problem hiding this comment.
The default compression level 10 is hardcoded here, and again as the default parameter in Utility.h line 249. Can there be a shared named constant, e.g. constexpr int DefaultSYCLCompressionLevel = 10 in Utility.h so a future change to the default only needs to happen in one place/the defaults across tool wrappers remain synced.
| BIF_Native, // Native Image kind | ||
| BIF_SPIRV, // SPIR-V | ||
| BIF_LLVMBC, // LLVM bitcode | ||
| BIF_Compressed // zstd-compressed image; format-of-original unknown |
There was a problem hiding this comment.
When the SYCL runtime decompresses the image, does it know whether the original image is SPIR-V, LLVM bitcode, or native?
Does the runtime always assume a fixed format post-decompression? (e.g. always SPIR-V/LLVM BC at this pipeline stage).
Would be helpful to document that information here.
There was a problem hiding this comment.
Q: Also, how can we differentiate between a JIT image and an AOT image that both get tagged BIF_Compressed ?
Co-authored-by: Srividya Sundaram <srividya.sundaram@intel.com>
Co-authored-by: Srividya Sundaram <srividya.sundaram@intel.com>
Co-authored-by: Srividya Sundaram <srividya.sundaram@intel.com>
Forward --offload-compress (and --offload-compression-level=) from the driver to clang-linker-wrapper under the new offloading driver. In wrapSYCLBinariesFromFile, zstd-compress each SYCL device image whose payload is at or above --offload-compression-threshold= (default 512 bytes) and tag it with the new BIF_Compressed image format so the SYCL runtime decompresses lazily via CompressedRTDeviceBinaryImage.
Significant help from Claude Opus 4.7.