[SYCL] Support --offload-compress in clang-linker-wrapper#22605
[SYCL] Support --offload-compress in clang-linker-wrapper#22605bviyer wants to merge 14 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>
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 |
| // --- 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. | ||
|
|
There was a problem hiding this comment.
nit. I think comments are unnecessarily verbose.
| // --- 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. |
| // 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. |
There was a problem hiding this comment.
nit. too verbose for me :)
| // 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. |
| // 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. |
There was a problem hiding this comment.
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.
| // 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. |
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! :)
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.