1209 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Analysis/DXILResource.h"10#include "llvm/ADT/APInt.h"11#include "llvm/ADT/STLExtras.h"12#include "llvm/ADT/SmallString.h"13#include "llvm/ADT/SmallVector.h"14#include "llvm/IR/Constants.h"15#include "llvm/IR/DerivedTypes.h"16#include "llvm/IR/DiagnosticInfo.h"17#include "llvm/IR/Instructions.h"18#include "llvm/IR/Intrinsics.h"19#include "llvm/IR/IntrinsicsDirectX.h"20#include "llvm/IR/Metadata.h"21#include "llvm/IR/Module.h"22#include "llvm/InitializePasses.h"23#include "llvm/Support/DXILABI.h"24#include "llvm/Support/FormatVariadic.h"25#include <cstdint>26 27#define DEBUG_TYPE "dxil-resource"28 29using namespace llvm;30using namespace dxil;31 32static StringRef getResourceKindName(ResourceKind RK) {33 switch (RK) {34 case ResourceKind::Texture1D:35 return "Texture1D";36 case ResourceKind::Texture2D:37 return "Texture2D";38 case ResourceKind::Texture2DMS:39 return "Texture2DMS";40 case ResourceKind::Texture3D:41 return "Texture3D";42 case ResourceKind::TextureCube:43 return "TextureCube";44 case ResourceKind::Texture1DArray:45 return "Texture1DArray";46 case ResourceKind::Texture2DArray:47 return "Texture2DArray";48 case ResourceKind::Texture2DMSArray:49 return "Texture2DMSArray";50 case ResourceKind::TextureCubeArray:51 return "TextureCubeArray";52 case ResourceKind::TypedBuffer:53 return "Buffer";54 case ResourceKind::RawBuffer:55 return "RawBuffer";56 case ResourceKind::StructuredBuffer:57 return "StructuredBuffer";58 case ResourceKind::CBuffer:59 return "CBuffer";60 case ResourceKind::Sampler:61 return "Sampler";62 case ResourceKind::TBuffer:63 return "TBuffer";64 case ResourceKind::RTAccelerationStructure:65 return "RTAccelerationStructure";66 case ResourceKind::FeedbackTexture2D:67 return "FeedbackTexture2D";68 case ResourceKind::FeedbackTexture2DArray:69 return "FeedbackTexture2DArray";70 case ResourceKind::NumEntries:71 case ResourceKind::Invalid:72 return "<invalid>";73 }74 llvm_unreachable("Unhandled ResourceKind");75}76 77static StringRef getElementTypeName(ElementType ET) {78 switch (ET) {79 case ElementType::I1:80 return "i1";81 case ElementType::I16:82 return "i16";83 case ElementType::U16:84 return "u16";85 case ElementType::I32:86 return "i32";87 case ElementType::U32:88 return "u32";89 case ElementType::I64:90 return "i64";91 case ElementType::U64:92 return "u64";93 case ElementType::F16:94 return "f16";95 case ElementType::F32:96 return "f32";97 case ElementType::F64:98 return "f64";99 case ElementType::SNormF16:100 return "snorm_f16";101 case ElementType::UNormF16:102 return "unorm_f16";103 case ElementType::SNormF32:104 return "snorm_f32";105 case ElementType::UNormF32:106 return "unorm_f32";107 case ElementType::SNormF64:108 return "snorm_f64";109 case ElementType::UNormF64:110 return "unorm_f64";111 case ElementType::PackedS8x32:112 return "p32i8";113 case ElementType::PackedU8x32:114 return "p32u8";115 case ElementType::Invalid:116 return "<invalid>";117 }118 llvm_unreachable("Unhandled ElementType");119}120 121static StringRef getElementTypeNameForTemplate(ElementType ET) {122 switch (ET) {123 case ElementType::I1:124 return "bool";125 case ElementType::I16:126 return "int16_t";127 case ElementType::U16:128 return "uint16_t";129 case ElementType::I32:130 return "int32_t";131 case ElementType::U32:132 return "uint32_t";133 case ElementType::I64:134 return "int64_t";135 case ElementType::U64:136 return "uint32_t";137 case ElementType::F16:138 case ElementType::SNormF16:139 case ElementType::UNormF16:140 return "half";141 case ElementType::F32:142 case ElementType::SNormF32:143 case ElementType::UNormF32:144 return "float";145 case ElementType::F64:146 case ElementType::SNormF64:147 case ElementType::UNormF64:148 return "double";149 case ElementType::PackedS8x32:150 return "int8_t4_packed";151 case ElementType::PackedU8x32:152 return "uint8_t4_packed";153 case ElementType::Invalid:154 return "<invalid>";155 }156 llvm_unreachable("Unhandled ElementType");157}158 159static StringRef getSamplerTypeName(SamplerType ST) {160 switch (ST) {161 case SamplerType::Default:162 return "Default";163 case SamplerType::Comparison:164 return "Comparison";165 case SamplerType::Mono:166 return "Mono";167 }168 llvm_unreachable("Unhandled SamplerType");169}170 171static StringRef getSamplerFeedbackTypeName(SamplerFeedbackType SFT) {172 switch (SFT) {173 case SamplerFeedbackType::MinMip:174 return "MinMip";175 case SamplerFeedbackType::MipRegionUsed:176 return "MipRegionUsed";177 }178 llvm_unreachable("Unhandled SamplerFeedbackType");179}180 181static dxil::ElementType toDXILElementType(Type *Ty, bool IsSigned) {182 // TODO: Handle unorm, snorm, and packed.183 Ty = Ty->getScalarType();184 185 if (Ty->isIntegerTy()) {186 switch (Ty->getIntegerBitWidth()) {187 case 16:188 return IsSigned ? ElementType::I16 : ElementType::U16;189 case 32:190 return IsSigned ? ElementType::I32 : ElementType::U32;191 case 64:192 return IsSigned ? ElementType::I64 : ElementType::U64;193 case 1:194 default:195 return ElementType::Invalid;196 }197 } else if (Ty->isFloatTy()) {198 return ElementType::F32;199 } else if (Ty->isDoubleTy()) {200 return ElementType::F64;201 } else if (Ty->isHalfTy()) {202 return ElementType::F16;203 }204 205 return ElementType::Invalid;206}207 208static dxil::ElementType toDXILStorageType(dxil::ElementType ET) {209 if (ET == dxil::ElementType::U64 || ET == dxil::ElementType::F64 ||210 ET == dxil::ElementType::I64 || ET == dxil::ElementType::SNormF64 ||211 ET == dxil::ElementType::UNormF64)212 return dxil::ElementType::U32;213 return ET;214}215 216ResourceTypeInfo::ResourceTypeInfo(TargetExtType *HandleTy,217 const dxil::ResourceClass RC_,218 const dxil::ResourceKind Kind_)219 : HandleTy(HandleTy) {220 // If we're provided a resource class and kind, trust them.221 if (Kind_ != dxil::ResourceKind::Invalid) {222 RC = RC_;223 Kind = Kind_;224 return;225 }226 227 if (auto *Ty = dyn_cast<RawBufferExtType>(HandleTy)) {228 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;229 Kind = Ty->isStructured() ? ResourceKind::StructuredBuffer230 : ResourceKind::RawBuffer;231 } else if (auto *Ty = dyn_cast<TypedBufferExtType>(HandleTy)) {232 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;233 Kind = ResourceKind::TypedBuffer;234 } else if (auto *Ty = dyn_cast<TextureExtType>(HandleTy)) {235 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;236 Kind = Ty->getDimension();237 } else if (auto *Ty = dyn_cast<MSTextureExtType>(HandleTy)) {238 RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;239 Kind = Ty->getDimension();240 } else if (auto *Ty = dyn_cast<FeedbackTextureExtType>(HandleTy)) {241 RC = ResourceClass::UAV;242 Kind = Ty->getDimension();243 } else if (isa<CBufferExtType>(HandleTy)) {244 RC = ResourceClass::CBuffer;245 Kind = ResourceKind::CBuffer;246 } else if (isa<SamplerExtType>(HandleTy)) {247 RC = ResourceClass::Sampler;248 Kind = ResourceKind::Sampler;249 } else250 llvm_unreachable("Unknown handle type");251}252 253static void formatTypeName(SmallString<64> &Dest, StringRef Name,254 bool IsWriteable, bool IsROV,255 Type *ContainedType = nullptr,256 bool IsSigned = true) {257 raw_svector_ostream DestStream(Dest);258 if (IsWriteable)259 DestStream << (IsROV ? "RasterizerOrdered" : "RW");260 DestStream << Name;261 262 if (!ContainedType)263 return;264 265 SmallVector<uint64_t> ArrayDimensions;266 while (ArrayType *AT = dyn_cast<ArrayType>(ContainedType)) {267 ArrayDimensions.push_back(AT->getNumElements());268 ContainedType = AT->getElementType();269 }270 271 StringRef ElementName;272 ElementType ET = toDXILElementType(ContainedType, IsSigned);273 if (ET != ElementType::Invalid) {274 ElementName = getElementTypeNameForTemplate(ET);275 } else {276 assert(isa<StructType>(ContainedType) &&277 "invalid element type for raw buffer");278 StructType *ST = cast<StructType>(ContainedType);279 if (!ST->hasName())280 return;281 ElementName = ST->getStructName();282 }283 284 DestStream << "<" << ElementName;285 if (const FixedVectorType *VTy = dyn_cast<FixedVectorType>(ContainedType))286 DestStream << VTy->getNumElements();287 for (uint64_t Dim : ArrayDimensions)288 DestStream << "[" << Dim << "]";289 DestStream << ">";290}291 292static StructType *getOrCreateElementStruct(Type *ElemType, StringRef Name) {293 StructType *Ty = StructType::getTypeByName(ElemType->getContext(), Name);294 if (Ty && Ty->getNumElements() == 1 && Ty->getElementType(0) == ElemType)295 return Ty;296 return StructType::create(ElemType, Name);297}298 299static Type *getTypeWithoutPadding(Type *Ty) {300 // Recursively remove padding from structures.301 if (auto *ST = dyn_cast<StructType>(Ty)) {302 LLVMContext &Ctx = Ty->getContext();303 SmallVector<Type *> ElementTypes;304 ElementTypes.reserve(ST->getNumElements());305 for (Type *ElTy : ST->elements()) {306 if (isa<PaddingExtType>(ElTy))307 continue;308 ElementTypes.push_back(getTypeWithoutPadding(ElTy));309 }310 311 // Handle explicitly padded cbuffer arrays like { [ n x paddedty ], ty }312 if (ElementTypes.size() == 2)313 if (auto *AT = dyn_cast<ArrayType>(ElementTypes[0]))314 if (ElementTypes[1] == AT->getElementType())315 return ArrayType::get(ElementTypes[1], AT->getNumElements() + 1);316 317 // If we only have a single element, don't wrap it in a struct.318 if (ElementTypes.size() == 1)319 return ElementTypes[0];320 321 return StructType::get(Ctx, ElementTypes, /*IsPacked=*/false);322 }323 // Arrays just need to have their element type adjusted.324 if (auto *AT = dyn_cast<ArrayType>(Ty))325 return ArrayType::get(getTypeWithoutPadding(AT->getElementType()),326 AT->getNumElements());327 // Anything else should be good as is.328 return Ty;329}330 331StructType *ResourceTypeInfo::createElementStruct(StringRef CBufferName) {332 SmallString<64> TypeName;333 334 switch (Kind) {335 case ResourceKind::Texture1D:336 case ResourceKind::Texture2D:337 case ResourceKind::Texture3D:338 case ResourceKind::TextureCube:339 case ResourceKind::Texture1DArray:340 case ResourceKind::Texture2DArray:341 case ResourceKind::TextureCubeArray: {342 auto *RTy = cast<TextureExtType>(HandleTy);343 formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),344 RTy->isROV(), RTy->getResourceType(), RTy->isSigned());345 return getOrCreateElementStruct(RTy->getResourceType(), TypeName);346 }347 case ResourceKind::Texture2DMS:348 case ResourceKind::Texture2DMSArray: {349 auto *RTy = cast<MSTextureExtType>(HandleTy);350 formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),351 /*IsROV=*/false, RTy->getResourceType(), RTy->isSigned());352 return getOrCreateElementStruct(RTy->getResourceType(), TypeName);353 }354 case ResourceKind::TypedBuffer: {355 auto *RTy = cast<TypedBufferExtType>(HandleTy);356 formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),357 RTy->isROV(), RTy->getResourceType(), RTy->isSigned());358 return getOrCreateElementStruct(RTy->getResourceType(), TypeName);359 }360 case ResourceKind::RawBuffer: {361 auto *RTy = cast<RawBufferExtType>(HandleTy);362 formatTypeName(TypeName, "ByteAddressBuffer", RTy->isWriteable(),363 RTy->isROV());364 return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),365 TypeName);366 }367 case ResourceKind::StructuredBuffer: {368 auto *RTy = cast<RawBufferExtType>(HandleTy);369 Type *Ty = RTy->getResourceType();370 formatTypeName(TypeName, "StructuredBuffer", RTy->isWriteable(),371 RTy->isROV(), RTy->getResourceType(), true);372 return getOrCreateElementStruct(Ty, TypeName);373 }374 case ResourceKind::FeedbackTexture2D:375 case ResourceKind::FeedbackTexture2DArray: {376 auto *RTy = cast<FeedbackTextureExtType>(HandleTy);377 TypeName = formatv("{0}<{1}>", getResourceKindName(Kind),378 llvm::to_underlying(RTy->getFeedbackType()));379 return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),380 TypeName);381 }382 case ResourceKind::CBuffer: {383 auto *RTy = cast<CBufferExtType>(HandleTy);384 SmallString<64> Name = getResourceKindName(Kind);385 if (!CBufferName.empty()) {386 Name.append(".");387 Name.append(CBufferName);388 }389 390 // TODO: Remove this when we update the frontend to use explicit padding.391 if (LayoutExtType *LayoutType =392 dyn_cast<LayoutExtType>(RTy->getResourceType())) {393 StructType *Ty = cast<StructType>(LayoutType->getWrappedType());394 return StructType::create(Ty->elements(), Name);395 }396 397 return getOrCreateElementStruct(398 getTypeWithoutPadding(RTy->getResourceType()), Name);399 }400 case ResourceKind::Sampler: {401 auto *RTy = cast<SamplerExtType>(HandleTy);402 TypeName = formatv("SamplerState<{0}>",403 llvm::to_underlying(RTy->getSamplerType()));404 return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),405 TypeName);406 }407 case ResourceKind::TBuffer:408 case ResourceKind::RTAccelerationStructure:409 llvm_unreachable("Unhandled resource kind");410 case ResourceKind::Invalid:411 case ResourceKind::NumEntries:412 llvm_unreachable("Invalid resource kind");413 }414 llvm_unreachable("Unhandled ResourceKind enum");415}416 417bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; }418 419bool ResourceTypeInfo::isCBuffer() const {420 return RC == ResourceClass::CBuffer;421}422 423bool ResourceTypeInfo::isSampler() const {424 return RC == ResourceClass::Sampler;425}426 427bool ResourceTypeInfo::isStruct() const {428 return Kind == ResourceKind::StructuredBuffer;429}430 431bool ResourceTypeInfo::isTyped() const {432 switch (Kind) {433 case ResourceKind::Texture1D:434 case ResourceKind::Texture2D:435 case ResourceKind::Texture2DMS:436 case ResourceKind::Texture3D:437 case ResourceKind::TextureCube:438 case ResourceKind::Texture1DArray:439 case ResourceKind::Texture2DArray:440 case ResourceKind::Texture2DMSArray:441 case ResourceKind::TextureCubeArray:442 case ResourceKind::TypedBuffer:443 return true;444 case ResourceKind::RawBuffer:445 case ResourceKind::StructuredBuffer:446 case ResourceKind::FeedbackTexture2D:447 case ResourceKind::FeedbackTexture2DArray:448 case ResourceKind::CBuffer:449 case ResourceKind::Sampler:450 case ResourceKind::TBuffer:451 case ResourceKind::RTAccelerationStructure:452 return false;453 case ResourceKind::Invalid:454 case ResourceKind::NumEntries:455 llvm_unreachable("Invalid resource kind");456 }457 llvm_unreachable("Unhandled ResourceKind enum");458}459 460bool ResourceTypeInfo::isFeedback() const {461 return Kind == ResourceKind::FeedbackTexture2D ||462 Kind == ResourceKind::FeedbackTexture2DArray;463}464 465bool ResourceTypeInfo::isMultiSample() const {466 return Kind == ResourceKind::Texture2DMS ||467 Kind == ResourceKind::Texture2DMSArray;468}469 470static bool isROV(dxil::ResourceKind Kind, TargetExtType *Ty) {471 switch (Kind) {472 case ResourceKind::Texture1D:473 case ResourceKind::Texture2D:474 case ResourceKind::Texture3D:475 case ResourceKind::TextureCube:476 case ResourceKind::Texture1DArray:477 case ResourceKind::Texture2DArray:478 case ResourceKind::TextureCubeArray:479 return cast<TextureExtType>(Ty)->isROV();480 case ResourceKind::TypedBuffer:481 return cast<TypedBufferExtType>(Ty)->isROV();482 case ResourceKind::RawBuffer:483 case ResourceKind::StructuredBuffer:484 return cast<RawBufferExtType>(Ty)->isROV();485 case ResourceKind::Texture2DMS:486 case ResourceKind::Texture2DMSArray:487 case ResourceKind::FeedbackTexture2D:488 case ResourceKind::FeedbackTexture2DArray:489 return false;490 case ResourceKind::CBuffer:491 case ResourceKind::Sampler:492 case ResourceKind::TBuffer:493 case ResourceKind::RTAccelerationStructure:494 case ResourceKind::Invalid:495 case ResourceKind::NumEntries:496 llvm_unreachable("Resource cannot be ROV");497 }498 llvm_unreachable("Unhandled ResourceKind enum");499}500 501ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const {502 assert(isUAV() && "Not a UAV");503 return {isROV(Kind, HandleTy)};504}505 506uint32_t ResourceTypeInfo::getCBufferSize(const DataLayout &DL) const {507 assert(isCBuffer() && "Not a CBuffer");508 509 Type *ElTy = cast<CBufferExtType>(HandleTy)->getResourceType();510 511 // TODO: Remove this when we update the frontend to use explicit padding.512 if (auto *LayoutTy = dyn_cast<LayoutExtType>(ElTy))513 return LayoutTy->getSize();514 515 return DL.getTypeAllocSize(ElTy);516}517 518dxil::SamplerType ResourceTypeInfo::getSamplerType() const {519 assert(isSampler() && "Not a Sampler");520 return cast<SamplerExtType>(HandleTy)->getSamplerType();521}522 523ResourceTypeInfo::StructInfo524ResourceTypeInfo::getStruct(const DataLayout &DL) const {525 assert(isStruct() && "Not a Struct");526 527 Type *ElTy = cast<RawBufferExtType>(HandleTy)->getResourceType();528 529 uint32_t Stride = DL.getTypeAllocSize(ElTy);530 MaybeAlign Alignment;531 if (auto *STy = dyn_cast<StructType>(ElTy))532 Alignment = DL.getStructLayout(STy)->getAlignment();533 uint32_t AlignLog2 = Alignment ? Log2(*Alignment) : 0;534 return {Stride, AlignLog2};535}536 537static std::pair<Type *, bool> getTypedElementType(dxil::ResourceKind Kind,538 TargetExtType *Ty) {539 switch (Kind) {540 case ResourceKind::Texture1D:541 case ResourceKind::Texture2D:542 case ResourceKind::Texture3D:543 case ResourceKind::TextureCube:544 case ResourceKind::Texture1DArray:545 case ResourceKind::Texture2DArray:546 case ResourceKind::TextureCubeArray: {547 auto *RTy = cast<TextureExtType>(Ty);548 return {RTy->getResourceType(), RTy->isSigned()};549 }550 case ResourceKind::Texture2DMS:551 case ResourceKind::Texture2DMSArray: {552 auto *RTy = cast<MSTextureExtType>(Ty);553 return {RTy->getResourceType(), RTy->isSigned()};554 }555 case ResourceKind::TypedBuffer: {556 auto *RTy = cast<TypedBufferExtType>(Ty);557 return {RTy->getResourceType(), RTy->isSigned()};558 }559 case ResourceKind::RawBuffer:560 case ResourceKind::StructuredBuffer:561 case ResourceKind::FeedbackTexture2D:562 case ResourceKind::FeedbackTexture2DArray:563 case ResourceKind::CBuffer:564 case ResourceKind::Sampler:565 case ResourceKind::TBuffer:566 case ResourceKind::RTAccelerationStructure:567 case ResourceKind::Invalid:568 case ResourceKind::NumEntries:569 llvm_unreachable("Resource is not typed");570 }571 llvm_unreachable("Unhandled ResourceKind enum");572}573 574ResourceTypeInfo::TypedInfo ResourceTypeInfo::getTyped() const {575 assert(isTyped() && "Not typed");576 577 auto [ElTy, IsSigned] = getTypedElementType(Kind, HandleTy);578 dxil::ElementType ET = toDXILElementType(ElTy, IsSigned);579 dxil::ElementType DXILStorageTy = toDXILStorageType(ET);580 uint32_t Count = 1;581 if (auto *VTy = dyn_cast<FixedVectorType>(ElTy))582 Count = VTy->getNumElements();583 return {ET, DXILStorageTy, Count};584}585 586dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const {587 assert(isFeedback() && "Not Feedback");588 return cast<FeedbackTextureExtType>(HandleTy)->getFeedbackType();589}590uint32_t ResourceTypeInfo::getMultiSampleCount() const {591 assert(isMultiSample() && "Not MultiSampled");592 return cast<MSTextureExtType>(HandleTy)->getSampleCount();593}594 595bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const {596 return HandleTy == RHS.HandleTy;597}598 599bool ResourceTypeInfo::operator<(const ResourceTypeInfo &RHS) const {600 // An empty datalayout is sufficient for sorting purposes.601 DataLayout DummyDL;602 if (std::tie(RC, Kind) < std::tie(RHS.RC, RHS.Kind))603 return true;604 if (isCBuffer() && RHS.isCBuffer() &&605 getCBufferSize(DummyDL) < RHS.getCBufferSize(DummyDL))606 return true;607 if (isSampler() && RHS.isSampler() && getSamplerType() < RHS.getSamplerType())608 return true;609 if (isUAV() && RHS.isUAV() && getUAV() < RHS.getUAV())610 return true;611 if (isStruct() && RHS.isStruct() &&612 getStruct(DummyDL) < RHS.getStruct(DummyDL))613 return true;614 if (isFeedback() && RHS.isFeedback() &&615 getFeedbackType() < RHS.getFeedbackType())616 return true;617 if (isTyped() && RHS.isTyped() && getTyped() < RHS.getTyped())618 return true;619 if (isMultiSample() && RHS.isMultiSample() &&620 getMultiSampleCount() < RHS.getMultiSampleCount())621 return true;622 return false;623}624 625void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const {626 OS << " Class: " << getResourceClassName(RC) << "\n"627 << " Kind: " << getResourceKindName(Kind) << "\n";628 629 if (isCBuffer()) {630 OS << " CBuffer size: " << getCBufferSize(DL) << "\n";631 } else if (isSampler()) {632 OS << " Sampler Type: " << getSamplerTypeName(getSamplerType()) << "\n";633 } else {634 if (isUAV()) {635 UAVInfo UAVFlags = getUAV();636 OS << " IsROV: " << UAVFlags.IsROV << "\n";637 }638 if (isMultiSample())639 OS << " Sample Count: " << getMultiSampleCount() << "\n";640 641 if (isStruct()) {642 StructInfo Struct = getStruct(DL);643 OS << " Buffer Stride: " << Struct.Stride << "\n";644 OS << " Alignment: " << Struct.AlignLog2 << "\n";645 } else if (isTyped()) {646 TypedInfo Typed = getTyped();647 OS << " Element Type: " << getElementTypeName(Typed.ElementTy);648 if (Typed.ElementTy != Typed.DXILStorageTy)649 OS << " (stored as " << getElementTypeName(Typed.DXILStorageTy) << ")";650 OS << "\n"651 << " Element Count: " << Typed.ElementCount << "\n";652 } else if (isFeedback())653 OS << " Feedback Type: " << getSamplerFeedbackTypeName(getFeedbackType())654 << "\n";655 }656}657 658GlobalVariable *ResourceInfo::createSymbol(Module &M, StructType *Ty) {659 assert(!Symbol && "Symbol has already been created");660 Type *ResTy = Ty;661 int64_t Size = Binding.Size;662 if (Size != 1)663 // unbounded arrays are represented as zero-sized arrays in LLVM IR664 ResTy = ArrayType::get(Ty, Size == ~0u ? 0 : Size);665 Symbol = new GlobalVariable(M, ResTy, /*isConstant=*/true,666 GlobalValue::ExternalLinkage,667 /*Initializer=*/nullptr, Name);668 return Symbol;669}670 671MDTuple *ResourceInfo::getAsMetadata(Module &M,672 dxil::ResourceTypeInfo &RTI) const {673 LLVMContext &Ctx = M.getContext();674 const DataLayout &DL = M.getDataLayout();675 676 SmallVector<Metadata *, 11> MDVals;677 678 Type *I32Ty = Type::getInt32Ty(Ctx);679 Type *I1Ty = Type::getInt1Ty(Ctx);680 auto getIntMD = [&I32Ty](uint32_t V) {681 return ConstantAsMetadata::get(682 Constant::getIntegerValue(I32Ty, APInt(32, V)));683 };684 auto getBoolMD = [&I1Ty](uint32_t V) {685 return ConstantAsMetadata::get(686 Constant::getIntegerValue(I1Ty, APInt(1, V)));687 };688 689 MDVals.push_back(getIntMD(Binding.RecordID));690 assert(Symbol && "Cannot yet create useful resource metadata without symbol");691 MDVals.push_back(ValueAsMetadata::get(Symbol));692 MDVals.push_back(MDString::get(Ctx, Name));693 MDVals.push_back(getIntMD(Binding.Space));694 MDVals.push_back(getIntMD(Binding.LowerBound));695 MDVals.push_back(getIntMD(Binding.Size));696 697 if (RTI.isCBuffer()) {698 MDVals.push_back(getIntMD(RTI.getCBufferSize(DL)));699 MDVals.push_back(nullptr);700 } else if (RTI.isSampler()) {701 MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getSamplerType())));702 MDVals.push_back(nullptr);703 } else {704 MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getResourceKind())));705 706 if (RTI.isUAV()) {707 ResourceTypeInfo::UAVInfo UAVFlags = RTI.getUAV();708 MDVals.push_back(getBoolMD(GloballyCoherent));709 MDVals.push_back(getBoolMD(hasCounter()));710 MDVals.push_back(getBoolMD(UAVFlags.IsROV));711 } else {712 // All SRVs include sample count in the metadata, but it's only meaningful713 // for multi-sampled textured. Also, UAVs can be multisampled in SM6.7+,714 // but this just isn't reflected in the metadata at all.715 uint32_t SampleCount =716 RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;717 MDVals.push_back(getIntMD(SampleCount));718 }719 720 // Further properties are attached to a metadata list of tag-value pairs.721 SmallVector<Metadata *> Tags;722 if (RTI.isStruct()) {723 Tags.push_back(724 getIntMD(llvm::to_underlying(ExtPropTags::StructuredBufferStride)));725 Tags.push_back(getIntMD(RTI.getStruct(DL).Stride));726 } else if (RTI.isTyped()) {727 Tags.push_back(getIntMD(llvm::to_underlying(ExtPropTags::ElementType)));728 Tags.push_back(729 getIntMD(llvm::to_underlying(RTI.getTyped().DXILStorageTy)));730 } else if (RTI.isFeedback()) {731 Tags.push_back(732 getIntMD(llvm::to_underlying(ExtPropTags::SamplerFeedbackKind)));733 Tags.push_back(getIntMD(llvm::to_underlying(RTI.getFeedbackType())));734 }735 MDVals.push_back(Tags.empty() ? nullptr : MDNode::get(Ctx, Tags));736 }737 738 return MDNode::get(Ctx, MDVals);739}740 741std::pair<uint32_t, uint32_t>742ResourceInfo::getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const {743 const DataLayout &DL = M.getDataLayout();744 745 uint32_t ResourceKind = llvm::to_underlying(RTI.getResourceKind());746 uint32_t AlignLog2 = RTI.isStruct() ? RTI.getStruct(DL).AlignLog2 : 0;747 bool IsUAV = RTI.isUAV();748 ResourceTypeInfo::UAVInfo UAVFlags =749 IsUAV ? RTI.getUAV() : ResourceTypeInfo::UAVInfo{};750 bool IsROV = IsUAV && UAVFlags.IsROV;751 bool IsGloballyCoherent = IsUAV && GloballyCoherent;752 uint8_t SamplerCmpOrHasCounter = 0;753 if (IsUAV)754 SamplerCmpOrHasCounter = hasCounter();755 else if (RTI.isSampler())756 SamplerCmpOrHasCounter = RTI.getSamplerType() == SamplerType::Comparison;757 758 // TODO: Document this format. Currently the only reference is the759 // implementation of dxc's DxilResourceProperties struct.760 uint32_t Word0 = 0;761 Word0 |= ResourceKind & 0xFF;762 Word0 |= (AlignLog2 & 0xF) << 8;763 Word0 |= (IsUAV & 1) << 12;764 Word0 |= (IsROV & 1) << 13;765 Word0 |= (IsGloballyCoherent & 1) << 14;766 Word0 |= (SamplerCmpOrHasCounter & 1) << 15;767 768 uint32_t Word1 = 0;769 if (RTI.isStruct())770 Word1 = RTI.getStruct(DL).Stride;771 else if (RTI.isCBuffer())772 Word1 = RTI.getCBufferSize(DL);773 else if (RTI.isFeedback())774 Word1 = llvm::to_underlying(RTI.getFeedbackType());775 else if (RTI.isTyped()) {776 ResourceTypeInfo::TypedInfo Typed = RTI.getTyped();777 uint32_t CompType = llvm::to_underlying(Typed.ElementTy);778 uint32_t CompCount = Typed.ElementCount;779 uint32_t SampleCount = RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;780 781 Word1 |= (CompType & 0xFF) << 0;782 Word1 |= (CompCount & 0xFF) << 8;783 Word1 |= (SampleCount & 0xFF) << 16;784 }785 786 return {Word0, Word1};787}788 789void ResourceInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI,790 const DataLayout &DL) const {791 if (!Name.empty())792 OS << " Name: " << Name << "\n";793 794 if (Symbol) {795 OS << " Symbol: ";796 Symbol->printAsOperand(OS);797 OS << "\n";798 }799 800 OS << " Binding:\n"801 << " Record ID: " << Binding.RecordID << "\n"802 << " Space: " << Binding.Space << "\n"803 << " Lower Bound: " << Binding.LowerBound << "\n"804 << " Size: " << Binding.Size << "\n";805 806 OS << " Globally Coherent: " << GloballyCoherent << "\n";807 OS << " Counter Direction: ";808 809 switch (CounterDirection) {810 case ResourceCounterDirection::Increment:811 OS << "Increment\n";812 break;813 case ResourceCounterDirection::Decrement:814 OS << "Decrement\n";815 break;816 case ResourceCounterDirection::Unknown:817 OS << "Unknown\n";818 break;819 case ResourceCounterDirection::Invalid:820 OS << "Invalid\n";821 break;822 }823 824 RTI.print(OS, DL);825}826 827//===----------------------------------------------------------------------===//828 829bool DXILResourceTypeMap::invalidate(Module &M, const PreservedAnalyses &PA,830 ModuleAnalysisManager::Invalidator &Inv) {831 // Passes that introduce resource types must explicitly invalidate this pass.832 auto PAC = PA.getChecker<DXILResourceTypeAnalysis>();833 return !PAC.preservedWhenStateless();834}835 836//===----------------------------------------------------------------------===//837static bool isUpdateCounterIntrinsic(Function &F) {838 return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter;839}840 841StringRef dxil::getResourceNameFromBindingCall(CallInst *CI) {842 Value *Op = nullptr;843 switch (CI->getCalledFunction()->getIntrinsicID()) {844 default:845 llvm_unreachable("unexpected handle creation intrinsic");846 case Intrinsic::dx_resource_handlefrombinding:847 case Intrinsic::dx_resource_handlefromimplicitbinding:848 Op = CI->getArgOperand(4);849 break;850 }851 852 auto *GV = dyn_cast<llvm::GlobalVariable>(Op);853 if (!GV)854 return "";855 856 auto *CA = dyn_cast<ConstantDataArray>(GV->getInitializer());857 assert(CA && CA->isString() && "expected constant string");858 StringRef Name = CA->getAsString();859 // strip trailing 0860 if (Name.ends_with('\0'))861 Name = Name.drop_back(1);862 return Name;863}864 865void DXILResourceMap::populateResourceInfos(Module &M,866 DXILResourceTypeMap &DRTM) {867 SmallVector<std::tuple<CallInst *, ResourceInfo, ResourceTypeInfo>> CIToInfos;868 869 for (Function &F : M.functions()) {870 if (!F.isDeclaration())871 continue;872 LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n");873 Intrinsic::ID ID = F.getIntrinsicID();874 switch (ID) {875 default:876 continue;877 case Intrinsic::dx_resource_handlefrombinding: {878 auto *HandleTy = cast<TargetExtType>(F.getReturnType());879 ResourceTypeInfo &RTI = DRTM[HandleTy];880 881 for (User *U : F.users())882 if (CallInst *CI = dyn_cast<CallInst>(U)) {883 LLVM_DEBUG(dbgs() << " Visiting: " << *U << "\n");884 uint32_t Space =885 cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();886 uint32_t LowerBound =887 cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();888 uint32_t Size =889 cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();890 StringRef Name = getResourceNameFromBindingCall(CI);891 892 ResourceInfo RI =893 ResourceInfo{/*RecordID=*/0, Space, LowerBound,894 Size, HandleTy, Name};895 896 CIToInfos.emplace_back(CI, RI, RTI);897 }898 899 break;900 }901 }902 }903 904 llvm::stable_sort(CIToInfos, [](auto &LHS, auto &RHS) {905 const auto &[LCI, LRI, LRTI] = LHS;906 const auto &[RCI, RRI, RRTI] = RHS;907 // Sort by resource class first for grouping purposes, and then by the908 // binding and type so we can remove duplicates.909 ResourceClass LRC = LRTI.getResourceClass();910 ResourceClass RRC = RRTI.getResourceClass();911 912 return std::tie(LRC, LRI, LRTI) < std::tie(RRC, RRI, RRTI);913 });914 for (auto [CI, RI, RTI] : CIToInfos) {915 if (Infos.empty() || RI != Infos.back())916 Infos.push_back(RI);917 CallMap[CI] = Infos.size() - 1;918 }919 920 unsigned Size = Infos.size();921 // In DXC, Record ID is unique per resource type. Match that.922 FirstUAV = FirstCBuffer = FirstSampler = Size;923 uint32_t NextID = 0;924 for (unsigned I = 0, E = Size; I != E; ++I) {925 ResourceInfo &RI = Infos[I];926 ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];927 if (RTI.isUAV() && FirstUAV == Size) {928 FirstUAV = I;929 NextID = 0;930 } else if (RTI.isCBuffer() && FirstCBuffer == Size) {931 FirstCBuffer = I;932 NextID = 0;933 } else if (RTI.isSampler() && FirstSampler == Size) {934 FirstSampler = I;935 NextID = 0;936 }937 938 // We need to make sure the types of resource are ordered even if some are939 // missing.940 FirstCBuffer = std::min({FirstCBuffer, FirstSampler});941 FirstUAV = std::min({FirstUAV, FirstCBuffer});942 943 // Adjust the resource binding to use the next ID.944 RI.setBindingID(NextID++);945 }946}947 948void DXILResourceMap::populateCounterDirections(Module &M) {949 for (Function &F : M.functions()) {950 if (!isUpdateCounterIntrinsic(F))951 continue;952 953 LLVM_DEBUG(dbgs() << "Update Counter Function: " << F.getName() << "\n");954 955 for (const User *U : F.users()) {956 const CallInst *CI = dyn_cast<CallInst>(U);957 assert(CI && "Users of dx_resource_updateCounter must be call instrs");958 959 // Determine if the use is an increment or decrement960 Value *CountArg = CI->getArgOperand(1);961 ConstantInt *CountValue = cast<ConstantInt>(CountArg);962 int64_t CountLiteral = CountValue->getSExtValue();963 964 // 0 is an unknown direction and shouldn't result in an insert965 if (CountLiteral == 0)966 continue;967 968 ResourceCounterDirection Direction = ResourceCounterDirection::Decrement;969 if (CountLiteral > 0)970 Direction = ResourceCounterDirection::Increment;971 972 // Collect all potential creation points for the handle arg973 Value *HandleArg = CI->getArgOperand(0);974 SmallVector<ResourceInfo *> RBInfos = findByUse(HandleArg);975 for (ResourceInfo *RBInfo : RBInfos) {976 if (RBInfo->CounterDirection == ResourceCounterDirection::Unknown)977 RBInfo->CounterDirection = Direction;978 else if (RBInfo->CounterDirection != Direction) {979 RBInfo->CounterDirection = ResourceCounterDirection::Invalid;980 HasInvalidDirection = true;981 }982 }983 }984 }985}986 987void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) {988 populateResourceInfos(M, DRTM);989 populateCounterDirections(M);990}991 992void DXILResourceMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM,993 const DataLayout &DL) const {994 for (unsigned I = 0, E = Infos.size(); I != E; ++I) {995 OS << "Resource " << I << ":\n";996 const dxil::ResourceInfo &RI = Infos[I];997 RI.print(OS, DRTM[RI.getHandleTy()], DL);998 OS << "\n";999 }1000 1001 for (const auto &[CI, Index] : CallMap) {1002 OS << "Call bound to " << Index << ":";1003 CI->print(OS);1004 OS << "\n";1005 }1006}1007 1008SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {1009 if (const PHINode *Phi = dyn_cast<PHINode>(Key)) {1010 SmallVector<dxil::ResourceInfo *> Children;1011 for (const Value *V : Phi->operands()) {1012 Children.append(findByUse(V));1013 }1014 return Children;1015 }1016 1017 const CallInst *CI = dyn_cast<CallInst>(Key);1018 if (!CI)1019 return {};1020 1021 switch (CI->getIntrinsicID()) {1022 // Found the create, return the binding1023 case Intrinsic::dx_resource_handlefrombinding: {1024 auto Pos = CallMap.find(CI);1025 assert(Pos != CallMap.end() && "HandleFromBinding must be in resource map");1026 return {&Infos[Pos->second]};1027 }1028 default:1029 break;1030 }1031 1032 // Check if any of the parameters are the resource we are following. If so1033 // keep searching. If none of them are return an empty list1034 const Type *UseType = CI->getType();1035 SmallVector<dxil::ResourceInfo *> Children;1036 for (const Value *V : CI->args()) {1037 if (V->getType() != UseType)1038 continue;1039 1040 Children.append(findByUse(V));1041 }1042 1043 return Children;1044}1045 1046//===----------------------------------------------------------------------===//1047 1048void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {1049 hlsl::BindingInfoBuilder Builder;1050 1051 // collect all of the llvm.dx.resource.handlefrombinding calls;1052 // make a note if there is llvm.dx.resource.handlefromimplicitbinding1053 for (Function &F : M.functions()) {1054 if (!F.isDeclaration())1055 continue;1056 1057 switch (F.getIntrinsicID()) {1058 default:1059 continue;1060 case Intrinsic::dx_resource_handlefrombinding: {1061 auto *HandleTy = cast<TargetExtType>(F.getReturnType());1062 ResourceTypeInfo &RTI = DRTM[HandleTy];1063 1064 for (User *U : F.users())1065 if (CallInst *CI = dyn_cast<CallInst>(U)) {1066 uint32_t Space =1067 cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();1068 uint32_t LowerBound =1069 cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();1070 int32_t Size =1071 cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();1072 Value *Name = CI->getArgOperand(4);1073 1074 // negative size means unbounded resource array;1075 // upper bound register overflow should be detected in Sema1076 assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) &&1077 "upper bound register overflow");1078 uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;1079 Builder.trackBinding(RTI.getResourceClass(), Space, LowerBound,1080 UpperBound, Name);1081 }1082 break;1083 }1084 case Intrinsic::dx_resource_handlefromimplicitbinding: {1085 HasImplicitBinding = true;1086 break;1087 }1088 }1089 }1090 1091 Bindings = Builder.calculateBindingInfo(1092 [this](auto, auto) { this->HasOverlappingBinding = true; });1093}1094 1095//===----------------------------------------------------------------------===//1096 1097AnalysisKey DXILResourceTypeAnalysis::Key;1098AnalysisKey DXILResourceAnalysis::Key;1099AnalysisKey DXILResourceBindingAnalysis::Key;1100 1101DXILResourceMap DXILResourceAnalysis::run(Module &M,1102 ModuleAnalysisManager &AM) {1103 DXILResourceMap Data;1104 DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);1105 Data.populate(M, DRTM);1106 return Data;1107}1108 1109DXILResourceBindingInfo1110DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {1111 DXILResourceBindingInfo Data;1112 DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);1113 Data.populate(M, DRTM);1114 return Data;1115}1116 1117PreservedAnalyses DXILResourcePrinterPass::run(Module &M,1118 ModuleAnalysisManager &AM) {1119 DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);1120 DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);1121 1122 DRM.print(OS, DRTM, M.getDataLayout());1123 return PreservedAnalyses::all();1124}1125 1126void DXILResourceTypeWrapperPass::anchor() {}1127 1128DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass()1129 : ImmutablePass(ID) {}1130 1131INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type",1132 "DXIL Resource Type Analysis", false, true)1133char DXILResourceTypeWrapperPass::ID = 0;1134 1135ModulePass *llvm::createDXILResourceTypeWrapperPassPass() {1136 return new DXILResourceTypeWrapperPass();1137}1138 1139DXILResourceWrapperPass::DXILResourceWrapperPass() : ModulePass(ID) {}1140 1141DXILResourceWrapperPass::~DXILResourceWrapperPass() = default;1142 1143void DXILResourceWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {1144 AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();1145 AU.setPreservesAll();1146}1147 1148bool DXILResourceWrapperPass::runOnModule(Module &M) {1149 Map.reset(new DXILResourceMap());1150 1151 DRTM = &getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();1152 Map->populate(M, *DRTM);1153 1154 return false;1155}1156 1157void DXILResourceWrapperPass::releaseMemory() { Map.reset(); }1158 1159void DXILResourceWrapperPass::print(raw_ostream &OS, const Module *M) const {1160 if (!Map) {1161 OS << "No resource map has been built!\n";1162 return;1163 }1164 Map->print(OS, *DRTM, M->getDataLayout());1165}1166 1167#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)1168LLVM_DUMP_METHOD1169void DXILResourceWrapperPass::dump() const { print(dbgs(), nullptr); }1170#endif1171 1172INITIALIZE_PASS(DXILResourceWrapperPass, "dxil-resources",1173 "DXIL Resources Analysis", false, true)1174char DXILResourceWrapperPass::ID = 0;1175 1176ModulePass *llvm::createDXILResourceWrapperPassPass() {1177 return new DXILResourceWrapperPass();1178}1179 1180DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()1181 : ModulePass(ID) {}1182 1183DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;1184 1185void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {1186 AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();1187 AU.setPreservesAll();1188}1189 1190bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {1191 BindingInfo.reset(new DXILResourceBindingInfo());1192 1193 DXILResourceTypeMap &DRTM =1194 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();1195 BindingInfo->populate(M, DRTM);1196 1197 return false;1198}1199 1200void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); }1201 1202INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",1203 "DXIL Resource Binding Analysis", false, true)1204char DXILResourceBindingWrapperPass::ID = 0;1205 1206ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {1207 return new DXILResourceWrapperPass();1208}1209