[llvm] aa61925 - [DirectX] Lower `@llvm.dx.handle.fromBinding` to DXIL ops
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 23 12:58:16 PDT 2024
Author: Justin Bogner
Date: 2024-08-23T12:58:12-07:00
New Revision: aa61925eace86602ce1da00bda4a993719061df2
URL: https://github.com/llvm/llvm-project/commit/aa61925eace86602ce1da00bda4a993719061df2
DIFF: https://github.com/llvm/llvm-project/commit/aa61925eace86602ce1da00bda4a993719061df2.diff
LOG: [DirectX] Lower `@llvm.dx.handle.fromBinding` to DXIL ops
The `@llvm.dx.handle.fromBinding` intrinsic is lowered either to the
`CreateHandle` op or a pair of `CreateHandleFromBinding` and `AnnotateHandle`
ops, depending on the DXIL version. Regardless of the DXIL version we need to
emit metadata about the binding, but that's left to a separate change.
These DXIL ops all need to return the `%dx.types.Handle` type, but the llvm
intrinsic returns a target extension type. To facilitate changing the type of
the operation and all of its users, we introduce `%llvm.dx.cast.handle`, which
can cast between the two handle representations.
Pull Request: https://github.com/llvm/llvm-project/pull/104251
Added:
llvm/test/CodeGen/DirectX/CreateHandle.ll
llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
Modified:
llvm/docs/DirectX/DXILResources.rst
llvm/include/llvm/IR/IntrinsicsDirectX.td
llvm/lib/Target/DirectX/DXIL.td
llvm/lib/Target/DirectX/DXILOpBuilder.cpp
llvm/lib/Target/DirectX/DXILOpBuilder.h
llvm/lib/Target/DirectX/DXILOpLowering.cpp
Removed:
################################################################################
diff --git a/llvm/docs/DirectX/DXILResources.rst b/llvm/docs/DirectX/DXILResources.rst
index aef88bc43b224d..a6ec80ce4329b2 100644
--- a/llvm/docs/DirectX/DXILResources.rst
+++ b/llvm/docs/DirectX/DXILResources.rst
@@ -162,6 +162,10 @@ the subsequent ``dx.op.annotateHandle`` operation in. Note that we don't have
an analogue for `dx.op.createHandle`_, since ``dx.op.createHandleFromBinding``
subsumes it.
+For simplicity of lowering, we match DXIL in using an index from the beginning
+of the binding space rather than an index from the lower bound of the binding
+itself.
+
.. _dx.op.createHandle: https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/DXIL.rst#resource-handles
.. list-table:: ``@llvm.dx.handle.fromBinding``
@@ -190,7 +194,7 @@ subsumes it.
* - ``%index``
- 4
- ``i32``
- - Index of the resource to access.
+ - Index from the beginning of the binding space to access.
* - ``%non-uniform``
- 5
- i1
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index e959e70dc1cd4f..32af50b25f3904 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -30,6 +30,9 @@ def int_dx_handle_fromBinding
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
[IntrNoMem]>;
+// Cast between target extension handle types and dxil-style opaque handles
+def int_dx_cast_handle : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
+
def int_dx_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
def int_dx_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
def int_dx_clamp : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index c4b278c109dbb9..83ea36ca048ad2 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -42,6 +42,8 @@ def FloatTy : DXILOpParamType;
def DoubleTy : DXILOpParamType;
def ResRetTy : DXILOpParamType;
def HandleTy : DXILOpParamType;
+def ResBindTy : DXILOpParamType;
+def ResPropsTy : DXILOpParamType;
class DXILOpClass;
@@ -683,6 +685,14 @@ def Dot4 : DXILOp<56, dot4> {
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
+def CreateHandle : DXILOp<57, createHandle> {
+ let Doc = "creates the handle to a resource";
+ // ResourceClass, RangeID, Index, NonUniform
+ let arguments = [Int8Ty, Int32Ty, Int32Ty, Int1Ty];
+ let result = HandleTy;
+ let stages = [Stages<DXIL1_0, [all_stages]>, Stages<DXIL1_6, [removed]>];
+}
+
def ThreadId : DXILOp<93, threadId> {
let Doc = "Reads the thread ID";
let LLVMIntrinsic = int_dx_thread_id;
@@ -722,3 +732,17 @@ def FlattenedThreadIdInGroup : DXILOp<96, flattenedThreadIdInGroup> {
let stages = [Stages<DXIL1_0, [compute, mesh, amplification, node]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
+
+def AnnotateHandle : DXILOp<217, annotateHandle> {
+ let Doc = "annotate handle with resource properties";
+ let arguments = [HandleTy, ResPropsTy];
+ let result = HandleTy;
+ let stages = [Stages<DXIL1_6, [all_stages]>];
+}
+
+def CreateHandleFromBinding : DXILOp<218, createHandleFromBinding> {
+ let Doc = "create resource handle from binding";
+ let arguments = [ResBindTy, Int32Ty, Int1Ty];
+ let result = HandleTy;
+ let stages = [Stages<DXIL1_6, [all_stages]>];
+}
diff --git a/llvm/lib/Target/DirectX/DXILOpBuilder.cpp b/llvm/lib/Target/DirectX/DXILOpBuilder.cpp
index 8e26483d675c89..ab3ea61d05fc45 100644
--- a/llvm/lib/Target/DirectX/DXILOpBuilder.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpBuilder.cpp
@@ -208,6 +208,23 @@ static StructType *getHandleType(LLVMContext &Ctx) {
Ctx);
}
+static StructType *getResBindType(LLVMContext &Context) {
+ if (auto *ST = StructType::getTypeByName(Context, "dx.types.ResBind"))
+ return ST;
+ Type *Int32Ty = Type::getInt32Ty(Context);
+ Type *Int8Ty = Type::getInt8Ty(Context);
+ return StructType::create({Int32Ty, Int32Ty, Int32Ty, Int8Ty},
+ "dx.types.ResBind");
+}
+
+static StructType *getResPropsType(LLVMContext &Context) {
+ if (auto *ST =
+ StructType::getTypeByName(Context, "dx.types.ResourceProperties"))
+ return ST;
+ Type *Int32Ty = Type::getInt32Ty(Context);
+ return StructType::create({Int32Ty, Int32Ty}, "dx.types.ResourceProperties");
+}
+
static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
Type *OverloadTy) {
switch (Kind) {
@@ -235,6 +252,10 @@ static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
return getResRetType(OverloadTy, Ctx);
case OpParamType::HandleTy:
return getHandleType(Ctx);
+ case OpParamType::ResBindTy:
+ return getResBindType(Ctx);
+ case OpParamType::ResPropsTy:
+ return getResPropsType(Ctx);
}
llvm_unreachable("Invalid parameter kind");
return nullptr;
@@ -430,6 +451,29 @@ CallInst *DXILOpBuilder::createOp(dxil::OpCode OpCode, ArrayRef<Value *> Args,
return *Result;
}
+StructType *DXILOpBuilder::getHandleType() {
+ return ::getHandleType(IRB.getContext());
+}
+
+Constant *DXILOpBuilder::getResBind(uint32_t LowerBound, uint32_t UpperBound,
+ uint32_t SpaceID, dxil::ResourceClass RC) {
+ Type *Int32Ty = IRB.getInt32Ty();
+ Type *Int8Ty = IRB.getInt8Ty();
+ return ConstantStruct::get(
+ getResBindType(IRB.getContext()),
+ {ConstantInt::get(Int32Ty, LowerBound),
+ ConstantInt::get(Int32Ty, UpperBound),
+ ConstantInt::get(Int32Ty, SpaceID),
+ ConstantInt::get(Int8Ty, llvm::to_underlying(RC))});
+}
+
+Constant *DXILOpBuilder::getResProps(uint32_t Word0, uint32_t Word1) {
+ Type *Int32Ty = IRB.getInt32Ty();
+ return ConstantStruct::get(
+ getResPropsType(IRB.getContext()),
+ {ConstantInt::get(Int32Ty, Word0), ConstantInt::get(Int32Ty, Word1)});
+}
+
const char *DXILOpBuilder::getOpCodeName(dxil::OpCode DXILOp) {
return ::getOpCodeName(DXILOp);
}
diff --git a/llvm/lib/Target/DirectX/DXILOpBuilder.h b/llvm/lib/Target/DirectX/DXILOpBuilder.h
index 483d5ddc8b6197..4a55a8ac9eadb5 100644
--- a/llvm/lib/Target/DirectX/DXILOpBuilder.h
+++ b/llvm/lib/Target/DirectX/DXILOpBuilder.h
@@ -15,6 +15,7 @@
#include "DXILConstants.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/IRBuilder.h"
+#include "llvm/Support/DXILABI.h"
#include "llvm/Support/Error.h"
#include "llvm/TargetParser/Triple.h"
@@ -22,6 +23,7 @@ namespace llvm {
class Module;
class IRBuilderBase;
class CallInst;
+class Constant;
class Value;
class Type;
class FunctionType;
@@ -44,6 +46,15 @@ class DXILOpBuilder {
Expected<CallInst *> tryCreateOp(dxil::OpCode Op, ArrayRef<Value *> Args,
Type *RetTy = nullptr);
+ /// Get the `%dx.types.Handle` type.
+ StructType *getHandleType();
+
+ /// Get a constant `%dx.types.ResBind` value.
+ Constant *getResBind(uint32_t LowerBound, uint32_t UpperBound,
+ uint32_t SpaceID, dxil::ResourceClass RC);
+ /// Get a constant `%dx.types.ResourceProperties` value.
+ Constant *getResProps(uint32_t Word0, uint32_t Word1);
+
/// Return the name of the given opcode.
static const char *getOpCodeName(dxil::OpCode DXILOp);
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index fb708a61dd318d..1f6d37087bc9f4 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -12,6 +12,7 @@
#include "DXILOpBuilder.h"
#include "DirectX.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/Analysis/DXILResource.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/IRBuilder.h"
@@ -20,6 +21,7 @@
#include "llvm/IR/IntrinsicsDirectX.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
+#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/ErrorHandling.h"
@@ -74,9 +76,11 @@ namespace {
class OpLowerer {
Module &M;
DXILOpBuilder OpBuilder;
+ DXILResourceMap &DRM;
+ SmallVector<CallInst *> CleanupCasts;
public:
- OpLowerer(Module &M) : M(M), OpBuilder(M) {}
+ OpLowerer(Module &M, DXILResourceMap &DRM) : M(M), OpBuilder(M), DRM(DRM) {}
void replaceFunction(Function &F,
llvm::function_ref<Error(CallInst *CI)> ReplaceCall) {
@@ -119,6 +123,142 @@ class OpLowerer {
});
}
+ /// Create a cast between a `target("dx")` type and `dx.types.Handle`, which
+ /// is intended to be removed by the end of lowering. This is used to allow
+ /// lowering of ops which need to change their return or argument types in a
+ /// piecemeal way - we can add the casts in to avoid updating all of the uses
+ /// or defs, and by the end all of the casts will be redundant.
+ Value *createTmpHandleCast(Value *V, Type *Ty) {
+ Function *CastFn = Intrinsic::getDeclaration(&M, Intrinsic::dx_cast_handle,
+ {Ty, V->getType()});
+ CallInst *Cast = OpBuilder.getIRB().CreateCall(CastFn, {V});
+ CleanupCasts.push_back(Cast);
+ return Cast;
+ }
+
+ void cleanupHandleCasts() {
+ SmallVector<CallInst *> ToRemove;
+ SmallVector<Function *> CastFns;
+
+ for (CallInst *Cast : CleanupCasts) {
+ // These casts were only put in to ease the move from `target("dx")` types
+ // to `dx.types.Handle in a piecemeal way. At this point, all of the
+ // non-cast uses should now be `dx.types.Handle`, and remaining casts
+ // should all form pairs to and from the now unused `target("dx")` type.
+ CastFns.push_back(Cast->getCalledFunction());
+
+ // If the cast is not to `dx.types.Handle`, it should be the first part of
+ // the pair. Keep track so we can remove it once it has no more uses.
+ if (Cast->getType() != OpBuilder.getHandleType()) {
+ ToRemove.push_back(Cast);
+ continue;
+ }
+ // Otherwise, we're the second handle in a pair. Forward the arguments and
+ // remove the (second) cast.
+ CallInst *Def = cast<CallInst>(Cast->getOperand(0));
+ assert(Def->getIntrinsicID() == Intrinsic::dx_cast_handle &&
+ "Unbalanced pair of temporary handle casts");
+ Cast->replaceAllUsesWith(Def->getOperand(0));
+ Cast->eraseFromParent();
+ }
+ for (CallInst *Cast : ToRemove) {
+ assert(Cast->user_empty() && "Temporary handle cast still has users");
+ Cast->eraseFromParent();
+ }
+
+ // Deduplicate the cast functions so that we only erase each one once.
+ llvm::sort(CastFns);
+ CastFns.erase(llvm::unique(CastFns), CastFns.end());
+ for (Function *F : CastFns)
+ F->eraseFromParent();
+
+ CleanupCasts.clear();
+ }
+
+ void lowerToCreateHandle(Function &F) {
+ IRBuilder<> &IRB = OpBuilder.getIRB();
+ Type *Int8Ty = IRB.getInt8Ty();
+ Type *Int32Ty = IRB.getInt32Ty();
+
+ replaceFunction(F, [&](CallInst *CI) -> Error {
+ IRB.SetInsertPoint(CI);
+
+ auto *It = DRM.find(CI);
+ assert(It != DRM.end() && "Resource not in map?");
+ dxil::ResourceInfo &RI = *It;
+ const auto &Binding = RI.getBinding();
+
+ std::array<Value *, 4> Args{
+ ConstantInt::get(Int8Ty, llvm::to_underlying(RI.getResourceClass())),
+ ConstantInt::get(Int32Ty, Binding.RecordID), CI->getArgOperand(3),
+ CI->getArgOperand(4)};
+ Expected<CallInst *> OpCall =
+ OpBuilder.tryCreateOp(OpCode::CreateHandle, Args);
+ if (Error E = OpCall.takeError())
+ return E;
+
+ Value *Cast = createTmpHandleCast(*OpCall, CI->getType());
+
+ CI->replaceAllUsesWith(Cast);
+ CI->eraseFromParent();
+ return Error::success();
+ });
+ }
+
+ void lowerToBindAndAnnotateHandle(Function &F) {
+ IRBuilder<> &IRB = OpBuilder.getIRB();
+
+ replaceFunction(F, [&](CallInst *CI) -> Error {
+ IRB.SetInsertPoint(CI);
+
+ auto *It = DRM.find(CI);
+ assert(It != DRM.end() && "Resource not in map?");
+ dxil::ResourceInfo &RI = *It;
+
+ const auto &Binding = RI.getBinding();
+ std::pair<uint32_t, uint32_t> Props = RI.getAnnotateProps();
+
+ // For `CreateHandleFromBinding` we need the upper bound rather than the
+ // size, so we need to be careful about the
diff erence for "unbounded".
+ uint32_t Unbounded = std::numeric_limits<uint32_t>::max();
+ uint32_t UpperBound = Binding.Size == Unbounded
+ ? Unbounded
+ : Binding.LowerBound + Binding.Size - 1;
+ Constant *ResBind = OpBuilder.getResBind(
+ Binding.LowerBound, UpperBound, Binding.Space, RI.getResourceClass());
+ std::array<Value *, 3> BindArgs{ResBind, CI->getArgOperand(3),
+ CI->getArgOperand(4)};
+ Expected<CallInst *> OpBind =
+ OpBuilder.tryCreateOp(OpCode::CreateHandleFromBinding, BindArgs);
+ if (Error E = OpBind.takeError())
+ return E;
+
+ std::array<Value *, 2> AnnotateArgs{
+ *OpBind, OpBuilder.getResProps(Props.first, Props.second)};
+ Expected<CallInst *> OpAnnotate =
+ OpBuilder.tryCreateOp(OpCode::AnnotateHandle, AnnotateArgs);
+ if (Error E = OpAnnotate.takeError())
+ return E;
+
+ Value *Cast = createTmpHandleCast(*OpAnnotate, CI->getType());
+
+ CI->replaceAllUsesWith(Cast);
+ CI->eraseFromParent();
+
+ return Error::success();
+ });
+ }
+
+ /// Lower `dx.handle.fromBinding` intrinsics depending on the shader model and
+ /// taking into account binding information from DXILResourceAnalysis.
+ void lowerHandleFromBinding(Function &F) {
+ Triple TT(Triple(M.getTargetTriple()));
+ if (TT.getDXILVersion() < VersionTuple(1, 6))
+ lowerToCreateHandle(F);
+ else
+ lowerToBindAndAnnotateHandle(F);
+ }
+
bool lowerIntrinsics() {
bool Updated = false;
@@ -134,33 +274,47 @@ class OpLowerer {
replaceFunctionWithOp(F, OpCode); \
break;
#include "DXILOperation.inc"
+ case Intrinsic::dx_handle_fromBinding:
+ lowerHandleFromBinding(F);
}
Updated = true;
}
+ if (Updated)
+ cleanupHandleCasts();
+
return Updated;
}
};
} // namespace
-PreservedAnalyses DXILOpLowering::run(Module &M, ModuleAnalysisManager &) {
- if (OpLowerer(M).lowerIntrinsics())
- return PreservedAnalyses::none();
- return PreservedAnalyses::all();
+PreservedAnalyses DXILOpLowering::run(Module &M, ModuleAnalysisManager &MAM) {
+ DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
+
+ bool MadeChanges = OpLowerer(M, DRM).lowerIntrinsics();
+ if (!MadeChanges)
+ return PreservedAnalyses::all();
+ PreservedAnalyses PA;
+ PA.preserve<DXILResourceAnalysis>();
+ return PA;
}
namespace {
class DXILOpLoweringLegacy : public ModulePass {
public:
bool runOnModule(Module &M) override {
- return OpLowerer(M).lowerIntrinsics();
+ DXILResourceMap &DRM =
+ getAnalysis<DXILResourceWrapperPass>().getResourceMap();
+
+ return OpLowerer(M, DRM).lowerIntrinsics();
}
StringRef getPassName() const override { return "DXIL Op Lowering"; }
DXILOpLoweringLegacy() : ModulePass(ID) {}
static char ID; // Pass identification.
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
- // Specify the passes that your pass depends on
AU.addRequired<DXILIntrinsicExpansionLegacy>();
+ AU.addRequired<DXILResourceWrapperPass>();
+ AU.addPreserved<DXILResourceWrapperPass>();
}
};
char DXILOpLoweringLegacy::ID = 0;
@@ -168,6 +322,7 @@ char DXILOpLoweringLegacy::ID = 0;
INITIALIZE_PASS_BEGIN(DXILOpLoweringLegacy, DEBUG_TYPE, "DXIL Op Lowering",
false, false)
+INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass)
INITIALIZE_PASS_END(DXILOpLoweringLegacy, DEBUG_TYPE, "DXIL Op Lowering", false,
false)
diff --git a/llvm/test/CodeGen/DirectX/CreateHandle.ll b/llvm/test/CodeGen/DirectX/CreateHandle.ll
new file mode 100644
index 00000000000000..13d59c6caf6c95
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/CreateHandle.ll
@@ -0,0 +1,53 @@
+; RUN: opt -S -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.0-compute"
+
+declare i32 @some_val();
+
+define void @test_buffers() {
+ ; RWBuffer<float4> Buf : register(u5, space3)
+ %typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0(
+ i32 3, i32 5, i32 1, i32 4, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 4, i1 false)
+ ; CHECK-NOT: @llvm.dx.cast.handle
+
+ ; RWBuffer<int> Buf : register(u7, space2)
+ %typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_1t(
+ i32 2, i32 7, i32 1, i32 6, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 0, i32 6, i1 false)
+
+ ; Buffer<uint4> Buf[24] : register(t3, space5)
+ ; Buffer<uint4> typed2 = Buf[4]
+ ; Note that the index below is 3 + 4 = 7
+ %typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
+ i32 5, i32 3, i32 24, i32 7, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 3, i32 7, i1 false)
+
+ ; struct S { float4 a; uint4 b; };
+ ; StructuredBuffer<S> Buf : register(t2, space4)
+ %struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
+ i32 4, i32 2, i32 1, i32 10, i1 true)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 10, i1 true)
+
+ ; ByteAddressBuffer Buf : register(t8, space1)
+ %byteaddr0 = call target("dx.RawBuffer", i8, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
+ i32 1, i32 8, i32 1, i32 12, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 12, i1 false)
+
+ ; Buffer<float4> Buf[] : register(t0)
+ ; Buffer<float4> typed3 = Buf[ix]
+ %typed3_ix = call i32 @some_val()
+ %typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0t(
+ i32 0, i32 0, i32 -1, i32 %typed3_ix, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 %typed3_ix, i1 false)
+
+ ret void
+}
+
+attributes #0 = { nocallback nofree nosync nounwind willreturn memory(none) }
diff --git a/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
new file mode 100644
index 00000000000000..e78a0bf02e4ae3
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
@@ -0,0 +1,58 @@
+; RUN: opt -S -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+declare i32 @some_val();
+
+define void @test_bindings() {
+ ; RWBuffer<float4> Buf : register(u5, space3)
+ %typed0 = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0(
+ i32 3, i32 5, i32 1, i32 4, i1 false)
+ ; CHECK: [[BUF0:%[0-9]*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 218, %dx.types.ResBind { i32 5, i32 5, i32 3, i8 1 }, i32 4, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 217, %dx.types.Handle [[BUF0]], %dx.types.ResourceProperties { i32 4106, i32 1033 })
+
+ ; RWBuffer<int> Buf : register(u7, space2)
+ %typed1 = call target("dx.TypedBuffer", i32, 1, 0, 1)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_0t(
+ i32 2, i32 7, i32 1, i32 6, i1 false)
+ ; CHECK: [[BUF1:%[0-9]*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 218, %dx.types.ResBind { i32 7, i32 7, i32 2, i8 1 }, i32 6, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 217, %dx.types.Handle [[BUF1]], %dx.types.ResourceProperties { i32 4106, i32 260 })
+
+ ; Buffer<uint4> Buf[24] : register(t3, space5)
+ ; Buffer<uint4> typed2 = Buf[4]
+ ; Note that the index below is 3 + 4 = 7
+ %typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
+ i32 5, i32 3, i32 24, i32 7, i1 false)
+ ; CHECK: [[BUF2:%[0-9]*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 218, %dx.types.ResBind { i32 3, i32 26, i32 5, i8 0 }, i32 7, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 217, %dx.types.Handle [[BUF2]], %dx.types.ResourceProperties { i32 10, i32 1029 })
+
+ ; struct S { float4 a; uint4 b; };
+ ; StructuredBuffer<S> Buf : register(t2, space4)
+ %struct0 = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
+ i32 4, i32 2, i32 1, i32 10, i1 true)
+ ; CHECK: [[BUF3:%[0-9]*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 218, %dx.types.ResBind { i32 2, i32 2, i32 4, i8 0 }, i32 10, i1 true)
+ ; CHECK: = call %dx.types.Handle @dx.op.annotateHandle(i32 217, %dx.types.Handle [[BUF3]], %dx.types.ResourceProperties { i32 1036, i32 32 })
+
+ ; ByteAddressBuffer Buf : register(t8, space1)
+ %byteaddr0 = call target("dx.RawBuffer", i8, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
+ i32 1, i32 8, i32 1, i32 12, i1 false)
+ ; CHECK: [[BUF4:%[0-9]*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 218, %dx.types.ResBind { i32 8, i32 8, i32 1, i8 0 }, i32 12, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 217, %dx.types.Handle [[BUF4]], %dx.types.ResourceProperties { i32 11, i32 0 })
+
+ ; Buffer<float4> Buf[] : register(t0)
+ ; Buffer<float4> typed3 = Buf[ix]
+ %typed3_ix = call i32 @some_val()
+ %typed3 = call target("dx.TypedBuffer", <4 x float>, 0, 0, 0)
+ @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_0_0_0t(
+ i32 0, i32 0, i32 -1, i32 %typed3_ix, i1 false)
+ ; CHECK: [[BUF5:%[0-9]*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 218, %dx.types.ResBind { i32 0, i32 -1, i32 0, i8 0 }, i32 %typed3_ix, i1 false)
+ ; CHECK: call %dx.types.Handle @dx.op.annotateHandle(i32 217, %dx.types.Handle [[BUF5]], %dx.types.ResourceProperties { i32 10, i32 1033 })
+
+ ret void
+}
+
+attributes #0 = { nocallback nofree nosync nounwind willreturn memory(none) }
More information about the llvm-commits
mailing list