1 //===- DXILResource.cpp - Representations of DXIL resources ---------------===// 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/Analysis/DXILResource.h" 10 #include "llvm/ADT/APInt.h" 11 #include "llvm/ADT/SmallString.h" 12 #include "llvm/IR/Constants.h" 13 #include "llvm/IR/DerivedTypes.h" 14 #include "llvm/IR/DiagnosticInfo.h" 15 #include "llvm/IR/Instructions.h" 16 #include "llvm/IR/Intrinsics.h" 17 #include "llvm/IR/IntrinsicsDirectX.h" 18 #include "llvm/IR/Metadata.h" 19 #include "llvm/IR/Module.h" 20 #include "llvm/InitializePasses.h" 21 #include "llvm/Support/FormatVariadic.h" 22 23 #define DEBUG_TYPE "dxil-resource" 24 25 using namespace llvm; 26 using namespace dxil; 27 28 static StringRef getResourceClassName(ResourceClass RC) { 29 switch (RC) { 30 case ResourceClass::SRV: 31 return "SRV"; 32 case ResourceClass::UAV: 33 return "UAV"; 34 case ResourceClass::CBuffer: 35 return "CBuffer"; 36 case ResourceClass::Sampler: 37 return "Sampler"; 38 } 39 llvm_unreachable("Unhandled ResourceClass"); 40 } 41 42 static StringRef getResourceKindName(ResourceKind RK) { 43 switch (RK) { 44 case ResourceKind::Texture1D: 45 return "Texture1D"; 46 case ResourceKind::Texture2D: 47 return "Texture2D"; 48 case ResourceKind::Texture2DMS: 49 return "Texture2DMS"; 50 case ResourceKind::Texture3D: 51 return "Texture3D"; 52 case ResourceKind::TextureCube: 53 return "TextureCube"; 54 case ResourceKind::Texture1DArray: 55 return "Texture1DArray"; 56 case ResourceKind::Texture2DArray: 57 return "Texture2DArray"; 58 case ResourceKind::Texture2DMSArray: 59 return "Texture2DMSArray"; 60 case ResourceKind::TextureCubeArray: 61 return "TextureCubeArray"; 62 case ResourceKind::TypedBuffer: 63 return "TypedBuffer"; 64 case ResourceKind::RawBuffer: 65 return "RawBuffer"; 66 case ResourceKind::StructuredBuffer: 67 return "StructuredBuffer"; 68 case ResourceKind::CBuffer: 69 return "CBuffer"; 70 case ResourceKind::Sampler: 71 return "Sampler"; 72 case ResourceKind::TBuffer: 73 return "TBuffer"; 74 case ResourceKind::RTAccelerationStructure: 75 return "RTAccelerationStructure"; 76 case ResourceKind::FeedbackTexture2D: 77 return "FeedbackTexture2D"; 78 case ResourceKind::FeedbackTexture2DArray: 79 return "FeedbackTexture2DArray"; 80 case ResourceKind::NumEntries: 81 case ResourceKind::Invalid: 82 return "<invalid>"; 83 } 84 llvm_unreachable("Unhandled ResourceKind"); 85 } 86 87 static StringRef getElementTypeName(ElementType ET) { 88 switch (ET) { 89 case ElementType::I1: 90 return "i1"; 91 case ElementType::I16: 92 return "i16"; 93 case ElementType::U16: 94 return "u16"; 95 case ElementType::I32: 96 return "i32"; 97 case ElementType::U32: 98 return "u32"; 99 case ElementType::I64: 100 return "i64"; 101 case ElementType::U64: 102 return "u64"; 103 case ElementType::F16: 104 return "f16"; 105 case ElementType::F32: 106 return "f32"; 107 case ElementType::F64: 108 return "f64"; 109 case ElementType::SNormF16: 110 return "snorm_f16"; 111 case ElementType::UNormF16: 112 return "unorm_f16"; 113 case ElementType::SNormF32: 114 return "snorm_f32"; 115 case ElementType::UNormF32: 116 return "unorm_f32"; 117 case ElementType::SNormF64: 118 return "snorm_f64"; 119 case ElementType::UNormF64: 120 return "unorm_f64"; 121 case ElementType::PackedS8x32: 122 return "p32i8"; 123 case ElementType::PackedU8x32: 124 return "p32u8"; 125 case ElementType::Invalid: 126 return "<invalid>"; 127 } 128 llvm_unreachable("Unhandled ElementType"); 129 } 130 131 static StringRef getSamplerTypeName(SamplerType ST) { 132 switch (ST) { 133 case SamplerType::Default: 134 return "Default"; 135 case SamplerType::Comparison: 136 return "Comparison"; 137 case SamplerType::Mono: 138 return "Mono"; 139 } 140 llvm_unreachable("Unhandled SamplerType"); 141 } 142 143 static StringRef getSamplerFeedbackTypeName(SamplerFeedbackType SFT) { 144 switch (SFT) { 145 case SamplerFeedbackType::MinMip: 146 return "MinMip"; 147 case SamplerFeedbackType::MipRegionUsed: 148 return "MipRegionUsed"; 149 } 150 llvm_unreachable("Unhandled SamplerFeedbackType"); 151 } 152 153 static dxil::ElementType toDXILElementType(Type *Ty, bool IsSigned) { 154 // TODO: Handle unorm, snorm, and packed. 155 Ty = Ty->getScalarType(); 156 157 if (Ty->isIntegerTy()) { 158 switch (Ty->getIntegerBitWidth()) { 159 case 16: 160 return IsSigned ? ElementType::I16 : ElementType::U16; 161 case 32: 162 return IsSigned ? ElementType::I32 : ElementType::U32; 163 case 64: 164 return IsSigned ? ElementType::I64 : ElementType::U64; 165 case 1: 166 default: 167 return ElementType::Invalid; 168 } 169 } else if (Ty->isFloatTy()) { 170 return ElementType::F32; 171 } else if (Ty->isDoubleTy()) { 172 return ElementType::F64; 173 } else if (Ty->isHalfTy()) { 174 return ElementType::F16; 175 } 176 177 return ElementType::Invalid; 178 } 179 180 ResourceTypeInfo::ResourceTypeInfo(TargetExtType *HandleTy, 181 const dxil::ResourceClass RC_, 182 const dxil::ResourceKind Kind_, 183 bool GloballyCoherent, bool HasCounter) 184 : HandleTy(HandleTy), GloballyCoherent(GloballyCoherent), 185 HasCounter(HasCounter) { 186 // If we're provided a resource class and kind, trust them. 187 if (Kind_ != dxil::ResourceKind::Invalid) { 188 RC = RC_; 189 Kind = Kind_; 190 return; 191 } 192 193 if (auto *Ty = dyn_cast<RawBufferExtType>(HandleTy)) { 194 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; 195 Kind = Ty->isStructured() ? ResourceKind::StructuredBuffer 196 : ResourceKind::RawBuffer; 197 } else if (auto *Ty = dyn_cast<TypedBufferExtType>(HandleTy)) { 198 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; 199 Kind = ResourceKind::TypedBuffer; 200 } else if (auto *Ty = dyn_cast<TextureExtType>(HandleTy)) { 201 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; 202 Kind = Ty->getDimension(); 203 } else if (auto *Ty = dyn_cast<MSTextureExtType>(HandleTy)) { 204 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV; 205 Kind = Ty->getDimension(); 206 } else if (auto *Ty = dyn_cast<FeedbackTextureExtType>(HandleTy)) { 207 RC = ResourceClass::UAV; 208 Kind = Ty->getDimension(); 209 } else if (isa<CBufferExtType>(HandleTy)) { 210 RC = ResourceClass::CBuffer; 211 Kind = ResourceKind::CBuffer; 212 } else if (isa<SamplerExtType>(HandleTy)) { 213 RC = ResourceClass::Sampler; 214 Kind = ResourceKind::Sampler; 215 } else 216 llvm_unreachable("Unknown handle type"); 217 } 218 219 static void formatTypeName(SmallString<64> &Dest, StringRef Name, 220 bool isWriteable, bool isROV) { 221 Dest = isWriteable ? (isROV ? "RasterizerOrdered" : "RW") : ""; 222 Dest += Name; 223 } 224 225 StructType *ResourceTypeInfo::createElementStruct() { 226 SmallString<64> TypeName; 227 228 switch (Kind) { 229 case ResourceKind::Texture1D: 230 case ResourceKind::Texture2D: 231 case ResourceKind::Texture3D: 232 case ResourceKind::TextureCube: 233 case ResourceKind::Texture1DArray: 234 case ResourceKind::Texture2DArray: 235 case ResourceKind::TextureCubeArray: { 236 auto *RTy = cast<TextureExtType>(HandleTy); 237 formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(), 238 RTy->isROV()); 239 return StructType::create(RTy->getResourceType(), TypeName); 240 } 241 case ResourceKind::Texture2DMS: 242 case ResourceKind::Texture2DMSArray: { 243 auto *RTy = cast<MSTextureExtType>(HandleTy); 244 formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(), 245 /*IsROV=*/false); 246 return StructType::create(RTy->getResourceType(), TypeName); 247 } 248 case ResourceKind::TypedBuffer: { 249 auto *RTy = cast<TypedBufferExtType>(HandleTy); 250 formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(), 251 RTy->isROV()); 252 return StructType::create(RTy->getResourceType(), TypeName); 253 } 254 case ResourceKind::RawBuffer: { 255 auto *RTy = cast<RawBufferExtType>(HandleTy); 256 formatTypeName(TypeName, "ByteAddressBuffer", RTy->isWriteable(), 257 RTy->isROV()); 258 return StructType::create(Type::getInt32Ty(HandleTy->getContext()), 259 TypeName); 260 } 261 case ResourceKind::StructuredBuffer: { 262 auto *RTy = cast<RawBufferExtType>(HandleTy); 263 formatTypeName(TypeName, "StructuredBuffer", RTy->isWriteable(), 264 RTy->isROV()); 265 return StructType::create(RTy->getResourceType(), TypeName); 266 } 267 case ResourceKind::FeedbackTexture2D: 268 case ResourceKind::FeedbackTexture2DArray: { 269 auto *RTy = cast<FeedbackTextureExtType>(HandleTy); 270 TypeName = formatv("{0}<{1}>", getResourceKindName(Kind), 271 llvm::to_underlying(RTy->getFeedbackType())); 272 return StructType::create(Type::getInt32Ty(HandleTy->getContext()), 273 TypeName); 274 } 275 case ResourceKind::CBuffer: 276 return StructType::create(HandleTy->getContext(), "cbuffer"); 277 case ResourceKind::Sampler: { 278 auto *RTy = cast<SamplerExtType>(HandleTy); 279 TypeName = formatv("SamplerState<{0}>", 280 llvm::to_underlying(RTy->getSamplerType())); 281 return StructType::create(Type::getInt32Ty(HandleTy->getContext()), 282 TypeName); 283 } 284 case ResourceKind::TBuffer: 285 case ResourceKind::RTAccelerationStructure: 286 llvm_unreachable("Unhandled resource kind"); 287 case ResourceKind::Invalid: 288 case ResourceKind::NumEntries: 289 llvm_unreachable("Invalid resource kind"); 290 } 291 llvm_unreachable("Unhandled ResourceKind enum"); 292 } 293 294 bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; } 295 296 bool ResourceTypeInfo::isCBuffer() const { 297 return RC == ResourceClass::CBuffer; 298 } 299 300 bool ResourceTypeInfo::isSampler() const { 301 return RC == ResourceClass::Sampler; 302 } 303 304 bool ResourceTypeInfo::isStruct() const { 305 return Kind == ResourceKind::StructuredBuffer; 306 } 307 308 bool ResourceTypeInfo::isTyped() const { 309 switch (Kind) { 310 case ResourceKind::Texture1D: 311 case ResourceKind::Texture2D: 312 case ResourceKind::Texture2DMS: 313 case ResourceKind::Texture3D: 314 case ResourceKind::TextureCube: 315 case ResourceKind::Texture1DArray: 316 case ResourceKind::Texture2DArray: 317 case ResourceKind::Texture2DMSArray: 318 case ResourceKind::TextureCubeArray: 319 case ResourceKind::TypedBuffer: 320 return true; 321 case ResourceKind::RawBuffer: 322 case ResourceKind::StructuredBuffer: 323 case ResourceKind::FeedbackTexture2D: 324 case ResourceKind::FeedbackTexture2DArray: 325 case ResourceKind::CBuffer: 326 case ResourceKind::Sampler: 327 case ResourceKind::TBuffer: 328 case ResourceKind::RTAccelerationStructure: 329 return false; 330 case ResourceKind::Invalid: 331 case ResourceKind::NumEntries: 332 llvm_unreachable("Invalid resource kind"); 333 } 334 llvm_unreachable("Unhandled ResourceKind enum"); 335 } 336 337 bool ResourceTypeInfo::isFeedback() const { 338 return Kind == ResourceKind::FeedbackTexture2D || 339 Kind == ResourceKind::FeedbackTexture2DArray; 340 } 341 342 bool ResourceTypeInfo::isMultiSample() const { 343 return Kind == ResourceKind::Texture2DMS || 344 Kind == ResourceKind::Texture2DMSArray; 345 } 346 347 static bool isROV(dxil::ResourceKind Kind, TargetExtType *Ty) { 348 switch (Kind) { 349 case ResourceKind::Texture1D: 350 case ResourceKind::Texture2D: 351 case ResourceKind::Texture3D: 352 case ResourceKind::TextureCube: 353 case ResourceKind::Texture1DArray: 354 case ResourceKind::Texture2DArray: 355 case ResourceKind::TextureCubeArray: 356 return cast<TextureExtType>(Ty)->isROV(); 357 case ResourceKind::TypedBuffer: 358 return cast<TypedBufferExtType>(Ty)->isROV(); 359 case ResourceKind::RawBuffer: 360 case ResourceKind::StructuredBuffer: 361 return cast<RawBufferExtType>(Ty)->isROV(); 362 case ResourceKind::Texture2DMS: 363 case ResourceKind::Texture2DMSArray: 364 case ResourceKind::FeedbackTexture2D: 365 case ResourceKind::FeedbackTexture2DArray: 366 return false; 367 case ResourceKind::CBuffer: 368 case ResourceKind::Sampler: 369 case ResourceKind::TBuffer: 370 case ResourceKind::RTAccelerationStructure: 371 case ResourceKind::Invalid: 372 case ResourceKind::NumEntries: 373 llvm_unreachable("Resource cannot be ROV"); 374 } 375 llvm_unreachable("Unhandled ResourceKind enum"); 376 } 377 378 ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const { 379 assert(isUAV() && "Not a UAV"); 380 return {GloballyCoherent, HasCounter, isROV(Kind, HandleTy)}; 381 } 382 383 uint32_t ResourceTypeInfo::getCBufferSize(const DataLayout &DL) const { 384 assert(isCBuffer() && "Not a CBuffer"); 385 return cast<CBufferExtType>(HandleTy)->getCBufferSize(); 386 } 387 388 dxil::SamplerType ResourceTypeInfo::getSamplerType() const { 389 assert(isSampler() && "Not a Sampler"); 390 return cast<SamplerExtType>(HandleTy)->getSamplerType(); 391 } 392 393 ResourceTypeInfo::StructInfo 394 ResourceTypeInfo::getStruct(const DataLayout &DL) const { 395 assert(isStruct() && "Not a Struct"); 396 397 Type *ElTy = cast<RawBufferExtType>(HandleTy)->getResourceType(); 398 399 uint32_t Stride = DL.getTypeAllocSize(ElTy); 400 MaybeAlign Alignment; 401 if (auto *STy = dyn_cast<StructType>(ElTy)) 402 Alignment = DL.getStructLayout(STy)->getAlignment(); 403 uint32_t AlignLog2 = Alignment ? Log2(*Alignment) : 0; 404 return {Stride, AlignLog2}; 405 } 406 407 static std::pair<Type *, bool> getTypedElementType(dxil::ResourceKind Kind, 408 TargetExtType *Ty) { 409 switch (Kind) { 410 case ResourceKind::Texture1D: 411 case ResourceKind::Texture2D: 412 case ResourceKind::Texture3D: 413 case ResourceKind::TextureCube: 414 case ResourceKind::Texture1DArray: 415 case ResourceKind::Texture2DArray: 416 case ResourceKind::TextureCubeArray: { 417 auto *RTy = cast<TextureExtType>(Ty); 418 return {RTy->getResourceType(), RTy->isSigned()}; 419 } 420 case ResourceKind::Texture2DMS: 421 case ResourceKind::Texture2DMSArray: { 422 auto *RTy = cast<MSTextureExtType>(Ty); 423 return {RTy->getResourceType(), RTy->isSigned()}; 424 } 425 case ResourceKind::TypedBuffer: { 426 auto *RTy = cast<TypedBufferExtType>(Ty); 427 return {RTy->getResourceType(), RTy->isSigned()}; 428 } 429 case ResourceKind::RawBuffer: 430 case ResourceKind::StructuredBuffer: 431 case ResourceKind::FeedbackTexture2D: 432 case ResourceKind::FeedbackTexture2DArray: 433 case ResourceKind::CBuffer: 434 case ResourceKind::Sampler: 435 case ResourceKind::TBuffer: 436 case ResourceKind::RTAccelerationStructure: 437 case ResourceKind::Invalid: 438 case ResourceKind::NumEntries: 439 llvm_unreachable("Resource is not typed"); 440 } 441 llvm_unreachable("Unhandled ResourceKind enum"); 442 } 443 444 ResourceTypeInfo::TypedInfo ResourceTypeInfo::getTyped() const { 445 assert(isTyped() && "Not typed"); 446 447 auto [ElTy, IsSigned] = getTypedElementType(Kind, HandleTy); 448 dxil::ElementType ET = toDXILElementType(ElTy, IsSigned); 449 uint32_t Count = 1; 450 if (auto *VTy = dyn_cast<FixedVectorType>(ElTy)) 451 Count = VTy->getNumElements(); 452 return {ET, Count}; 453 } 454 455 dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const { 456 assert(isFeedback() && "Not Feedback"); 457 return cast<FeedbackTextureExtType>(HandleTy)->getFeedbackType(); 458 } 459 uint32_t ResourceTypeInfo::getMultiSampleCount() const { 460 assert(isMultiSample() && "Not MultiSampled"); 461 return cast<MSTextureExtType>(HandleTy)->getSampleCount(); 462 } 463 464 bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const { 465 return std::tie(HandleTy, GloballyCoherent, HasCounter) == 466 std::tie(RHS.HandleTy, RHS.GloballyCoherent, RHS.HasCounter); 467 } 468 469 bool ResourceTypeInfo::operator<(const ResourceTypeInfo &RHS) const { 470 // An empty datalayout is sufficient for sorting purposes. 471 DataLayout DummyDL; 472 if (std::tie(RC, Kind) < std::tie(RHS.RC, RHS.Kind)) 473 return true; 474 if (isCBuffer() && RHS.isCBuffer() && 475 getCBufferSize(DummyDL) < RHS.getCBufferSize(DummyDL)) 476 return true; 477 if (isSampler() && RHS.isSampler() && getSamplerType() < RHS.getSamplerType()) 478 return true; 479 if (isUAV() && RHS.isUAV() && getUAV() < RHS.getUAV()) 480 return true; 481 if (isStruct() && RHS.isStruct() && 482 getStruct(DummyDL) < RHS.getStruct(DummyDL)) 483 return true; 484 if (isFeedback() && RHS.isFeedback() && 485 getFeedbackType() < RHS.getFeedbackType()) 486 return true; 487 if (isTyped() && RHS.isTyped() && getTyped() < RHS.getTyped()) 488 return true; 489 if (isMultiSample() && RHS.isMultiSample() && 490 getMultiSampleCount() < RHS.getMultiSampleCount()) 491 return true; 492 return false; 493 } 494 495 void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const { 496 OS << " Class: " << getResourceClassName(RC) << "\n" 497 << " Kind: " << getResourceKindName(Kind) << "\n"; 498 499 if (isCBuffer()) { 500 OS << " CBuffer size: " << getCBufferSize(DL) << "\n"; 501 } else if (isSampler()) { 502 OS << " Sampler Type: " << getSamplerTypeName(getSamplerType()) << "\n"; 503 } else { 504 if (isUAV()) { 505 UAVInfo UAVFlags = getUAV(); 506 OS << " Globally Coherent: " << UAVFlags.GloballyCoherent << "\n" 507 << " HasCounter: " << UAVFlags.HasCounter << "\n" 508 << " IsROV: " << UAVFlags.IsROV << "\n"; 509 } 510 if (isMultiSample()) 511 OS << " Sample Count: " << getMultiSampleCount() << "\n"; 512 513 if (isStruct()) { 514 StructInfo Struct = getStruct(DL); 515 OS << " Buffer Stride: " << Struct.Stride << "\n"; 516 OS << " Alignment: " << Struct.AlignLog2 << "\n"; 517 } else if (isTyped()) { 518 TypedInfo Typed = getTyped(); 519 OS << " Element Type: " << getElementTypeName(Typed.ElementTy) << "\n" 520 << " Element Count: " << Typed.ElementCount << "\n"; 521 } else if (isFeedback()) 522 OS << " Feedback Type: " << getSamplerFeedbackTypeName(getFeedbackType()) 523 << "\n"; 524 } 525 } 526 527 GlobalVariable *ResourceBindingInfo::createSymbol(Module &M, StructType *Ty, 528 StringRef Name) { 529 assert(!Symbol && "Symbol has already been created"); 530 Symbol = new GlobalVariable(M, Ty, /*isConstant=*/true, 531 GlobalValue::ExternalLinkage, 532 /*Initializer=*/nullptr, Name); 533 return Symbol; 534 } 535 536 MDTuple *ResourceBindingInfo::getAsMetadata(Module &M, 537 dxil::ResourceTypeInfo &RTI) const { 538 LLVMContext &Ctx = M.getContext(); 539 const DataLayout &DL = M.getDataLayout(); 540 541 SmallVector<Metadata *, 11> MDVals; 542 543 Type *I32Ty = Type::getInt32Ty(Ctx); 544 Type *I1Ty = Type::getInt1Ty(Ctx); 545 auto getIntMD = [&I32Ty](uint32_t V) { 546 return ConstantAsMetadata::get( 547 Constant::getIntegerValue(I32Ty, APInt(32, V))); 548 }; 549 auto getBoolMD = [&I1Ty](uint32_t V) { 550 return ConstantAsMetadata::get( 551 Constant::getIntegerValue(I1Ty, APInt(1, V))); 552 }; 553 554 MDVals.push_back(getIntMD(Binding.RecordID)); 555 assert(Symbol && "Cannot yet create useful resource metadata without symbol"); 556 MDVals.push_back(ValueAsMetadata::get(Symbol)); 557 MDVals.push_back(MDString::get(Ctx, Symbol->getName())); 558 MDVals.push_back(getIntMD(Binding.Space)); 559 MDVals.push_back(getIntMD(Binding.LowerBound)); 560 MDVals.push_back(getIntMD(Binding.Size)); 561 562 if (RTI.isCBuffer()) { 563 MDVals.push_back(getIntMD(RTI.getCBufferSize(DL))); 564 MDVals.push_back(nullptr); 565 } else if (RTI.isSampler()) { 566 MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getSamplerType()))); 567 MDVals.push_back(nullptr); 568 } else { 569 MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getResourceKind()))); 570 571 if (RTI.isUAV()) { 572 ResourceTypeInfo::UAVInfo UAVFlags = RTI.getUAV(); 573 MDVals.push_back(getBoolMD(UAVFlags.GloballyCoherent)); 574 MDVals.push_back(getBoolMD(UAVFlags.HasCounter)); 575 MDVals.push_back(getBoolMD(UAVFlags.IsROV)); 576 } else { 577 // All SRVs include sample count in the metadata, but it's only meaningful 578 // for multi-sampled textured. Also, UAVs can be multisampled in SM6.7+, 579 // but this just isn't reflected in the metadata at all. 580 uint32_t SampleCount = 581 RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0; 582 MDVals.push_back(getIntMD(SampleCount)); 583 } 584 585 // Further properties are attached to a metadata list of tag-value pairs. 586 SmallVector<Metadata *> Tags; 587 if (RTI.isStruct()) { 588 Tags.push_back( 589 getIntMD(llvm::to_underlying(ExtPropTags::StructuredBufferStride))); 590 Tags.push_back(getIntMD(RTI.getStruct(DL).Stride)); 591 } else if (RTI.isTyped()) { 592 Tags.push_back(getIntMD(llvm::to_underlying(ExtPropTags::ElementType))); 593 Tags.push_back(getIntMD(llvm::to_underlying(RTI.getTyped().ElementTy))); 594 } else if (RTI.isFeedback()) { 595 Tags.push_back( 596 getIntMD(llvm::to_underlying(ExtPropTags::SamplerFeedbackKind))); 597 Tags.push_back(getIntMD(llvm::to_underlying(RTI.getFeedbackType()))); 598 } 599 MDVals.push_back(Tags.empty() ? nullptr : MDNode::get(Ctx, Tags)); 600 } 601 602 return MDNode::get(Ctx, MDVals); 603 } 604 605 std::pair<uint32_t, uint32_t> 606 ResourceBindingInfo::getAnnotateProps(Module &M, 607 dxil::ResourceTypeInfo &RTI) const { 608 const DataLayout &DL = M.getDataLayout(); 609 610 uint32_t ResourceKind = llvm::to_underlying(RTI.getResourceKind()); 611 uint32_t AlignLog2 = RTI.isStruct() ? RTI.getStruct(DL).AlignLog2 : 0; 612 bool IsUAV = RTI.isUAV(); 613 ResourceTypeInfo::UAVInfo UAVFlags = 614 IsUAV ? RTI.getUAV() : ResourceTypeInfo::UAVInfo{}; 615 bool IsROV = IsUAV && UAVFlags.IsROV; 616 bool IsGloballyCoherent = IsUAV && UAVFlags.GloballyCoherent; 617 uint8_t SamplerCmpOrHasCounter = 0; 618 if (IsUAV) 619 SamplerCmpOrHasCounter = UAVFlags.HasCounter; 620 else if (RTI.isSampler()) 621 SamplerCmpOrHasCounter = RTI.getSamplerType() == SamplerType::Comparison; 622 623 // TODO: Document this format. Currently the only reference is the 624 // implementation of dxc's DxilResourceProperties struct. 625 uint32_t Word0 = 0; 626 Word0 |= ResourceKind & 0xFF; 627 Word0 |= (AlignLog2 & 0xF) << 8; 628 Word0 |= (IsUAV & 1) << 12; 629 Word0 |= (IsROV & 1) << 13; 630 Word0 |= (IsGloballyCoherent & 1) << 14; 631 Word0 |= (SamplerCmpOrHasCounter & 1) << 15; 632 633 uint32_t Word1 = 0; 634 if (RTI.isStruct()) 635 Word1 = RTI.getStruct(DL).Stride; 636 else if (RTI.isCBuffer()) 637 Word1 = RTI.getCBufferSize(DL); 638 else if (RTI.isFeedback()) 639 Word1 = llvm::to_underlying(RTI.getFeedbackType()); 640 else if (RTI.isTyped()) { 641 ResourceTypeInfo::TypedInfo Typed = RTI.getTyped(); 642 uint32_t CompType = llvm::to_underlying(Typed.ElementTy); 643 uint32_t CompCount = Typed.ElementCount; 644 uint32_t SampleCount = RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0; 645 646 Word1 |= (CompType & 0xFF) << 0; 647 Word1 |= (CompCount & 0xFF) << 8; 648 Word1 |= (SampleCount & 0xFF) << 16; 649 } 650 651 return {Word0, Word1}; 652 } 653 654 void ResourceBindingInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI, 655 const DataLayout &DL) const { 656 if (Symbol) { 657 OS << " Symbol: "; 658 Symbol->printAsOperand(OS); 659 OS << "\n"; 660 } 661 662 OS << " Binding:\n" 663 << " Record ID: " << Binding.RecordID << "\n" 664 << " Space: " << Binding.Space << "\n" 665 << " Lower Bound: " << Binding.LowerBound << "\n" 666 << " Size: " << Binding.Size << "\n"; 667 668 RTI.print(OS, DL); 669 } 670 671 //===----------------------------------------------------------------------===// 672 673 bool DXILResourceTypeMap::invalidate(Module &M, const PreservedAnalyses &PA, 674 ModuleAnalysisManager::Invalidator &Inv) { 675 // Passes that introduce resource types must explicitly invalidate this pass. 676 auto PAC = PA.getChecker<DXILResourceTypeAnalysis>(); 677 return !PAC.preservedWhenStateless(); 678 } 679 680 //===----------------------------------------------------------------------===// 681 682 void DXILBindingMap::populate(Module &M, DXILResourceTypeMap &DRTM) { 683 SmallVector<std::tuple<CallInst *, ResourceBindingInfo, ResourceTypeInfo>> 684 CIToInfos; 685 686 for (Function &F : M.functions()) { 687 if (!F.isDeclaration()) 688 continue; 689 LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n"); 690 Intrinsic::ID ID = F.getIntrinsicID(); 691 switch (ID) { 692 default: 693 continue; 694 case Intrinsic::dx_resource_handlefrombinding: { 695 auto *HandleTy = cast<TargetExtType>(F.getReturnType()); 696 ResourceTypeInfo &RTI = DRTM[HandleTy]; 697 698 for (User *U : F.users()) 699 if (CallInst *CI = dyn_cast<CallInst>(U)) { 700 LLVM_DEBUG(dbgs() << " Visiting: " << *U << "\n"); 701 uint32_t Space = 702 cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue(); 703 uint32_t LowerBound = 704 cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue(); 705 uint32_t Size = 706 cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue(); 707 ResourceBindingInfo RBI = ResourceBindingInfo{ 708 /*RecordID=*/0, Space, LowerBound, Size, HandleTy}; 709 710 CIToInfos.emplace_back(CI, RBI, RTI); 711 } 712 713 break; 714 } 715 } 716 } 717 718 llvm::stable_sort(CIToInfos, [](auto &LHS, auto &RHS) { 719 const auto &[LCI, LRBI, LRTI] = LHS; 720 const auto &[RCI, RRBI, RRTI] = RHS; 721 // Sort by resource class first for grouping purposes, and then by the 722 // binding and type so we can remove duplicates. 723 ResourceClass LRC = LRTI.getResourceClass(); 724 ResourceClass RRC = RRTI.getResourceClass(); 725 726 return std::tie(LRC, LRBI, LRTI) < std::tie(RRC, RRBI, RRTI); 727 }); 728 for (auto [CI, RBI, RTI] : CIToInfos) { 729 if (Infos.empty() || RBI != Infos.back()) 730 Infos.push_back(RBI); 731 CallMap[CI] = Infos.size() - 1; 732 } 733 734 unsigned Size = Infos.size(); 735 // In DXC, Record ID is unique per resource type. Match that. 736 FirstUAV = FirstCBuffer = FirstSampler = Size; 737 uint32_t NextID = 0; 738 for (unsigned I = 0, E = Size; I != E; ++I) { 739 ResourceBindingInfo &RBI = Infos[I]; 740 ResourceTypeInfo &RTI = DRTM[RBI.getHandleTy()]; 741 if (RTI.isUAV() && FirstUAV == Size) { 742 FirstUAV = I; 743 NextID = 0; 744 } else if (RTI.isCBuffer() && FirstCBuffer == Size) { 745 FirstCBuffer = I; 746 NextID = 0; 747 } else if (RTI.isSampler() && FirstSampler == Size) { 748 FirstSampler = I; 749 NextID = 0; 750 } 751 752 // Adjust the resource binding to use the next ID. 753 RBI.setBindingID(NextID++); 754 } 755 } 756 757 void DXILBindingMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM, 758 const DataLayout &DL) const { 759 for (unsigned I = 0, E = Infos.size(); I != E; ++I) { 760 OS << "Binding " << I << ":\n"; 761 const dxil::ResourceBindingInfo &RBI = Infos[I]; 762 RBI.print(OS, DRTM[RBI.getHandleTy()], DL); 763 OS << "\n"; 764 } 765 766 for (const auto &[CI, Index] : CallMap) { 767 OS << "Call bound to " << Index << ":"; 768 CI->print(OS); 769 OS << "\n"; 770 } 771 } 772 773 //===----------------------------------------------------------------------===// 774 775 AnalysisKey DXILResourceTypeAnalysis::Key; 776 AnalysisKey DXILResourceBindingAnalysis::Key; 777 778 DXILBindingMap DXILResourceBindingAnalysis::run(Module &M, 779 ModuleAnalysisManager &AM) { 780 DXILBindingMap Data; 781 DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M); 782 Data.populate(M, DRTM); 783 return Data; 784 } 785 786 PreservedAnalyses 787 DXILResourceBindingPrinterPass::run(Module &M, ModuleAnalysisManager &AM) { 788 DXILBindingMap &DBM = AM.getResult<DXILResourceBindingAnalysis>(M); 789 DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M); 790 791 DBM.print(OS, DRTM, M.getDataLayout()); 792 return PreservedAnalyses::all(); 793 } 794 795 void DXILResourceTypeWrapperPass::anchor() {} 796 797 DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass() : ImmutablePass(ID) { 798 initializeDXILResourceTypeWrapperPassPass(*PassRegistry::getPassRegistry()); 799 } 800 801 INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type", 802 "DXIL Resource Type Analysis", false, true) 803 char DXILResourceTypeWrapperPass::ID = 0; 804 805 ModulePass *llvm::createDXILResourceTypeWrapperPassPass() { 806 return new DXILResourceTypeWrapperPass(); 807 } 808 809 DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass() 810 : ModulePass(ID) { 811 initializeDXILResourceBindingWrapperPassPass( 812 *PassRegistry::getPassRegistry()); 813 } 814 815 DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default; 816 817 void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { 818 AU.addRequiredTransitive<DXILResourceTypeWrapperPass>(); 819 AU.setPreservesAll(); 820 } 821 822 bool DXILResourceBindingWrapperPass::runOnModule(Module &M) { 823 Map.reset(new DXILBindingMap()); 824 825 DRTM = &getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap(); 826 Map->populate(M, *DRTM); 827 828 return false; 829 } 830 831 void DXILResourceBindingWrapperPass::releaseMemory() { Map.reset(); } 832 833 void DXILResourceBindingWrapperPass::print(raw_ostream &OS, 834 const Module *M) const { 835 if (!Map) { 836 OS << "No resource map has been built!\n"; 837 return; 838 } 839 Map->print(OS, *DRTM, M->getDataLayout()); 840 } 841 842 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 843 LLVM_DUMP_METHOD 844 void DXILResourceBindingWrapperPass::dump() const { print(dbgs(), nullptr); } 845 #endif 846 847 INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding", 848 "DXIL Resource Binding Analysis", false, true) 849 char DXILResourceBindingWrapperPass::ID = 0; 850 851 ModulePass *llvm::createDXILResourceBindingWrapperPassPass() { 852 return new DXILResourceBindingWrapperPass(); 853 } 854