77// ===----------------------------------------------------------------------===//
88
99#include " DirectXIRPasses/PointerTypeAnalysis.h"
10+ #include " DirectXTargetMachine.h"
1011#include " llvm/Analysis/DXILResource.h"
1112#include " llvm/AsmParser/Parser.h"
13+ #include " llvm/CodeGen/CommandFlags.h"
1214#include " llvm/IR/Instructions.h"
1315#include " llvm/IR/LLVMContext.h"
1416#include " llvm/IR/Module.h"
1517#include " llvm/IR/Type.h"
1618#include " llvm/IR/TypedPointerType.h"
19+ #include " llvm/MC/TargetRegistry.h"
20+ #include " llvm/Passes/PassBuilder.h"
1721#include " llvm/Support/Casting.h"
22+ #include " llvm/Support/CodeGen.h"
1823#include " llvm/Support/SourceMgr.h"
1924#include " llvm/Transforms/Utils/Debugify.h"
2025
2126#include " gmock/gmock.h"
2227#include " gtest/gtest.h"
28+ #include < optional>
2329
2430using ::testing::Contains;
2531using ::testing::Pair;
@@ -31,31 +37,153 @@ template <typename T> struct IsA {
3137 friend bool operator ==(const Value *V, const IsA &) { return isa<T>(V); }
3238};
3339
34- TEST (UniqueResourceFromUse, TestTrivialUse) {
40+ namespace {
41+ class UniqueResourceFromUseTest : public testing ::Test {
42+ protected:
43+ PassBuilder *PB;
44+ ModuleAnalysisManager* MAM;
45+
46+ virtual void SetUp () {
47+ MAM = new ModuleAnalysisManager ();
48+ PB = new PassBuilder ();
49+ PB->registerModuleAnalyses (*MAM);
50+ MAM->registerPass ([&]{ return DXILResourceTypeAnalysis (); });
51+ MAM->registerPass ([&] { return DXILResourceBindingAnalysis (); });
52+ }
53+
54+ virtual void TearDown () {
55+ delete PB;
56+ delete MAM;
57+ }
58+ };
59+
60+ TEST_F (UniqueResourceFromUseTest, TestTrivialUse) {
3561 StringRef Assembly = R"(
3662define void @main() {
3763entry:
3864 %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 2, i32 3, i32 4, i1 false)
3965 call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
66+ call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
67+ ret void
68+ }
69+
70+ declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1)
71+ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
72+ )" ;
73+
74+ LLVMContext Context;
75+ SMDiagnostic Error;
76+ auto M = parseAssemblyString (Assembly, Error, Context);
77+ ASSERT_TRUE (M) << " Bad assembly?" ;
78+
79+ const DXILBindingMap &DBM = MAM->getResult <DXILResourceBindingAnalysis>(*M);
80+ for (const Function &F : M->functions ()) {
81+ if (F.getName () != " a.func" ) {
82+ continue ;
83+ }
84+
85+ unsigned CalledResources = 0 ;
86+
87+ for (const User *U : F.users ()) {
88+ const CallInst *CI = dyn_cast<CallInst>(U);
89+ ASSERT_TRUE (CI) << " All users of @a.func must be CallInst" ;
90+
91+ const Value *Handle = CI->getArgOperand (0 );
92+
93+ const auto Bindings = DBM.findByUse (Handle);
94+ ASSERT_EQ (Bindings.size (), 1u ) << " Handle should resolve into one resource" ;
95+
96+ auto Binding = Bindings[0 ].getBinding ();
97+ EXPECT_EQ (0u , Binding.RecordID );
98+ EXPECT_EQ (1u , Binding.Space );
99+ EXPECT_EQ (2u , Binding.LowerBound );
100+ EXPECT_EQ (3u , Binding.Size );
101+
102+ CalledResources++;
103+ }
104+
105+ EXPECT_EQ (2u , CalledResources)
106+ << " Expected 2 resolved call to create resource" ;
107+ }
108+ }
109+
110+ TEST_F (UniqueResourceFromUseTest, TestIndirectUse) {
111+ StringRef Assembly = R"(
112+ define void @foo() {
113+ %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 2, i32 3, i32 4, i1 false)
114+ %handle2 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle)
115+ %handle3 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle2)
116+ %handle4 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle3)
117+ call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle4)
40118 ret void
41119}
42120
43121declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1)
122+ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
123+ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle)
124+ )" ;
125+
126+ LLVMContext Context;
127+ SMDiagnostic Error;
128+ auto M = parseAssemblyString (Assembly, Error, Context);
129+ ASSERT_TRUE (M) << " Bad assembly?" ;
130+
131+ const DXILBindingMap &DBM = MAM->getResult <DXILResourceBindingAnalysis>(*M);
132+ for (const Function &F : M->functions ()) {
133+ if (F.getName () != " a.func" ) {
134+ continue ;
135+ }
136+
137+ unsigned CalledResources = 0 ;
138+
139+ for (const User *U : F.users ()) {
140+ const CallInst *CI = dyn_cast<CallInst>(U);
141+ ASSERT_TRUE (CI) << " All users of @a.func must be CallInst" ;
142+
143+ const Value *Handle = CI->getArgOperand (0 );
44144
145+ const auto Bindings = DBM.findByUse (Handle);
146+ ASSERT_EQ (Bindings.size (), 1u ) << " Handle should resolve into one resource" ;
147+
148+ auto Binding = Bindings[0 ].getBinding ();
149+ EXPECT_EQ (0u , Binding.RecordID );
150+ EXPECT_EQ (1u , Binding.Space );
151+ EXPECT_EQ (2u , Binding.LowerBound );
152+ EXPECT_EQ (3u , Binding.Size );
153+
154+ CalledResources++;
155+ }
156+
157+ EXPECT_EQ (1u , CalledResources)
158+ << " Expected 1 resolved call to create resource" ;
159+ }
160+ }
161+
162+ TEST_F (UniqueResourceFromUseTest, TestAmbigousIndirectUse) {
163+ StringRef Assembly = R"(
164+ define void @foo() {
165+ %foo = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 1, i32 1, i32 1, i1 false)
166+ %bar = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 2, i32 2, i32 2, i32 2, i1 false)
167+ %baz = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 3, i32 3, i32 3, i32 3, i1 false)
168+ %bat = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 4, i32 4, i32 4, i32 4, i1 false)
169+ %a = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %foo, target("dx.RawBuffer", float, 1, 0) %bar)
170+ %b = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %baz, target("dx.RawBuffer", float, 1, 0) %bat)
171+ %handle = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %a, target("dx.RawBuffer", float, 1, 0) %b)
172+ call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
173+ ret void
174+ }
175+
176+ declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1)
45177declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
178+ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x, target("dx.RawBuffer", float, 1, 0) %y)
46179 )" ;
47180
48181 LLVMContext Context;
49182 SMDiagnostic Error;
50183 auto M = parseAssemblyString (Assembly, Error, Context);
51184 ASSERT_TRUE (M) << " Bad assembly?" ;
52- DebugifyCustomPassManager Passes;
53- Passes.add (createDXILResourceTypeWrapperPassPass ());
54- DXILResourceBindingWrapperPass *RBPass = new DXILResourceBindingWrapperPass ();
55- Passes.add (RBPass);
56- Passes.run (*M);
57185
58- const DXILBindingMap &DBM = RBPass-> getBindingMap ( );
186+ const DXILBindingMap &DBM = MAM-> getResult <DXILResourceBindingAnalysis>(*M );
59187 for (const Function &F : M->functions ()) {
60188 if (F.getName () != " a.func" ) {
61189 continue ;
@@ -69,20 +197,109 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
69197
70198 const Value *Handle = CI->getArgOperand (0 );
71199
72- const auto *It = DBM.findByUse (Handle);
73- ASSERT_TRUE (It != DBM. end () ) << " Handle should resolve into resource " ;
200+ const auto Bindings = DBM.findByUse (Handle);
201+ ASSERT_EQ (Bindings. size (), 4u ) << " Handle should resolve into four resources " ;
74202
75- const llvm::dxil::ResourceBindingInfo::ResourceBinding &Binding =
76- It->getBinding ();
203+ auto Binding = Bindings[0 ].getBinding ();
77204 EXPECT_EQ (0u , Binding.RecordID );
78205 EXPECT_EQ (1u , Binding.Space );
206+ EXPECT_EQ (1u , Binding.LowerBound );
207+ EXPECT_EQ (1u , Binding.Size );
208+
209+ Binding = Bindings[1 ].getBinding ();
210+ EXPECT_EQ (1u , Binding.RecordID );
211+ EXPECT_EQ (2u , Binding.Space );
79212 EXPECT_EQ (2u , Binding.LowerBound );
213+ EXPECT_EQ (2u , Binding.Size );
214+
215+ Binding = Bindings[2 ].getBinding ();
216+ EXPECT_EQ (2u , Binding.RecordID );
217+ EXPECT_EQ (3u , Binding.Space );
218+ EXPECT_EQ (3u , Binding.LowerBound );
80219 EXPECT_EQ (3u , Binding.Size );
81220
221+ Binding = Bindings[3 ].getBinding ();
222+ EXPECT_EQ (3u , Binding.RecordID );
223+ EXPECT_EQ (4u , Binding.Space );
224+ EXPECT_EQ (4u , Binding.LowerBound );
225+ EXPECT_EQ (4u , Binding.Size );
226+
227+ CalledResources++;
228+ }
229+
230+ EXPECT_EQ (1u , CalledResources)
231+ << " Expected 1 resolved call to create resource" ;
232+ }
233+ }
234+
235+ TEST_F (UniqueResourceFromUseTest, TestConditionalUse) {
236+ StringRef Assembly = R"(
237+ define void @foo(i32 %n) {
238+ entry:
239+ %x = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 1, i32 1, i32 1, i1 false)
240+ %y = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 4, i32 4, i32 4, i32 4, i1 false)
241+ %cond = icmp eq i32 %n, 0
242+ br i1 %cond, label %bb.true, label %bb.false
243+
244+ bb.true:
245+ %handle_t = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x)
246+ br label %bb.exit
247+
248+ bb.false:
249+ %handle_f = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %y)
250+ br label %bb.exit
251+
252+ bb.exit:
253+ %handle = phi target("dx.RawBuffer", float, 1, 0) [ %handle_t, %bb.true ], [ %handle_f, %bb.false ]
254+ call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
255+ ret void
256+ }
257+
258+ declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1)
259+ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle)
260+ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x)
261+ )" ;
262+
263+ LLVMContext Context;
264+ SMDiagnostic Error;
265+ auto M = parseAssemblyString (Assembly, Error, Context);
266+ ASSERT_TRUE (M) << " Bad assembly?" ;
267+
268+ const DXILBindingMap &DBM = MAM->getResult <DXILResourceBindingAnalysis>(*M);
269+ for (const Function &F : M->functions ()) {
270+ if (F.getName () != " a.func" ) {
271+ continue ;
272+ }
273+
274+ unsigned CalledResources = 0 ;
275+
276+ for (const User *U : F.users ()) {
277+ const CallInst *CI = dyn_cast<CallInst>(U);
278+ ASSERT_TRUE (CI) << " All users of @a.func must be CallInst" ;
279+
280+ const Value *Handle = CI->getArgOperand (0 );
281+
282+ const auto Bindings = DBM.findByUse (Handle);
283+ ASSERT_EQ (Bindings.size (), 2u ) << " Handle should resolve into four resources" ;
284+
285+ auto Binding = Bindings[0 ].getBinding ();
286+ EXPECT_EQ (0u , Binding.RecordID );
287+ EXPECT_EQ (1u , Binding.Space );
288+ EXPECT_EQ (1u , Binding.LowerBound );
289+ EXPECT_EQ (1u , Binding.Size );
290+
291+ Binding = Bindings[1 ].getBinding ();
292+ EXPECT_EQ (1u , Binding.RecordID );
293+ EXPECT_EQ (4u , Binding.Space );
294+ EXPECT_EQ (4u , Binding.LowerBound );
295+ EXPECT_EQ (4u , Binding.Size );
296+
82297 CalledResources++;
83298 }
84299
85300 EXPECT_EQ (1u , CalledResources)
86- << " Expected exactly 1 resolved call to create resource" ;
301+ << " Expected 1 resolved call to create resource" ;
87302 }
88303}
304+
305+ } // namespace
0 commit comments