22#include "llvm/IR/IntrinsicsDirectX.h"
29#define DEBUG_TYPE "dxil-resource-access"
42 for (
auto *Handle : Handles) {
43 std::string HandleStr;
45 Handle->print(HandleOS);
50 "Resource access is not guaranteed to map to a unique global resource"));
59 assert(
II->getIntrinsicID() == Intrinsic::dx_resource_getpointer &&
60 "Resource access through unexpected intrinsic");
61 return Offset ?
Offset : ConstantInt::get(Builder.getInt32Ty(), 0);
65 assert(
GEP &&
"Resource access through unexpected instruction");
67 unsigned NumIndices =
GEP->getNumIndices();
68 uint64_t IndexScale =
DL.getTypeAllocSize(
GEP->getSourceElementType());
69 APInt ConstantOffset(
DL.getIndexTypeSizeInBits(
GEP->getType()), 0);
71 if (
GEP->accumulateConstantOffset(
DL, ConstantOffset)) {
74 ConstantInt::get(
DL.getIndexType(
GEP->getType()), ConstantOffset);
76 }
else if (NumIndices == 1) {
79 GEPOffset = *
GEP->idx_begin();
80 }
else if (NumIndices == 2) {
82 auto *IndexIt =
GEP->idx_begin();
84 "GEP is not indexing through pointer");
85 GEPOffset = *(++IndexIt);
90 if (!(IndexScale % ElemSize)) {
93 IndexScale /= ElemSize;
97 GEPOffset = Builder.CreateMul(
98 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), IndexScale));
100 GEPOffset = Builder.CreateUDiv(
101 GEPOffset, ConstantInt::get(Builder.getInt32Ty(), ElemSize));
104 Ptr =
GEP->getPointerOperand();
118 Value *V =
SI->getValueOperand();
119 if (V->getType() == ContainedType) {
122 "Store of whole element has mismatched address to store to");
123 }
else if (V->getType() == ScalarType) {
126 auto *Load = Builder.CreateIntrinsic(
127 LoadType, Intrinsic::dx_resource_load_typedbuffer,
128 {
II->getOperand(0),
II->getOperand(1)});
129 auto *Struct = Builder.CreateExtractValue(Load, {0});
131 uint64_t AccessSize =
DL.getTypeSizeInBits(ScalarType) / 8;
134 V = Builder.CreateInsertElement(Struct, V,
Offset);
139 auto *Inst = Builder.CreateIntrinsic(
140 Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
141 {II->getOperand(0), II->getOperand(1), V});
142 SI->replaceAllUsesWith(Inst);
150 Value *V =
SI->getValueOperand();
151 assert(!V->getType()->isAggregateType() &&
152 "Resource store should be scalar or vector type");
154 Value *Index =
II->getOperand(1);
164 if (!ConstantOffset || !ConstantOffset->isZero())
165 Index = Builder.CreateAdd(Index,
Offset);
169 auto *Inst = Builder.CreateIntrinsic(Builder.getVoidTy(),
170 Intrinsic::dx_resource_store_rawbuffer,
171 {II->getOperand(0), Index, Offset, V});
172 SI->replaceAllUsesWith(Inst);
215 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
216 {
II->getOperand(0),
II->getOperand(1)});
217 V = Builder.CreateExtractValue(V, {0});
220 uint64_t AccessSize =
DL.getTypeSizeInBits(ScalarType) / 8;
224 if (!ConstantOffset || !ConstantOffset->isZero())
225 V = Builder.CreateExtractElement(V,
Offset);
232 Builder.getInt32(0));
244 "Resource load should be scalar or vector type");
246 Value *Index =
II->getOperand(1);
256 if (!ConstantOffset || !ConstantOffset->isZero())
257 Index = Builder.CreateAdd(Index,
Offset);
262 Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,
263 {
II->getOperand(0), Index,
Offset});
264 V = Builder.CreateExtractValue(V, {0});
271struct CBufferRowIntrin {
274 unsigned int EltSize;
275 unsigned int NumElts;
277 CBufferRowIntrin(
const DataLayout &
DL,
Type *Ty) {
280 switch (
DL.getTypeSizeInBits(Ty)) {
282 IID = Intrinsic::dx_resource_load_cbufferrow_8;
288 IID = Intrinsic::dx_resource_load_cbufferrow_4;
294 IID = Intrinsic::dx_resource_load_cbufferrow_2;
312 CBufferRowIntrin Intrin(
DL, Ty->getScalarType());
315 Value *Handle =
II->getOperand(0);
320 assert(GlobalOffset &&
"CBuffer getpointer index must be constant");
323 Value *CurrentRow = ConstantInt::get(
325 unsigned int CurrentIndex =
335 "Unexpected indirect access to resource without GEP");
339 CurrentRow = Builder.CreateAdd(GEPOffset, CurrentRow);
341 APInt ConstantOffset(
DL.getIndexTypeSizeInBits(LastGEP->getType()), 0);
342 if (LastGEP->accumulateConstantOffset(
DL, ConstantOffset)) {
343 APInt Remainder(
DL.getIndexTypeSizeInBits(LastGEP->getType()),
345 APInt::udivrem(ConstantOffset, Remainder, ConstantOffset, Remainder);
346 CurrentRow = Builder.CreateAdd(
347 CurrentRow, ConstantInt::get(Builder.getInt32Ty(), ConstantOffset));
350 assert(LastGEP->getNumIndices() == 1 &&
351 "Last GEP of cbuffer access is not array or struct access");
357 ? *LastGEP->idx_begin()
358 : Builder.CreateAdd(CurrentRow, *LastGEP->idx_begin());
363 auto *CBufLoad = Builder.CreateIntrinsic(
364 Intrin.RetTy, Intrin.IID, {Handle, CurrentRow},
nullptr, Name +
".load");
366 Builder.CreateExtractValue(CBufLoad, {CurrentIndex++}, Name +
".extract");
370 unsigned int Remaining =
371 ((
DL.getTypeSizeInBits(Ty) / 8) / Intrin.EltSize) - 1;
372 if (Remaining == 0) {
378 assert(VT->getNumElements() == 1 &&
"Can't have multiple elements here");
380 Builder.getInt32(0), Name);
388 while (Remaining--) {
389 CurrentIndex %= Intrin.NumElts;
391 if (CurrentIndex == 0) {
392 CurrentRow = Builder.CreateAdd(CurrentRow,
393 ConstantInt::get(Builder.getInt32Ty(), 1));
394 CBufLoad = Builder.CreateIntrinsic(Intrin.RetTy, Intrin.IID,
395 {Handle, CurrentRow},
nullptr,
399 Extracts.
push_back(Builder.CreateExtractValue(CBufLoad, {CurrentIndex++},
405 for (
int I = 0,
E = Extracts.
size();
I <
E; ++
I)
406 Result = Builder.CreateInsertElement(
407 Result, Extracts[
I], Builder.getInt32(
I), Name +
formatv(
".upto{}",
I));
454 Intrinsic::dx_resource_handlefrombinding,
455 Intrinsic::dx_resource_handlefromimplicitbinding,
462 while (!Worklist.
empty()) {
465 if (!
X->getType()->isPointerTy() && !
X->getType()->isTargetExtTy())
469 for (
Use &V : Phi->incoming_values())
477 if (IID == Intrinsic::dx_resource_getpointer)
491 "Only expects a Handle as determined from collectUsedHandles.");
499 uint32_t UpperBound =
Size == UINT32_MAX ? UINT32_MAX : LowerBound +
Size - 1;
501 return hlsl::Binding(Class, Space, LowerBound, UpperBound,
nullptr);
506struct AccessIndices {
510 bool hasGetPtrIdx() {
return GetPtrIdx !=
nullptr; }
511 bool hasHandleIdx() {
return HandleIdx !=
nullptr; }
525 return {
nullptr,
II->getArgOperand(3)};
528 if (
II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
531 assert(!AccessIdx.hasGetPtrIdx() &&
532 "Encountered multiple dx.resource.getpointers in ptr chain?");
533 AccessIdx.GetPtrIdx =
II->getArgOperand(1);
541 unsigned NumEdges = Phi->getNumIncomingValues();
542 assert(NumEdges != 0 &&
"Malformed Phi Node");
548 bool HasGetPtr =
true;
549 for (
unsigned Idx = 0; Idx < NumEdges; Idx++) {
550 auto *BB = Phi->getIncomingBlock(Idx);
553 HasGetPtr &= AccessIdx.hasGetPtrIdx();
560 Builder.Insert(GetPtrPhi);
564 Builder.Insert(HandlePhi);
567 return {GetPtrPhi, HandlePhi};
578 Value *GetPtrSelect =
nullptr;
580 if (TrueAccessIdx.hasGetPtrIdx() && FalseAccessIdx.hasGetPtrIdx())
582 Builder.CreateSelect(
Select->getCondition(), TrueAccessIdx.GetPtrIdx,
583 FalseAccessIdx.GetPtrIdx);
586 Builder.CreateSelect(
Select->getCondition(), TrueAccessIdx.HandleIdx,
587 FalseAccessIdx.HandleIdx);
589 return {GetPtrSelect, HandleSelect};
599 assert(AccessIdx.hasGetPtrIdx() && AccessIdx.hasHandleIdx() &&
600 "Couldn't retrieve indices. This is guaranteed by getAccessIndices");
605 Builder.Insert(Handle);
608 Builder.CreateIntrinsic(Ptr->
getType(), Intrinsic::dx_resource_getpointer,
609 {Handle, AccessIdx.GetPtrIdx});
631 unsigned NumHandles = Handles.
size();
635 bool SameGlobalBinding =
true;
637 for (
unsigned Idx = 1; Idx < NumHandles; Idx++)
641 if (!SameGlobalBinding) {
651 bool MadeChanges =
false;
654 if (
I->hasNUses(0)) {
655 I->eraseFromParent();
664 for (
User *U :
II->users())
668 while (!Worklist.
empty()) {
678 assert(
SI->getValueOperand() !=
II &&
"Pointer escaped!");
691 Dead->eraseFromParent();
692 II->eraseFromParent();
700 if (
II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
705 for (
auto &[
II, RI] : Resources)
708 return !Resources.
empty();
716 assert(DRTM &&
"DXILResourceTypeAnalysis must be available");
720 if (!(MadeHandleChanges || MadeResourceChanges))
734 getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
737 return MadeHandleChanges || MadeResourceChanges;
739 StringRef getPassName()
const override {
return "DXIL Resource Access"; }
740 DXILResourceAccessLegacy() : FunctionPass(
ID) {}
743 void getAnalysisUsage(llvm::AnalysisUsage &AU)
const override {
748char DXILResourceAccessLegacy::ID = 0;
752 "DXIL Resource Access",
false,
false)
758 return new DXILResourceAccessLegacy();
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Register Bank Select
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static void diagnoseNonUniqueResourceAccess(Instruction *I, ArrayRef< IntrinsicInst * > Handles)
static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void createRawStore(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static bool legalizeResourceHandles(Function &F, DXILResourceTypeMap &DRTM)
static AccessIndices getAccessIndices(Instruction *I, SmallSetVector< Instruction *, 16 > &DeadInsts)
static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static SmallVector< IntrinsicInst * > collectUsedHandles(Value *Ptr)
static const std::array< Intrinsic::ID, 2 > HandleIntrins
static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM)
static void replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle, SmallSetVector< Instruction *, 16 > &DeadInsts)
static void createRawLoad(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static Value * traverseGEPOffsets(const DataLayout &DL, IRBuilder<> &Builder, Value *Ptr, uint64_t AccessSize)
static hlsl::Binding getHandleIntrinsicBinding(IntrinsicInst *Handle, DXILResourceTypeMap &DRTM)
static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI, dxil::ResourceTypeInfo &RTI)
static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI, dxil::ResourceTypeInfo &RTI)
static Instruction * getStoreLoadPointerOperand(Instruction *AI)
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI)
static bool runOnFunction(Function &F, bool PostInlining)
uint64_t IntrinsicInst * II
FunctionAnalysisManager FAM
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
This file implements a set that has insertion order iteration characteristics.
static TableGen::Emitter::OptClass< SkeletonEmitter > X("gen-skeleton-class", "Generate example skeleton class")
Class for arbitrary precision integers.
LLVM_ABI APInt udiv(const APInt &RHS) const
Unsigned division operation.
static LLVM_ABI void udivrem(const APInt &LHS, const APInt &RHS, APInt &Quotient, APInt &Remainder)
Dual division/remainder interface.
uint64_t getZExtValue() const
Get zero extended value.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
LLVM Basic Block Representation.
Value * getArgOperand(unsigned i) const
void setArgOperand(unsigned i, Value *v)
This is the shared class of boolean and integer constants.
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)
A parsed version of the target data layout string in and methods for querying it.
Analysis pass which computes a DominatorTree.
FunctionPass class - This class is used to implement most global optimizations.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
LLVM_ABI Instruction * clone() const
Create a copy of 'this' instruction that is identical in all ways except the following:
LLVM_ABI const DataLayout & getDataLayout() const
Get the data layout of the module this instruction belongs to.
A wrapper class for inspecting calls to intrinsic functions.
Intrinsic::ID getIntrinsicID() const
Return the intrinsic ID of this intrinsic.
This is an important class for using LLVM in a threaded context.
An instruction for reading from memory.
Value * getPointerOperand()
void addIncoming(Value *V, BasicBlock *BB)
Add an incoming value to the end of the PHI list.
static PHINode * Create(Type *Ty, unsigned NumReservedValues, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
Constructors - NumReservedValues is a hint for the number of incoming edges that this phi node will h...
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
PreservedAnalyses & preserve()
Mark an analysis as preserved.
bool insert(const value_type &X)
Insert a new element into the SetVector.
A SetVector that performs no allocations if smaller than a certain size.
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
StringRef - Represent a constant reference to a string, i.e.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Type * getTypeParameter(unsigned i) const
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
The instances of the Type class are immutable: once they are created, they are never changed.
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
bool isAggregateType() const
Return true if the type is an aggregate type.
static LLVM_ABI IntegerType * getInt1Ty(LLVMContext &C)
A Use represents the edge between a Value definition and its users.
LLVM Value Representation.
Type * getType() const
All values are typed, get the type of this value.
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
TargetExtType * getHandleTy() const
LLVM_ABI bool isStruct() const
dxil::ResourceKind getResourceKind() const
A raw_ostream that writes to an std::string.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
@ RTAccelerationStructure
const unsigned CBufferRowSizeInBytes
This is an optimization pass for GlobalISel generic memory operations.
FunctionAddr VTableAddr Value
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
OuterAnalysisManagerProxy< ModuleAnalysisManager, Function > ModuleAnalysisManagerFunctionProxy
Provide the ModuleAnalysisManager to Function proxy.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
auto reverse(ContainerTy &&C)
FunctionPass * createDXILResourceAccessLegacyPass()
Pass to update resource accesses to use load/store directly.
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.