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]; } +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, Stages]; +} + 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]; let attributes = [Attributes]; } + +def AnnotateHandle : DXILOp<217, annotateHandle> { + let Doc = "annotate handle with resource properties"; + let arguments = [HandleTy, ResPropsTy]; + let result = HandleTy; + let stages = [Stages]; +} + +def CreateHandleFromBinding : DXILOp<218, createHandleFromBinding> { + let Doc = "create resource handle from binding"; + let arguments = [ResBindTy, Int32Ty, Int1Ty]; + let result = HandleTy; + let stages = [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 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 tryCreateOp(dxil::OpCode Op, ArrayRef 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 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 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 ToRemove; + SmallVector 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(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 Args{ + ConstantInt::get(Int8Ty, llvm::to_underlying(RI.getResourceClass())), + ConstantInt::get(Int32Ty, Binding.RecordID), CI->getArgOperand(3), + CI->getArgOperand(4)}; + Expected 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 Props = RI.getAnnotateProps(); + + // For `CreateHandleFromBinding` we need the upper bound rather than the + // size, so we need to be careful about the difference for "unbounded". + uint32_t Unbounded = std::numeric_limits::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 BindArgs{ResBind, CI->getArgOperand(3), + CI->getArgOperand(4)}; + Expected OpBind = + OpBuilder.tryCreateOp(OpCode::CreateHandleFromBinding, BindArgs); + if (Error E = OpBind.takeError()) + return E; + + std::array AnnotateArgs{ + *OpBind, OpBuilder.getResProps(Props.first, Props.second)}; + Expected 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(M); + + bool MadeChanges = OpLowerer(M, DRM).lowerIntrinsics(); + if (!MadeChanges) + return PreservedAnalyses::all(); + PreservedAnalyses PA; + PA.preserve(); + return PA; } namespace { class DXILOpLoweringLegacy : public ModulePass { public: bool runOnModule(Module &M) override { - return OpLowerer(M).lowerIntrinsics(); + DXILResourceMap &DRM = + getAnalysis().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(); + AU.addRequired(); + AU.addPreserved(); } }; 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 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 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 Buf[24] : register(t3, space5) + ; Buffer 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 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 Buf[] : register(t0) + ; Buffer 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 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 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 Buf[24] : register(t3, space5) + ; Buffer 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 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 Buf[] : register(t0) + ; Buffer 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) }