[llvm] [DirectX] Implement DXILResourceBindingAnalysis (PR #137258)
Helena Kotas via llvm-commits
llvm-commits at lists.llvm.org
Thu May 8 22:19:42 PDT 2025
================
@@ -0,0 +1,245 @@
+//===- llvm/unittests/Target/DirectX/ResourceBindingAnalysisTests.cpp -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Analysis/DXILResource.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/DXILABI.h"
+#include "gtest/gtest.h"
+#include <cstdint>
+
+using namespace llvm;
+using namespace llvm::dxil;
+
+namespace {
+class ResourceBindingAnalysisTest : public testing::Test {
+protected:
+ PassBuilder *PB;
+ ModuleAnalysisManager *MAM;
+ LLVMContext *Context;
+
+ virtual void SetUp() {
+ PB = new PassBuilder();
+ MAM = new ModuleAnalysisManager();
+ Context = new LLVMContext();
+ PB->registerModuleAnalyses(*MAM);
+ MAM->registerPass([&] { return DXILResourceBindingAnalysis(); });
+ }
+
+ std::unique_ptr<Module> parseAsm(StringRef Asm) {
+ SMDiagnostic Error;
+ std::unique_ptr<Module> M = parseAssemblyString(Asm, Error, *Context);
+ EXPECT_TRUE(M) << "Bad assembly?: " << Error.getMessage();
+ return M;
+ }
+
+ virtual void TearDown() {
+ delete PB;
+ delete MAM;
+ delete Context;
+ }
+
+ void checkExpectedSpaceAndFreeRanges(
+ DXILResourceBindingInfo::RegisterSpace &RegSpace, uint32_t ExpSpace,
+ ArrayRef<uint32_t> ExpValues) {
+ EXPECT_EQ(RegSpace.Space, ExpSpace);
+ EXPECT_EQ(RegSpace.FreeRanges.size() * 2, ExpValues.size());
+ unsigned I = 0;
+ for (auto &R : RegSpace.FreeRanges) {
+ EXPECT_EQ(R.LowerBound, ExpValues[I]);
+ EXPECT_EQ(R.UpperBound, ExpValues[I + 1]);
+ I += 2;
+ }
+ }
+};
+
+TEST_F(ResourceBindingAnalysisTest, TestTrivialCase) {
+ // RWBuffer<float> Buf : register(u5);
+ StringRef Assembly = R"(
+define void @main() {
+entry:
+ %handle = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ // check that UAV has exactly one gap
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0,
+ {0, 4, 6, UINT32_MAX});
+
+ // check that other kinds of register spaces are all available
+ for (auto RC :
+ {ResourceClass::SRV, ResourceClass::CBuffer, ResourceClass::Sampler}) {
+ DXILResourceBindingInfo::BindingSpaces &Spaces = DRBI.getBindingSpaces(RC);
+ EXPECT_EQ(Spaces.ResClass, RC);
+ EXPECT_EQ(Spaces.Spaces.size(), 0u);
+ }
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestManyBindings) {
+ // cbuffer CB : register(b3) { int a; }
+ // RWBuffer<float4> A[5] : register(u10, space20);
+ // StructuredBuffer<int> B : register(t5);
+ // RWBuffer<float> C : register(u5);
+ // StructuredBuffer<int> D[5] : register(t0);
+ // RWBuffer<float> E[2] : register(u2);
+ StringRef Assembly = R"(
+%__cblayout_CB = type <{ i32 }>
+define void @main() {
+entry:
+ %handleCB = call target("dx.CBuffer", target("dx.Layout", %__cblayout_CB, 4, 0)) @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 1, i32 0, i1 false)
+ %handleA = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 20, i32 10, i32 5, i32 0, i1 false)
+ %handleB = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ %handleC = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 5, i32 1, i32 0, i1 false)
+ %handleD = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 5, i32 4, i1 false)
+ %handleE = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 2, i32 0, i1 false)
+ ret void
+}
+ )";
+
+ auto M = parseAsm(Assembly);
+
+ DXILResourceBindingInfo &DRBI =
+ MAM->getResult<DXILResourceBindingAnalysis>(*M);
+
+ EXPECT_EQ(false, DRBI.hasImplicitBinding());
+ EXPECT_EQ(false, DRBI.hasOverlappingBinding());
+
+ DXILResourceBindingInfo::BindingSpaces &SRVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.ResClass, ResourceClass::SRV);
+ EXPECT_EQ(SRVSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(SRVSpaces.Spaces[0], 0, {6, UINT32_MAX});
+
+ DXILResourceBindingInfo::BindingSpaces &UAVSpaces =
+ DRBI.getBindingSpaces(ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.ResClass, ResourceClass::UAV);
+ EXPECT_EQ(UAVSpaces.Spaces.size(), 2u);
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[0], 0,
+ {0, 1, 4, 4, 6, UINT32_MAX});
+ checkExpectedSpaceAndFreeRanges(UAVSpaces.Spaces[1], 20,
+ {0, 9, 15, UINT32_MAX});
+
+ DXILResourceBindingInfo::BindingSpaces &CBufferSpaces =
+ DRBI.getBindingSpaces(ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.ResClass, ResourceClass::CBuffer);
+ EXPECT_EQ(CBufferSpaces.Spaces.size(), 1u);
+ checkExpectedSpaceAndFreeRanges(CBufferSpaces.Spaces[0], 0,
+ {0, 2, 4, UINT32_MAX});
+}
+
+TEST_F(ResourceBindingAnalysisTest, TestUnboundedAndOverlap) {
----------------
hekota wrote:
I can add a test for exact overlap. For this PR it is will be XFAIL because we currently do not have a way to distinguish between 2 resources of the same type with identical binding. That will be addressed in https://github.com/llvm/llvm-project/issues/110723.
https://github.com/llvm/llvm-project/pull/137258
More information about the llvm-commits
mailing list