Path: blob/main/contrib/llvm-project/llvm/lib/Analysis/DXILResource.cpp
213765 views
//===- DXILResource.cpp - Representations of DXIL resources ---------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/Analysis/DXILResource.h"9#include "llvm/ADT/APInt.h"10#include "llvm/ADT/STLExtras.h"11#include "llvm/ADT/SmallString.h"12#include "llvm/ADT/SmallVector.h"13#include "llvm/IR/Constants.h"14#include "llvm/IR/DerivedTypes.h"15#include "llvm/IR/DiagnosticInfo.h"16#include "llvm/IR/Instructions.h"17#include "llvm/IR/Intrinsics.h"18#include "llvm/IR/IntrinsicsDirectX.h"19#include "llvm/IR/Metadata.h"20#include "llvm/IR/Module.h"21#include "llvm/InitializePasses.h"22#include "llvm/Support/FormatVariadic.h"23#include <cstdint>24#include <optional>2526#define DEBUG_TYPE "dxil-resource"2728using namespace llvm;29using namespace dxil;3031static StringRef getResourceClassName(ResourceClass RC) {32switch (RC) {33case ResourceClass::SRV:34return "SRV";35case ResourceClass::UAV:36return "UAV";37case ResourceClass::CBuffer:38return "CBuffer";39case ResourceClass::Sampler:40return "Sampler";41}42llvm_unreachable("Unhandled ResourceClass");43}4445static StringRef getResourceKindName(ResourceKind RK) {46switch (RK) {47case ResourceKind::Texture1D:48return "Texture1D";49case ResourceKind::Texture2D:50return "Texture2D";51case ResourceKind::Texture2DMS:52return "Texture2DMS";53case ResourceKind::Texture3D:54return "Texture3D";55case ResourceKind::TextureCube:56return "TextureCube";57case ResourceKind::Texture1DArray:58return "Texture1DArray";59case ResourceKind::Texture2DArray:60return "Texture2DArray";61case ResourceKind::Texture2DMSArray:62return "Texture2DMSArray";63case ResourceKind::TextureCubeArray:64return "TextureCubeArray";65case ResourceKind::TypedBuffer:66return "Buffer";67case ResourceKind::RawBuffer:68return "RawBuffer";69case ResourceKind::StructuredBuffer:70return "StructuredBuffer";71case ResourceKind::CBuffer:72return "CBuffer";73case ResourceKind::Sampler:74return "Sampler";75case ResourceKind::TBuffer:76return "TBuffer";77case ResourceKind::RTAccelerationStructure:78return "RTAccelerationStructure";79case ResourceKind::FeedbackTexture2D:80return "FeedbackTexture2D";81case ResourceKind::FeedbackTexture2DArray:82return "FeedbackTexture2DArray";83case ResourceKind::NumEntries:84case ResourceKind::Invalid:85return "<invalid>";86}87llvm_unreachable("Unhandled ResourceKind");88}8990static StringRef getElementTypeName(ElementType ET) {91switch (ET) {92case ElementType::I1:93return "i1";94case ElementType::I16:95return "i16";96case ElementType::U16:97return "u16";98case ElementType::I32:99return "i32";100case ElementType::U32:101return "u32";102case ElementType::I64:103return "i64";104case ElementType::U64:105return "u64";106case ElementType::F16:107return "f16";108case ElementType::F32:109return "f32";110case ElementType::F64:111return "f64";112case ElementType::SNormF16:113return "snorm_f16";114case ElementType::UNormF16:115return "unorm_f16";116case ElementType::SNormF32:117return "snorm_f32";118case ElementType::UNormF32:119return "unorm_f32";120case ElementType::SNormF64:121return "snorm_f64";122case ElementType::UNormF64:123return "unorm_f64";124case ElementType::PackedS8x32:125return "p32i8";126case ElementType::PackedU8x32:127return "p32u8";128case ElementType::Invalid:129return "<invalid>";130}131llvm_unreachable("Unhandled ElementType");132}133134static StringRef getElementTypeNameForTemplate(ElementType ET) {135switch (ET) {136case ElementType::I1:137return "bool";138case ElementType::I16:139return "int16_t";140case ElementType::U16:141return "uint16_t";142case ElementType::I32:143return "int32_t";144case ElementType::U32:145return "uint32_t";146case ElementType::I64:147return "int64_t";148case ElementType::U64:149return "uint32_t";150case ElementType::F16:151case ElementType::SNormF16:152case ElementType::UNormF16:153return "half";154case ElementType::F32:155case ElementType::SNormF32:156case ElementType::UNormF32:157return "float";158case ElementType::F64:159case ElementType::SNormF64:160case ElementType::UNormF64:161return "double";162case ElementType::PackedS8x32:163return "int8_t4_packed";164case ElementType::PackedU8x32:165return "uint8_t4_packed";166case ElementType::Invalid:167return "<invalid>";168}169llvm_unreachable("Unhandled ElementType");170}171172static StringRef getSamplerTypeName(SamplerType ST) {173switch (ST) {174case SamplerType::Default:175return "Default";176case SamplerType::Comparison:177return "Comparison";178case SamplerType::Mono:179return "Mono";180}181llvm_unreachable("Unhandled SamplerType");182}183184static StringRef getSamplerFeedbackTypeName(SamplerFeedbackType SFT) {185switch (SFT) {186case SamplerFeedbackType::MinMip:187return "MinMip";188case SamplerFeedbackType::MipRegionUsed:189return "MipRegionUsed";190}191llvm_unreachable("Unhandled SamplerFeedbackType");192}193194static dxil::ElementType toDXILElementType(Type *Ty, bool IsSigned) {195// TODO: Handle unorm, snorm, and packed.196Ty = Ty->getScalarType();197198if (Ty->isIntegerTy()) {199switch (Ty->getIntegerBitWidth()) {200case 16:201return IsSigned ? ElementType::I16 : ElementType::U16;202case 32:203return IsSigned ? ElementType::I32 : ElementType::U32;204case 64:205return IsSigned ? ElementType::I64 : ElementType::U64;206case 1:207default:208return ElementType::Invalid;209}210} else if (Ty->isFloatTy()) {211return ElementType::F32;212} else if (Ty->isDoubleTy()) {213return ElementType::F64;214} else if (Ty->isHalfTy()) {215return ElementType::F16;216}217218return ElementType::Invalid;219}220221ResourceTypeInfo::ResourceTypeInfo(TargetExtType *HandleTy,222const dxil::ResourceClass RC_,223const dxil::ResourceKind Kind_)224: HandleTy(HandleTy) {225// If we're provided a resource class and kind, trust them.226if (Kind_ != dxil::ResourceKind::Invalid) {227RC = RC_;228Kind = Kind_;229return;230}231232if (auto *Ty = dyn_cast<RawBufferExtType>(HandleTy)) {233RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;234Kind = Ty->isStructured() ? ResourceKind::StructuredBuffer235: ResourceKind::RawBuffer;236} else if (auto *Ty = dyn_cast<TypedBufferExtType>(HandleTy)) {237RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;238Kind = ResourceKind::TypedBuffer;239} else if (auto *Ty = dyn_cast<TextureExtType>(HandleTy)) {240RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;241Kind = Ty->getDimension();242} else if (auto *Ty = dyn_cast<MSTextureExtType>(HandleTy)) {243RC = Ty->isWriteable() ? ResourceClass::UAV : ResourceClass::SRV;244Kind = Ty->getDimension();245} else if (auto *Ty = dyn_cast<FeedbackTextureExtType>(HandleTy)) {246RC = ResourceClass::UAV;247Kind = Ty->getDimension();248} else if (isa<CBufferExtType>(HandleTy)) {249RC = ResourceClass::CBuffer;250Kind = ResourceKind::CBuffer;251} else if (isa<SamplerExtType>(HandleTy)) {252RC = ResourceClass::Sampler;253Kind = ResourceKind::Sampler;254} else255llvm_unreachable("Unknown handle type");256}257258static void formatTypeName(SmallString<64> &Dest, StringRef Name,259bool IsWriteable, bool IsROV,260Type *ContainedType = nullptr,261bool IsSigned = true) {262raw_svector_ostream DestStream(Dest);263if (IsWriteable)264DestStream << (IsROV ? "RasterizerOrdered" : "RW");265DestStream << Name;266267if (!ContainedType)268return;269270StringRef ElementName;271ElementType ET = toDXILElementType(ContainedType, IsSigned);272if (ET != ElementType::Invalid) {273ElementName = getElementTypeNameForTemplate(ET);274} else {275assert(isa<StructType>(ContainedType) &&276"invalid element type for raw buffer");277StructType *ST = cast<StructType>(ContainedType);278if (!ST->hasName())279return;280ElementName = ST->getStructName();281}282283DestStream << "<" << ElementName;284if (const FixedVectorType *VTy = dyn_cast<FixedVectorType>(ContainedType))285DestStream << VTy->getNumElements();286DestStream << ">";287}288289static StructType *getOrCreateElementStruct(Type *ElemType, StringRef Name) {290StructType *Ty = StructType::getTypeByName(ElemType->getContext(), Name);291if (Ty && Ty->getNumElements() == 1 && Ty->getElementType(0) == ElemType)292return Ty;293return StructType::create(ElemType, Name);294}295296StructType *ResourceTypeInfo::createElementStruct(StringRef CBufferName) {297SmallString<64> TypeName;298299switch (Kind) {300case ResourceKind::Texture1D:301case ResourceKind::Texture2D:302case ResourceKind::Texture3D:303case ResourceKind::TextureCube:304case ResourceKind::Texture1DArray:305case ResourceKind::Texture2DArray:306case ResourceKind::TextureCubeArray: {307auto *RTy = cast<TextureExtType>(HandleTy);308formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),309RTy->isROV(), RTy->getResourceType(), RTy->isSigned());310return getOrCreateElementStruct(RTy->getResourceType(), TypeName);311}312case ResourceKind::Texture2DMS:313case ResourceKind::Texture2DMSArray: {314auto *RTy = cast<MSTextureExtType>(HandleTy);315formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),316/*IsROV=*/false, RTy->getResourceType(), RTy->isSigned());317return getOrCreateElementStruct(RTy->getResourceType(), TypeName);318}319case ResourceKind::TypedBuffer: {320auto *RTy = cast<TypedBufferExtType>(HandleTy);321formatTypeName(TypeName, getResourceKindName(Kind), RTy->isWriteable(),322RTy->isROV(), RTy->getResourceType(), RTy->isSigned());323return getOrCreateElementStruct(RTy->getResourceType(), TypeName);324}325case ResourceKind::RawBuffer: {326auto *RTy = cast<RawBufferExtType>(HandleTy);327formatTypeName(TypeName, "ByteAddressBuffer", RTy->isWriteable(),328RTy->isROV());329return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),330TypeName);331}332case ResourceKind::StructuredBuffer: {333auto *RTy = cast<RawBufferExtType>(HandleTy);334Type *Ty = RTy->getResourceType();335formatTypeName(TypeName, "StructuredBuffer", RTy->isWriteable(),336RTy->isROV(), RTy->getResourceType(), true);337return getOrCreateElementStruct(Ty, TypeName);338}339case ResourceKind::FeedbackTexture2D:340case ResourceKind::FeedbackTexture2DArray: {341auto *RTy = cast<FeedbackTextureExtType>(HandleTy);342TypeName = formatv("{0}<{1}>", getResourceKindName(Kind),343llvm::to_underlying(RTy->getFeedbackType()));344return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),345TypeName);346}347case ResourceKind::CBuffer: {348auto *RTy = cast<CBufferExtType>(HandleTy);349LayoutExtType *LayoutType = cast<LayoutExtType>(RTy->getResourceType());350StructType *Ty = cast<StructType>(LayoutType->getWrappedType());351SmallString<64> Name = getResourceKindName(Kind);352if (!CBufferName.empty()) {353Name.append(".");354Name.append(CBufferName);355}356return StructType::create(Ty->elements(), Name);357}358case ResourceKind::Sampler: {359auto *RTy = cast<SamplerExtType>(HandleTy);360TypeName = formatv("SamplerState<{0}>",361llvm::to_underlying(RTy->getSamplerType()));362return getOrCreateElementStruct(Type::getInt32Ty(HandleTy->getContext()),363TypeName);364}365case ResourceKind::TBuffer:366case ResourceKind::RTAccelerationStructure:367llvm_unreachable("Unhandled resource kind");368case ResourceKind::Invalid:369case ResourceKind::NumEntries:370llvm_unreachable("Invalid resource kind");371}372llvm_unreachable("Unhandled ResourceKind enum");373}374375bool ResourceTypeInfo::isUAV() const { return RC == ResourceClass::UAV; }376377bool ResourceTypeInfo::isCBuffer() const {378return RC == ResourceClass::CBuffer;379}380381bool ResourceTypeInfo::isSampler() const {382return RC == ResourceClass::Sampler;383}384385bool ResourceTypeInfo::isStruct() const {386return Kind == ResourceKind::StructuredBuffer;387}388389bool ResourceTypeInfo::isTyped() const {390switch (Kind) {391case ResourceKind::Texture1D:392case ResourceKind::Texture2D:393case ResourceKind::Texture2DMS:394case ResourceKind::Texture3D:395case ResourceKind::TextureCube:396case ResourceKind::Texture1DArray:397case ResourceKind::Texture2DArray:398case ResourceKind::Texture2DMSArray:399case ResourceKind::TextureCubeArray:400case ResourceKind::TypedBuffer:401return true;402case ResourceKind::RawBuffer:403case ResourceKind::StructuredBuffer:404case ResourceKind::FeedbackTexture2D:405case ResourceKind::FeedbackTexture2DArray:406case ResourceKind::CBuffer:407case ResourceKind::Sampler:408case ResourceKind::TBuffer:409case ResourceKind::RTAccelerationStructure:410return false;411case ResourceKind::Invalid:412case ResourceKind::NumEntries:413llvm_unreachable("Invalid resource kind");414}415llvm_unreachable("Unhandled ResourceKind enum");416}417418bool ResourceTypeInfo::isFeedback() const {419return Kind == ResourceKind::FeedbackTexture2D ||420Kind == ResourceKind::FeedbackTexture2DArray;421}422423bool ResourceTypeInfo::isMultiSample() const {424return Kind == ResourceKind::Texture2DMS ||425Kind == ResourceKind::Texture2DMSArray;426}427428static bool isROV(dxil::ResourceKind Kind, TargetExtType *Ty) {429switch (Kind) {430case ResourceKind::Texture1D:431case ResourceKind::Texture2D:432case ResourceKind::Texture3D:433case ResourceKind::TextureCube:434case ResourceKind::Texture1DArray:435case ResourceKind::Texture2DArray:436case ResourceKind::TextureCubeArray:437return cast<TextureExtType>(Ty)->isROV();438case ResourceKind::TypedBuffer:439return cast<TypedBufferExtType>(Ty)->isROV();440case ResourceKind::RawBuffer:441case ResourceKind::StructuredBuffer:442return cast<RawBufferExtType>(Ty)->isROV();443case ResourceKind::Texture2DMS:444case ResourceKind::Texture2DMSArray:445case ResourceKind::FeedbackTexture2D:446case ResourceKind::FeedbackTexture2DArray:447return false;448case ResourceKind::CBuffer:449case ResourceKind::Sampler:450case ResourceKind::TBuffer:451case ResourceKind::RTAccelerationStructure:452case ResourceKind::Invalid:453case ResourceKind::NumEntries:454llvm_unreachable("Resource cannot be ROV");455}456llvm_unreachable("Unhandled ResourceKind enum");457}458459ResourceTypeInfo::UAVInfo ResourceTypeInfo::getUAV() const {460assert(isUAV() && "Not a UAV");461return {isROV(Kind, HandleTy)};462}463464uint32_t ResourceTypeInfo::getCBufferSize(const DataLayout &DL) const {465assert(isCBuffer() && "Not a CBuffer");466467Type *ElTy = cast<CBufferExtType>(HandleTy)->getResourceType();468469if (auto *LayoutTy = dyn_cast<LayoutExtType>(ElTy))470return LayoutTy->getSize();471472// TODO: What should we do with unannotated arrays?473return DL.getTypeAllocSize(ElTy);474}475476dxil::SamplerType ResourceTypeInfo::getSamplerType() const {477assert(isSampler() && "Not a Sampler");478return cast<SamplerExtType>(HandleTy)->getSamplerType();479}480481ResourceTypeInfo::StructInfo482ResourceTypeInfo::getStruct(const DataLayout &DL) const {483assert(isStruct() && "Not a Struct");484485Type *ElTy = cast<RawBufferExtType>(HandleTy)->getResourceType();486487uint32_t Stride = DL.getTypeAllocSize(ElTy);488MaybeAlign Alignment;489if (auto *STy = dyn_cast<StructType>(ElTy))490Alignment = DL.getStructLayout(STy)->getAlignment();491uint32_t AlignLog2 = Alignment ? Log2(*Alignment) : 0;492return {Stride, AlignLog2};493}494495static std::pair<Type *, bool> getTypedElementType(dxil::ResourceKind Kind,496TargetExtType *Ty) {497switch (Kind) {498case ResourceKind::Texture1D:499case ResourceKind::Texture2D:500case ResourceKind::Texture3D:501case ResourceKind::TextureCube:502case ResourceKind::Texture1DArray:503case ResourceKind::Texture2DArray:504case ResourceKind::TextureCubeArray: {505auto *RTy = cast<TextureExtType>(Ty);506return {RTy->getResourceType(), RTy->isSigned()};507}508case ResourceKind::Texture2DMS:509case ResourceKind::Texture2DMSArray: {510auto *RTy = cast<MSTextureExtType>(Ty);511return {RTy->getResourceType(), RTy->isSigned()};512}513case ResourceKind::TypedBuffer: {514auto *RTy = cast<TypedBufferExtType>(Ty);515return {RTy->getResourceType(), RTy->isSigned()};516}517case ResourceKind::RawBuffer:518case ResourceKind::StructuredBuffer:519case ResourceKind::FeedbackTexture2D:520case ResourceKind::FeedbackTexture2DArray:521case ResourceKind::CBuffer:522case ResourceKind::Sampler:523case ResourceKind::TBuffer:524case ResourceKind::RTAccelerationStructure:525case ResourceKind::Invalid:526case ResourceKind::NumEntries:527llvm_unreachable("Resource is not typed");528}529llvm_unreachable("Unhandled ResourceKind enum");530}531532ResourceTypeInfo::TypedInfo ResourceTypeInfo::getTyped() const {533assert(isTyped() && "Not typed");534535auto [ElTy, IsSigned] = getTypedElementType(Kind, HandleTy);536dxil::ElementType ET = toDXILElementType(ElTy, IsSigned);537uint32_t Count = 1;538if (auto *VTy = dyn_cast<FixedVectorType>(ElTy))539Count = VTy->getNumElements();540return {ET, Count};541}542543dxil::SamplerFeedbackType ResourceTypeInfo::getFeedbackType() const {544assert(isFeedback() && "Not Feedback");545return cast<FeedbackTextureExtType>(HandleTy)->getFeedbackType();546}547uint32_t ResourceTypeInfo::getMultiSampleCount() const {548assert(isMultiSample() && "Not MultiSampled");549return cast<MSTextureExtType>(HandleTy)->getSampleCount();550}551552bool ResourceTypeInfo::operator==(const ResourceTypeInfo &RHS) const {553return HandleTy == RHS.HandleTy;554}555556bool ResourceTypeInfo::operator<(const ResourceTypeInfo &RHS) const {557// An empty datalayout is sufficient for sorting purposes.558DataLayout DummyDL;559if (std::tie(RC, Kind) < std::tie(RHS.RC, RHS.Kind))560return true;561if (isCBuffer() && RHS.isCBuffer() &&562getCBufferSize(DummyDL) < RHS.getCBufferSize(DummyDL))563return true;564if (isSampler() && RHS.isSampler() && getSamplerType() < RHS.getSamplerType())565return true;566if (isUAV() && RHS.isUAV() && getUAV() < RHS.getUAV())567return true;568if (isStruct() && RHS.isStruct() &&569getStruct(DummyDL) < RHS.getStruct(DummyDL))570return true;571if (isFeedback() && RHS.isFeedback() &&572getFeedbackType() < RHS.getFeedbackType())573return true;574if (isTyped() && RHS.isTyped() && getTyped() < RHS.getTyped())575return true;576if (isMultiSample() && RHS.isMultiSample() &&577getMultiSampleCount() < RHS.getMultiSampleCount())578return true;579return false;580}581582void ResourceTypeInfo::print(raw_ostream &OS, const DataLayout &DL) const {583OS << " Class: " << getResourceClassName(RC) << "\n"584<< " Kind: " << getResourceKindName(Kind) << "\n";585586if (isCBuffer()) {587OS << " CBuffer size: " << getCBufferSize(DL) << "\n";588} else if (isSampler()) {589OS << " Sampler Type: " << getSamplerTypeName(getSamplerType()) << "\n";590} else {591if (isUAV()) {592UAVInfo UAVFlags = getUAV();593OS << " IsROV: " << UAVFlags.IsROV << "\n";594}595if (isMultiSample())596OS << " Sample Count: " << getMultiSampleCount() << "\n";597598if (isStruct()) {599StructInfo Struct = getStruct(DL);600OS << " Buffer Stride: " << Struct.Stride << "\n";601OS << " Alignment: " << Struct.AlignLog2 << "\n";602} else if (isTyped()) {603TypedInfo Typed = getTyped();604OS << " Element Type: " << getElementTypeName(Typed.ElementTy) << "\n"605<< " Element Count: " << Typed.ElementCount << "\n";606} else if (isFeedback())607OS << " Feedback Type: " << getSamplerFeedbackTypeName(getFeedbackType())608<< "\n";609}610}611612GlobalVariable *ResourceInfo::createSymbol(Module &M, StructType *Ty) {613assert(!Symbol && "Symbol has already been created");614Symbol = new GlobalVariable(M, Ty, /*isConstant=*/true,615GlobalValue::ExternalLinkage,616/*Initializer=*/nullptr, Name);617return Symbol;618}619620MDTuple *ResourceInfo::getAsMetadata(Module &M,621dxil::ResourceTypeInfo &RTI) const {622LLVMContext &Ctx = M.getContext();623const DataLayout &DL = M.getDataLayout();624625SmallVector<Metadata *, 11> MDVals;626627Type *I32Ty = Type::getInt32Ty(Ctx);628Type *I1Ty = Type::getInt1Ty(Ctx);629auto getIntMD = [&I32Ty](uint32_t V) {630return ConstantAsMetadata::get(631Constant::getIntegerValue(I32Ty, APInt(32, V)));632};633auto getBoolMD = [&I1Ty](uint32_t V) {634return ConstantAsMetadata::get(635Constant::getIntegerValue(I1Ty, APInt(1, V)));636};637638MDVals.push_back(getIntMD(Binding.RecordID));639assert(Symbol && "Cannot yet create useful resource metadata without symbol");640MDVals.push_back(ValueAsMetadata::get(Symbol));641MDVals.push_back(MDString::get(Ctx, Name));642MDVals.push_back(getIntMD(Binding.Space));643MDVals.push_back(getIntMD(Binding.LowerBound));644MDVals.push_back(getIntMD(Binding.Size));645646if (RTI.isCBuffer()) {647MDVals.push_back(getIntMD(RTI.getCBufferSize(DL)));648MDVals.push_back(nullptr);649} else if (RTI.isSampler()) {650MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getSamplerType())));651MDVals.push_back(nullptr);652} else {653MDVals.push_back(getIntMD(llvm::to_underlying(RTI.getResourceKind())));654655if (RTI.isUAV()) {656ResourceTypeInfo::UAVInfo UAVFlags = RTI.getUAV();657MDVals.push_back(getBoolMD(GloballyCoherent));658MDVals.push_back(getBoolMD(hasCounter()));659MDVals.push_back(getBoolMD(UAVFlags.IsROV));660} else {661// All SRVs include sample count in the metadata, but it's only meaningful662// for multi-sampled textured. Also, UAVs can be multisampled in SM6.7+,663// but this just isn't reflected in the metadata at all.664uint32_t SampleCount =665RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;666MDVals.push_back(getIntMD(SampleCount));667}668669// Further properties are attached to a metadata list of tag-value pairs.670SmallVector<Metadata *> Tags;671if (RTI.isStruct()) {672Tags.push_back(673getIntMD(llvm::to_underlying(ExtPropTags::StructuredBufferStride)));674Tags.push_back(getIntMD(RTI.getStruct(DL).Stride));675} else if (RTI.isTyped()) {676Tags.push_back(getIntMD(llvm::to_underlying(ExtPropTags::ElementType)));677Tags.push_back(getIntMD(llvm::to_underlying(RTI.getTyped().ElementTy)));678} else if (RTI.isFeedback()) {679Tags.push_back(680getIntMD(llvm::to_underlying(ExtPropTags::SamplerFeedbackKind)));681Tags.push_back(getIntMD(llvm::to_underlying(RTI.getFeedbackType())));682}683MDVals.push_back(Tags.empty() ? nullptr : MDNode::get(Ctx, Tags));684}685686return MDNode::get(Ctx, MDVals);687}688689std::pair<uint32_t, uint32_t>690ResourceInfo::getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const {691const DataLayout &DL = M.getDataLayout();692693uint32_t ResourceKind = llvm::to_underlying(RTI.getResourceKind());694uint32_t AlignLog2 = RTI.isStruct() ? RTI.getStruct(DL).AlignLog2 : 0;695bool IsUAV = RTI.isUAV();696ResourceTypeInfo::UAVInfo UAVFlags =697IsUAV ? RTI.getUAV() : ResourceTypeInfo::UAVInfo{};698bool IsROV = IsUAV && UAVFlags.IsROV;699bool IsGloballyCoherent = IsUAV && GloballyCoherent;700uint8_t SamplerCmpOrHasCounter = 0;701if (IsUAV)702SamplerCmpOrHasCounter = hasCounter();703else if (RTI.isSampler())704SamplerCmpOrHasCounter = RTI.getSamplerType() == SamplerType::Comparison;705706// TODO: Document this format. Currently the only reference is the707// implementation of dxc's DxilResourceProperties struct.708uint32_t Word0 = 0;709Word0 |= ResourceKind & 0xFF;710Word0 |= (AlignLog2 & 0xF) << 8;711Word0 |= (IsUAV & 1) << 12;712Word0 |= (IsROV & 1) << 13;713Word0 |= (IsGloballyCoherent & 1) << 14;714Word0 |= (SamplerCmpOrHasCounter & 1) << 15;715716uint32_t Word1 = 0;717if (RTI.isStruct())718Word1 = RTI.getStruct(DL).Stride;719else if (RTI.isCBuffer())720Word1 = RTI.getCBufferSize(DL);721else if (RTI.isFeedback())722Word1 = llvm::to_underlying(RTI.getFeedbackType());723else if (RTI.isTyped()) {724ResourceTypeInfo::TypedInfo Typed = RTI.getTyped();725uint32_t CompType = llvm::to_underlying(Typed.ElementTy);726uint32_t CompCount = Typed.ElementCount;727uint32_t SampleCount = RTI.isMultiSample() ? RTI.getMultiSampleCount() : 0;728729Word1 |= (CompType & 0xFF) << 0;730Word1 |= (CompCount & 0xFF) << 8;731Word1 |= (SampleCount & 0xFF) << 16;732}733734return {Word0, Word1};735}736737void ResourceInfo::print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI,738const DataLayout &DL) const {739if (!Name.empty())740OS << " Name: " << Name << "\n";741742if (Symbol) {743OS << " Symbol: ";744Symbol->printAsOperand(OS);745OS << "\n";746}747748OS << " Binding:\n"749<< " Record ID: " << Binding.RecordID << "\n"750<< " Space: " << Binding.Space << "\n"751<< " Lower Bound: " << Binding.LowerBound << "\n"752<< " Size: " << Binding.Size << "\n";753754OS << " Globally Coherent: " << GloballyCoherent << "\n";755OS << " Counter Direction: ";756757switch (CounterDirection) {758case ResourceCounterDirection::Increment:759OS << "Increment\n";760break;761case ResourceCounterDirection::Decrement:762OS << "Decrement\n";763break;764case ResourceCounterDirection::Unknown:765OS << "Unknown\n";766break;767case ResourceCounterDirection::Invalid:768OS << "Invalid\n";769break;770}771772RTI.print(OS, DL);773}774775//===----------------------------------------------------------------------===//776777bool DXILResourceTypeMap::invalidate(Module &M, const PreservedAnalyses &PA,778ModuleAnalysisManager::Invalidator &Inv) {779// Passes that introduce resource types must explicitly invalidate this pass.780auto PAC = PA.getChecker<DXILResourceTypeAnalysis>();781return !PAC.preservedWhenStateless();782}783784//===----------------------------------------------------------------------===//785static bool isUpdateCounterIntrinsic(Function &F) {786return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter;787}788789StringRef dxil::getResourceNameFromBindingCall(CallInst *CI) {790Value *Op = nullptr;791switch (CI->getCalledFunction()->getIntrinsicID()) {792default:793llvm_unreachable("unexpected handle creation intrinsic");794case Intrinsic::dx_resource_handlefrombinding:795case Intrinsic::dx_resource_handlefromimplicitbinding:796Op = CI->getArgOperand(5);797break;798}799800auto *GV = dyn_cast<llvm::GlobalVariable>(Op);801if (!GV)802return "";803804auto *CA = dyn_cast<ConstantDataArray>(GV->getInitializer());805assert(CA && CA->isString() && "expected constant string");806StringRef Name = CA->getAsString();807// strip trailing 0808if (Name.ends_with('\0'))809Name = Name.drop_back(1);810return Name;811}812813void DXILResourceMap::populateResourceInfos(Module &M,814DXILResourceTypeMap &DRTM) {815SmallVector<std::tuple<CallInst *, ResourceInfo, ResourceTypeInfo>> CIToInfos;816817for (Function &F : M.functions()) {818if (!F.isDeclaration())819continue;820LLVM_DEBUG(dbgs() << "Function: " << F.getName() << "\n");821Intrinsic::ID ID = F.getIntrinsicID();822switch (ID) {823default:824continue;825case Intrinsic::dx_resource_handlefrombinding: {826auto *HandleTy = cast<TargetExtType>(F.getReturnType());827ResourceTypeInfo &RTI = DRTM[HandleTy];828829for (User *U : F.users())830if (CallInst *CI = dyn_cast<CallInst>(U)) {831LLVM_DEBUG(dbgs() << " Visiting: " << *U << "\n");832uint32_t Space =833cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();834uint32_t LowerBound =835cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();836uint32_t Size =837cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();838StringRef Name = getResourceNameFromBindingCall(CI);839840ResourceInfo RI =841ResourceInfo{/*RecordID=*/0, Space, LowerBound,842Size, HandleTy, Name};843844CIToInfos.emplace_back(CI, RI, RTI);845}846847break;848}849}850}851852llvm::stable_sort(CIToInfos, [](auto &LHS, auto &RHS) {853const auto &[LCI, LRI, LRTI] = LHS;854const auto &[RCI, RRI, RRTI] = RHS;855// Sort by resource class first for grouping purposes, and then by the856// binding and type so we can remove duplicates.857ResourceClass LRC = LRTI.getResourceClass();858ResourceClass RRC = RRTI.getResourceClass();859860return std::tie(LRC, LRI, LRTI) < std::tie(RRC, RRI, RRTI);861});862for (auto [CI, RI, RTI] : CIToInfos) {863if (Infos.empty() || RI != Infos.back())864Infos.push_back(RI);865CallMap[CI] = Infos.size() - 1;866}867868unsigned Size = Infos.size();869// In DXC, Record ID is unique per resource type. Match that.870FirstUAV = FirstCBuffer = FirstSampler = Size;871uint32_t NextID = 0;872for (unsigned I = 0, E = Size; I != E; ++I) {873ResourceInfo &RI = Infos[I];874ResourceTypeInfo &RTI = DRTM[RI.getHandleTy()];875if (RTI.isUAV() && FirstUAV == Size) {876FirstUAV = I;877NextID = 0;878} else if (RTI.isCBuffer() && FirstCBuffer == Size) {879FirstCBuffer = I;880NextID = 0;881} else if (RTI.isSampler() && FirstSampler == Size) {882FirstSampler = I;883NextID = 0;884}885886// We need to make sure the types of resource are ordered even if some are887// missing.888FirstCBuffer = std::min({FirstCBuffer, FirstSampler});889FirstUAV = std::min({FirstUAV, FirstCBuffer});890891// Adjust the resource binding to use the next ID.892RI.setBindingID(NextID++);893}894}895896void DXILResourceMap::populateCounterDirections(Module &M) {897for (Function &F : M.functions()) {898if (!isUpdateCounterIntrinsic(F))899continue;900901LLVM_DEBUG(dbgs() << "Update Counter Function: " << F.getName() << "\n");902903for (const User *U : F.users()) {904const CallInst *CI = dyn_cast<CallInst>(U);905assert(CI && "Users of dx_resource_updateCounter must be call instrs");906907// Determine if the use is an increment or decrement908Value *CountArg = CI->getArgOperand(1);909ConstantInt *CountValue = cast<ConstantInt>(CountArg);910int64_t CountLiteral = CountValue->getSExtValue();911912// 0 is an unknown direction and shouldn't result in an insert913if (CountLiteral == 0)914continue;915916ResourceCounterDirection Direction = ResourceCounterDirection::Decrement;917if (CountLiteral > 0)918Direction = ResourceCounterDirection::Increment;919920// Collect all potential creation points for the handle arg921Value *HandleArg = CI->getArgOperand(0);922SmallVector<ResourceInfo *> RBInfos = findByUse(HandleArg);923for (ResourceInfo *RBInfo : RBInfos) {924if (RBInfo->CounterDirection == ResourceCounterDirection::Unknown)925RBInfo->CounterDirection = Direction;926else if (RBInfo->CounterDirection != Direction) {927RBInfo->CounterDirection = ResourceCounterDirection::Invalid;928HasInvalidDirection = true;929}930}931}932}933}934935void DXILResourceMap::populate(Module &M, DXILResourceTypeMap &DRTM) {936populateResourceInfos(M, DRTM);937populateCounterDirections(M);938}939940void DXILResourceMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM,941const DataLayout &DL) const {942for (unsigned I = 0, E = Infos.size(); I != E; ++I) {943OS << "Resource " << I << ":\n";944const dxil::ResourceInfo &RI = Infos[I];945RI.print(OS, DRTM[RI.getHandleTy()], DL);946OS << "\n";947}948949for (const auto &[CI, Index] : CallMap) {950OS << "Call bound to " << Index << ":";951CI->print(OS);952OS << "\n";953}954}955956SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {957if (const PHINode *Phi = dyn_cast<PHINode>(Key)) {958SmallVector<dxil::ResourceInfo *> Children;959for (const Value *V : Phi->operands()) {960Children.append(findByUse(V));961}962return Children;963}964965const CallInst *CI = dyn_cast<CallInst>(Key);966if (!CI)967return {};968969switch (CI->getIntrinsicID()) {970// Found the create, return the binding971case Intrinsic::dx_resource_handlefrombinding: {972auto Pos = CallMap.find(CI);973assert(Pos != CallMap.end() && "HandleFromBinding must be in resource map");974return {&Infos[Pos->second]};975}976default:977break;978}979980// Check if any of the parameters are the resource we are following. If so981// keep searching. If none of them are return an empty list982const Type *UseType = CI->getType();983SmallVector<dxil::ResourceInfo *> Children;984for (const Value *V : CI->args()) {985if (V->getType() != UseType)986continue;987988Children.append(findByUse(V));989}990991return Children;992}993994//===----------------------------------------------------------------------===//995996void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {997struct Binding {998ResourceClass RC;999uint32_t Space;1000uint32_t LowerBound;1001uint32_t UpperBound;1002Value *Name;1003Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound,1004uint32_t UpperBound, Value *Name)1005: RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),1006Name(Name) {}1007};1008SmallVector<Binding> Bindings;10091010// collect all of the llvm.dx.resource.handlefrombinding calls;1011// make a note if there is llvm.dx.resource.handlefromimplicitbinding1012for (Function &F : M.functions()) {1013if (!F.isDeclaration())1014continue;10151016switch (F.getIntrinsicID()) {1017default:1018continue;1019case Intrinsic::dx_resource_handlefrombinding: {1020auto *HandleTy = cast<TargetExtType>(F.getReturnType());1021ResourceTypeInfo &RTI = DRTM[HandleTy];10221023for (User *U : F.users())1024if (CallInst *CI = dyn_cast<CallInst>(U)) {1025uint32_t Space =1026cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();1027uint32_t LowerBound =1028cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();1029int32_t Size =1030cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();1031Value *Name = CI->getArgOperand(5);10321033// negative size means unbounded resource array;1034// upper bound register overflow should be detected in Sema1035assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) &&1036"upper bound register overflow");1037uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;1038Bindings.emplace_back(RTI.getResourceClass(), Space, LowerBound,1039UpperBound, Name);1040}1041break;1042}1043case Intrinsic::dx_resource_handlefromimplicitbinding: {1044ImplicitBinding = true;1045break;1046}1047}1048}10491050// sort all the collected bindings1051llvm::stable_sort(Bindings, [](auto &LHS, auto &RHS) {1052return std::tie(LHS.RC, LHS.Space, LHS.LowerBound) <1053std::tie(RHS.RC, RHS.Space, RHS.LowerBound);1054});10551056// remove duplicates1057Binding *NewEnd = llvm::unique(Bindings, [](auto &LHS, auto &RHS) {1058return std::tie(LHS.RC, LHS.Space, LHS.LowerBound, LHS.UpperBound,1059LHS.Name) == std::tie(RHS.RC, RHS.Space, RHS.LowerBound,1060RHS.UpperBound, RHS.Name);1061});1062if (NewEnd != Bindings.end())1063Bindings.erase(NewEnd);10641065// Go over the sorted bindings and build up lists of free register ranges1066// for each binding type and used spaces. Bindings are sorted by resource1067// class, space, and lower bound register slot.1068BindingSpaces *BS = &SRVSpaces;1069for (const Binding &B : Bindings) {1070if (BS->RC != B.RC)1071// move to the next resource class spaces1072BS = &getBindingSpaces(B.RC);10731074RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(B.Space)1075: &BS->Spaces.back();1076assert(S->Space <= B.Space && "bindings not sorted correctly?");1077if (B.Space != S->Space)1078// add new space1079S = &BS->Spaces.emplace_back(B.Space);10801081// the space is full - set flag to report overlapping binding later1082if (S->FreeRanges.empty()) {1083OverlappingBinding = true;1084continue;1085}10861087// adjust the last free range lower bound, split it in two, or remove it1088BindingRange &LastFreeRange = S->FreeRanges.back();1089assert(LastFreeRange.UpperBound == UINT32_MAX);1090if (LastFreeRange.LowerBound == B.LowerBound) {1091if (B.UpperBound < UINT32_MAX)1092LastFreeRange.LowerBound = B.UpperBound + 1;1093else1094S->FreeRanges.pop_back();1095} else if (LastFreeRange.LowerBound < B.LowerBound) {1096LastFreeRange.UpperBound = B.LowerBound - 1;1097if (B.UpperBound < UINT32_MAX)1098S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);1099} else {1100OverlappingBinding = true;1101if (B.UpperBound < UINT32_MAX)1102LastFreeRange.LowerBound =1103std::max(LastFreeRange.LowerBound, B.UpperBound + 1);1104else1105S->FreeRanges.pop_back();1106}1107}1108}11091110// returns std::nulopt if binding could not be found in given space1111std::optional<uint32_t>1112DXILResourceBindingInfo::findAvailableBinding(dxil::ResourceClass RC,1113uint32_t Space, int32_t Size) {1114BindingSpaces &BS = getBindingSpaces(RC);1115RegisterSpace &RS = BS.getOrInsertSpace(Space);1116return RS.findAvailableBinding(Size);1117}11181119DXILResourceBindingInfo::RegisterSpace &1120DXILResourceBindingInfo::BindingSpaces::getOrInsertSpace(uint32_t Space) {1121for (auto *I = Spaces.begin(); I != Spaces.end(); ++I) {1122if (I->Space == Space)1123return *I;1124if (I->Space < Space)1125continue;1126return *Spaces.insert(I, Space);1127}1128return Spaces.emplace_back(Space);1129}11301131std::optional<uint32_t>1132DXILResourceBindingInfo::RegisterSpace::findAvailableBinding(int32_t Size) {1133assert((Size == -1 || Size > 0) && "invalid size");11341135if (FreeRanges.empty())1136return std::nullopt;11371138// unbounded array1139if (Size == -1) {1140BindingRange &Last = FreeRanges.back();1141if (Last.UpperBound != UINT32_MAX)1142// this space is already occupied by an unbounded array1143return std::nullopt;1144uint32_t RegSlot = Last.LowerBound;1145FreeRanges.pop_back();1146return RegSlot;1147}11481149// single resource or fixed-size array1150for (BindingRange &R : FreeRanges) {1151// compare the size as uint64_t to prevent overflow for range (0,1152// UINT32_MAX)1153if ((uint64_t)R.UpperBound - R.LowerBound + 1 < (uint64_t)Size)1154continue;1155uint32_t RegSlot = R.LowerBound;1156// This might create a range where (LowerBound == UpperBound + 1). When1157// that happens, the next time this function is called the range will1158// skipped over by the check above (at this point Size is always > 0).1159R.LowerBound += Size;1160return RegSlot;1161}11621163return std::nullopt;1164}11651166//===----------------------------------------------------------------------===//11671168AnalysisKey DXILResourceTypeAnalysis::Key;1169AnalysisKey DXILResourceAnalysis::Key;1170AnalysisKey DXILResourceBindingAnalysis::Key;11711172DXILResourceMap DXILResourceAnalysis::run(Module &M,1173ModuleAnalysisManager &AM) {1174DXILResourceMap Data;1175DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);1176Data.populate(M, DRTM);1177return Data;1178}11791180DXILResourceBindingInfo1181DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {1182DXILResourceBindingInfo Data;1183DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);1184Data.populate(M, DRTM);1185return Data;1186}11871188PreservedAnalyses DXILResourcePrinterPass::run(Module &M,1189ModuleAnalysisManager &AM) {1190DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);1191DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);11921193DRM.print(OS, DRTM, M.getDataLayout());1194return PreservedAnalyses::all();1195}11961197void DXILResourceTypeWrapperPass::anchor() {}11981199DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass()1200: ImmutablePass(ID) {}12011202INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type",1203"DXIL Resource Type Analysis", false, true)1204char DXILResourceTypeWrapperPass::ID = 0;12051206ModulePass *llvm::createDXILResourceTypeWrapperPassPass() {1207return new DXILResourceTypeWrapperPass();1208}12091210DXILResourceWrapperPass::DXILResourceWrapperPass() : ModulePass(ID) {}12111212DXILResourceWrapperPass::~DXILResourceWrapperPass() = default;12131214void DXILResourceWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {1215AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();1216AU.setPreservesAll();1217}12181219bool DXILResourceWrapperPass::runOnModule(Module &M) {1220Map.reset(new DXILResourceMap());12211222DRTM = &getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();1223Map->populate(M, *DRTM);12241225return false;1226}12271228void DXILResourceWrapperPass::releaseMemory() { Map.reset(); }12291230void DXILResourceWrapperPass::print(raw_ostream &OS, const Module *M) const {1231if (!Map) {1232OS << "No resource map has been built!\n";1233return;1234}1235Map->print(OS, *DRTM, M->getDataLayout());1236}12371238#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)1239LLVM_DUMP_METHOD1240void DXILResourceWrapperPass::dump() const { print(dbgs(), nullptr); }1241#endif12421243INITIALIZE_PASS(DXILResourceWrapperPass, "dxil-resources",1244"DXIL Resources Analysis", false, true)1245char DXILResourceWrapperPass::ID = 0;12461247ModulePass *llvm::createDXILResourceWrapperPassPass() {1248return new DXILResourceWrapperPass();1249}12501251DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()1252: ModulePass(ID) {}12531254DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;12551256void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {1257AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();1258AU.setPreservesAll();1259}12601261bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {1262BindingInfo.reset(new DXILResourceBindingInfo());12631264DXILResourceTypeMap &DRTM =1265getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();1266BindingInfo->populate(M, DRTM);12671268return false;1269}12701271void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); }12721273INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",1274"DXIL Resource Binding Analysis", false, true)1275char DXILResourceBindingWrapperPass::ID = 0;12761277ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {1278return new DXILResourceWrapperPass();1279}128012811282