diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h index d399457e16916..05937ac6b00ae 100644 --- a/llvm/include/llvm/Analysis/DXILResource.h +++ b/llvm/include/llvm/Analysis/DXILResource.h @@ -18,6 +18,9 @@ #include "llvm/Support/Alignment.h" #include "llvm/Support/DXILABI.h" +#include +#include + namespace llvm { class CallInst; class DataLayout; @@ -471,7 +474,8 @@ class DXILBindingMap { /// ambiguous so multiple creation instructions may be returned. The resulting /// ResourceBindingInfo can be used to depuplicate unique handles that /// reference the same resource - SmallVector findByUse(const Value *Key) const; + SmallVector + findByUse(const Value *Key) const; const_iterator find(const CallInst *Key) const { auto Pos = CallMap.find(Key); @@ -574,6 +578,65 @@ class DXILResourceBindingWrapperPass : public ModulePass { ModulePass *createDXILResourceBindingWrapperPassPass(); +enum class ResourceCounterDirection { + Increment, + Decrement, + Unknown, + Invalid, +}; + +class DXILResourceCounterDirectionMap { + std::vector< + std::pair> + CounterDirections; + +public: + void populate(Module &M, DXILBindingMap &DBM); + + ResourceCounterDirection + operator[](const dxil::ResourceBindingInfo &Info) const; +}; + +class DXILResourceCounterDirectionAnalysis + : public AnalysisInfoMixin { + friend AnalysisInfoMixin; + + static AnalysisKey Key; + +public: + using Result = DXILResourceCounterDirectionMap; + + DXILResourceCounterDirectionMap run(Module &M, ModuleAnalysisManager &AM) { + DXILResourceCounterDirectionMap DRCDM{}; + DXILBindingMap &DBM = AM.getResult(M); + DRCDM.populate(M, DBM); + return DRCDM; + } +}; + +class DXILResourceCounterDirectionWrapperPass : public ModulePass { + std::unique_ptr Map; + +public: + static char ID; + DXILResourceCounterDirectionWrapperPass(); + ~DXILResourceCounterDirectionWrapperPass() override = default; + + DXILResourceCounterDirectionMap &getResourceCounterDirectionMap() { + return *Map; + } + const DXILResourceCounterDirectionMap & + getResourceCounterDirectionMap() const { + return *Map; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override; + bool runOnModule(Module &M) override; + void releaseMemory() override; +}; + +ModulePass *createDXILResourceCounterDirectionWrapperPassPass(); + } // namespace llvm #endif // LLVM_ANALYSIS_DXILRESOURCE_H diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index 460c7eb3ebe24..18a46bd59d332 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -85,6 +85,7 @@ void initializeDCELegacyPassPass(PassRegistry &); void initializeDXILMetadataAnalysisWrapperPassPass(PassRegistry &); void initializeDXILMetadataAnalysisWrapperPrinterPass(PassRegistry &); void initializeDXILResourceBindingWrapperPassPass(PassRegistry &); +void initializeDXILResourceCounterDirectionWrapperPassPass(PassRegistry &); void initializeDXILResourceTypeWrapperPassPass(PassRegistry &); void initializeDeadMachineInstructionElimPass(PassRegistry &); void initializeDebugifyMachineModulePass(PassRegistry &); diff --git a/llvm/lib/Analysis/DXILResource.cpp b/llvm/lib/Analysis/DXILResource.cpp index 22afb4cba6f26..41c4daa53c057 100644 --- a/llvm/lib/Analysis/DXILResource.cpp +++ b/llvm/lib/Analysis/DXILResource.cpp @@ -9,6 +9,7 @@ #include "llvm/Analysis/DXILResource.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DiagnosticInfo.h" @@ -19,6 +20,7 @@ #include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #include "llvm/Support/FormatVariadic.h" +#include #define DEBUG_TYPE "dxil-resource" @@ -782,10 +784,10 @@ void DXILBindingMap::print(raw_ostream &OS, DXILResourceTypeMap &DRTM, } } -SmallVector +SmallVector DXILBindingMap::findByUse(const Value *Key) const { if (const PHINode *Phi = dyn_cast(Key)) { - SmallVector Children; + SmallVector Children; for (const Value *V : Phi->operands()) { Children.append(findByUse(V)); } @@ -801,7 +803,7 @@ DXILBindingMap::findByUse(const Value *Key) const { case Intrinsic::dx_resource_handlefrombinding: { const auto *It = find(CI); assert(It != Infos.end() && "HandleFromBinding must be in resource map"); - return {*It}; + return {It}; } default: break; @@ -810,7 +812,7 @@ DXILBindingMap::findByUse(const Value *Key) const { // Check if any of the parameters are the resource we are following. If so // keep searching. If none of them are return an empty list const Type *UseType = CI->getType(); - SmallVector Children; + SmallVector Children; for (const Value *V : CI->args()) { if (V->getType() != UseType) continue; @@ -823,8 +825,111 @@ DXILBindingMap::findByUse(const Value *Key) const { //===----------------------------------------------------------------------===// +static bool isUpdateCounterIntrinsic(Function &F) { + return F.getIntrinsicID() == Intrinsic::dx_resource_updatecounter; +} + +void DXILResourceCounterDirectionMap::populate(Module &M, DXILBindingMap &DBM) { + for (Function &F : M.functions()) { + if (!isUpdateCounterIntrinsic(F)) + continue; + + for (const User *U : F.users()) { + const CallInst *CI = dyn_cast(U); + assert(CI && "Users of dx_resource_updateCounter must be call instrs"); + + // Determine if the use is an increment or decrement + Value *CountArg = CI->getArgOperand(1); + ConstantInt *CountValue = cast(CountArg); + int64_t CountLiteral = CountValue->getSExtValue(); + + // 0 is an unknown direction and shouldn't result in an insert + if (CountLiteral == 0) + continue; + + ResourceCounterDirection Direction = ResourceCounterDirection::Decrement; + if (CountLiteral > 0) + Direction = ResourceCounterDirection::Increment; + + // Collect all potential creation points for the handle arg + Value *HandleArg = CI->getArgOperand(0); + SmallVector RBInfos = + DBM.findByUse(HandleArg); + for (const dxil::ResourceBindingInfo *RBInfo : RBInfos) + CounterDirections.emplace_back(RBInfo, Direction); + } + } + + // Sort by the Binding and Direction for fast lookup + std::sort(CounterDirections.begin(), CounterDirections.end()); + + // Remove the duplicate entries. Since direction is considered for equality + // a unique resource with more than one direction will not be deduped. + auto UniqueEnd = + std::unique(CounterDirections.begin(), CounterDirections.end()); + + // If any duplicate entries still exist at this point then it must be a + // resource that was both incremented and decremented which is not allowed. + // Mark all those entries as invalid. + { + auto DupFirst = CounterDirections.begin(); + auto DupNext = DupFirst + 1; + auto DupLast = UniqueEnd; + for (; DupFirst < DupLast && DupNext < DupLast; ++DupFirst, ++DupNext) { + if (std::get(*DupFirst) == + std::get(*DupNext)) { + std::get(*DupFirst) = + ResourceCounterDirection::Invalid; + std::get(*DupNext) = + ResourceCounterDirection::Invalid; + } + } + } + + // Remove the duplicate entries again now that each duplicate resource has the + // same direction for each entry leaving one entry per RBI + UniqueEnd = std::unique(CounterDirections.begin(), UniqueEnd); + + // Actually erase the invalidated items + CounterDirections.erase(UniqueEnd, CounterDirections.end()); +} + +ResourceCounterDirection DXILResourceCounterDirectionMap::operator[]( + const dxil::ResourceBindingInfo &Info) const { + auto Lower = llvm::lower_bound( + CounterDirections, Info, + [](const auto &LHS, const auto &RHS) { return *LHS.first < RHS; }); + + if (Lower == CounterDirections.end()) + return ResourceCounterDirection::Unknown; + if (*Lower->first != Info) + return ResourceCounterDirection::Unknown; + + return Lower->second; +} + +void DXILResourceCounterDirectionWrapperPass::getAnalysisUsage( + AnalysisUsage &AU) const { + AU.addRequiredTransitive(); + AU.setPreservesAll(); +} + +bool DXILResourceCounterDirectionWrapperPass::runOnModule(Module &M) { + Map.reset(new DXILResourceCounterDirectionMap()); + + auto DBM = getAnalysis().getBindingMap(); + Map->populate(M, DBM); + + return false; +} + +void DXILResourceCounterDirectionWrapperPass::releaseMemory() { Map.reset(); } + +//===----------------------------------------------------------------------===// + AnalysisKey DXILResourceTypeAnalysis::Key; AnalysisKey DXILResourceBindingAnalysis::Key; +AnalysisKey DXILResourceCounterDirectionAnalysis::Key; DXILBindingMap DXILResourceBindingAnalysis::run(Module &M, ModuleAnalysisManager &AM) { @@ -843,6 +948,17 @@ DXILResourceBindingPrinterPass::run(Module &M, ModuleAnalysisManager &AM) { return PreservedAnalyses::all(); } +INITIALIZE_PASS(DXILResourceCounterDirectionWrapperPass, + "dxil-resource-counter", "DXIL Resource Counter Analysis", + false, true) + +DXILResourceCounterDirectionWrapperPass:: + DXILResourceCounterDirectionWrapperPass() + : ModulePass(ID) { + initializeDXILResourceCounterDirectionWrapperPassPass( + *PassRegistry::getPassRegistry()); +} + void DXILResourceTypeWrapperPass::anchor() {} DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass() : ImmutablePass(ID) { @@ -852,11 +968,16 @@ DXILResourceTypeWrapperPass::DXILResourceTypeWrapperPass() : ImmutablePass(ID) { INITIALIZE_PASS(DXILResourceTypeWrapperPass, "dxil-resource-type", "DXIL Resource Type Analysis", false, true) char DXILResourceTypeWrapperPass::ID = 0; +char DXILResourceCounterDirectionWrapperPass::ID = 0; ModulePass *llvm::createDXILResourceTypeWrapperPassPass() { return new DXILResourceTypeWrapperPass(); } +ModulePass *llvm::createDXILResourceCounterDirectionWrapperPassPass() { + return new DXILResourceCounterDirectionWrapperPass(); +} + DXILResourceBindingWrapperPass::DXILResourceBindingWrapperPass() : ModulePass(ID) { initializeDXILResourceBindingWrapperPassPass( diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 49135c5e1a658..7915347fbddae 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -24,6 +24,7 @@ MODULE_ANALYSIS("ctx-prof-analysis", CtxProfAnalysis()) MODULE_ANALYSIS("dxil-metadata", DXILMetadataAnalysis()) MODULE_ANALYSIS("dxil-resource-binding", DXILResourceBindingAnalysis()) MODULE_ANALYSIS("dxil-resource-type", DXILResourceTypeAnalysis()) +MODULE_ANALYSIS("dxil-resource-counter-direction", DXILResourceCounterDirectionAnalysis()) MODULE_ANALYSIS("inline-advisor", InlineAdvisorAnalysis()) MODULE_ANALYSIS("ir-similarity", IRSimilarityAnalysis()) MODULE_ANALYSIS("last-run-tracking", LastRunTrackingAnalysis()) diff --git a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp index f272381c0c250..9d9ade897b6e2 100644 --- a/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp +++ b/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp @@ -11,6 +11,7 @@ #include "llvm/AsmParser/Parser.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicsDirectX.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" @@ -28,18 +29,28 @@ class UniqueResourceFromUseTest : public testing::Test { protected: PassBuilder *PB; ModuleAnalysisManager *MAM; - + LLVMContext *Context; virtual void SetUp() { + Context = new LLVMContext(); MAM = new ModuleAnalysisManager(); PB = new PassBuilder(); PB->registerModuleAnalyses(*MAM); MAM->registerPass([&] { return DXILResourceTypeAnalysis(); }); MAM->registerPass([&] { return DXILResourceBindingAnalysis(); }); + MAM->registerPass([&] { return DXILResourceCounterDirectionAnalysis(); }); + } + + std::unique_ptr parseAsm(StringRef Asm) { + SMDiagnostic Error; + std::unique_ptr M = parseAssemblyString(Asm, Error, *Context); + EXPECT_TRUE(M) << "Bad assembly?: " << Error.getMessage(); + return M; } virtual void TearDown() { delete PB; delete MAM; + delete Context; } }; @@ -47,20 +58,16 @@ TEST_F(UniqueResourceFromUseTest, TestTrivialUse) { StringRef Assembly = R"( define void @main() { entry: - %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) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) call void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) ret void } -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); const DXILBindingMap &DBM = MAM->getResult(*M); for (const Function &F : M->functions()) { @@ -77,7 +84,7 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) ASSERT_EQ(Bindings.size(), 1u) << "Handle should resolve into one resource"; - auto Binding = Bindings[0].getBinding(); + auto Binding = Bindings[0]->getBinding(); EXPECT_EQ(0u, Binding.RecordID); EXPECT_EQ(1u, Binding.Space); EXPECT_EQ(2u, Binding.LowerBound); @@ -94,7 +101,7 @@ declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) TEST_F(UniqueResourceFromUseTest, TestIndirectUse) { StringRef Assembly = R"( define void @foo() { - %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) + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) %handle2 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle) %handle3 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle2) %handle4 = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle3) @@ -102,15 +109,11 @@ define void @foo() { ret void } -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %handle) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); const DXILBindingMap &DBM = MAM->getResult(*M); for (const Function &F : M->functions()) { @@ -127,7 +130,7 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo ASSERT_EQ(Bindings.size(), 1u) << "Handle should resolve into one resource"; - auto Binding = Bindings[0].getBinding(); + auto Binding = Bindings[0]->getBinding(); EXPECT_EQ(0u, Binding.RecordID); EXPECT_EQ(1u, Binding.Space); EXPECT_EQ(2u, Binding.LowerBound); @@ -144,10 +147,10 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo TEST_F(UniqueResourceFromUseTest, TestAmbigousIndirectUse) { StringRef Assembly = R"( define void @foo() { - %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) - %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) - %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) - %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) + %foo = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 1, i32 1, i32 1, i1 false) + %bar = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 2, i32 2, i32 2, i32 2, i1 false) + %baz = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 3, i32 3, i32 3, i32 3, i1 false) + %bat = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 4, i32 4, i32 4, i1 false) %a = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %foo, target("dx.RawBuffer", float, 1, 0) %bar) %b = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %baz, target("dx.RawBuffer", float, 1, 0) %bat) %handle = call target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %a, target("dx.RawBuffer", float, 1, 0) %b) @@ -155,15 +158,11 @@ define void @foo() { ret void } -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x, target("dx.RawBuffer", float, 1, 0) %y) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); const DXILBindingMap &DBM = MAM->getResult(*M); for (const Function &F : M->functions()) { @@ -180,25 +179,25 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo ASSERT_EQ(Bindings.size(), 4u) << "Handle should resolve into four resources"; - auto Binding = Bindings[0].getBinding(); + auto Binding = Bindings[0]->getBinding(); EXPECT_EQ(0u, Binding.RecordID); EXPECT_EQ(1u, Binding.Space); EXPECT_EQ(1u, Binding.LowerBound); EXPECT_EQ(1u, Binding.Size); - Binding = Bindings[1].getBinding(); + Binding = Bindings[1]->getBinding(); EXPECT_EQ(1u, Binding.RecordID); EXPECT_EQ(2u, Binding.Space); EXPECT_EQ(2u, Binding.LowerBound); EXPECT_EQ(2u, Binding.Size); - Binding = Bindings[2].getBinding(); + Binding = Bindings[2]->getBinding(); EXPECT_EQ(2u, Binding.RecordID); EXPECT_EQ(3u, Binding.Space); EXPECT_EQ(3u, Binding.LowerBound); EXPECT_EQ(3u, Binding.Size); - Binding = Bindings[3].getBinding(); + Binding = Bindings[3]->getBinding(); EXPECT_EQ(3u, Binding.RecordID); EXPECT_EQ(4u, Binding.Space); EXPECT_EQ(4u, Binding.LowerBound); @@ -216,8 +215,8 @@ TEST_F(UniqueResourceFromUseTest, TestConditionalUse) { StringRef Assembly = R"( define void @foo(i32 %n) { entry: - %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) - %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) + %x = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 1, i32 1, i32 1, i1 false) + %y = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 4, i32 4, i32 4, i1 false) %cond = icmp eq i32 %n, 0 br i1 %cond, label %bb.true, label %bb.false @@ -235,15 +234,11 @@ bb.exit: ret void } -declare target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32, i32, i32, i32, i1) declare void @a.func(target("dx.RawBuffer", float, 1, 0) %handle) declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", float, 1, 0) %x) )"; - LLVMContext Context; - SMDiagnostic Error; - auto M = parseAssemblyString(Assembly, Error, Context); - ASSERT_TRUE(M) << "Bad assembly?"; + auto M = parseAsm(Assembly); const DXILBindingMap &DBM = MAM->getResult(*M); for (const Function &F : M->functions()) { @@ -260,13 +255,13 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo ASSERT_EQ(Bindings.size(), 2u) << "Handle should resolve into four resources"; - auto Binding = Bindings[0].getBinding(); + auto Binding = Bindings[0]->getBinding(); EXPECT_EQ(0u, Binding.RecordID); EXPECT_EQ(1u, Binding.Space); EXPECT_EQ(1u, Binding.LowerBound); EXPECT_EQ(1u, Binding.Size); - Binding = Bindings[1].getBinding(); + Binding = Bindings[1]->getBinding(); EXPECT_EQ(1u, Binding.RecordID); EXPECT_EQ(4u, Binding.Space); EXPECT_EQ(4u, Binding.LowerBound); @@ -280,4 +275,169 @@ declare target("dx.RawBuffer", float, 1, 0) @ind.func(target("dx.RawBuffer", flo } } +// Test that several calls to decrement on the same resource don't raise a +// Diagnositic and resolves to a single decrement entry +TEST_F(UniqueResourceFromUseTest, TestResourceCounterDecrement) { + StringRef Assembly = R"( +define void @main() { +entry: + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) + ret void +} + )"; + + auto M = parseAsm(Assembly); + + const DXILBindingMap &DBM = MAM->getResult(*M); + const DXILResourceCounterDirectionMap &DCDM = + MAM->getResult(*M); + + for (const Function &F : M->functions()) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + continue; + } + + for (const User *U : F.users()) { + const CallInst *CI = cast(U); + const auto *const Binding = DBM.find(CI); + ASSERT_EQ(DCDM[*Binding], ResourceCounterDirection::Decrement); + } + } +} + +// Test that several calls to increment on the same resource don't raise a +// Diagnositic and resolves to a single increment entry +TEST_F(UniqueResourceFromUseTest, TestResourceCounterIncrement) { + StringRef Assembly = R"( +define void @main() { +entry: + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) + ret void +} + )"; + + auto M = parseAsm(Assembly); + + const DXILBindingMap &DBM = MAM->getResult(*M); + const DXILResourceCounterDirectionMap &DCDM = + MAM->getResult(*M); + + for (const Function &F : M->functions()) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + continue; + } + + for (const User *U : F.users()) { + const CallInst *CI = cast(U); + const auto *const Binding = DBM.find(CI); + ASSERT_EQ(DCDM[*Binding], ResourceCounterDirection::Increment); + } + } +} + +// Test that looking up a resource that doesn't have the counter updated +// resoves to unknown +TEST_F(UniqueResourceFromUseTest, TestResourceCounterUnknown) { + StringRef Assembly = R"( +define void @main() { +entry: + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + ret void +} + )"; + + auto M = parseAsm(Assembly); + + const DXILBindingMap &DBM = MAM->getResult(*M); + const DXILResourceCounterDirectionMap &DCDM = + MAM->getResult(*M); + + for (const Function &F : M->functions()) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + continue; + } + + for (const User *U : F.users()) { + const CallInst *CI = cast(U); + const auto *const Binding = DBM.find(CI); + ASSERT_EQ(DCDM[*Binding], ResourceCounterDirection::Unknown); + } + } +} + +// Test that multiple different resources with unique incs/decs aren't +// marked invalid +TEST_F(UniqueResourceFromUseTest, TestResourceCounterMultiple) { + StringRef Assembly = R"( +define void @main() { +entry: + %handle1 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + %handle2 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 3, i32 2, i32 1, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle1, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle2, i8 1) + ret void +} + )"; + + auto M = parseAsm(Assembly); + + const DXILBindingMap &DBM = MAM->getResult(*M); + const DXILResourceCounterDirectionMap &DCDM = + MAM->getResult(*M); + + ResourceCounterDirection Dirs[2] = {ResourceCounterDirection::Decrement, + ResourceCounterDirection::Increment}; + ResourceCounterDirection *Dir = Dirs; + + for (const Function &F : M->functions()) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + continue; + } + + for (const User *U : F.users()) { + const CallInst *CI = cast(U); + const auto *const Binding = DBM.find(CI); + ASSERT_EQ(DCDM[*Binding], *Dir); + Dir++; + } + } +} + +// Test that single different resources with unique incs/decs is marked invalid +TEST_F(UniqueResourceFromUseTest, TestResourceCounterInvalid) { + StringRef Assembly = R"( +define void @main() { +entry: + %handle = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 1, i32 2, i32 3, i32 4, i1 false) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 -1) + call i32 @llvm.dx.resource.updatecounter(target("dx.RawBuffer", float, 1, 0) %handle, i8 1) + ret void +} + )"; + + auto M = parseAsm(Assembly); + + const DXILBindingMap &DBM = MAM->getResult(*M); + const DXILResourceCounterDirectionMap &DCDM = + MAM->getResult(*M); + + for (const Function &F : M->functions()) { + if (F.getIntrinsicID() != Intrinsic::dx_resource_handlefrombinding) { + continue; + } + + for (const User *U : F.users()) { + const CallInst *CI = cast(U); + const auto *const Binding = DBM.find(CI); + ASSERT_EQ(DCDM[*Binding], ResourceCounterDirection::Invalid); + } + } +} + } // namespace