Skip to content

Commit 155fe2a

Browse files
[SYCL][Clang] Add support for device image compression (#15124)
This PR adds support for device image compression for the old offloading model. I'll make another follow-up PR to extend support for the new offload model. **Design summary**: ``` ZSTD (compression algo) ----> LLVMSupport (Interface) ------> clang-offload-wrapper (For compression) | ----------------------------------------------------- --------> SYCL RT (For decompression) ``` This PR introduces ZSTD (https://github.com/facebook/zstd) as a 3rd party dependency of DPCPP. **Similar to upstream LLVM, we expect user to have `zstd-dev` package installed on their machine** - we won't be installing zstd from sources. **How to use** To compress device images, add `--offload-compress` CLI option to your clang invocation. Note that we compress device images only if the size of device images exceeds a threshold, which is 512 bytes by default. Moreover, by default, we use ZSTD level 10 for compression. ZSTD compression levels provides a tradeoff between (de)compression time and compression ratio, and the compression level can be changed using `--offload-compression-level=<int>` CLI option.
1 parent 60f6e16 commit 155fe2a

23 files changed

+718
-10
lines changed

buildbot/configure.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ def do_configure(args):
177177
"-DLLVM_ENABLE_PROJECTS={}".format(llvm_enable_projects),
178178
"-DSYCL_BUILD_PI_HIP_PLATFORM={}".format(sycl_build_pi_hip_platform),
179179
"-DLLVM_BUILD_TOOLS=ON",
180+
"-DLLVM_ENABLE_ZSTD=ON",
181+
"-DLLVM_USE_STATIC_ZSTD=ON",
180182
"-DSYCL_ENABLE_WERROR={}".format(sycl_werror),
181183
"-DCMAKE_INSTALL_PREFIX={}".format(install_dir),
182184
"-DSYCL_INCLUDE_TESTS=ON", # Explicitly include all kinds of SYCL tests.

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10174,6 +10174,19 @@ void OffloadWrapper::ConstructJob(Compilation &C, const JobAction &JA,
1017410174
SmallString<128> TargetTripleOpt = TT.getArchName();
1017510175
bool WrapFPGADevice = false;
1017610176
bool FPGAEarly = false;
10177+
10178+
// Validate and propogate CLI options related to device image compression.
10179+
// -offload-compress
10180+
if (C.getInputArgs().getLastArg(options::OPT_offload_compress)) {
10181+
WrapperArgs.push_back(
10182+
C.getArgs().MakeArgString(Twine("-offload-compress")));
10183+
// -offload-compression-level=<>
10184+
if (Arg *A = C.getInputArgs().getLastArg(
10185+
options::OPT_offload_compression_level_EQ))
10186+
WrapperArgs.push_back(C.getArgs().MakeArgString(
10187+
Twine("-offload-compression-level=") + A->getValue()));
10188+
}
10189+
1017710190
if (Arg *A = C.getInputArgs().getLastArg(options::OPT_fsycl_link_EQ)) {
1017810191
WrapFPGADevice = true;
1017910192
FPGAEarly = (A->getValue() == StringRef("early"));
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// REQUIRES: zstd && (system-windows || system-linux)
2+
3+
// clang-offload-wrapper compression test: checks that the wrapper can compress the device images.
4+
// Checks the '--offload-compress', '--offload-compression-level', and '--offload-compression-threshold'
5+
// CLI options.
6+
7+
// --- Prepare test data by creating the debice binary image.
8+
// RUN: echo -e -n 'device binary image1\n' > %t.bin
9+
// RUN: echo -e -n '[Category1]\nint_prop1=1|10\n[Category2]\nint_prop2=1|20\n' > %t.props
10+
// RUN: echo -e -n 'kernel1\nkernel2\n' > %t.sym
11+
// RUN: echo -e -n 'Manifest file - arbitrary data generated by the toolchain\n' > %t.mnf
12+
// RUN: echo '[Code|Properties|Symbols|Manifest]' > %t.img1
13+
// RUN: echo %t.bin"|"%t.props"|"%t.sym"|"%t.mnf >> %t.img1
14+
15+
///////////////////////////////////////////////////////
16+
// Compress the test image using clang-offload-wrapper.
17+
///////////////////////////////////////////////////////
18+
19+
// RUN: clang-offload-wrapper -kind=sycl -target=TARGET -batch %t.img1 -o %t.wrapped.bc -v \
20+
// RUN: --offload-compress --offload-compression-level=9 --offload-compression-threshold=0 \
21+
// RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-COMPRESS
22+
23+
// CHECK-COMPRESS: [Compression] Original image size:
24+
// CHECK-COMPRESS: [Compression] Compressed image size:
25+
// CHECK-COMPRESS: [Compression] Compression level used: 9
26+
27+
///////////////////////////////////////////////////////////
28+
// Check that there is no compression when the threshold is set to a value higher than the image size
29+
// or '--offload-compress' is not set.
30+
///////////////////////////////////////////////////////////
31+
32+
// RUN: clang-offload-wrapper -kind=sycl -target=TARGET -batch %t.img1 -o %t.wrapped.bc -v \
33+
// RUN: --offload-compress --offload-compression-level=3 --offload-compression-threshold=1000 \
34+
// RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-NO-COMPRESS
35+
36+
// RUN: clang-offload-wrapper -kind=sycl -target=TARGET -batch %t.img1 -o %t.wrapped.bc -v \
37+
// RUN: --offload-compression-level=3 --offload-compression-threshold=0 \
38+
// RUN: 2>&1 | FileCheck %s --check-prefix=CHECK-NO-COMPRESS
39+
40+
// CHECK-NO-COMPRESS-NOT: [Compression] Original image size:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
///
2+
/// Check if '--offload-compress' and '--offload-compression-level' CLI
3+
/// options are passed to the clang-offload-wrapper.
4+
///
5+
6+
// RUN: %clangxx -### -fsycl --offload-compress --offload-compression-level=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-COMPRESS
7+
// CHECK-COMPRESS: {{.*}}clang-offload-wrapper{{.*}}"-offload-compress"{{.*}}"-offload-compression-level=3"{{.*}}
8+
9+
// Make sure that the compression options are not passed when --offload-compress is not set.
10+
// RUN: %clangxx -### -fsycl %s 2>&1 | FileCheck %s --check-prefix=CHECK-NO-COMPRESS
11+
// RUN: %clangxx -### -fsycl --offload-compression-level=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-NO-COMPRESS
12+
13+
// CHECK-NO-COMPRESS-NOT: {{.*}}clang-offload-wrapper{{.*}}"-offload-compress"{{.*}}
14+
// CHECK-NO-COMPRESS-NOT: {{.*}}clang-offload-wrapper{{.*}}"-offload-compression-level=3"{{.*}}

clang/tools/clang-offload-wrapper/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_clang_tool(clang-offload-wrapper
1010

1111
set(CLANG_OFFLOAD_WRAPPER_LIB_DEPS
1212
clangBasic
13+
LLVMSupport
1314
)
1415

1516
add_dependencies(clang clang-offload-wrapper)

clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
#include <string>
6868
#include <tuple>
6969

70+
// For device image compression.
71+
#include <llvm/Support/Compression.h>
72+
7073
#define OPENMP_OFFLOAD_IMAGE_VERSION "1.0"
7174

7275
using namespace llvm;
@@ -139,15 +142,35 @@ static cl::list<std::string> Inputs(cl::Positional, cl::OneOrMore,
139142
cl::desc("<input files>"),
140143
cl::cat(ClangOffloadWrapperCategory));
141144

145+
// CLI options for device image compression.
146+
static cl::opt<bool> OffloadCompressDevImgs(
147+
"offload-compress", cl::init(false), cl::Optional,
148+
cl::desc("Enable device image compression using ZSTD."),
149+
cl::cat(ClangOffloadWrapperCategory));
150+
151+
static cl::opt<int>
152+
OffloadCompressLevel("offload-compression-level", cl::init(10),
153+
cl::Optional,
154+
cl::desc("ZSTD Compression level. Default: 10"),
155+
cl::cat(ClangOffloadWrapperCategory));
156+
157+
static cl::opt<int>
158+
OffloadCompressThreshold("offload-compression-threshold", cl::init(512),
159+
cl::Optional,
160+
cl::desc("Threshold (in bytes) over which to "
161+
"compress images. Default: 512"),
162+
cl::cat(ClangOffloadWrapperCategory));
163+
142164
// Binary image formats supported by this tool. The support basically means
143165
// mapping string representation given at the command line to a value from this
144166
// enum. No format checking is performed.
145167
enum BinaryImageFormat {
146168
none, // image kind is not determined
147169
native, // image kind is native
148170
// portable image kinds go next
149-
spirv, // SPIR-V
150-
llvmbc // LLVM bitcode
171+
spirv, // SPIR-V
172+
llvmbc, // LLVM bitcode
173+
compressed_none // compressed image with unknown format
151174
};
152175

153176
/// Sets offload kind.
@@ -265,6 +288,8 @@ static StringRef formatToString(BinaryImageFormat Fmt) {
265288
return "llvmbc";
266289
case BinaryImageFormat::native:
267290
return "native";
291+
case BinaryImageFormat::compressed_none:
292+
return "compressed_none";
268293
}
269294
llvm_unreachable("bad format");
270295

@@ -1083,10 +1108,66 @@ class BinaryWrapper {
10831108
return FBinOrErr.takeError();
10841109
Fbin = *FBinOrErr;
10851110
} else {
1086-
Fbin = addDeviceImageToModule(
1087-
ArrayRef<char>(Bin->getBufferStart(), Bin->getBufferSize()),
1088-
Twine(OffloadKindTag) + Twine(ImgId) + Twine(".data"), Kind,
1089-
Img.Tgt);
1111+
1112+
// If '--offload-compress' option is specified and zstd is not
1113+
// available, throw an error.
1114+
if (OffloadCompressDevImgs && !llvm::compression::zstd::isAvailable()) {
1115+
return createStringError(
1116+
inconvertibleErrorCode(),
1117+
"'--offload-compress' option is specified but zstd "
1118+
"is not available. The device image will not be "
1119+
"compressed.");
1120+
}
1121+
1122+
// Don't compress if the user explicitly specifies the binary image
1123+
// format or if the image is smaller than OffloadCompressThreshold
1124+
// bytes.
1125+
if (Kind != OffloadKind::SYCL || !OffloadCompressDevImgs ||
1126+
Img.Fmt != BinaryImageFormat::none ||
1127+
!llvm::compression::zstd::isAvailable() ||
1128+
static_cast<int>(Bin->getBufferSize()) < OffloadCompressThreshold) {
1129+
Fbin = addDeviceImageToModule(
1130+
ArrayRef<char>(Bin->getBufferStart(), Bin->getBufferSize()),
1131+
Twine(OffloadKindTag) + Twine(ImgId) + Twine(".data"), Kind,
1132+
Img.Tgt);
1133+
} else {
1134+
1135+
// Compress the image using zstd.
1136+
SmallVector<uint8_t, 512> CompressedBuffer;
1137+
#if LLVM_ENABLE_EXCEPTIONS
1138+
try {
1139+
#endif
1140+
llvm::compression::zstd::compress(
1141+
ArrayRef<unsigned char>(
1142+
(const unsigned char *)(Bin->getBufferStart()),
1143+
Bin->getBufferSize()),
1144+
CompressedBuffer, OffloadCompressLevel);
1145+
#if LLVM_ENABLE_EXCEPTIONS
1146+
} catch (const std::exception &ex) {
1147+
return createStringError(inconvertibleErrorCode(),
1148+
std::string("Failed to compress the device image: \n") +
1149+
std::string(ex.what()));
1150+
}
1151+
#endif
1152+
if (Verbose)
1153+
errs() << "[Compression] Original image size: "
1154+
<< Bin->getBufferSize() << "\n"
1155+
<< "[Compression] Compressed image size: "
1156+
<< CompressedBuffer.size() << "\n"
1157+
<< "[Compression] Compression level used: "
1158+
<< OffloadCompressLevel << "\n";
1159+
1160+
// Add the compressed image to the module.
1161+
Fbin = addDeviceImageToModule(
1162+
ArrayRef<char>((const char *)CompressedBuffer.data(),
1163+
CompressedBuffer.size()),
1164+
Twine(OffloadKindTag) + Twine(ImgId) + Twine(".data"), Kind,
1165+
Img.Tgt);
1166+
1167+
// Change image format to compressed_none.
1168+
Ffmt = ConstantInt::get(Type::getInt8Ty(C),
1169+
BinaryImageFormat::compressed_none);
1170+
}
10901171
}
10911172

10921173
if (Kind == OffloadKind::SYCL) {

sycl/doc/UsersManual.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,19 @@ and not recommended to use in production environment.
195195
which may or may not perform additional inlining.
196196
Default value is 225.
197197

198+
**`--offload-compress`**
199+
200+
Enables device image compression for SYCL offloading. Device images
201+
are compressed using `zstd` compression algorithm and only if their size
202+
exceeds 512 bytes.
203+
Default value is false.
204+
205+
**`--offload-compression-level=<int>`**
206+
207+
`zstd` compression level used to compress device images when `--offload-
208+
compress` is enabled.
209+
The default value is 10.
210+
198211
## Target toolchain options
199212

200213
**`-Xsycl-target-backend=<T> "options"`**

sycl/source/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ function(add_sycl_rt_library LIB_NAME LIB_OBJ_NAME)
6969
target_link_libraries(${LIB_NAME} PRIVATE ${ARG_XPTI_LIB})
7070
endif()
7171

72+
if (NOT LLVM_ENABLE_ZSTD)
73+
target_compile_definitions(${LIB_OBJ_NAME} PRIVATE SYCL_RT_ZSTD_NOT_AVAIABLE)
74+
else()
75+
target_link_libraries(${LIB_NAME} PRIVATE ${zstd_STATIC_LIBRARY})
76+
target_include_directories(${LIB_OBJ_NAME} PRIVATE ${zstd_INCLUDE_DIR})
77+
endif()
78+
7279
target_include_directories(${LIB_OBJ_NAME} PRIVATE ${BOOST_UNORDERED_INCLUDE_DIRS})
7380

7481
# ur_win_proxy_loader

sycl/source/detail/compiler.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ enum sycl_device_binary_type : uint8_t {
115115
SYCL_DEVICE_BINARY_TYPE_NONE = 0, // undetermined
116116
SYCL_DEVICE_BINARY_TYPE_NATIVE = 1, // specific to a device
117117
SYCL_DEVICE_BINARY_TYPE_SPIRV = 2,
118-
SYCL_DEVICE_BINARY_TYPE_LLVMIR_BITCODE = 3
118+
SYCL_DEVICE_BINARY_TYPE_LLVMIR_BITCODE = 3,
119+
SYCL_DEVICE_BINARY_TYPE_COMPRESSED_NONE = 4
119120
};
120121

121122
// Device binary descriptor version supported by this library.

0 commit comments

Comments
 (0)