[llvm] [HLSL] Diagnose overlapping resource bindings (PR #140982)
Helena Kotas via llvm-commits
llvm-commits at lists.llvm.org
Thu May 29 19:05:19 PDT 2025
https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/140982
>From 6252a27d341040713f58de104b6366836a5830e2 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Wed, 21 May 2025 19:06:38 -0700
Subject: [PATCH 1/2] [HLSL] Diagnose overlapping resource bindings
---
llvm/include/llvm/Analysis/DXILResource.h | 7 ++-
.../DXILPostOptimizationValidation.cpp | 59 ++++++++++++++++---
.../DirectX/Binding/binding-overlap-1.ll | 17 ++++++
.../DirectX/Binding/binding-overlap-2.ll | 17 ++++++
.../DirectX/Binding/binding-overlap-3.ll | 44 ++++++++++++++
.../typed-uav-load-additional-formats.ll | 4 +-
6 files changed, 136 insertions(+), 12 deletions(-)
create mode 100644 llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll
create mode 100644 llvm/test/CodeGen/DirectX/Binding/binding-overlap-2.ll
create mode 100644 llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 9eac7faab6e0f..1ede0ef601fc9 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -358,6 +358,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:
@@ -394,8 +397,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
+
+ at A.str = private unnamed_addr constant [2 x i8] c"A\00", align 1
+ at 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
+
+ at R.str = private unnamed_addr constant [2 x i8] c"R\00", align 1
+ at 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
+
+ at A.str = private unnamed_addr constant [2 x i8] c"A\00", align 1
+ at B.str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+ at C.str = private unnamed_addr constant [2 x i8] c"C\00", align 1
+ at 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.
+ at One = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+ at Two = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+ at Three = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+ at 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)
>From 43ffd43e22d662e18c9b9f2bd0e8e1eae4bf3484 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 29 May 2025 19:04:54 -0700
Subject: [PATCH 2/2] code review feedback - detect all collisions, change
error message, add const ref
---
.../DXILPostOptimizationValidation.cpp | 16 ++++----
.../DirectX/Binding/binding-overlap-1.ll | 8 ++--
.../DirectX/Binding/binding-overlap-2.ll | 8 ++--
.../DirectX/Binding/binding-overlap-3.ll | 18 ++++----
.../DirectX/Binding/binding-overlap-4.ll | 41 +++++++++++++++++++
.../DirectX/Binding/binding-overlap-5.ll | 39 ++++++++++++++++++
6 files changed, 109 insertions(+), 21 deletions(-)
create mode 100644 llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll
create mode 100644 llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll
diff --git a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
index c0bb6a61e8a24..398dcbb8d1737 100644
--- a/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
+++ b/llvm/lib/Target/DirectX/DXILPostOptimizationValidation.cpp
@@ -53,11 +53,11 @@ static void reportInvalidDirection(Module &M, DXILResourceMap &DRM) {
static void reportOverlappingError(Module &M, ResourceInfo R1,
ResourceInfo R2) {
- SmallString<64> Message;
+ SmallString<128> 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 "
+ << " at register " << R2.getBinding().LowerBound << " in space "
<< R2.getBinding().Space;
M.getContext().diagnose(DiagnosticInfoGeneric(Message));
}
@@ -66,18 +66,20 @@ static void reportOverlappingBinding(Module &M, DXILResourceMap &DRM) {
if (DRM.empty())
return;
- for (auto ResList :
+ for (const 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())) {
+ const ResourceInfo *CurrentRI = &*I;
+ const ResourceInfo *RI = CurrentRI;
+ while (RI != ResList.end() &&
+ PrevRI->getBinding().overlapsWith(RI->getBinding())) {
reportOverlappingError(M, *PrevRI, *RI);
- continue;
+ RI++;
}
- PrevRI = RI;
+ PrevRI = CurrentRI;
}
}
}
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll
index 2f8ad52e72e3b..9f87f5bc58f25 100644
--- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-1.ll
@@ -2,15 +2,17 @@
; Check overlap error for two resource arrays.
-; CHECK: error: resource A at register 0 overlaps with resource B at register 5, space 0
+; A overlaps with B
+; RWBuffer<float> A[10] : register(u0);
+; RWBuffer<float> B[10] : register(u5);
+
+; CHECK: error: resource A at register 0 overlaps with resource B at register 5 in 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
index 043f8c87dcc90..dd50428b4b7a7 100644
--- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-2.ll
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-2.ll
@@ -2,15 +2,17 @@
; 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 overlaps exactly with S
+; RWBuffer<float> R : register(u5, space10);
+; RWBuffer<float> S : register(u5, space10);
+
+; CHECK: error: resource R at register 5 overlaps with resource S at register 5 in 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
index f69f82f206106..31b1dbfc595f7 100644
--- a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-3.ll
@@ -4,10 +4,17 @@
; 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"
+; C overlaps with A
+; C overlaps with B
+; StructuredBuffer<float> A : register(t5);
+; StructuredBuffer<float> B : register(t9);
+; StructuredBuffer<float> C[10] : register(t0);
+; RWBuffer<float> S[10] : register(u0);
+
+; CHECK: error: resource C at register 0 overlaps with resource A at register 5 in space 0
+; CHECK: error: resource C at register 0 overlaps with resource B at register 9 in space 0
-; 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
+target triple = "dxil-pc-shadermodel6.3-library"
@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
@@ -23,11 +30,6 @@ target triple = "dxil-pc-shadermodel6.3-library"
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
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll
new file mode 100644
index 0000000000000..8ca87089e5e1a
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-4.ll
@@ -0,0 +1,41 @@
+; 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.
+
+; A overlaps with B
+; A overlaps with C
+; B overlaps with C
+; StructuredBuffer<float> A[5] : register(t1); // 1-5
+; StructuredBuffer<float> B[2] : register(t2); // 2-3
+; StructuredBuffer<float> C[3] : register(t3); // 3-5
+
+; CHECK: error: resource A at register 1 overlaps with resource B at register 2 in space 0
+; CHECK: error: resource A at register 1 overlaps with resource C at register 3 in space 0
+; CHECK: error: resource B at register 2 overlaps with resource C at register 3 in space 0
+
+target triple = "dxil-pc-shadermodel6.3-library"
+
+ at A.str = private unnamed_addr constant [2 x i8] c"A\00", align 1
+ at B.str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+ at C.str = private unnamed_addr constant [2 x i8] c"C\00", align 1
+
+; Fake globals to store handles in; this is to make sure the handlefrombinding calls
+; are not optimized away by llc.
+ at One = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+ at Two = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+ at Three = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+
+define void @test_overlapping() "hlsl.export" {
+entry:
+ %h1 = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 5, 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 2, i32 2, 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 3, i32 3, i32 4, i1 false, ptr @C.str)
+ store target("dx.RawBuffer", float, 0, 0) %h3, ptr @Three, align 4
+
+ ret void
+}
diff --git a/llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll
new file mode 100644
index 0000000000000..7f1631703fb45
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/Binding/binding-overlap-5.ll
@@ -0,0 +1,39 @@
+; 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.
+
+; A overlaps with B
+; B overlaps with C
+; StructuredBuffer<float> A[5] : register(t1, space11); // 1-5
+; StructuredBuffer<float> B[6] : register(t2, space11); // 2-7
+; StructuredBuffer<float> C[3] : register(t6, space11); // 6-8
+
+; CHECK: error: resource A at register 1 overlaps with resource B at register 2 in space 11
+; CHECK: error: resource B at register 2 overlaps with resource C at register 6 in space 11
+
+target triple = "dxil-pc-shadermodel6.3-library"
+
+ at A.str = private unnamed_addr constant [2 x i8] c"A\00", align 1
+ at B.str = private unnamed_addr constant [2 x i8] c"B\00", align 1
+ at C.str = private unnamed_addr constant [2 x i8] c"C\00", align 1
+
+; Fake globals to store handles in; this is to make sure the handlefrombinding calls
+; are not optimized away by llc.
+ at One = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+ at Two = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+ at Three = internal global { target("dx.RawBuffer", float, 0, 0) } poison, align 4
+
+define void @test_overlapping() "hlsl.export" {
+entry:
+ %h1 = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding(i32 11, i32 1, i32 5, 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 11, i32 2, i32 6, 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 11, i32 6, i32 3, i32 4, i1 false, ptr @C.str)
+ store target("dx.RawBuffer", float, 0, 0) %h3, ptr @Three, align 4
+
+ ret void
+}
More information about the llvm-commits
mailing list