blob: 96e90e563e230cc0b009e54a684b24ecb3e7c798 [file] [log] [blame]
//===- DXILResource.h - Representations of DXIL resources -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_ANALYSIS_DXILRESOURCE_H
#define LLVM_ANALYSIS_DXILRESOURCE_H
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Support/DXILABI.h"
namespace llvm {
class CallInst;
class DataLayout;
class LLVMContext;
class MDTuple;
class Value;
class DXILResourceTypeMap;
namespace dxil {
/// The dx.RawBuffer target extension type
///
/// `target("dx.RawBuffer", Type, IsWriteable, IsROV)`
class RawBufferExtType : public TargetExtType {
public:
RawBufferExtType() = delete;
RawBufferExtType(const RawBufferExtType &) = delete;
RawBufferExtType &operator=(const RawBufferExtType &) = delete;
bool isStructured() const {
// TODO: We need to be more prescriptive here, but since there's some debate
// over whether byte address buffer should have a void type or an i8 type,
// accept either for now.
Type *Ty = getTypeParameter(0);
return !Ty->isVoidTy() && !Ty->isIntegerTy(8);
}
Type *getResourceType() const {
return isStructured() ? getTypeParameter(0) : nullptr;
}
bool isWriteable() const { return getIntParameter(0); }
bool isROV() const { return getIntParameter(1); }
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.RawBuffer";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
/// The dx.TypedBuffer target extension type
///
/// `target("dx.TypedBuffer", Type, IsWriteable, IsROV, IsSigned)`
class TypedBufferExtType : public TargetExtType {
public:
TypedBufferExtType() = delete;
TypedBufferExtType(const TypedBufferExtType &) = delete;
TypedBufferExtType &operator=(const TypedBufferExtType &) = delete;
Type *getResourceType() const { return getTypeParameter(0); }
bool isWriteable() const { return getIntParameter(0); }
bool isROV() const { return getIntParameter(1); }
bool isSigned() const { return getIntParameter(2); }
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.TypedBuffer";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
/// The dx.Texture target extension type
///
/// `target("dx.Texture", Type, IsWriteable, IsROV, IsSigned, Dimension)`
class TextureExtType : public TargetExtType {
public:
TextureExtType() = delete;
TextureExtType(const TextureExtType &) = delete;
TextureExtType &operator=(const TextureExtType &) = delete;
Type *getResourceType() const { return getTypeParameter(0); }
bool isWriteable() const { return getIntParameter(0); }
bool isROV() const { return getIntParameter(1); }
bool isSigned() const { return getIntParameter(2); }
dxil::ResourceKind getDimension() const {
return static_cast<dxil::ResourceKind>(getIntParameter(3));
}
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.Texture";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
/// The dx.MSTexture target extension type
///
/// `target("dx.MSTexture", Type, IsWriteable, Samples, IsSigned, Dimension)`
class MSTextureExtType : public TargetExtType {
public:
MSTextureExtType() = delete;
MSTextureExtType(const MSTextureExtType &) = delete;
MSTextureExtType &operator=(const MSTextureExtType &) = delete;
Type *getResourceType() const { return getTypeParameter(0); }
bool isWriteable() const { return getIntParameter(0); }
uint32_t getSampleCount() const { return getIntParameter(1); }
bool isSigned() const { return getIntParameter(2); }
dxil::ResourceKind getDimension() const {
return static_cast<dxil::ResourceKind>(getIntParameter(3));
}
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.MSTexture";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
/// The dx.FeedbackTexture target extension type
///
/// `target("dx.FeedbackTexture", FeedbackType, Dimension)`
class FeedbackTextureExtType : public TargetExtType {
public:
FeedbackTextureExtType() = delete;
FeedbackTextureExtType(const FeedbackTextureExtType &) = delete;
FeedbackTextureExtType &operator=(const FeedbackTextureExtType &) = delete;
dxil::SamplerFeedbackType getFeedbackType() const {
return static_cast<dxil::SamplerFeedbackType>(getIntParameter(0));
}
dxil::ResourceKind getDimension() const {
return static_cast<dxil::ResourceKind>(getIntParameter(1));
}
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.FeedbackTexture";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
/// The dx.CBuffer target extension type
///
/// `target("dx.CBuffer", <Type>, ...)`
class CBufferExtType : public TargetExtType {
public:
CBufferExtType() = delete;
CBufferExtType(const CBufferExtType &) = delete;
CBufferExtType &operator=(const CBufferExtType &) = delete;
Type *getResourceType() const { return getTypeParameter(0); }
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.CBuffer";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
/// The dx.Sampler target extension type
///
/// `target("dx.Sampler", SamplerType)`
class SamplerExtType : public TargetExtType {
public:
SamplerExtType() = delete;
SamplerExtType(const SamplerExtType &) = delete;
SamplerExtType &operator=(const SamplerExtType &) = delete;
dxil::SamplerType getSamplerType() const {
return static_cast<dxil::SamplerType>(getIntParameter(0));
}
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.Sampler";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
/// The dx.Layout target extension type
///
/// `target("dx.Layout", <Type>, <size>, [offsets...])`
class LayoutExtType : public TargetExtType {
public:
LayoutExtType() = delete;
LayoutExtType(const LayoutExtType &) = delete;
LayoutExtType &operator=(const LayoutExtType &) = delete;
Type *getWrappedType() const { return getTypeParameter(0); }
uint32_t getSize() const { return getIntParameter(0); }
uint32_t getOffsetOfElement(int I) const { return getIntParameter(I + 1); }
static bool classof(const TargetExtType *T) {
return T->getName() == "dx.Layout";
}
static bool classof(const Type *T) {
return isa<TargetExtType>(T) && classof(cast<TargetExtType>(T));
}
};
//===----------------------------------------------------------------------===//
class ResourceTypeInfo {
public:
struct UAVInfo {
bool IsROV;
bool operator==(const UAVInfo &RHS) const { return IsROV == RHS.IsROV; }
bool operator!=(const UAVInfo &RHS) const { return !(*this == RHS); }
bool operator<(const UAVInfo &RHS) const { return IsROV < RHS.IsROV; }
};
struct StructInfo {
uint32_t Stride;
// Note: we store an integer here rather than using `MaybeAlign` because in
// GCC 7 MaybeAlign isn't trivial so having one in this union would delete
// our move constructor.
// See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0602r4.html
uint32_t AlignLog2;
bool operator==(const StructInfo &RHS) const {
return std::tie(Stride, AlignLog2) == std::tie(RHS.Stride, RHS.AlignLog2);
}
bool operator!=(const StructInfo &RHS) const { return !(*this == RHS); }
bool operator<(const StructInfo &RHS) const {
return std::tie(Stride, AlignLog2) < std::tie(RHS.Stride, RHS.AlignLog2);
}
};
struct TypedInfo {
dxil::ElementType ElementTy;
uint32_t ElementCount;
bool operator==(const TypedInfo &RHS) const {
return std::tie(ElementTy, ElementCount) ==
std::tie(RHS.ElementTy, RHS.ElementCount);
}
bool operator!=(const TypedInfo &RHS) const { return !(*this == RHS); }
bool operator<(const TypedInfo &RHS) const {
return std::tie(ElementTy, ElementCount) <
std::tie(RHS.ElementTy, RHS.ElementCount);
}
};
private:
TargetExtType *HandleTy;
dxil::ResourceClass RC;
dxil::ResourceKind Kind;
public:
ResourceTypeInfo(TargetExtType *HandleTy, const dxil::ResourceClass RC,
const dxil::ResourceKind Kind);
ResourceTypeInfo(TargetExtType *HandleTy)
: ResourceTypeInfo(HandleTy, {}, dxil::ResourceKind::Invalid) {}
TargetExtType *getHandleTy() const { return HandleTy; }
StructType *createElementStruct();
// Conditions to check before accessing specific views.
bool isUAV() const;
bool isCBuffer() const;
bool isSampler() const;
bool isStruct() const;
bool isTyped() const;
bool isFeedback() const;
bool isMultiSample() const;
// Views into the type.
UAVInfo getUAV() const;
uint32_t getCBufferSize(const DataLayout &DL) const;
dxil::SamplerType getSamplerType() const;
StructInfo getStruct(const DataLayout &DL) const;
TypedInfo getTyped() const;
dxil::SamplerFeedbackType getFeedbackType() const;
uint32_t getMultiSampleCount() const;
dxil::ResourceClass getResourceClass() const { return RC; }
dxil::ResourceKind getResourceKind() const { return Kind; }
bool operator==(const ResourceTypeInfo &RHS) const;
bool operator!=(const ResourceTypeInfo &RHS) const { return !(*this == RHS); }
bool operator<(const ResourceTypeInfo &RHS) const;
void print(raw_ostream &OS, const DataLayout &DL) const;
};
//===----------------------------------------------------------------------===//
enum class ResourceCounterDirection {
Increment,
Decrement,
Unknown,
Invalid,
};
class ResourceInfo {
public:
struct ResourceBinding {
uint32_t RecordID;
uint32_t Space;
uint32_t LowerBound;
uint32_t Size;
bool operator==(const ResourceBinding &RHS) const {
return std::tie(RecordID, Space, LowerBound, Size) ==
std::tie(RHS.RecordID, RHS.Space, RHS.LowerBound, RHS.Size);
}
bool operator!=(const ResourceBinding &RHS) const {
return !(*this == RHS);
}
bool operator<(const ResourceBinding &RHS) const {
return std::tie(RecordID, Space, LowerBound, Size) <
std::tie(RHS.RecordID, RHS.Space, RHS.LowerBound, RHS.Size);
}
};
private:
ResourceBinding Binding;
TargetExtType *HandleTy;
GlobalVariable *Symbol = nullptr;
public:
bool GloballyCoherent = false;
ResourceCounterDirection CounterDirection = ResourceCounterDirection::Unknown;
ResourceInfo(uint32_t RecordID, uint32_t Space, uint32_t LowerBound,
uint32_t Size, TargetExtType *HandleTy,
GlobalVariable *Symbol = nullptr)
: Binding{RecordID, Space, LowerBound, Size}, HandleTy(HandleTy),
Symbol(Symbol) {}
void setBindingID(unsigned ID) { Binding.RecordID = ID; }
bool hasCounter() const {
return CounterDirection != ResourceCounterDirection::Unknown;
}
const ResourceBinding &getBinding() const { return Binding; }
TargetExtType *getHandleTy() const { return HandleTy; }
const StringRef getName() const { return Symbol ? Symbol->getName() : ""; }
bool hasSymbol() const { return Symbol; }
GlobalVariable *createSymbol(Module &M, StructType *Ty, StringRef Name = "");
MDTuple *getAsMetadata(Module &M, dxil::ResourceTypeInfo &RTI) const;
std::pair<uint32_t, uint32_t>
getAnnotateProps(Module &M, dxil::ResourceTypeInfo &RTI) const;
bool operator==(const ResourceInfo &RHS) const {
return std::tie(Binding, HandleTy, Symbol) ==
std::tie(RHS.Binding, RHS.HandleTy, RHS.Symbol);
}
bool operator!=(const ResourceInfo &RHS) const { return !(*this == RHS); }
bool operator<(const ResourceInfo &RHS) const {
return Binding < RHS.Binding;
}
void print(raw_ostream &OS, dxil::ResourceTypeInfo &RTI,
const DataLayout &DL) const;
};
} // namespace dxil
//===----------------------------------------------------------------------===//
class DXILResourceTypeMap {
DenseMap<TargetExtType *, dxil::ResourceTypeInfo> Infos;
public:
bool invalidate(Module &M, const PreservedAnalyses &PA,
ModuleAnalysisManager::Invalidator &Inv);
dxil::ResourceTypeInfo &operator[](TargetExtType *Ty) {
auto It = Infos.find(Ty);
if (It != Infos.end())
return It->second;
auto [NewIt, Inserted] = Infos.try_emplace(Ty, Ty);
return NewIt->second;
}
};
class DXILResourceTypeAnalysis
: public AnalysisInfoMixin<DXILResourceTypeAnalysis> {
friend AnalysisInfoMixin<DXILResourceTypeAnalysis>;
static AnalysisKey Key;
public:
using Result = DXILResourceTypeMap;
DXILResourceTypeMap run(Module &M, ModuleAnalysisManager &AM) {
// Running the pass just generates an empty map, which will be filled when
// users of the pass query the results.
return Result();
}
};
class DXILResourceTypeWrapperPass : public ImmutablePass {
DXILResourceTypeMap DRTM;
virtual void anchor();
public:
static char ID;
DXILResourceTypeWrapperPass();
DXILResourceTypeMap &getResourceTypeMap() { return DRTM; }
const DXILResourceTypeMap &getResourceTypeMap() const { return DRTM; }
};
ModulePass *createDXILResourceTypeWrapperPassPass();
//===----------------------------------------------------------------------===//
class DXILResourceMap {
SmallVector<dxil::ResourceInfo> Infos;
DenseMap<CallInst *, unsigned> CallMap;
unsigned FirstUAV = 0;
unsigned FirstCBuffer = 0;
unsigned FirstSampler = 0;
/// Populate the map given the resource binding calls in the given module.
void populate(Module &M, DXILResourceTypeMap &DRTM);
public:
using iterator = SmallVector<dxil::ResourceInfo>::iterator;
using const_iterator = SmallVector<dxil::ResourceInfo>::const_iterator;
iterator begin() { return Infos.begin(); }
const_iterator begin() const { return Infos.begin(); }
iterator end() { return Infos.end(); }
const_iterator end() const { return Infos.end(); }
bool empty() const { return Infos.empty(); }
iterator find(const CallInst *Key) {
auto Pos = CallMap.find(Key);
return Pos == CallMap.end() ? Infos.end() : (Infos.begin() + Pos->second);
}
/// Resolves a resource handle into a vector of ResourceInfos that
/// represent the possible unique creations of the handle. Certain cases are
/// ambiguous so multiple creation instructions may be returned. The resulting
/// ResourceInfo can be used to depuplicate unique handles that
/// reference the same resource
SmallVector<dxil::ResourceInfo> findByUse(const Value *Key) const;
const_iterator find(const CallInst *Key) const {
auto Pos = CallMap.find(Key);
return Pos == CallMap.end() ? Infos.end() : (Infos.begin() + Pos->second);
}
iterator srv_begin() { return begin(); }
const_iterator srv_begin() const { return begin(); }
iterator srv_end() { return begin() + FirstUAV; }
const_iterator srv_end() const { return begin() + FirstUAV; }
iterator_range<iterator> srvs() { return make_range(srv_begin(), srv_end()); }
iterator_range<const_iterator> srvs() const {
return make_range(srv_begin(), srv_end());
}
iterator uav_begin() { return begin() + FirstUAV; }
const_iterator uav_begin() const { return begin() + FirstUAV; }
iterator uav_end() { return begin() + FirstCBuffer; }
const_iterator uav_end() const { return begin() + FirstCBuffer; }
iterator_range<iterator> uavs() { return make_range(uav_begin(), uav_end()); }
iterator_range<const_iterator> uavs() const {
return make_range(uav_begin(), uav_end());
}
iterator cbuffer_begin() { return begin() + FirstCBuffer; }
const_iterator cbuffer_begin() const { return begin() + FirstCBuffer; }
iterator cbuffer_end() { return begin() + FirstSampler; }
const_iterator cbuffer_end() const { return begin() + FirstSampler; }
iterator_range<iterator> cbuffers() {
return make_range(cbuffer_begin(), cbuffer_end());
}
iterator_range<const_iterator> cbuffers() const {
return make_range(cbuffer_begin(), cbuffer_end());
}
iterator sampler_begin() { return begin() + FirstSampler; }
const_iterator sampler_begin() const { return begin() + FirstSampler; }
iterator sampler_end() { return end(); }
const_iterator sampler_end() const { return end(); }
iterator_range<iterator> samplers() {
return make_range(sampler_begin(), sampler_end());
}
iterator_range<const_iterator> samplers() const {
return make_range(sampler_begin(), sampler_end());
}
void print(raw_ostream &OS, DXILResourceTypeMap &DRTM,
const DataLayout &DL) const;
friend class DXILResourceAnalysis;
friend class DXILResourceWrapperPass;
};
class DXILResourceAnalysis : public AnalysisInfoMixin<DXILResourceAnalysis> {
friend AnalysisInfoMixin<DXILResourceAnalysis>;
static AnalysisKey Key;
public:
using Result = DXILResourceMap;
/// Gather resource info for the module \c M.
DXILResourceMap run(Module &M, ModuleAnalysisManager &AM);
};
/// Printer pass for the \c DXILResourceAnalysis results.
class DXILResourcePrinterPass : public PassInfoMixin<DXILResourcePrinterPass> {
raw_ostream &OS;
public:
explicit DXILResourcePrinterPass(raw_ostream &OS) : OS(OS) {}
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
static bool isRequired() { return true; }
};
class DXILResourceWrapperPass : public ModulePass {
std::unique_ptr<DXILResourceMap> Map;
DXILResourceTypeMap *DRTM;
public:
static char ID; // Class identification, replacement for typeinfo
DXILResourceWrapperPass();
~DXILResourceWrapperPass() override;
const DXILResourceMap &getBindingMap() const { return *Map; }
DXILResourceMap &getBindingMap() { return *Map; }
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnModule(Module &M) override;
void releaseMemory() override;
void print(raw_ostream &OS, const Module *M) const override;
void dump() const;
};
ModulePass *createDXILResourceWrapperPassPass();
} // namespace llvm
#endif // LLVM_ANALYSIS_DXILRESOURCE_H