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