Skip to content

Commit 2039b3b

Browse files
committed
Fully implement and add tests
1 parent 09b6373 commit 2039b3b

File tree

4 files changed

+265
-24
lines changed

4 files changed

+265
-24
lines changed

llvm/include/llvm/Analysis/DXILResource.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ class DXILBindingMap {
448448

449449
// Resoloves the use of a resource handle into the unique description of that
450450
// resource by deduping calls to create.
451-
const_iterator findByUse(const Value *Key) const;
451+
SmallVector<dxil::ResourceBindingInfo> findByUse(const Value *Key) const;
452452

453453
const_iterator find(const CallInst *Key) const {
454454
auto Pos = CallMap.find(Key);

llvm/lib/Analysis/DXILResource.cpp

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -770,23 +770,48 @@ void DXILBindingMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM,
770770
}
771771
}
772772

773-
SmallVector<dxil::ResourceBindingInfo>::const_iterator
773+
SmallVector<dxil::ResourceBindingInfo>
774774
DXILBindingMap::findByUse(const Value *Key) const {
775+
const PHINode *Phi = dyn_cast<PHINode>(Key);
776+
if (Phi) {
777+
SmallVector<dxil::ResourceBindingInfo> Children;
778+
for (const Value *V : Phi->operands()) {
779+
Children.append(findByUse(V));
780+
}
781+
return Children;
782+
}
783+
775784
const CallInst *CI = dyn_cast<CallInst>(Key);
776785
if (!CI) {
777-
// TODO: Any other cases to follow up the tree?
778-
return Infos.end();
786+
return {};
779787
}
780788

789+
const Type* UseType = CI->getType();
790+
781791
switch (CI->getIntrinsicID()) {
782-
case Intrinsic::not_intrinsic:
783-
// TODO: Walk the call tree
784-
return Infos.end();
792+
// Check if any of the parameters are the resource we are following. If so
793+
// keep searching
794+
case Intrinsic::not_intrinsic: {
795+
SmallVector<dxil::ResourceBindingInfo> Children;
796+
for (const Value *V : CI->args()) {
797+
if (V->getType() != UseType) {
798+
continue;
799+
}
800+
801+
Children.append(findByUse(V));
802+
}
803+
804+
return Children;
805+
}
806+
// Found the create, return the binding
785807
case Intrinsic::dx_resource_handlefrombinding:
786-
return find(CI);
808+
const auto *It = find(CI);
809+
if (It == Infos.end())
810+
return {};
811+
return {*It};
787812
}
788813

789-
return Infos.end();
814+
return {};
790815
}
791816

792817
//===----------------------------------------------------------------------===//
@@ -847,9 +872,7 @@ bool DXILResourceBindingWrapperPass::runOnModule(Module &M) {
847872
return false;
848873
}
849874

850-
void DXILResourceBindingWrapperPass::releaseMemory() {
851-
// TODO: Can't comment out this code
852-
/*Map.reset();*/ }
875+
void DXILResourceBindingWrapperPass::releaseMemory() { Map.reset(); }
853876

854877
void DXILResourceBindingWrapperPass::print(raw_ostream &OS,
855878
const Module *M) const {

llvm/unittests/Target/DirectX/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS
88
Core
99
DirectXCodeGen
1010
DirectXPointerTypeAnalysis
11+
Passes
1112
Support
1213
)
1314

llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp

Lines changed: 229 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,25 @@
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

2430
using ::testing::Contains;
2531
using ::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"(
3662
define void @main() {
3763
entry:
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
43121
declare 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)
45177
declare 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

Comments
 (0)