Skip to content

Commit c997f77

Browse files
[SYCL][Offload] Add SYCLBIN format and dump tool (#16873)
This commit adds a new option `--syclbin` to the clang-linker-wrapper tool to produce SYCLBIN files from packaged device code binaries. The SYCLBIN format parsing added with these changes follow the format defined in the SYCLBIN design document. To help both users and the testing framework, a tool (syclbin-dump) for printing information about a SYCLBIN file is added. --------- Signed-off-by: Larsen, Steffen <[email protected]>
1 parent b6188a1 commit c997f77

File tree

16 files changed

+1260
-18
lines changed

16 files changed

+1260
-18
lines changed

clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "llvm/Object/IRObjectFile.h"
3838
#include "llvm/Object/ObjectFile.h"
3939
#include "llvm/Object/OffloadBinary.h"
40+
#include "llvm/Object/SYCLBIN.h"
4041
#include "llvm/Option/ArgList.h"
4142
#include "llvm/Option/OptTable.h"
4243
#include "llvm/Option/Option.h"
@@ -151,6 +152,8 @@ static std::optional<llvm::module_split::IRSplitMode> SYCLModuleSplitMode;
151152

152153
static bool UseSYCLPostLinkTool;
153154

155+
static bool OutputSYCLBIN;
156+
154157
static SmallString<128> OffloadImageDumpDir;
155158

156159
using OffloadingImage = OffloadBinary::OffloadingImage;
@@ -1182,6 +1185,62 @@ static Expected<StringRef> runCompile(StringRef &InputFile,
11821185
return *OutputFileOrErr;
11831186
}
11841187

1188+
/// Write an OffloadBinary containing the serialized SYCLBIN resulting from
1189+
/// \p ModuleDescs to the ExecutableName file with the .syclbin extension.
1190+
static Expected<StringRef>
1191+
packageSYCLBIN(SYCLBIN::BundleState State,
1192+
const ArrayRef<SYCLBIN::SYCLBINModuleDesc> Modules) {
1193+
SYCLBIN::SYCLBINDesc SYCLBIND{State, Modules};
1194+
size_t SYCLBINByteSize = 0;
1195+
if (Error E = SYCLBIND.getSYCLBINByteSite().moveInto(SYCLBINByteSize))
1196+
return std::move(E);
1197+
1198+
SmallString<0> SYCLBINImage;
1199+
SYCLBINImage.reserve(SYCLBINByteSize);
1200+
raw_svector_ostream SYCLBINImageOS{SYCLBINImage};
1201+
if (Error E = SYCLBIN::write(SYCLBIND, SYCLBINImageOS))
1202+
return std::move(E);
1203+
1204+
OffloadingImage Image{};
1205+
Image.TheImageKind = IMG_SYCLBIN;
1206+
Image.TheOffloadKind = OFK_SYCL;
1207+
Image.Image = MemoryBuffer::getMemBuffer(SYCLBINImage, /*BufferName=*/"",
1208+
/*RequiresNullTerminator=*/false);
1209+
1210+
std::unique_ptr<MemoryBuffer> Binary = MemoryBuffer::getMemBufferCopy(
1211+
OffloadBinary::write(Image), Image.Image->getBufferIdentifier());
1212+
1213+
auto OutFileOrErr =
1214+
createOutputFile(sys::path::filename(ExecutableName), "syclbin");
1215+
if (!OutFileOrErr)
1216+
return OutFileOrErr.takeError();
1217+
1218+
Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
1219+
FileOutputBuffer::create(*OutFileOrErr, Binary->getBufferSize());
1220+
if (!OutputOrErr)
1221+
return OutputOrErr.takeError();
1222+
std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
1223+
llvm::copy(Binary->getBuffer(), Output->getBufferStart());
1224+
if (Error E = Output->commit())
1225+
return std::move(E);
1226+
1227+
return *OutFileOrErr;
1228+
}
1229+
1230+
Error mergeSYCLBIN(ArrayRef<StringRef> Files, const ArgList &Args) {
1231+
// Fast path for the general case where there's only one file. In this case we
1232+
// do not need to parse it and can instead simply copy it.
1233+
if (Files.size() == 1) {
1234+
if (std::error_code EC = sys::fs::copy_file(Files[0], ExecutableName))
1235+
return createFileError(ExecutableName, EC);
1236+
return Error::success();
1237+
}
1238+
// TODO: Merge SYCLBIN files here and write to ExecutableName output.
1239+
// Use the first file as the base and modify.
1240+
assert(Files.size() == 1);
1241+
return Error::success();
1242+
}
1243+
11851244
// Run wrapping library and clang
11861245
static Expected<StringRef>
11871246
runWrapperAndCompile(std::vector<module_split::SplitModule> &SplitModules,
@@ -1962,6 +2021,12 @@ Expected<SmallVector<StringRef>> linkAndWrapDeviceFiles(
19622021
// object file.
19632022
SmallVector<StringRef> WrappedOutput;
19642023

2024+
// When creating SYCLBIN files, we need to store the compiled modules for
2025+
// combined packaging.
2026+
std::mutex SYCLBINModulesMtx;
2027+
SYCLBIN::BundleState SYCLBINState = SYCLBIN::BundleState::Input;
2028+
SmallVector<SYCLBIN::SYCLBINModuleDesc> SYCLBINModules;
2029+
19652030
// Initialize the images with any overriding inputs.
19662031
if (Args.hasArg(OPT_override_image))
19672032
if (Error Err = handleOverrideImages(Args, Images))
@@ -2067,18 +2132,26 @@ Expected<SmallVector<StringRef>> linkAndWrapDeviceFiles(
20672132
}
20682133
}
20692134

2070-
// TODO(NOM7): Remove this call and use community flow for bundle/wrap
2071-
auto OutputFile = sycl::runWrapperAndCompile(SplitModules, LinkerArgs);
2072-
if (!OutputFile)
2073-
return OutputFile.takeError();
2074-
2075-
// SYCL offload kind images are all ready to be sent to host linker.
2076-
// TODO: Currently, device code wrapping for SYCL offload happens in a
2077-
// separate path inside 'linkDevice' call seen above.
2078-
// This will eventually be refactored to use the 'common' wrapping logic
2079-
// that is used for other offload kinds.
2080-
std::scoped_lock Guard(ImageMtx);
2081-
WrappedOutput.push_back(*OutputFile);
2135+
if (OutputSYCLBIN) {
2136+
SYCLBIN::SYCLBINModuleDesc MD;
2137+
MD.ArchString = LinkerArgs.getLastArgValue(OPT_arch_EQ);
2138+
MD.SplitModules = std::move(SplitModules);
2139+
std::scoped_lock Guard(SYCLBINModulesMtx);
2140+
SYCLBINModules.emplace_back(std::move(MD));
2141+
} else {
2142+
// TODO(NOM7): Remove this call and use community flow for bundle/wrap
2143+
auto OutputFile = sycl::runWrapperAndCompile(SplitModules, LinkerArgs);
2144+
if (!OutputFile)
2145+
return OutputFile.takeError();
2146+
2147+
// SYCL offload kind images are all ready to be sent to host linker.
2148+
// TODO: Currently, device code wrapping for SYCL offload happens in a
2149+
// separate path inside 'linkDevice' call seen above.
2150+
// This will eventually be refactored to use the 'common' wrapping logic
2151+
// that is used for other offload kinds.
2152+
std::scoped_lock Guard(ImageMtx);
2153+
WrappedOutput.push_back(*OutputFile);
2154+
}
20822155
}
20832156
if (HasNonSYCLOffloadKinds) {
20842157
// Write any remaining device inputs to an output file.
@@ -2129,6 +2202,13 @@ Expected<SmallVector<StringRef>> linkAndWrapDeviceFiles(
21292202
if (Err)
21302203
return std::move(Err);
21312204

2205+
if (OutputSYCLBIN) {
2206+
auto OutputOrErr = sycl::packageSYCLBIN(SYCLBINState, SYCLBINModules);
2207+
if (!OutputOrErr)
2208+
return OutputOrErr.takeError();
2209+
WrappedOutput.push_back(*OutputOrErr);
2210+
}
2211+
21322212
for (auto &[Kind, Input] : Images) {
21332213
if (Kind == OFK_SYCL)
21342214
continue;
@@ -2585,6 +2665,11 @@ int main(int Argc, char **Argv) {
25852665
"-no-use-sycl-post-link-tool options can't "
25862666
"be used together."));
25872667

2668+
OutputSYCLBIN = Args.hasArg(OPT_syclbin);
2669+
if (OutputSYCLBIN && Args.hasArg(OPT_sycl_embed_ir))
2670+
reportError(createStringError(
2671+
"-sycl-embed_ir and -syclbin can't be used together."));
2672+
25882673
if (Args.hasArg(OPT_sycl_module_split_mode_EQ)) {
25892674
if (UseSYCLPostLinkTool)
25902675
reportError(createStringError(
@@ -2623,9 +2708,14 @@ int main(int Argc, char **Argv) {
26232708
if (!FilesOrErr)
26242709
reportError(FilesOrErr.takeError());
26252710

2626-
// Run the host linking job with the rendered arguments.
2627-
if (Error Err = runLinker(*FilesOrErr, Args))
2628-
reportError(std::move(Err));
2711+
if (OutputSYCLBIN) {
2712+
if (Error Err = sycl::mergeSYCLBIN(*FilesOrErr, Args))
2713+
reportError(std::move(Err));
2714+
} else {
2715+
// Run the host linking job with the rendered arguments.
2716+
if (Error Err = runLinker(*FilesOrErr, Args))
2717+
reportError(std::move(Err));
2718+
}
26292719
}
26302720

26312721
if (const opt::Arg *Arg = Args.getLastArg(OPT_wrapper_time_trace_eq)) {

clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,8 @@ def sycl_dump_device_code_EQ : Joined<["--", "-"], "sycl-dump-device-code=">,
236236
def sycl_allow_device_image_dependencies : Flag<["--", "-"], "sycl-allow-device-image-dependencies">,
237237
Flags<[WrapperOnlyOption, HelpHidden]>,
238238
HelpText<"Allow dependencies between device code images">;
239+
240+
// Options to force the output to be of the SYCLBIN format.
241+
def syclbin : Flag<["--", "-"], "syclbin">,
242+
Flags<[WrapperOnlyOption]>,
243+
HelpText<"Output in the SYCLBIN binary format">;

llvm/include/llvm/Object/OffloadBinary.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum ImageKind : uint16_t {
4747
IMG_Cubin,
4848
IMG_Fatbinary,
4949
IMG_PTX,
50+
IMG_SYCLBIN,
5051
IMG_LAST,
5152
};
5253

llvm/include/llvm/Object/SYCLBIN.h

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//===- SYCLBIN.h - SYCLBIN binary format support ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_OBJECT_SYCLBIN_H
10+
#define LLVM_OBJECT_SYCLBIN_H
11+
12+
#include "llvm/ADT/SmallString.h"
13+
#include "llvm/SYCLPostLink/ModuleSplitter.h"
14+
#include "llvm/Support/MemoryBuffer.h"
15+
#include <string>
16+
17+
namespace llvm {
18+
19+
namespace object {
20+
21+
// Representation of a SYCLBIN binary object. This is intended for use as an
22+
// image inside a OffloadBinary.
23+
class SYCLBIN {
24+
public:
25+
SYCLBIN(MemoryBufferRef Source) : Data{Source} {}
26+
27+
SYCLBIN(const SYCLBIN &Other) = delete;
28+
SYCLBIN(SYCLBIN &&Other) = default;
29+
30+
SYCLBIN &operator=(const SYCLBIN &Other) = delete;
31+
SYCLBIN &operator=(SYCLBIN &&Other) = default;
32+
33+
MemoryBufferRef getMemoryBufferRef() const { return Data; }
34+
35+
enum class BundleState : uint8_t { Input = 0, Object = 1, Executable = 2 };
36+
37+
struct SYCLBINModuleDesc {
38+
std::string ArchString;
39+
std::vector<module_split::SplitModule> SplitModules;
40+
};
41+
42+
class SYCLBINDesc {
43+
public:
44+
SYCLBINDesc(BundleState State, ArrayRef<SYCLBINModuleDesc> ModuleDescs);
45+
46+
SYCLBINDesc(const SYCLBINDesc &Other) = delete;
47+
SYCLBINDesc(SYCLBINDesc &&Other) = default;
48+
49+
SYCLBINDesc &operator=(const SYCLBINDesc &Other) = delete;
50+
SYCLBINDesc &operator=(SYCLBINDesc &&Other) = default;
51+
52+
size_t getMetadataTableByteSize() const;
53+
Expected<size_t> getBinaryTableByteSize() const;
54+
Expected<size_t> getSYCLBINByteSite() const;
55+
56+
private:
57+
struct ImageDesc {
58+
SmallString<0> Metadata;
59+
SmallString<0> FilePath;
60+
};
61+
62+
struct AbstractModuleDesc {
63+
SmallString<0> Metadata;
64+
SmallVector<ImageDesc, 4> IRModuleDescs;
65+
SmallVector<ImageDesc, 4> NativeDeviceCodeImageDescs;
66+
};
67+
68+
SmallString<0> GlobalMetadata;
69+
SmallVector<AbstractModuleDesc, 4> AbstractModuleDescs;
70+
71+
friend class SYCLBIN;
72+
};
73+
74+
/// The current version of the binary used for backwards compatibility.
75+
static constexpr uint32_t CurrentVersion = 1;
76+
77+
/// Magic number used to identify SYCLBIN files.
78+
static constexpr uint32_t MagicNumber = 0x53594249;
79+
80+
/// Serialize \p Desc to \p OS .
81+
static Error write(const SYCLBIN::SYCLBINDesc &Desc, raw_ostream &OS);
82+
83+
/// Deserialize the contents of \p Source to produce a SYCLBIN object.
84+
static Expected<std::unique_ptr<SYCLBIN>> read(MemoryBufferRef Source);
85+
86+
struct IRModule {
87+
std::unique_ptr<llvm::util::PropertySetRegistry> Metadata;
88+
StringRef RawIRBytes;
89+
};
90+
struct NativeDeviceCodeImage {
91+
std::unique_ptr<llvm::util::PropertySetRegistry> Metadata;
92+
StringRef RawDeviceCodeImageBytes;
93+
};
94+
95+
struct AbstractModule {
96+
std::unique_ptr<llvm::util::PropertySetRegistry> Metadata;
97+
SmallVector<IRModule> IRModules;
98+
SmallVector<NativeDeviceCodeImage> NativeDeviceCodeImages;
99+
};
100+
101+
uint32_t Version;
102+
std::unique_ptr<llvm::util::PropertySetRegistry> GlobalMetadata;
103+
SmallVector<AbstractModule, 4> AbstractModules;
104+
105+
private:
106+
MemoryBufferRef Data;
107+
108+
struct alignas(8) FileHeaderType {
109+
uint32_t Magic;
110+
uint32_t Version;
111+
uint32_t AbstractModuleCount;
112+
uint32_t IRModuleCount;
113+
uint32_t NativeDeviceCodeImageCount;
114+
uint64_t MetadataByteTableSize;
115+
uint64_t BinaryByteTableSize;
116+
uint64_t GlobalMetadataOffset;
117+
uint64_t GlobalMetadataSize;
118+
};
119+
120+
struct alignas(8) AbstractModuleHeaderType {
121+
uint64_t MetadataOffset;
122+
uint64_t MetadataSize;
123+
uint32_t IRModuleCount;
124+
uint32_t IRModuleOffset;
125+
uint32_t NativeDeviceCodeImageCount;
126+
uint32_t NativeDeviceCodeImageOffset;
127+
};
128+
129+
struct alignas(8) IRModuleHeaderType {
130+
uint64_t MetadataOffset;
131+
uint64_t MetadataSize;
132+
uint64_t RawIRBytesOffset;
133+
uint64_t RawIRBytesSize;
134+
};
135+
136+
struct alignas(8) NativeDeviceCodeImageHeaderType {
137+
uint64_t MetadataOffset;
138+
uint64_t MetadataSize;
139+
uint64_t BinaryBytesOffset;
140+
uint64_t BinaryBytesSize;
141+
};
142+
};
143+
144+
} // namespace object
145+
146+
} // namespace llvm
147+
148+
#endif

llvm/include/llvm/SYCLPostLink/ModuleSplitter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/ADT/SetVector.h"
1717
#include "llvm/ADT/StringRef.h"
1818
#include "llvm/IR/Function.h"
19+
#include "llvm/IR/Module.h"
1920
#include "llvm/SYCLLowerIR/SYCLDeviceRequirements.h"
2021
#include "llvm/Support/Error.h"
2122
#include "llvm/Support/PropertySetIO.h"
@@ -29,7 +30,6 @@
2930
namespace llvm {
3031

3132
class Function;
32-
class Module;
3333

3434
namespace cl {
3535
class OptionCategory;

llvm/include/llvm/Support/PropertySetIO.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ class PropertyValue {
158158
}
159159
}
160160

161+
const char *data() const { return reinterpret_cast<const char *>(&Val); }
162+
161163
private:
162164
template <typename T> T &getValueRef();
163165
void copy(const PropertyValue &P);
@@ -219,6 +221,13 @@ class PropertySetRegistry {
219221
static constexpr char PROPERTY_REQD_WORK_GROUP_SIZE[] =
220222
"reqd_work_group_size_uint64_t";
221223

224+
// SYCLBIN specific property sets.
225+
static constexpr char SYCLBIN_GLOBAL_METADATA[] = "SYCLBIN/global metadata";
226+
static constexpr char SYCLBIN_IR_MODULE_METADATA[] =
227+
"SYCLBIN/ir module metadata";
228+
static constexpr char SYCLBIN_NATIVE_DEVICE_CODE_IMAGE_METADATA[] =
229+
"SYCLBIN/native device code image metadata";
230+
222231
/// Function for bulk addition of an entire property set in the given
223232
/// \p Category .
224233
template <typename MapTy> void add(StringRef Category, const MapTy &Props) {

llvm/lib/Object/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_llvm_component_library(LLVMObject
2424
OffloadBinary.cpp
2525
RecordStreamer.cpp
2626
RelocationResolver.cpp
27+
SYCLBIN.cpp
2728
SymbolicFile.cpp
2829
SymbolSize.cpp
2930
TapiFile.cpp

0 commit comments

Comments
 (0)