xref: /llvm-project/llvm/unittests/Object/DXContainerTest.cpp (revision dd3f7b02255a52b709797cdc6d121b113df9b4ab)
1 //===- DXContainerTest.cpp - Tests for DXContainerFile --------------------===//
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 #include "llvm/Object/DXContainer.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/BinaryFormat/Magic.h"
12 #include "llvm/ObjectYAML/DXContainerYAML.h"
13 #include "llvm/ObjectYAML/yaml2obj.h"
14 #include "llvm/Support/MemoryBufferRef.h"
15 #include "llvm/Testing/Support/Error.h"
16 #include "gtest/gtest.h"
17 
18 using namespace llvm;
19 using namespace llvm::object;
20 
21 template <std::size_t X> MemoryBufferRef getMemoryBuffer(uint8_t Data[X]) {
22   StringRef Obj(reinterpret_cast<char *>(&Data[0]), X);
23   return MemoryBufferRef(Obj, "");
24 }
25 
26 TEST(DXCFile, IdentifyMagic) {
27   {
28     StringRef Buffer("DXBC");
29     EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object);
30   }
31   {
32     StringRef Buffer("DXBCBlahBlahBlah");
33     EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object);
34   }
35 }
36 
37 TEST(DXCFile, ParseHeaderErrors) {
38   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43};
39   EXPECT_THAT_EXPECTED(
40       DXContainer::create(getMemoryBuffer<4>(Buffer)),
41       FailedWithMessage("Reading structure out of file bounds"));
42 }
43 
44 TEST(DXCFile, EmptyFile) {
45   EXPECT_THAT_EXPECTED(
46       DXContainer::create(MemoryBufferRef(StringRef("", 0), "")),
47       FailedWithMessage("Reading structure out of file bounds"));
48 }
49 
50 TEST(DXCFile, ParseHeader) {
51   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
52                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53                       0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
54                       0x70, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
55   DXContainer C =
56       llvm::cantFail(DXContainer::create(getMemoryBuffer<32>(Buffer)));
57   EXPECT_TRUE(memcmp(C.getHeader().Magic, "DXBC", 4) == 0);
58   EXPECT_TRUE(memcmp(C.getHeader().FileHash.Digest,
59                      "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0);
60   EXPECT_EQ(C.getHeader().Version.Major, 1u);
61   EXPECT_EQ(C.getHeader().Version.Minor, 0u);
62 }
63 
64 TEST(DXCFile, ParsePartMissingOffsets) {
65   uint8_t Buffer[] = {
66       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
68       0x00, 0x00, 0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
69   };
70   EXPECT_THAT_EXPECTED(
71       DXContainer::create(getMemoryBuffer<32>(Buffer)),
72       FailedWithMessage("Reading structure out of file bounds"));
73 }
74 
75 TEST(DXCFile, ParsePartInvalidOffsets) {
76   // This test covers a case where the part offset is beyond the buffer size.
77   uint8_t Buffer[] = {
78       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
80       0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
81   };
82   EXPECT_THAT_EXPECTED(
83       DXContainer::create(getMemoryBuffer<36>(Buffer)),
84       FailedWithMessage("Part offset points beyond boundary of the file"));
85 }
86 
87 TEST(DXCFile, ParsePartTooSmallBuffer) {
88   // This test covers a case where there is insufficent space to read a full
89   // part name, but the offset for the part is inside the buffer.
90   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
91                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92                       0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
93                       0x26, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
94                       0x24, 0x00, 0x00, 0x00, 0x46, 0x4B};
95   EXPECT_THAT_EXPECTED(
96       DXContainer::create(getMemoryBuffer<38>(Buffer)),
97       FailedWithMessage("File not large enough to read part name"));
98 }
99 
100 TEST(DXCFile, ParsePartNoSize) {
101   // This test covers a case where the part's header is readable, but the size
102   // the part extends beyond the boundaries of the file.
103   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
104                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105                       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x0D, 0x00,
106                       0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
107                       0x46, 0x4B, 0x45, 0x30, 0x00, 0x00};
108   EXPECT_THAT_EXPECTED(
109       DXContainer::create(getMemoryBuffer<42>(Buffer)),
110       FailedWithMessage("Reading part size out of file bounds"));
111 }
112 
113 TEST(DXCFile, ParseOverlappingParts) {
114   // This test covers a case where a part's offset is inside the size range
115   // covered by the previous part.
116   uint8_t Buffer[] = {
117       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
119       0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
120       0x2C, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x30, 0x08, 0x00, 0x00, 0x00,
121       0x46, 0x4B, 0x45, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122   };
123   EXPECT_THAT_EXPECTED(
124       DXContainer::create(getMemoryBuffer<60>(Buffer)),
125       FailedWithMessage(
126           "Part offset for part 1 begins before the previous part ends"));
127 }
128 
129 TEST(DXCFile, ParseEmptyParts) {
130   uint8_t Buffer[] = {
131       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
133       0x70, 0x0D, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
134       0x44, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
135       0x5C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
136       0x46, 0x4B, 0x45, 0x30, 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x31,
137       0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x32, 0x00, 0x00, 0x00, 0x00,
138       0x46, 0x4B, 0x45, 0x33, 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x34,
139       0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x35, 0x00, 0x00, 0x00, 0x00,
140       0x46, 0x4B, 0x45, 0x36, 0x00, 0x00, 0x00, 0x00,
141   };
142   DXContainer C =
143       llvm::cantFail(DXContainer::create(getMemoryBuffer<116>(Buffer)));
144   EXPECT_EQ(C.getHeader().PartCount, 7u);
145 
146   // All the part sizes are 0, which makes a nice test of the range based for
147   int ElementsVisited = 0;
148   for (auto Part : C) {
149     EXPECT_EQ(Part.Part.Size, 0u);
150     EXPECT_EQ(Part.Data.size(), 0u);
151     ++ElementsVisited;
152   }
153   EXPECT_EQ(ElementsVisited, 7);
154 
155   {
156     // These are all intended to be fake part names so that the parser doesn't
157     // try to parse the part data.
158     auto It = C.begin();
159     EXPECT_TRUE(memcmp(It->Part.Name, "FKE0", 4) == 0);
160     ++It;
161     EXPECT_TRUE(memcmp(It->Part.Name, "FKE1", 4) == 0);
162     ++It;
163     EXPECT_TRUE(memcmp(It->Part.Name, "FKE2", 4) == 0);
164     ++It;
165     EXPECT_TRUE(memcmp(It->Part.Name, "FKE3", 4) == 0);
166     ++It;
167     EXPECT_TRUE(memcmp(It->Part.Name, "FKE4", 4) == 0);
168     ++It;
169     EXPECT_TRUE(memcmp(It->Part.Name, "FKE5", 4) == 0);
170     ++It;
171     EXPECT_TRUE(memcmp(It->Part.Name, "FKE6", 4) == 0);
172     ++It; // Don't increment past the end
173     EXPECT_TRUE(memcmp(It->Part.Name, "FKE6", 4) == 0);
174   }
175 }
176 
177 static Expected<DXContainer>
178 generateDXContainer(StringRef Yaml, SmallVectorImpl<char> &BinaryData) {
179   DXContainerYAML::Object Obj;
180   SMDiagnostic GenerateDiag;
181   yaml::Input YIn(
182       Yaml, /*Ctxt=*/nullptr,
183       [](const SMDiagnostic &Diag, void *DiagContext) {
184         *static_cast<SMDiagnostic *>(DiagContext) = Diag;
185       },
186       &GenerateDiag);
187 
188   YIn >> Obj;
189   if (YIn.error())
190     return createStringError(YIn.error(), GenerateDiag.getMessage());
191 
192   raw_svector_ostream OS(BinaryData);
193   std::string ErrorMsg;
194   if (!yaml::yaml2dxcontainer(
195           Obj, OS, [&ErrorMsg](const Twine &Msg) { ErrorMsg = Msg.str(); }))
196     return createStringError(YIn.error(), ErrorMsg);
197 
198   MemoryBufferRef BinaryDataRef = MemoryBufferRef(OS.str(), "");
199 
200   return DXContainer::create(BinaryDataRef);
201 }
202 
203 TEST(DXCFile, PSVResourceIterators) {
204   const char *Yaml = R"(
205 --- !dxcontainer
206 Header:
207   Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
208                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
209   Version:
210     Major:           1
211     Minor:           0
212   PartCount:       2
213 Parts:
214   - Name:            PSV0
215     Size:            144
216     PSVInfo:
217       Version:         0
218       ShaderStage:     14
219       PayloadSizeInBytes: 4092
220       MinimumWaveLaneCount: 0
221       MaximumWaveLaneCount: 4294967295
222       Resources:
223         - Type:            1
224           Space:           1
225           LowerBound:      1
226           UpperBound:      1
227         - Type:            2
228           Space:           2
229           LowerBound:      2
230           UpperBound:      2
231         - Type:            3
232           Space:           3
233           LowerBound:      3
234           UpperBound:      3
235   - Name:            DXIL
236     Size:            24
237     Program:
238       MajorVersion:    6
239       MinorVersion:    0
240       ShaderKind:      14
241       Size:            6
242       DXILMajorVersion: 0
243       DXILMinorVersion: 1
244       DXILSize:        0
245 ...
246 )";
247 
248   SmallVector<char, 256> BinaryData;
249   auto C = generateDXContainer(Yaml, BinaryData);
250 
251   ASSERT_THAT_EXPECTED(C, Succeeded());
252 
253   const auto &PSVInfo = C->getPSVInfo();
254   ASSERT_TRUE(PSVInfo.has_value());
255 
256   EXPECT_EQ(PSVInfo->getResourceCount(), 3u);
257 
258   auto It = PSVInfo->getResources().begin();
259 
260   EXPECT_TRUE(It == PSVInfo->getResources().begin());
261 
262   dxbc::PSV::v2::ResourceBindInfo Binding;
263 
264   Binding = *It;
265   EXPECT_EQ(Binding.Type, 1u);
266   EXPECT_EQ(Binding.Flags, 0u);
267 
268   ++It;
269   Binding = *It;
270 
271   EXPECT_EQ(Binding.Type, 2u);
272   EXPECT_EQ(Binding.Flags, 0u);
273 
274   --It;
275   Binding = *It;
276 
277   EXPECT_TRUE(It == PSVInfo->getResources().begin());
278 
279   EXPECT_EQ(Binding.Type, 1u);
280   EXPECT_EQ(Binding.Flags, 0u);
281 
282   --It;
283   Binding = *It;
284 
285   EXPECT_EQ(Binding.Type, 1u);
286   EXPECT_EQ(Binding.Flags, 0u);
287 
288   ++It;
289   Binding = *It;
290 
291   EXPECT_EQ(Binding.Type, 2u);
292   EXPECT_EQ(Binding.Flags, 0u);
293 
294   ++It;
295   Binding = *It;
296 
297   EXPECT_EQ(Binding.Type, 3u);
298   EXPECT_EQ(Binding.Flags, 0u);
299 
300   EXPECT_FALSE(It == PSVInfo->getResources().end());
301 
302   ++It;
303   Binding = *It;
304 
305   EXPECT_TRUE(It == PSVInfo->getResources().end());
306   EXPECT_FALSE(It != PSVInfo->getResources().end());
307 
308   EXPECT_EQ(Binding.Type, 0u);
309   EXPECT_EQ(Binding.Flags, 0u);
310 
311   {
312     auto Old = It++;
313     Binding = *Old;
314 
315     EXPECT_TRUE(Old == PSVInfo->getResources().end());
316     EXPECT_FALSE(Old != PSVInfo->getResources().end());
317 
318     EXPECT_EQ(Binding.Type, 0u);
319     EXPECT_EQ(Binding.Flags, 0u);
320   }
321 
322   Binding = *It;
323 
324   EXPECT_TRUE(It == PSVInfo->getResources().end());
325 
326   EXPECT_EQ(Binding.Type, 0u);
327   EXPECT_EQ(Binding.Flags, 0u);
328 
329   {
330     auto Old = It--;
331     Binding = *Old;
332     EXPECT_TRUE(Old == PSVInfo->getResources().end());
333 
334     EXPECT_EQ(Binding.Type, 0u);
335     EXPECT_EQ(Binding.Flags, 0u);
336   }
337 
338   Binding = *It;
339 
340   EXPECT_EQ(Binding.Type, 3u);
341   EXPECT_EQ(Binding.Flags, 0u);
342 }
343