-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[HLSL] Diagnose overlapping resource bindings #140982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-backend-directx Author: Helena Kotas (hekota) ChangesAdds reporting of overlapping binding errors to Fixes #110723 Depends on #140635, #140645, #140981 Full diff: https://github.com/llvm/llvm-project/pull/140982.diff 6 Files Affected:
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index a274e2294561e..f6e924041da11 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -355,6 +355,9 @@ class ResourceInfo {
return std::tie(RecordID, Space, LowerBound, Size) <
std::tie(RHS.RecordID, RHS.Space, RHS.LowerBound, RHS.Size);
}
+ bool overlapsWith(const ResourceBinding &RHS) const {
+ return Space == RHS.Space && LowerBound + Size - 1 >= RHS.LowerBound;
+ }
};
private:
@@ -393,8 +396,8 @@ class ResourceInfo {
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);
+ return std::tie(Binding, HandleTy, Symbol, Name) ==
+ std::tie(RHS.Binding, RHS.HandleTy, RHS.Symbol, RHS.Name);
}
bool operator!=(const ResourceInfo &RHS) const { return !(*this == RHS); }
bool operator<(const ResourceInfo &RHS) const {
diff --git a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
index 1dc0c2fb13c11..c0bb6a61e8a24 100644
--- a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
+++ b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
@@ -9,6 +9,7 @@
#include "DXILPostOptimizationValidation.h"
#include "DXILShaderFlags.h"
#include "DirectX.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/Analysis/DXILMetadataAnalysis.h"
#include "llvm/Analysis/DXILResource.h"
#include "llvm/IR/DiagnosticInfo.h"
@@ -50,15 +51,55 @@ static void reportInvalidDirection(Module &M, DXILResourceMap &DRM) {
}
}
-} // namespace
+static void reportOverlappingError(Module &M, ResourceInfo R1,
+ ResourceInfo R2) {
+ SmallString<64> Message;
+ raw_svector_ostream OS(Message);
+ OS << "resource " << R1.getName() << " at register "
+ << R1.getBinding().LowerBound << " overlaps with resource " << R2.getName()
+ << " at register " << R2.getBinding().LowerBound << ", space "
+ << R2.getBinding().Space;
+ M.getContext().diagnose(DiagnosticInfoGeneric(Message));
+}
-PreservedAnalyses
-DXILPostOptimizationValidation::run(Module &M, ModuleAnalysisManager &MAM) {
- DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
+static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
+ if (DRM.empty())
+ return;
+ for (auto ResList :
+ {DRM.srvs(), DRM.uavs(), DRM.cbuffers(), DRM.samplers()}) {
+ if (ResList.empty())
+ continue;
+ const ResourceInfo *PrevRI = &*ResList.begin();
+ for (auto *I = ResList.begin() + 1; I != ResList.end(); ++I) {
+ const ResourceInfo *RI = &*I;
+ if (PrevRI->getBinding().overlapsWith(RI->getBinding())) {
+ reportOverlappingError(M, *PrevRI, *RI);
+ continue;
+ }
+ PrevRI = RI;
+ }
+ }
+}
+
+static void reportErrors(Module &M, DXILResourceMap &DRM,
+ DXILResourceBindingInfo &DRBI) {
if (DRM.hasInvalidCounterDirection())
reportInvalidDirection(M, DRM);
+ if (DRBI.hasOverlappingBinding())
+ reportOverlappingBinding(M, DRM);
+
+ assert(!DRBI.hasImplicitBinding() && "implicit bindings should be handled in "
+ "DXILResourceImplicitBinding pass");
+}
+} // namespace
+
+PreservedAnalyses
+DXILPostOptimizationValidation::run(Module &M, ModuleAnalysisManager &MAM) {
+ DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
+ DXILResourceBindingInfo &DRBI = MAM.getResult<DXILResourceBindingAnalysis>(M);
+ reportErrors(M, DRM, DRBI);
return PreservedAnalyses::all();
}
@@ -68,10 +109,9 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
bool runOnModule(Module &M) override {
DXILResourceMap &DRM =
getAnalysis<DXILResourceWrapperPass>().getResourceMap();
-
- if (DRM.hasInvalidCounterDirection())
- reportInvalidDirection(M, DRM);
-
+ DXILResourceBindingInfo &DRBI =
+ getAnalysis<DXILResourceBindingWrapperPass>().getBindingInfo();
+ reportErrors(M, DRM, DRBI);
return false;
}
StringRef getPassName() const override {
@@ -82,7 +122,9 @@ class DXILPostOptimizationValidationLegacy : public ModulePass {
static char ID; // Pass identification.
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
AU.addRequired<DXILResourceWrapperPass>();
+ AU.addRequired<DXILResourceBindingWrapperPass>();
AU.addPreserved<DXILResourceWrapperPass>();
+ AU.addPreserved<DXILResourceBindingWrapperPass>();
AU.addPreserved<DXILMetadataAnalysisWrapperPass>();
AU.addPreserved<ShaderFlagsAnalysisWrapper>();
}
@@ -92,6 +134,7 @@ char DXILPostOptimizationValidationLegacy::ID = 0;
INITIALIZE_PASS_BEGIN(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,
"DXIL Post Optimization Validation", false, false)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceBindingWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
INITIALIZE_PASS_END(DXILPostOptimizationValidationLegacy, DEBUG_TYPE,
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll
new file mode 100644
index 0000000000000..2f8ad52e72e3b
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll
@@ -0,0 +1,17 @@
+; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; Check overlap error for two resource arrays.
+
+; CHECK: error: resource A at register 0 overlaps with resource B at register 5, space 0
+
+@A.str = private unnamed_addr constant [2 x i8] c"A\00", align 1
+@B.str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+
+define void @test_overlapping() {
+entry:
+; RWBuffer<float> A[10] : register(u0);
+; RWBuffer<float> B[10] : register(u5);
+ %h1 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 10, i32 4, i1 false, ptr @A.str)
+ %h2 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 10, i32 4, i1 false, ptr @B.str)
+ ret void
+}
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-2.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-2.ll
new file mode 100644
index 0000000000000..043f8c87dcc90
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-2.ll
@@ -0,0 +1,17 @@
+; RUN: not opt -S -passes='dxil-post-optimization-validation' -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; Check overlap error for two resources with identical binding
+
+; CHECK: error: resource R at register 5 overlaps with resource S at register 5, space 10
+
+@R.str = private unnamed_addr constant [2 x i8] c"R\00", align 1
+@S.str = private unnamed_addr constant [2 x i8] c"S\00", align 1
+
+define void @test_overlapping() {
+entry:
+; RWBuffer<float> R : register(u5, space10);
+; RWBuffer<float> S : register(u5, space10);
+ %h1 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 10, i32 5, i32 1, i32 0, i1 false, ptr @R.str)
+ %h2 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 10, i32 5, i32 1, i32 0, i1 false, ptr @S.str)
+ ret void
+}
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll
new file mode 100644
index 0000000000000..f69f82f206106
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll
@@ -0,0 +1,44 @@
+; Use llc for this test so that we don't abort after the first error.
+; RUN: not llc %s -o /dev/null 2>&1 | FileCheck %s
+
+; Check multiple overlap errors.
+; Also check different resource class with same binding values is ok (no error expected).
+
+target triple = "dxil-pc-shadermodel6.3-library"
+
+; CHECK: error: resource C at register 0 overlaps with resource A at register 5, space 0
+; CHECK: error: resource C at register 0 overlaps with resource B at register 9, space 0
+
+@A.str = private unnamed_addr constant [2 x i8] c"A\00", align 1
+@B.str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+@C.str = private unnamed_addr constant [2 x i8] c"C\00", align 1
+@S.str = private unnamed_addr constant [2 x i8] c"S\00", align 1
+
+; Fake globals to store handles in; this is to make sure the handlefrombinding calls
+; are not optimized away by llc.
+@One = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+@Two = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+@Three = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+@Four = internal global { target("dx.TypedBuffer", float, 1, 0, 0) } poison, align 4
+
+define void @test_overlapping() "hlsl.export" {
+entry:
+; StructuredBuffer<float> A : register(t5);
+; StructuredBuffer<float> B : register(t9);
+; StructuredBuffer<float> C[10] : register(t0);
+; RWBuffer<float> S[10] : register(u0);
+
+ %h1 = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false, ptr @A.str)
+ store target("dx.RawBuffer", float, 0, 0) %h1, ptr @One, align 4
+
+ %h2 = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 9, i32 1, i32 0, i1 false, ptr @B.str)
+ store target("dx.RawBuffer", float, 0, 0) %h2, ptr @Two, align 4
+
+ %h3 = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 10, i32 4, i1 false, ptr @C.str)
+ store target("dx.RawBuffer", float, 0, 0) %h3, ptr @Three, align 4
+
+ %h4 = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false, ptr @S.str)
+ store target("dx.TypedBuffer", float, 1, 0, 0) %h4, ptr @Four, align 4
+
+ ret void
+}
diff --git a/llvm/test/CodeGen/DirectX/ShaderFlags/typed-uav-load-additional-formats.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/typed-uav-load-additional-formats.ll
index 2c18d0b24326a..b0a5d5de77c29 100644
--- a/llvm/test/CodeGen/DirectX/ShaderFlags/typed-uav-load-additional-formats.ll
+++ b/llvm/test/CodeGen/DirectX/ShaderFlags/typed-uav-load-additional-formats.ll
@@ -26,7 +26,7 @@ define <4 x float> @multicomponent() #0 {
; CHECK: Function onecomponent : 0x00000000
define float @onecomponent() #0 {
%res = call target("dx.TypedBuffer", float, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false, ptr null)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, i1 false, ptr null)
%load = call {float, i1} @llvm.dx.resource.load.typedbuffer(
target("dx.TypedBuffer", float, 1, 0, 0) %res, i32 0)
%val = extractvalue {float, i1} %load, 0
@@ -36,7 +36,7 @@ define float @onecomponent() #0 {
; CHECK: Function noload : 0x00000000
define void @noload(<4 x float> %val) #0 {
%res = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
- @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false, ptr null)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, i1 false, ptr null)
call void @llvm.dx.resource.store.typedbuffer(
target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %res, i32 0,
<4 x float> %val)
|
inbelic
reviewed
May 26, 2025
This was referenced May 27, 2025
pow2clk
reviewed
May 27, 2025
V-FEXrt
approved these changes
May 27, 2025
dc8a3fa
to
6252a27
Compare
bogner
reviewed
May 29, 2025
bogner
approved these changes
May 30, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Adds reporting of overlapping binding errors to
DXILPostOptimizationValidation
pass. Only runs whenDXILResourceBindingAnalysis
detects that there is a resource binding that overlaps while it is building up a map of available register spaces.Fixes #110723