Skip to content

Commit c66f401

Browse files
authored
[DirectX] Implement DXILResourceBindingAnalysis (#137258)
`DXILResourceBindingAnalysis` analyses explicit resource bindings in the module and puts together lists of used virtual register spaces and available virtual register slot ranges for each binding type. It also stores additional information found during the analysis such as whether the module uses implicit bindings or if any of the bindings overlap. This information will be used in `DXILResourceImplicitBindings` pass (coming soon) to assign register slots to resources with implicit bindings, and in a post-optimization validation pass that will raise diagnostic about overlapping bindings. Part 1/2 of #136786
1 parent 72c3ed6 commit c66f401

File tree

8 files changed

+588
-2
lines changed

8 files changed

+588
-2
lines changed

llvm/include/llvm/Analysis/DXILResource.h

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
#define LLVM_ANALYSIS_DXILRESOURCE_H
1111

1212
#include "llvm/ADT/MapVector.h"
13+
#include "llvm/ADT/SmallVector.h"
1314
#include "llvm/ADT/StringRef.h"
1415
#include "llvm/IR/DerivedTypes.h"
1516
#include "llvm/IR/GlobalVariable.h"
1617
#include "llvm/IR/PassManager.h"
1718
#include "llvm/Pass.h"
1819
#include "llvm/Support/Alignment.h"
1920
#include "llvm/Support/DXILABI.h"
21+
#include <climits>
22+
#include <cstdint>
2023

2124
namespace llvm {
2225
class CallInst;
@@ -586,6 +589,125 @@ class DXILResourceWrapperPass : public ModulePass {
586589

587590
ModulePass *createDXILResourceWrapperPassPass();
588591

592+
//===----------------------------------------------------------------------===//
593+
594+
// DXILResourceBindingInfo stores the results of DXILResourceBindingAnalysis
595+
// which analyses all llvm.dx.resource.handlefrombinding calls in the module
596+
// and puts together lists of used virtual register spaces and available
597+
// virtual register slot ranges for each binding type.
598+
// It also stores additional information found during the analysis such as
599+
// whether the module uses implicit bindings or if any of the bindings overlap.
600+
//
601+
// This information will be used in DXILResourceImplicitBindings pass to assign
602+
// register slots to resources with implicit bindings, and in a
603+
// post-optimization validation pass that will raise diagnostic about
604+
// overlapping bindings.
605+
//
606+
// For example for these resource bindings:
607+
//
608+
// RWBuffer<float> A[10] : register(u3);
609+
// RWBuffer<float> B[] : register(u5, space2)
610+
//
611+
// The analysis result for UAV binding type will look like this:
612+
//
613+
// UAVSpaces {
614+
// ResClass = ResourceClass::UAV,
615+
// Spaces = {
616+
// { Space = 0, FreeRanges = {{ 0, 2 }, { 13, UINT32_MAX }} },
617+
// { Space = 2, FreeRanges = {{ 0, 4 }} }
618+
// }
619+
// }
620+
//
621+
class DXILResourceBindingInfo {
622+
public:
623+
struct BindingRange {
624+
uint32_t LowerBound;
625+
uint32_t UpperBound;
626+
BindingRange(uint32_t LB, uint32_t UB) : LowerBound(LB), UpperBound(UB) {}
627+
};
628+
629+
struct RegisterSpace {
630+
uint32_t Space;
631+
SmallVector<BindingRange> FreeRanges;
632+
RegisterSpace(uint32_t Space) : Space(Space) {
633+
FreeRanges.emplace_back(0, UINT32_MAX);
634+
}
635+
};
636+
637+
struct BindingSpaces {
638+
dxil::ResourceClass RC;
639+
llvm::SmallVector<RegisterSpace> Spaces;
640+
BindingSpaces(dxil::ResourceClass RC) : RC(RC) {}
641+
};
642+
643+
private:
644+
BindingSpaces SRVSpaces, UAVSpaces, CBufferSpaces, SamplerSpaces;
645+
bool ImplicitBinding;
646+
bool OverlappingBinding;
647+
648+
// Populate the resource binding info given explicit resource binding calls
649+
// in the module.
650+
void populate(Module &M, DXILResourceTypeMap &DRTM);
651+
652+
public:
653+
DXILResourceBindingInfo()
654+
: SRVSpaces(dxil::ResourceClass::SRV),
655+
UAVSpaces(dxil::ResourceClass::UAV),
656+
CBufferSpaces(dxil::ResourceClass::CBuffer),
657+
SamplerSpaces(dxil::ResourceClass::Sampler), ImplicitBinding(false),
658+
OverlappingBinding(false) {}
659+
660+
bool hasImplicitBinding() const { return ImplicitBinding; }
661+
bool hasOverlappingBinding() const { return OverlappingBinding; }
662+
663+
BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
664+
switch (RC) {
665+
case dxil::ResourceClass::SRV:
666+
return SRVSpaces;
667+
case dxil::ResourceClass::UAV:
668+
return UAVSpaces;
669+
case dxil::ResourceClass::CBuffer:
670+
return CBufferSpaces;
671+
case dxil::ResourceClass::Sampler:
672+
return SamplerSpaces;
673+
}
674+
}
675+
676+
friend class DXILResourceBindingAnalysis;
677+
friend class DXILResourceBindingWrapperPass;
678+
};
679+
680+
class DXILResourceBindingAnalysis
681+
: public AnalysisInfoMixin<DXILResourceBindingAnalysis> {
682+
friend AnalysisInfoMixin<DXILResourceBindingAnalysis>;
683+
684+
static AnalysisKey Key;
685+
686+
public:
687+
using Result = DXILResourceBindingInfo;
688+
689+
DXILResourceBindingInfo run(Module &M, ModuleAnalysisManager &AM);
690+
};
691+
692+
class DXILResourceBindingWrapperPass : public ModulePass {
693+
std::unique_ptr<DXILResourceBindingInfo> BindingInfo;
694+
695+
public:
696+
static char ID;
697+
698+
DXILResourceBindingWrapperPass();
699+
~DXILResourceBindingWrapperPass() override;
700+
701+
DXILResourceBindingInfo &getBindingInfo() { return *BindingInfo; }
702+
const DXILResourceBindingInfo &getBindingInfo() const { return *BindingInfo; }
703+
704+
void getAnalysisUsage(AnalysisUsage &AU) const override;
705+
bool runOnModule(Module &M) override;
706+
void releaseMemory() override;
707+
};
708+
709+
ModulePass *createDXILResourceBindingWrapperPassPass();
710+
589711
} // namespace llvm
590712

591713
#endif // LLVM_ANALYSIS_DXILRESOURCE_H

llvm/include/llvm/IR/IntrinsicsDirectX.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ def int_dx_resource_handlefrombinding
2727
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
2828
[IntrNoMem]>;
2929

30+
// Create resource handle with implicit binding in given register space.
31+
// Returns a `target("dx.")` type appropriate for the kind of resource and
32+
// the range size and index of the binding.
33+
def int_dx_resource_handlefromimplicitbinding
34+
: DefaultAttrsIntrinsic<
35+
[llvm_any_ty],
36+
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
37+
[IntrNoMem]>;
38+
3039
def int_dx_resource_getpointer
3140
: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_i32_ty],
3241
[IntrNoMem]>;

llvm/include/llvm/InitializePasses.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ void initializeDAHPass(PassRegistry &);
8484
void initializeDCELegacyPassPass(PassRegistry &);
8585
void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &);
8686
void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &);
87-
void initializeDXILResourceWrapperPassPass(PassRegistry &);
87+
void initializeDXILResourceBindingWrapperPassPass(PassRegistry &);
8888
void initializeDXILResourceTypeWrapperPassPass(PassRegistry &);
89+
void initializeDXILResourceWrapperPassPass(PassRegistry &);
8990
void initializeDeadMachineInstructionElimPass(PassRegistry &);
9091
void initializeDebugifyMachineModulePass(PassRegistry &);
9192
void initializeDependenceAnalysisWrapperPassPass(PassRegistry &);

llvm/lib/Analysis/Analysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ void llvm::initializeAnalysis(PassRegistry &Registry) {
2727
initializeCallGraphViewerPass(Registry);
2828
initializeCycleInfoWrapperPassPass(Registry);
2929
initializeDXILMetadataAnalysisWrapperPassPass(Registry);
30-
initializeDXILResourceWrapperPassPass(Registry);
30+
initializeDXILResourceBindingWrapperPassPass(Registry);
3131
initializeDXILResourceTypeWrapperPassPass(Registry);
32+
initializeDXILResourceWrapperPassPass(Registry);
3233
initializeDependenceAnalysisWrapperPassPass(Registry);
3334
initializeDominanceFrontierWrapperPassPass(Registry);
3435
initializeDomViewerWrapperPassPass(Registry);

llvm/lib/Analysis/DXILResource.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88

99
#include "llvm/Analysis/DXILResource.h"
1010
#include "llvm/ADT/APInt.h"
11+
#include "llvm/ADT/STLExtras.h"
1112
#include "llvm/ADT/SmallString.h"
13+
#include "llvm/ADT/SmallVector.h"
1214
#include "llvm/IR/Constants.h"
1315
#include "llvm/IR/DerivedTypes.h"
1416
#include "llvm/IR/DiagnosticInfo.h"
@@ -19,6 +21,8 @@
1921
#include "llvm/IR/Module.h"
2022
#include "llvm/InitializePasses.h"
2123
#include "llvm/Support/FormatVariadic.h"
24+
#include <climits>
25+
#include <cstdint>
2226

2327
#define DEBUG_TYPE "dxil-resource"
2428

@@ -879,8 +883,126 @@ SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
879883

880884
//===----------------------------------------------------------------------===//
881885

886+
void DXILResourceBindingInfo::populate(Module &M, DXILResourceTypeMap &DRTM) {
887+
struct Binding {
888+
ResourceClass RC;
889+
uint32_t Space;
890+
uint32_t LowerBound;
891+
uint32_t UpperBound;
892+
Binding(ResourceClass RC, uint32_t Space, uint32_t LowerBound,
893+
uint32_t UpperBound)
894+
: RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound) {
895+
}
896+
};
897+
SmallVector<Binding> Bindings;
898+
899+
// collect all of the llvm.dx.resource.handlefrombinding calls;
900+
// make a note if there is llvm.dx.resource.handlefromimplicitbinding
901+
for (Function &F : M.functions()) {
902+
if (!F.isDeclaration())
903+
continue;
904+
905+
switch (F.getIntrinsicID()) {
906+
default:
907+
continue;
908+
case Intrinsic::dx_resource_handlefrombinding: {
909+
auto *HandleTy = cast<TargetExtType>(F.getReturnType());
910+
ResourceTypeInfo &RTI = DRTM[HandleTy];
911+
912+
for (User *U : F.users())
913+
if (CallInst *CI = dyn_cast<CallInst>(U)) {
914+
uint32_t Space =
915+
cast<ConstantInt>(CI->getArgOperand(0))->getZExtValue();
916+
uint32_t LowerBound =
917+
cast<ConstantInt>(CI->getArgOperand(1))->getZExtValue();
918+
int32_t Size =
919+
cast<ConstantInt>(CI->getArgOperand(2))->getZExtValue();
920+
921+
// negative size means unbounded resource array;
922+
// upper bound register overflow should be detected in Sema
923+
assert((Size < 0 || (unsigned)LowerBound + Size - 1 <= UINT32_MAX) &&
924+
"upper bound register overflow");
925+
uint32_t UpperBound = Size < 0 ? UINT32_MAX : LowerBound + Size - 1;
926+
Bindings.emplace_back(RTI.getResourceClass(), Space, LowerBound,
927+
UpperBound);
928+
}
929+
break;
930+
}
931+
case Intrinsic::dx_resource_handlefromimplicitbinding: {
932+
ImplicitBinding = true;
933+
break;
934+
}
935+
}
936+
}
937+
938+
// sort all the collected bindings
939+
llvm::stable_sort(Bindings, [](auto &LHS, auto &RHS) {
940+
return std::tie(LHS.RC, LHS.Space, LHS.LowerBound) <
941+
std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
942+
});
943+
944+
// remove duplicates
945+
Binding *NewEnd = llvm::unique(Bindings, [](auto &LHS, auto &RHS) {
946+
return std::tie(LHS.RC, LHS.Space, LHS.LowerBound, LHS.UpperBound) ==
947+
std::tie(RHS.RC, RHS.Space, RHS.LowerBound, RHS.UpperBound);
948+
});
949+
if (NewEnd != Bindings.end())
950+
Bindings.erase(NewEnd);
951+
952+
// Go over the sorted bindings and build up lists of free register ranges
953+
// for each binding type and used spaces. Bindings are sorted by resource
954+
// class, space, and lower bound register slot.
955+
BindingSpaces *BS = &SRVSpaces;
956+
for (unsigned I = 0, E = Bindings.size(); I != E; ++I) {
957+
Binding &B = Bindings[I];
958+
959+
if (BS->RC != B.RC)
960+
// move to the next resource class spaces
961+
BS = &getBindingSpaces(B.RC);
962+
963+
RegisterSpace *S = BS->Spaces.empty() ? &BS->Spaces.emplace_back(B.Space)
964+
: &BS->Spaces.back();
965+
assert(S->Space <= B.Space && "bindings not sorted correctly?");
966+
if (B.Space != S->Space)
967+
// add new space
968+
S = &BS->Spaces.emplace_back(B.Space);
969+
970+
// the space is full - set flag to report overlapping binding later
971+
if (S->FreeRanges.empty()) {
972+
OverlappingBinding = true;
973+
continue;
974+
}
975+
976+
// adjust the last free range lower bound, split it in two, or remove it
977+
BindingRange &LastFreeRange = S->FreeRanges.back();
978+
assert(LastFreeRange.UpperBound == UINT32_MAX);
979+
if (LastFreeRange.LowerBound == B.LowerBound) {
980+
if (B.UpperBound < UINT32_MAX)
981+
LastFreeRange.LowerBound = B.UpperBound + 1;
982+
else
983+
S->FreeRanges.pop_back();
984+
} else if (LastFreeRange.LowerBound < B.LowerBound) {
985+
LastFreeRange.UpperBound = B.LowerBound - 1;
986+
if (B.UpperBound < UINT32_MAX)
987+
S->FreeRanges.emplace_back(B.UpperBound + 1, UINT32_MAX);
988+
} else {
989+
// FIXME: This only detects overlapping bindings that are not an exact
990+
// match (llvm/llvm-project#110723)
991+
OverlappingBinding = true;
992+
if (B.UpperBound < UINT32_MAX)
993+
LastFreeRange.LowerBound =
994+
std::max(LastFreeRange.LowerBound, B.UpperBound + 1);
995+
else
996+
S->FreeRanges.pop_back();
997+
}
998+
}
999+
}
1000+
1001+
//===----------------------------------------------------------------------===//
1002+
8821003
AnalysisKey DXILResourceTypeAnalysis::Key;
8831004
AnalysisKey DXILResourceAnalysis::Key;
1005+
AnalysisKey DXILResourceBindingAnalysis::Key;
8841006

8851007
DXILResourceMap DXILResourceAnalysis::run(Module &M,
8861008
ModuleAnalysisManager &AM) {
@@ -890,6 +1012,14 @@ DXILResourceMap DXILResourceAnalysis::run(Module &M,
8901012
return Data;
8911013
}
8921014

1015+
DXILResourceBindingInfo
1016+
DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
1017+
DXILResourceBindingInfo Data;
1018+
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
1019+
Data.populate(M, DRTM);
1020+
return Data;
1021+
}
1022+
8931023
PreservedAnalyses DXILResourcePrinterPass::run(Module &M,
8941024
ModuleAnalysisManager &AM) {
8951025
DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);
@@ -952,3 +1082,33 @@ char DXILResourceWrapperPass::ID = 0;
9521082
ModulePass *llvm::createDXILResourceWrapperPassPass() {
9531083
return new DXILResourceWrapperPass();
9541084
}
1085+
1086+
DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass()
1087+
: ModulePass(ID) {}
1088+
1089+
DXILResourceBindingWrapperPass::~DXILResourceBindingWrapperPass() = default;
1090+
1091+
void DXILResourceBindingWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
1092+
AU.addRequiredTransitive<DXILResourceTypeWrapperPass>();
1093+
AU.setPreservesAll();
1094+
}
1095+
1096+
bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {
1097+
BindingInfo.reset(new DXILResourceBindingInfo());
1098+
1099+
DXILResourceTypeMap &DRTM =
1100+
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
1101+
BindingInfo->populate(M, DRTM);
1102+
1103+
return false;
1104+
}
1105+
1106+
void DXILResourceBindingWrapperPass::releaseMemory() { BindingInfo.reset(); }
1107+
1108+
INITIALIZE_PASS(DXILResourceBindingWrapperPass, "dxil-resource-binding",
1109+
"DXIL Resource Binding Analysis", false, true)
1110+
char DXILResourceBindingWrapperPass::ID = 0;
1111+
1112+
ModulePass *llvm::createDXILResourceBindingWrapperPassPass() {
1113+
return new DXILResourceWrapperPass();
1114+
}

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis())
2424
MODULE_ANALYSIS("dxil-metadata", DXILMetadataAnalysis())
2525
MODULE_ANALYSIS("dxil-resources", DXILResourceAnalysis())
2626
MODULE_ANALYSIS("dxil-resource-type", DXILResourceTypeAnalysis())
27+
MODULE_ANALYSIS("dxil-resource-bindings", DXILResourceBindingAnalysis())
2728
MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis())
2829
MODULE_ANALYSIS("ir-similarity", IRSimilarityAnalysis())
2930
MODULE_ANALYSIS("last-run-tracking", LastRunTrackingAnalysis())

llvm/unittests/Target/DirectX/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ add_llvm_target_unittest(DirectXTests
2222
PointerTypeAnalysisTests.cpp
2323
UniqueResourceFromUseTests.cpp
2424
RegisterCostTests.cpp
25+
ResourceBindingAnalysisTests.cpp
2526
)

0 commit comments

Comments
 (0)