Skip to content

Commit 35d3e91

Browse files
joaosaffranjoaosaffranjoaosaffran-zz
authored andcommitted
Automerge: [DirectX] Validate registers are bound to root signature (#146785)
DXC checks if registers are correctly bound to root signature descriptors. This implements the same check. closes: #[126645](llvm/llvm-project#126645) --------- Co-authored-by: joaosaffran <[email protected]> Co-authored-by: Joao Saffran <[email protected]>
2 parents 957c24c + 36ebd17 commit 35d3e91

11 files changed

+237
-44
lines changed

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,15 +1159,14 @@ struct PerVisibilityBindingChecker {
11591159
bool HadOverlap = false;
11601160

11611161
using llvm::hlsl::BindingInfoBuilder;
1162-
auto ReportOverlap = [this, &HadOverlap](
1163-
const BindingInfoBuilder &Builder,
1164-
const BindingInfoBuilder::Binding &Reported) {
1162+
auto ReportOverlap = [this,
1163+
&HadOverlap](const BindingInfoBuilder &Builder,
1164+
const llvm::hlsl::Binding &Reported) {
11651165
HadOverlap = true;
11661166

11671167
const auto *Elem =
11681168
static_cast<const hlsl::RootSignatureElement *>(Reported.Cookie);
1169-
const BindingInfoBuilder::Binding &Previous =
1170-
Builder.findOverlapping(Reported);
1169+
const llvm::hlsl::Binding &Previous = Builder.findOverlapping(Reported);
11711170
const auto *PrevElem =
11721171
static_cast<const hlsl::RootSignatureElement *>(Previous.Cookie);
11731172

llvm/include/llvm/Frontend/HLSL/HLSLBinding.h

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_FRONTEND_HLSL_HLSLBINDING_H
1414
#define LLVM_FRONTEND_HLSL_HLSLBINDING_H
1515

16+
#include "llvm/ADT/STLExtras.h"
1617
#include "llvm/ADT/STLFunctionalExtras.h"
1718
#include "llvm/ADT/SmallVector.h"
1819
#include "llvm/Support/Compiler.h"
@@ -99,36 +100,55 @@ class BindingInfo {
99100
friend class BindingInfoBuilder;
100101
};
101102

102-
/// Builder class for creating a /c BindingInfo.
103-
class BindingInfoBuilder {
104-
public:
105-
struct Binding {
106-
dxil::ResourceClass RC;
107-
uint32_t Space;
108-
uint32_t LowerBound;
109-
uint32_t UpperBound;
110-
const void *Cookie;
103+
struct Binding {
104+
dxil::ResourceClass RC;
105+
uint32_t Space;
106+
uint32_t LowerBound;
107+
uint32_t UpperBound;
108+
const void *Cookie;
111109

112-
Binding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
113-
uint32_t UpperBound, const void *Cookie)
114-
: RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),
115-
Cookie(Cookie) {}
110+
Binding(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
111+
uint32_t UpperBound, const void *Cookie)
112+
: RC(RC), Space(Space), LowerBound(LowerBound), UpperBound(UpperBound),
113+
Cookie(Cookie) {}
116114

117-
bool isUnbounded() const { return UpperBound == ~0U; }
115+
bool isUnbounded() const { return UpperBound == ~0U; }
118116

119-
bool operator==(const Binding &RHS) const {
120-
return std::tie(RC, Space, LowerBound, UpperBound, Cookie) ==
121-
std::tie(RHS.RC, RHS.Space, RHS.LowerBound, RHS.UpperBound,
122-
RHS.Cookie);
123-
}
124-
bool operator!=(const Binding &RHS) const { return !(*this == RHS); }
117+
bool operator==(const Binding &RHS) const {
118+
return std::tie(RC, Space, LowerBound, UpperBound, Cookie) ==
119+
std::tie(RHS.RC, RHS.Space, RHS.LowerBound, RHS.UpperBound,
120+
RHS.Cookie);
121+
}
122+
bool operator!=(const Binding &RHS) const { return !(*this == RHS); }
125123

126-
bool operator<(const Binding &RHS) const {
127-
return std::tie(RC, Space, LowerBound) <
128-
std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
129-
}
130-
};
124+
bool operator<(const Binding &RHS) const {
125+
return std::tie(RC, Space, LowerBound) <
126+
std::tie(RHS.RC, RHS.Space, RHS.LowerBound);
127+
}
128+
};
129+
130+
class BoundRegs {
131+
SmallVector<Binding> Bindings;
131132

133+
public:
134+
BoundRegs(SmallVector<Binding> &&Bindings) : Bindings(std::move(Bindings)) {}
135+
136+
bool isBound(dxil::ResourceClass RC, uint32_t Space, uint32_t LowerBound,
137+
uint32_t UpperBound) const {
138+
// UpperBound and Cookie are given dummy values, since they aren't
139+
// interesting for operator<
140+
const Binding *It =
141+
llvm::upper_bound(Bindings, Binding{RC, Space, LowerBound, 0, nullptr});
142+
if (It == Bindings.begin())
143+
return false;
144+
--It;
145+
return It->RC == RC && It->Space == Space && It->LowerBound <= LowerBound &&
146+
It->UpperBound >= UpperBound;
147+
}
148+
};
149+
150+
/// Builder class for creating a /c BindingInfo.
151+
class BindingInfoBuilder {
132152
private:
133153
SmallVector<Binding> Bindings;
134154

@@ -152,6 +172,12 @@ class BindingInfoBuilder {
152172
[&HasOverlap](auto, auto) { HasOverlap = true; });
153173
}
154174

175+
LLVM_ABI BoundRegs takeBoundRegs() {
176+
assert(std::is_sorted(Bindings.begin(), Bindings.end()) &&
177+
"takeBoundRegs should only be called after calculateBindingInfo");
178+
return BoundRegs(std::move(Bindings));
179+
}
180+
155181
/// For use in the \c ReportOverlap callback of \c calculateBindingInfo -
156182
/// finds a binding that the \c ReportedBinding overlaps with.
157183
LLVM_ABI const Binding &findOverlapping(const Binding &ReportedBinding) const;

llvm/lib/Frontend/HLSL/HLSLBinding.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ BindingInfo BindingInfoBuilder::calculateBindingInfo(
131131
return Info;
132132
}
133133

134-
const BindingInfoBuilder::Binding &BindingInfoBuilder::findOverlapping(
135-
const BindingInfoBuilder::Binding &ReportedBinding) const {
136-
for (const BindingInfoBuilder::Binding &Other : Bindings)
134+
const Binding &
135+
BindingInfoBuilder::findOverlapping(const Binding &ReportedBinding) const {
136+
for (const Binding &Other : Bindings)
137137
if (ReportedBinding.LowerBound <= Other.UpperBound &&
138138
Other.LowerBound <= ReportedBinding.UpperBound)
139139
return Other;

llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,8 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
118118
"true, yet no overlapping binding was found");
119119
}
120120

121-
static void
122-
reportOverlappingRegisters(Module &M,
123-
const llvm::hlsl::BindingInfoBuilder::Binding &R1,
124-
const llvm::hlsl::BindingInfoBuilder::Binding &R2) {
121+
static void reportOverlappingRegisters(Module &M, const llvm::hlsl::Binding &R1,
122+
const llvm::hlsl::Binding &R2) {
125123
SmallString<128> Message;
126124

127125
raw_svector_ostream OS(Message);
@@ -133,6 +131,17 @@ reportOverlappingRegisters(Module &M,
133131
M.getContext().diagnose(DiagnosticInfoGeneric(Message));
134132
}
135133

134+
static void
135+
reportRegNotBound(Module &M, ResourceClass Class,
136+
const llvm::dxil::ResourceInfo::ResourceBinding &Unbound) {
137+
SmallString<128> Message;
138+
raw_svector_ostream OS(Message);
139+
OS << getResourceClassName(Class) << " register " << Unbound.LowerBound
140+
<< " in space " << Unbound.Space
141+
<< " does not have a binding in the Root Signature";
142+
M.getContext().diagnose(DiagnosticInfoGeneric(Message));
143+
}
144+
136145
static dxbc::ShaderVisibility
137146
tripleToVisibility(llvm::Triple::EnvironmentType ET) {
138147
switch (ET) {
@@ -157,7 +166,9 @@ tripleToVisibility(llvm::Triple::EnvironmentType ET) {
157166

158167
static void validateRootSignature(Module &M,
159168
const mcdxbc::RootSignatureDesc &RSD,
160-
dxil::ModuleMetadataInfo &MMI) {
169+
dxil::ModuleMetadataInfo &MMI,
170+
DXILResourceMap &DRM,
171+
DXILResourceTypeMap &DRTM) {
161172

162173
hlsl::BindingInfoBuilder Builder;
163174
dxbc::ShaderVisibility Visibility = tripleToVisibility(MMI.ShaderProfile);
@@ -216,11 +227,19 @@ static void validateRootSignature(Module &M,
216227

217228
Builder.calculateBindingInfo(
218229
[&M](const llvm::hlsl::BindingInfoBuilder &Builder,
219-
const llvm::hlsl::BindingInfoBuilder::Binding &ReportedBinding) {
220-
const llvm::hlsl::BindingInfoBuilder::Binding &Overlaping =
230+
const llvm::hlsl::Binding &ReportedBinding) {
231+
const llvm::hlsl::Binding &Overlaping =
221232
Builder.findOverlapping(ReportedBinding);
222233
reportOverlappingRegisters(M, ReportedBinding, Overlaping);
223234
});
235+
const hlsl::BoundRegs &BoundRegs = Builder.takeBoundRegs();
236+
for (const ResourceInfo &RI : DRM) {
237+
const ResourceInfo::ResourceBinding &Binding = RI.getBinding();
238+
ResourceClass RC = DRTM[RI.getHandleTy()].getResourceClass();
239+
if (!BoundRegs.isBound(RC, Binding.Space, Binding.LowerBound,
240+
Binding.LowerBound + Binding.Size - 1))
241+
reportRegNotBound(M, RC, Binding);
242+
}
224243
}
225244

226245
static mcdxbc::RootSignatureDesc *
@@ -234,7 +253,8 @@ getRootSignature(RootSignatureBindingInfo &RSBI,
234253
static void reportErrors(Module &M, DXILResourceMap &DRM,
235254
DXILResourceBindingInfo &DRBI,
236255
RootSignatureBindingInfo &RSBI,
237-
dxil::ModuleMetadataInfo &MMI) {
256+
dxil::ModuleMetadataInfo &MMI,
257+
DXILResourceTypeMap &DRTM) {
238258
if (DRM.hasInvalidCounterDirection())
239259
reportInvalidDirection(M, DRM);
240260

@@ -245,7 +265,7 @@ static void reportErrors(Module &M, DXILResourceMap &DRM,
245265
"DXILResourceImplicitBinding pass");
246266

247267
if (mcdxbc::RootSignatureDesc *RSD = getRootSignature(RSBI, MMI))
248-
validateRootSignature(M, *RSD, MMI);
268+
validateRootSignature(M, *RSD, MMI, DRM, DRTM);
249269
}
250270

251271
PreservedAnalyses
@@ -254,8 +274,9 @@ DXILPostOptimizationValidation::run(Module &M, ModuleAnalysisManager &MAM) {
254274
DXILResourceBindingInfo &DRBI = MAM.getResult<DXILResourceBindingAnalysis>(M);
255275
RootSignatureBindingInfo &RSBI = MAM.getResult<RootSignatureAnalysis>(M);
256276
ModuleMetadataInfo &MMI = MAM.getResult<DXILMetadataAnalysis>(M);
277+
DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(M);
257278

258-
reportErrors(M, DRM, DRBI, RSBI, MMI);
279+
reportErrors(M, DRM, DRBI, RSBI, MMI, DRTM);
259280
return PreservedAnalyses::all();
260281
}
261282

@@ -271,8 +292,10 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
271292
getAnalysis<RootSignatureAnalysisWrapper>().getRSInfo();
272293
dxil::ModuleMetadataInfo &MMI =
273294
getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
295+
DXILResourceTypeMap &DRTM =
296+
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
274297

275-
reportErrors(M, DRM, DRBI, RSBI, MMI);
298+
reportErrors(M, DRM, DRBI, RSBI, MMI, DRTM);
276299
return false;
277300
}
278301
StringRef getPassName() const override {
@@ -286,6 +309,7 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
286309
AU.addRequired<DXILResourceBindingWrapperPass>();
287310
AU.addRequired<DXILMetadataAnalysisWrapperPass>();
288311
AU.addRequired<RootSignatureAnalysisWrapper>();
312+
AU.addRequired<DXILResourceTypeWrapperPass>();
289313
AU.addPreserved<DXILResourceWrapperPass>();
290314
AU.addPreserved<DXILResourceBindingWrapperPass>();
291315
AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
@@ -303,6 +327,7 @@ INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
303327
INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
304328
INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass)
305329
INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper)
330+
INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
306331
INITIALIZE_PASS_END(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,
307332
"DXIL Post Optimization Validation", false, false)
308333

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
2+
; This is a valid code, it checks the limits of a binding space
3+
; CHECK-NOT: error:
4+
5+
%__cblayout_CB = type <{ float }>
6+
7+
@CB.str = private unnamed_addr constant [3 x i8] c"CB\00", align 1
8+
9+
define void @CSMain() "hlsl.shader"="compute" {
10+
entry:
11+
%CB = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 0, ptr nonnull @CB.str)
12+
ret void
13+
}
14+
15+
!dx.rootsignatures = !{!0}
16+
17+
!0 = !{ptr @CSMain, !1, i32 2}
18+
!1 = !{!2}
19+
!2 = !{!"DescriptorTable", i32 0, !3}
20+
!3 = !{!"CBV", i32 5, i32 0, i32 0, i32 0, i32 4}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
; RUN: opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
2+
; This is a valid code, it checks the limits of a binding space
3+
4+
; CHECK-NOT: error:
5+
6+
%__cblayout_CB = type <{ float }>
7+
8+
@CB.str = private unnamed_addr constant [3 x i8] c"CB\00", align 1
9+
10+
define void @CSMain() "hlsl.shader"="compute" {
11+
entry:
12+
%CB = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 4294967294, i32 1, i32 0, ptr nonnull @CB.str)
13+
%CB1 = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr nonnull @CB.str)
14+
ret void
15+
}
16+
17+
!dx.rootsignatures = !{!0}
18+
19+
!0 = !{ptr @CSMain, !1, i32 2}
20+
!1 = !{!2, !3}
21+
!2 = !{!"RootCBV", i32 0, i32 4294967294, i32 0, i32 4}
22+
!3 = !{!"RootCBV", i32 0, i32 0, i32 0, i32 4}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
2+
; CHECK: error: CBV register 2 in space 666 does not have a binding in the Root Signature
3+
4+
%__cblayout_CB = type <{ float }>
5+
6+
@CB.str = private unnamed_addr constant [3 x i8] c"CB\00", align 1
7+
8+
define void @CSMain() "hlsl.shader"="compute" {
9+
entry:
10+
%CB = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 666, i32 2, i32 1, i32 0, ptr nonnull @CB.str)
11+
ret void
12+
}
13+
14+
!dx.rootsignatures = !{!0}
15+
16+
!0 = !{ptr @CSMain, !1, i32 2}
17+
!1 = !{!2}
18+
!2 = !{!"RootConstants", i32 0, i32 2, i32 0, i32 4}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
2+
; CHECK: CBV register 3 in space 0 does not have a binding in the Root Signature
3+
%__cblayout_CB = type <{ float }>
4+
5+
@CB.str = private unnamed_addr constant [3 x i8] c"CB\00", align 1
6+
7+
define void @CSMain() "hlsl.shader"="compute" {
8+
entry:
9+
%CB = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 6, i32 0, ptr nonnull @CB.str)
10+
ret void
11+
}
12+
13+
!dx.rootsignatures = !{!0}
14+
15+
!0 = !{ptr @CSMain, !1, i32 2}
16+
!1 = !{!2}
17+
!2 = !{!"DescriptorTable", i32 0, !3, !4}
18+
!3 = !{!"CBV", i32 5, i32 2, i32 0, i32 0, i32 4}
19+
!4 = !{!"CBV", i32 4, i32 7, i32 0, i32 10, i32 4}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
2+
; CHECK: error: Sampler register 3 in space 2 does not have a binding in the Root Signature
3+
4+
@Smp.str = private unnamed_addr constant [4 x i8] c"Smp\00", align 1
5+
6+
7+
define void @CSMain() "hlsl.shader"="compute" {
8+
entry:
9+
%Sampler = call target("dx.Sampler", 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 3, i32 1, i32 0, ptr nonnull @Smp.str)
10+
ret void
11+
}
12+
13+
!dx.rootsignatures = !{!0}
14+
15+
!0 = !{ptr @CSMain, !1, i32 2}
16+
!1 = !{!2}
17+
!2 = !{!"DescriptorTable", i32 0, !3}
18+
!3 = !{!"Sampler", i32 1, i32 42, i32 0, i32 -1, i32 0}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.6-compute %s 2>&1 | FileCheck %s
2+
; CHECK: error: SRV register 0 in space 0 does not have a binding in the Root Signature
3+
4+
@SB.str = private unnamed_addr constant [3 x i8] c"SB\00", align 1
5+
6+
define void @CSMain() "hlsl.shader"="compute" {
7+
entry:
8+
; StructuredBuffer<int> In : register(t0, space0);
9+
%SB = tail call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @SB.str)
10+
ret void
11+
}
12+
13+
!dx.rootsignatures = !{!0}
14+
15+
!0 = !{ptr @CSMain, !1, i32 2}
16+
!1 = !{!2, !3, !5, !7}
17+
!2 = !{!"RootCBV", i32 0, i32 3, i32 666, i32 4}
18+
!3 = !{!"DescriptorTable", i32 1, !4}
19+
!4 = !{!"SRV", i32 1, i32 0, i32 0, i32 -1, i32 4}
20+
!5 = !{!"DescriptorTable", i32 0, !6}
21+
!6 = !{!"Sampler", i32 2, i32 0, i32 0, i32 -1, i32 0}
22+
!7 = !{!"DescriptorTable", i32 0, !8}
23+
!8 = !{!"UAV", i32 -1, i32 0, i32 0, i32 -1, i32 2}

0 commit comments

Comments
 (0)