Skip to content

Commit 8678ebf

Browse files
[SYCL] Fix SYCLBIN generation for multiple images (#18512)
Due to incorrect tracking of offsets, SYCLBIN files with multiple images would not point to the correct binaries. This commit fixes the offsets. --------- Signed-off-by: Larsen, Steffen <[email protected]>
1 parent c476710 commit 8678ebf

File tree

2 files changed

+148
-156
lines changed

2 files changed

+148
-156
lines changed

llvm/lib/Object/SYCLBIN.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ Error SYCLBIN::write(const SYCLBIN::SYCLBINDesc &Desc, raw_ostream &OS) {
224224
OS << StringRef(reinterpret_cast<char *>(&AMHeader), sizeof(AMHeader));
225225
OS.write_zeros(alignTo(OS.tell(), 8) - OS.tell());
226226
HeaderTrackedMetadataOffset += AMHeader.MetadataSize;
227+
IRModuleOffset += AMHeader.IRModuleCount;
228+
NativeDeviceCodeImageOffset += AMHeader.NativeDeviceCodeImageCount;
227229
BinariesCount +=
228230
AMHeader.IRModuleCount + AMHeader.NativeDeviceCodeImageCount;
229231
}

llvm/unittests/Object/SYCLBINTest.cpp

Lines changed: 146 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -56,20 +56,39 @@ class ManagedBinaryFile {
5656
};
5757

5858
std::vector<uint8_t> generateRandomImage() {
59-
std::mt19937 Rng(42);
60-
std::uniform_int_distribution<uint64_t> SizeDist(0, 256);
61-
std::uniform_int_distribution<uint16_t> KindDist(0);
62-
std::uniform_int_distribution<uint16_t> BinaryDist(
59+
static std::mt19937 Rng(42);
60+
static std::uniform_int_distribution<uint64_t> SizeDist(0, 256);
61+
static std::uniform_int_distribution<uint16_t> BinaryDist(
6362
std::numeric_limits<uint8_t>::min(), std::numeric_limits<uint8_t>::max());
6463
std::vector<uint8_t> Image(SizeDist(Rng));
6564
std::generate(Image.begin(), Image.end(), [&]() { return BinaryDist(Rng); });
6665
return Image;
6766
}
6867

69-
} // namespace
68+
std::vector<std::vector<uint8_t>> generateUniqueRandomImages(size_t NumImages) {
69+
std::vector<std::vector<uint8_t>> Images;
70+
Images.reserve(NumImages);
71+
Images.emplace_back(generateRandomImage());
72+
for (size_t I = 1; I < NumImages; ++I) {
73+
std::vector<uint8_t> GenImage;
74+
auto CheckImageExist = [&](const std::vector<uint8_t> &Image) {
75+
return GenImage.size() == Image.size() &&
76+
std::memcmp(GenImage.data(), Image.data(), GenImage.size()) == 0;
77+
};
78+
// Generate images until we get a unique one.
79+
do {
80+
GenImage = generateRandomImage();
81+
} while (std::any_of(Images.begin(), Images.end(), CheckImageExist));
82+
Images.emplace_back(std::move(GenImage));
83+
}
84+
return Images;
85+
}
7086

71-
TEST(SYCLBINTest, checkSYCLBINBinaryBasicIRModule) {
87+
template <size_t NumIRModules, size_t NumNativeDeviceCodeImages>
88+
void CommonCheck() {
89+
constexpr size_t NumImages = NumIRModules + NumNativeDeviceCodeImages;
7290
constexpr SYCLBIN::BundleState State = SYCLBIN::BundleState::Input;
91+
static constexpr char Arch[] = "some-arch";
7392

7493
// Create unique temporary directory for these tests
7594
SmallString<128> TestDirectory;
@@ -78,21 +97,32 @@ TEST(SYCLBINTest, checkSYCLBINBinaryBasicIRModule) {
7897
fs::createUniqueDirectory("SYCLBINTest-test", TestDirectory));
7998
}
8099

81-
// Create a random file to be used in the SYCLBIN.
82-
SmallString<128> File1(TestDirectory);
83-
File1.append("/image_file1");
84-
std::vector<uint8_t> Image = generateRandomImage();
85-
Expected<ManagedBinaryFile> ManagedModuleFileOrError =
86-
ManagedBinaryFile::create(File1, Image);
87-
ASSERT_THAT_EXPECTED(ManagedModuleFileOrError, Succeeded());
88-
ManagedBinaryFile ManagedModuleFile = std::move(*ManagedModuleFileOrError);
89-
90-
// Create IR module.
91-
std::vector<module_split::SplitModule> SplitModules{module_split::SplitModule{
92-
File1.c_str(), util::PropertySetRegistry{}, ""}};
93-
SmallVector<SYCLBIN::SYCLBINModuleDesc> MDs{
94-
SYCLBIN::SYCLBINModuleDesc{"", std::move(SplitModules)}};
95-
SYCLBIN::SYCLBINDesc Desc{SYCLBIN::BundleState::Input, MDs};
100+
// Create random files to be used in the SYCLBIN. If the same image is
101+
// generated, retry until they are unique.
102+
std::vector<std::vector<uint8_t>> Images =
103+
generateUniqueRandomImages(NumImages);
104+
105+
std::vector<ManagedBinaryFile> ManagedModuleFiles;
106+
SmallVector<SYCLBIN::SYCLBINModuleDesc> MDs;
107+
ManagedModuleFiles.reserve(NumImages);
108+
MDs.reserve(NumImages);
109+
for (size_t I = 0; I < NumImages; ++I) {
110+
SmallString<128> File(TestDirectory);
111+
llvm::sys::path::append(File, "image_file" + std::to_string(I));
112+
Expected<ManagedBinaryFile> ManagedModuleFileOrError =
113+
ManagedBinaryFile::create(File, Images[I]);
114+
ASSERT_THAT_EXPECTED(ManagedModuleFileOrError, Succeeded());
115+
ManagedModuleFiles.emplace_back(std::move(*ManagedModuleFileOrError));
116+
std::vector<module_split::SplitModule> SplitModules{
117+
module_split::SplitModule{File.c_str(), util::PropertySetRegistry{},
118+
""}};
119+
const char *ArchStr = I < NumIRModules ? "" : Arch;
120+
MDs.emplace_back(
121+
SYCLBIN::SYCLBINModuleDesc{ArchStr, std::move(SplitModules)});
122+
}
123+
124+
// Create IR modules.
125+
SYCLBIN::SYCLBINDesc Desc{State, MDs};
96126
size_t SYCLBINByteSize = 0;
97127
if (Error E = Desc.getSYCLBINByteSite().moveInto(SYCLBINByteSize))
98128
FAIL() << "Failed to get SYCLBIN byte size.";
@@ -133,146 +163,106 @@ TEST(SYCLBINTest, checkSYCLBINBinaryBasicIRModule) {
133163
ASSERT_EQ(GlobalMetadataState.getType(), PropertyValue::Type::UINT32);
134164
EXPECT_EQ(GlobalMetadataState.asUint32(), static_cast<uint32_t>(State));
135165

136-
ASSERT_EQ(SYCLBINObj->AbstractModules.size(), size_t{1});
137-
const SYCLBIN::AbstractModule &AM = SYCLBINObj->AbstractModules[0];
138-
139-
// This metadata should be the same as in the corresponding split module, so
140-
// testing should be expanded to ensure preservation.
141-
ASSERT_NE(AM.Metadata.get(), nullptr);
142-
EXPECT_TRUE(AM.Metadata->getPropSets().empty());
143-
144-
// There was no arch string, so there should be no native device code images.
145-
EXPECT_TRUE(AM.NativeDeviceCodeImages.empty());
146-
147-
// There was a single module with no arch string, so we should have an IR
148-
// module.
149-
ASSERT_EQ(AM.IRModules.size(), size_t{1});
150-
const SYCLBIN::IRModule &IRM = AM.IRModules[0];
151-
152-
ASSERT_NE(IRM.Metadata.get(), nullptr);
153-
SmallString<16> IRMMetadataKey{
154-
PropertySetRegistry::SYCLBIN_IR_MODULE_METADATA};
155-
const auto &IRMMetadataIt = IRM.Metadata->getPropSets().find(IRMMetadataKey);
156-
ASSERT_NE(IRMMetadataIt, IRM.Metadata->end());
157-
const PropertySet &IRMMetadata = IRMMetadataIt->second;
158-
EXPECT_EQ(IRMMetadata.size(), size_t{1});
159-
160-
// The type is currently locked to SPIR-V. This will change in the future.
161-
SmallString<16> IRMMetadataTypeKey{"type"};
162-
const auto &IRMMetadataTypeIt = IRMMetadata.find(IRMMetadataTypeKey);
163-
ASSERT_NE(IRMMetadataTypeIt, IRMMetadata.end());
164-
const PropertyValue &IRMMetadataType = IRMMetadataTypeIt->second;
165-
ASSERT_EQ(IRMMetadataType.getType(), PropertyValue::Type::UINT32);
166-
EXPECT_EQ(IRMMetadataType.asUint32(), uint32_t{0});
167-
168-
// Check that the image is the same.
169-
ASSERT_EQ(Image.size(), IRM.RawIRBytes.size());
170-
EXPECT_EQ(std::memcmp(Image.data(), IRM.RawIRBytes.data(), Image.size()), 0);
171-
}
172-
173-
TEST(SYCLBINTest, checkSYCLBINBinaryBasicNativeDeviceCodeImage) {
174-
constexpr SYCLBIN::BundleState State = SYCLBIN::BundleState::Input;
175-
static constexpr char Arch[] = "some-arch";
176-
177-
// Create unique temporary directory for these tests
178-
SmallString<128> TestDirectory;
179-
{
180-
ASSERT_NO_ERROR(
181-
fs::createUniqueDirectory("SYCLBINTest-test", TestDirectory));
166+
// Currently we have an abstract module per image.
167+
ASSERT_EQ(SYCLBINObj->AbstractModules.size(), size_t{NumImages});
168+
169+
std::vector<decltype(Images)::const_iterator> ImageIts;
170+
ImageIts.reserve(NumImages);
171+
size_t ObservedIRModules = 0, ObservedNativeDeviceCodeImages = 0;
172+
for (size_t I = 0; I < NumImages; ++I) {
173+
const SYCLBIN::AbstractModule &AM = SYCLBINObj->AbstractModules[I];
174+
175+
// This metadata should be the same as in the corresponding split module, so
176+
// testing should be expanded to ensure preservation.
177+
ASSERT_NE(AM.Metadata.get(), nullptr);
178+
EXPECT_TRUE(AM.Metadata->getPropSets().empty());
179+
180+
ObservedIRModules += AM.IRModules.size();
181+
for (size_t J = 0; J < AM.IRModules.size(); ++J) {
182+
const SYCLBIN::IRModule &IRM = AM.IRModules[J];
183+
ASSERT_NE(IRM.Metadata.get(), nullptr);
184+
SmallString<16> IRMMetadataKey{
185+
PropertySetRegistry::SYCLBIN_IR_MODULE_METADATA};
186+
const auto &IRMMetadataIt =
187+
IRM.Metadata->getPropSets().find(IRMMetadataKey);
188+
ASSERT_NE(IRMMetadataIt, IRM.Metadata->end());
189+
const PropertySet &IRMMetadata = IRMMetadataIt->second;
190+
EXPECT_EQ(IRMMetadata.size(), size_t{1});
191+
192+
// The type is currently locked to SPIR-V. This will change in the future.
193+
SmallString<16> IRMMetadataTypeKey{"type"};
194+
const auto &IRMMetadataTypeIt = IRMMetadata.find(IRMMetadataTypeKey);
195+
ASSERT_NE(IRMMetadataTypeIt, IRMMetadata.end());
196+
const PropertyValue &IRMMetadataType = IRMMetadataTypeIt->second;
197+
ASSERT_EQ(IRMMetadataType.getType(), PropertyValue::Type::UINT32);
198+
EXPECT_EQ(IRMMetadataType.asUint32(), uint32_t{0});
199+
200+
// Find the image that matches.
201+
std::vector<uint8_t> IRImage{IRM.RawIRBytes.begin(),
202+
IRM.RawIRBytes.end()};
203+
auto ImageMatchIt = std::find(Images.begin(), Images.end(), IRImage);
204+
ASSERT_NE(ImageMatchIt, Images.end());
205+
ImageIts.push_back(ImageMatchIt);
206+
}
207+
208+
ObservedNativeDeviceCodeImages += AM.NativeDeviceCodeImages.size();
209+
for (size_t J = 0; J < AM.NativeDeviceCodeImages.size(); ++J) {
210+
const SYCLBIN::NativeDeviceCodeImage &NDCI = AM.NativeDeviceCodeImages[J];
211+
212+
ASSERT_NE(NDCI.Metadata.get(), nullptr);
213+
SmallString<16> NDCIMetadataKey{
214+
PropertySetRegistry::SYCLBIN_NATIVE_DEVICE_CODE_IMAGE_METADATA};
215+
const auto &NDCIMetadataIt =
216+
NDCI.Metadata->getPropSets().find(NDCIMetadataKey);
217+
ASSERT_NE(NDCIMetadataIt, NDCI.Metadata->end());
218+
const PropertySet &NDCIMetadata = NDCIMetadataIt->second;
219+
ASSERT_EQ(NDCIMetadata.size(), size_t{1});
220+
221+
// Make sure the arch string is preserved.
222+
SmallString<16> NDCIMetadataArchKey{"arch"};
223+
const auto &NDCIMetadataArchIt = NDCIMetadata.find(NDCIMetadataArchKey);
224+
ASSERT_NE(NDCIMetadataArchIt, NDCIMetadata.end());
225+
const PropertyValue &NDCIMetadataArch = NDCIMetadataArchIt->second;
226+
ASSERT_EQ(NDCIMetadataArch.getType(), PropertyValue::Type::BYTE_ARRAY);
227+
ASSERT_EQ(NDCIMetadataArch.getByteArraySize(), strlen(Arch));
228+
EXPECT_EQ(std::memcmp(NDCIMetadataArch.asByteArray(), Arch, strlen(Arch)),
229+
0);
230+
231+
// Find the image that matches.
232+
std::vector<uint8_t> RawDeviceCodeImage{
233+
NDCI.RawDeviceCodeImageBytes.begin(),
234+
NDCI.RawDeviceCodeImageBytes.end()};
235+
auto ImageMatchIt =
236+
std::find(Images.begin(), Images.end(), RawDeviceCodeImage);
237+
ASSERT_NE(ImageMatchIt, Images.end());
238+
ImageIts.push_back(ImageMatchIt);
239+
}
182240
}
241+
ASSERT_EQ(NumIRModules, ObservedIRModules);
242+
ASSERT_EQ(NumNativeDeviceCodeImages, ObservedNativeDeviceCodeImages);
243+
// The images may not appear in the same order they were written. They
244+
// shouldn't be the same however.
245+
ASSERT_TRUE(std::unique(ImageIts.begin(), ImageIts.end()) == ImageIts.end());
246+
}
183247

184-
// Create a random file to be used in the SYCLBIN.
185-
SmallString<128> File1(TestDirectory);
186-
File1.append("/image_file1");
187-
std::vector<uint8_t> Image = generateRandomImage();
188-
Expected<ManagedBinaryFile> ManagedModuleFileOrError =
189-
ManagedBinaryFile::create(File1, Image);
190-
ASSERT_THAT_EXPECTED(ManagedModuleFileOrError, Succeeded());
191-
ManagedBinaryFile ManagedModuleFile = std::move(*ManagedModuleFileOrError);
192-
193-
// Create IR module.
194-
std::vector<module_split::SplitModule> SplitModules{module_split::SplitModule{
195-
File1.c_str(), util::PropertySetRegistry{}, ""}};
196-
SmallVector<SYCLBIN::SYCLBINModuleDesc> MDs{
197-
SYCLBIN::SYCLBINModuleDesc{Arch, std::move(SplitModules)}};
198-
SYCLBIN::SYCLBINDesc Desc{SYCLBIN::BundleState::Input, MDs};
199-
size_t SYCLBINByteSize = 0;
200-
if (Error E = Desc.getSYCLBINByteSite().moveInto(SYCLBINByteSize))
201-
FAIL() << "Failed to get SYCLBIN byte size.";
202-
203-
// Serialize the SYCLBIN.
204-
SmallString<0> SYCLBINImage;
205-
SYCLBINImage.reserve(SYCLBINByteSize);
206-
raw_svector_ostream SYCLBINImageOS{SYCLBINImage};
207-
if (Error E = SYCLBIN::write(Desc, SYCLBINImageOS))
208-
FAIL() << "Failed to write SYCLBIN.";
248+
} // namespace
209249

210-
// Deserialize the SYCLBIN.
211-
std::unique_ptr<MemoryBuffer> SYCBINDataBuffer =
212-
MemoryBuffer::getMemBuffer(SYCLBINImage, /*BufferName=*/"",
213-
/*RequiresNullTerminator=*/false);
214-
Expected<std::unique_ptr<SYCLBIN>> SYCLBINObjOrError =
215-
SYCLBIN::read(*SYCBINDataBuffer);
216-
ASSERT_THAT_EXPECTED(SYCLBINObjOrError, Succeeded());
217-
SYCLBIN *SYCLBINObj = SYCLBINObjOrError->get();
218-
ASSERT_NE(SYCLBINObj, nullptr);
250+
TEST(SYCLBINTest, checkSYCLBINBinaryBasicIRModule) {
251+
CommonCheck</*NumIRModules=*/1, /*NumNativeDeviceCodeImages=*/0>();
252+
}
219253

220-
EXPECT_EQ(SYCLBINObj->Version, SYCLBIN::CurrentVersion);
254+
TEST(SYCLBINTest, checkSYCLBINBinaryDoubleBasicIRModules) {
255+
CommonCheck</*NumIRModules=*/2, /*NumNativeDeviceCodeImages=*/0>();
256+
}
221257

222-
ASSERT_NE(SYCLBINObj->GlobalMetadata.get(), nullptr);
223-
SmallString<16> GlobalMetadataKey{
224-
PropertySetRegistry::SYCLBIN_GLOBAL_METADATA};
225-
const auto &GlobalMetadataIt =
226-
SYCLBINObj->GlobalMetadata->getPropSets().find(GlobalMetadataKey);
227-
ASSERT_NE(GlobalMetadataIt, SYCLBINObj->GlobalMetadata->end());
228-
const PropertySet &GlobalMetadata = GlobalMetadataIt->second;
229-
EXPECT_EQ(GlobalMetadata.size(), size_t{1});
258+
TEST(SYCLBINTest, checkSYCLBINBinaryBasicNativeDeviceCodeImage) {
259+
CommonCheck</*NumIRModules=*/0, /*NumNativeDeviceCodeImages=*/1>();
260+
}
230261

231-
SmallString<16> GlobalMetadataStateKey{"state"};
232-
const auto &GlobalMetadataStateIt =
233-
GlobalMetadata.find(GlobalMetadataStateKey);
234-
ASSERT_NE(GlobalMetadataStateIt, GlobalMetadata.end());
235-
const PropertyValue &GlobalMetadataState = GlobalMetadataStateIt->second;
236-
ASSERT_EQ(GlobalMetadataState.getType(), PropertyValue::Type::UINT32);
237-
EXPECT_EQ(GlobalMetadataState.asUint32(), static_cast<uint32_t>(State));
262+
TEST(SYCLBINTest, checkSYCLBINBinaryDoubleBasicNativeDeviceCodeImages) {
263+
CommonCheck</*NumIRModules=*/0, /*NumNativeDeviceCodeImages=*/2>();
264+
}
238265

239-
ASSERT_EQ(SYCLBINObj->AbstractModules.size(), size_t{1});
240-
const SYCLBIN::AbstractModule &AM = SYCLBINObj->AbstractModules[0];
241-
242-
// This metadata should be the same as in the corresponding split module, so
243-
// testing should be expanded to ensure preservation.
244-
ASSERT_NE(AM.Metadata.get(), nullptr);
245-
EXPECT_TRUE(AM.Metadata->getPropSets().empty());
246-
247-
// There was a single module with an arch string, so we should not have an IR
248-
// module.
249-
EXPECT_TRUE(AM.IRModules.empty());
250-
251-
// There was an arch string, so there should be a native device code images.
252-
ASSERT_EQ(AM.NativeDeviceCodeImages.size(), size_t{1});
253-
const SYCLBIN::NativeDeviceCodeImage &NDCI = AM.NativeDeviceCodeImages[0];
254-
255-
ASSERT_NE(NDCI.Metadata.get(), nullptr);
256-
SmallString<16> NDCIMetadataKey{
257-
PropertySetRegistry::SYCLBIN_NATIVE_DEVICE_CODE_IMAGE_METADATA};
258-
const auto &NDCIMetadataIt =
259-
NDCI.Metadata->getPropSets().find(NDCIMetadataKey);
260-
ASSERT_NE(NDCIMetadataIt, NDCI.Metadata->end());
261-
const PropertySet &NDCIMetadata = NDCIMetadataIt->second;
262-
ASSERT_EQ(NDCIMetadata.size(), size_t{1});
263-
264-
// Make sure the arch string is preserved.
265-
SmallString<16> NDCIMetadataArchKey{"arch"};
266-
const auto &NDCIMetadataArchIt = NDCIMetadata.find(NDCIMetadataArchKey);
267-
ASSERT_NE(NDCIMetadataArchIt, NDCIMetadata.end());
268-
const PropertyValue &NDCIMetadataArch = NDCIMetadataArchIt->second;
269-
ASSERT_EQ(NDCIMetadataArch.getType(), PropertyValue::Type::BYTE_ARRAY);
270-
ASSERT_EQ(NDCIMetadataArch.getByteArraySize(), strlen(Arch));
271-
EXPECT_EQ(std::memcmp(NDCIMetadataArch.asByteArray(), Arch, strlen(Arch)), 0);
272-
273-
// Check that the image is the same.
274-
ASSERT_EQ(Image.size(), NDCI.RawDeviceCodeImageBytes.size());
275-
EXPECT_EQ(std::memcmp(Image.data(), NDCI.RawDeviceCodeImageBytes.data(),
276-
Image.size()),
277-
0);
266+
TEST(SYCLBINTest, checkSYCLBINBinaryMixBasicImages) {
267+
CommonCheck</*NumIRModules=*/1, /*NumNativeDeviceCodeImages=*/1>();
278268
}

0 commit comments

Comments
 (0)