llvm-project/llvm/unittests/Target/DirectX/UniqueResourceFromUseTests.cpp
Helena Kotas 27675ccdd6
[DirectX] Add resource name argument to llvm.dx.handlefrom[implicit]binding intrinsics (#139991)
Adds resource name argument to `llvm.dx.handlefrombinding` and `llvm.dx.handlefromimplicitbinding` intrinsics.
SPIR-V currently does not seem to need the resource names so this change only affects DirectX binding intrinsics.

Part 2/4 of https://github.com/llvm/llvm-project/issues/105059
2025-05-27 22:57:01 -07:00

209 lines
7.1 KiB
C++

//===- llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.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 "DirectXTargetMachine.h"
#include "llvm/Analysis/DXILResource.h"
#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"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::dxil;
namespace {
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 DXILResourceAnalysis(); });
}
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;
}
};
// 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, ptr null)
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);
DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(U);
const auto *const Binding = DRM.find(CI);
ASSERT_EQ(Binding->CounterDirection, 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, ptr null)
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);
DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(U);
const auto *const Binding = DRM.find(CI);
ASSERT_EQ(Binding->CounterDirection, 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, ptr null)
ret void
}
)";
auto M = parseAsm(Assembly);
DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(U);
const auto *const Binding = DRM.find(CI);
ASSERT_EQ(Binding->CounterDirection, 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, ptr null)
%handle2 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding(i32 4, i32 3, i32 2, i32 1, i1 false, ptr null)
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);
DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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;
uint32_t ExpectedDirsIndex = 0;
for (const User *U : F.users()) {
const CallInst *CI = cast<CallInst>(U);
const auto *const Binding = DRM.find(CI);
ASSERT_TRUE(ExpectedDirsIndex < 2);
ASSERT_EQ(Binding->CounterDirection, Dir[ExpectedDirsIndex]);
ExpectedDirsIndex++;
}
}
}
// 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, ptr null)
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);
DXILResourceMap &DRM = MAM->getResult<DXILResourceAnalysis>(*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<CallInst>(U);
const auto *const Binding = DRM.find(CI);
ASSERT_EQ(Binding->CounterDirection, ResourceCounterDirection::Invalid);
}
}
}
} // namespace