brintos

brintos / llvm-project-archived public Read only

0
0
Text · 39.7 KiB · 033f516 Raw
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