Skip to content

Commit d5685ec

Browse files
committed
[HLSL] Implement DXILResourceBindingAnalysis
DXILResourceBindingAnalysis analyses all 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 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 63f5c6a commit d5685ec

File tree

6 files changed

+509
-0
lines changed

6 files changed

+509
-0
lines changed

llvm/include/llvm/Analysis/DXILResource.h

Lines changed: 105 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,108 @@ class DXILResourceWrapperPass : public ModulePass {
586589

587590
ModulePass *createDXILResourceWrapperPassPass();
588591

592+
//===----------------------------------------------------------------------===//
593+
594+
// DXILResourceBindingsInfo 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 DXILResourceBindingsInfo {
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 ResClass;
639+
llvm::SmallVector<RegisterSpace> Spaces;
640+
BindingSpaces(dxil::ResourceClass ResClass) : ResClass(ResClass) {
641+
// initialize space0
642+
Spaces.emplace_back(0);
643+
}
644+
};
645+
646+
private:
647+
BindingSpaces SRVSpaces, UAVSpaces, CBufferSpaces, SamplerSpaces;
648+
bool ImplicitBinding;
649+
bool OverlappingBinding;
650+
651+
// Populate the resource binding info given explicit resource binding calls
652+
// in the module.
653+
void populate(Module &M, DXILResourceTypeMap &DRTM);
654+
655+
public:
656+
DXILResourceBindingsInfo()
657+
: SRVSpaces(dxil::ResourceClass::SRV),
658+
UAVSpaces(dxil::ResourceClass::UAV),
659+
CBufferSpaces(dxil::ResourceClass::CBuffer),
660+
SamplerSpaces(dxil::ResourceClass::Sampler), ImplicitBinding(false),
661+
OverlappingBinding(false) {}
662+
663+
bool containsImplicitBinding() const { return ImplicitBinding; }
664+
bool containsOverlappingBinding() const { return OverlappingBinding; }
665+
666+
BindingSpaces &getBindingSpaces(dxil::ResourceClass RC) {
667+
switch (RC) {
668+
case dxil::ResourceClass::SRV:
669+
return SRVSpaces;
670+
case dxil::ResourceClass::UAV:
671+
return UAVSpaces;
672+
case dxil::ResourceClass::CBuffer:
673+
return CBufferSpaces;
674+
case dxil::ResourceClass::Sampler:
675+
return SamplerSpaces;
676+
}
677+
}
678+
679+
friend class DXILResourceBindingAnalysis;
680+
};
681+
682+
class DXILResourceBindingAnalysis
683+
: public AnalysisInfoMixin<DXILResourceBindingAnalysis> {
684+
friend AnalysisInfoMixin<DXILResourceBindingAnalysis>;
685+
686+
static AnalysisKey Key;
687+
688+
public:
689+
using Result = DXILResourceBindingsInfo;
690+
691+
DXILResourceBindingsInfo run(Module &M, ModuleAnalysisManager &AM);
692+
};
693+
589694
} // namespace llvm
590695

591696
#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],
37+
[IntrNoMem]>;
38+
3039
def int_dx_resource_getpointer
3140
: DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_i32_ty],
3241
[IntrNoMem]>;

llvm/lib/Analysis/DXILResource.cpp

Lines changed: 125 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,121 @@ SmallVector<dxil::ResourceInfo *> DXILResourceMap::findByUse(const Value *Key) {
879883

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

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

8851002
DXILResourceMap DXILResourceAnalysis::run(Module &M,
8861003
ModuleAnalysisManager &AM) {
@@ -890,6 +1007,14 @@ DXILResourceMap DXILResourceAnalysis::run(Module &M,
8901007
return Data;
8911008
}
8921009

1010+
DXILResourceBindingsInfo
1011+
DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
1012+
DXILResourceBindingsInfo Data;
1013+
DXILResourceTypeMap &DRTM = AM.getResult<DXILResourceTypeAnalysis>(M);
1014+
Data.populate(M, DRTM);
1015+
return Data;
1016+
}
1017+
8931018
PreservedAnalyses DXILResourcePrinterPass::run(Module &M,
8941019
ModuleAnalysisManager &AM) {
8951020
DXILResourceMap &DRM = AM.getResult<DXILResourceAnalysis>(M);

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)