Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DirectX] Lower @llvm.dx.handle.fromBinding to DXIL ops #104251

Merged

Conversation

bogner
Copy link
Contributor

@bogner bogner commented Aug 14, 2024

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.

bogner added 2 commits August 15, 2024 00:28
Created using spr 1.3.5-bogner

[skip ci]
Created using spr 1.3.5-bogner
@llvmbot
Copy link
Member

llvmbot commented Aug 14, 2024

@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-directx

Author: Justin Bogner (bogner)

Changes

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.


Patch is 21.67 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/104251.diff

8 Files Affected:

  • (modified) llvm/include/llvm/Analysis/DXILResource.h (+8)
  • (modified) llvm/include/llvm/IR/IntrinsicsDirectX.td (+3)
  • (modified) llvm/lib/Target/DirectX/DXIL.td (+24)
  • (modified) llvm/lib/Target/DirectX/DXILOpBuilder.cpp (+44)
  • (modified) llvm/lib/Target/DirectX/DXILOpBuilder.h (+11)
  • (modified) llvm/lib/Target/DirectX/DXILOpLowering.cpp (+139-7)
  • (added) llvm/test/CodeGen/DirectX/CreateHandle.ll (+59)
  • (added) llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll (+63)
diff --git a/llvm/include/llvm/Analysis/DXILResource.h b/llvm/include/llvm/Analysis/DXILResource.h
index 3ba0ae5de61d5..2ed508b28a908 100644
--- a/llvm/include/llvm/Analysis/DXILResource.h
+++ b/llvm/include/llvm/Analysis/DXILResource.h
@@ -23,6 +23,7 @@ class TargetExtType;
 namespace dxil {
 
 class ResourceInfo {
+public:
   struct ResourceBinding {
     uint32_t RecordID;
     uint32_t Space;
@@ -89,6 +90,7 @@ class ResourceInfo {
     bool operator!=(const FeedbackInfo &RHS) const { return !(*this == RHS); }
   };
 
+private:
   // Universal properties.
   Value *Symbol;
   StringRef Name;
@@ -115,6 +117,10 @@ class ResourceInfo {
 
   MSInfo MultiSample;
 
+  // We need a default constructor if we want to insert this in a MapVector.
+  ResourceInfo() {}
+  friend class MapVector<CallInst *, ResourceInfo>;
+
 public:
   ResourceInfo(dxil::ResourceClass RC, dxil::ResourceKind Kind, Value *Symbol,
                StringRef Name)
@@ -166,6 +172,8 @@ class ResourceInfo {
     MultiSample.Count = Count;
   }
 
+  dxil::ResourceClass getResourceClass() const { return RC; }
+
   bool operator==(const ResourceInfo &RHS) const;
 
   static ResourceInfo SRV(Value *Symbol, StringRef Name,
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index c9102aa3dd972..ca3682fa47767 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 34c7f84b1ca5b..31fee04d82158 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;
 
@@ -673,6 +675,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]>];
+}
+
 def ThreadId :  DXILOp<93, threadId> {
   let Doc = "Reads the thread ID";
   let LLVMIntrinsic = int_dx_thread_id;
@@ -712,3 +722,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 7d2b40cc515cc..692af1b359ced 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 483d5ddc8b619..4a55a8ac9eadb 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 fb708a61dd318..ab18c57efa307 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,119 @@ class OpLowerer {
     });
   }
 
+  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) {
+      CastFns.push_back(Cast->getCalledFunction());
+      // All of the ops should be using `dx.types.Handle` at this point, so if
+      // we're not producing that we should be part of a pair. Track this so we
+      // can remove it at the end.
+      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();
+    }
+    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);
+
+      dxil::ResourceInfo &RI = DRM[CI];
+      dxil::ResourceInfo::ResourceBinding 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);
+
+      dxil::ResourceInfo &RI = DRM[CI];
+      dxil::ResourceInfo::ResourceBinding Binding = RI.getBinding();
+      std::pair<uint32_t, uint32_t> Props = RI.getAnnotateProps();
+
+      Constant *ResBind = OpBuilder.getResBind(
+          Binding.LowerBound, Binding.LowerBound + Binding.Size - 1,
+          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();
+    });
+  }
+
+  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 +251,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 +299,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 0000000000000..ca4def78e73de
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/CreateHandle.ll
@@ -0,0 +1,59 @@
+; RUN: opt -S -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.0-compute"
+
+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 0, 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 1, i32 6, i1 false)
+
+  ; Buffer<uint4> Buf[24] : register(t3, space5)
+  %typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
+      @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
+          i32 2, i32 7, i32 24, i32 8, i1 false)
+  ; CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 8, 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 1, 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 2, i32 12, i1 false)
+
+  ret void
+}
+
+; Note: We need declarations for each handle.fromBinding in the same order as
+; they appear in source to force a deterministic ordering of record IDs.
+declare target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
+        @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4f32_1_0_0t(
+        i32, i32, i32, i32, i1) #0
+declare target("dx.TypedBuffer", i32, 1, 0, 1)
+        @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_1_0_1t(
+            i32, i32, i32, i32, i1) #0
+declare target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
+        @llvm.dx.handle.fromBinding.tdx.TypedBuffer_v4i32_0_0_0t(
+            i32, i32, i32, i32, i1) #0
+declare target("dx.RawBuffer", { <4 x float>, <4 x i32> }, 0, 0)
+        @llvm.dx.handle.fromBinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0t(
+            i32, i32, i32, i32, i1) #0
+declare target("dx.RawBuffer", i8, 0, 0)
+        @llvm.dx.handle.fromBinding.tdx.RawBuffer_i8_0_0t(
+            i32, i32, i32, i32, i1) #0
+
+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 0000000000000..9b6688cb11d54
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/CreateHandleFromBinding.ll
@@ -0,0 +1,63 @@
+; RUN: opt -S -dxil-op-lower %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+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)
+  %typed2 = call target("dx.TypedBuffer", <4 x i32>, 0, 0, 0)
+      @llvm.dx.handle.fromBinding.tdx.TypedBuffer_i32_0_0_0t(
+          i32 2, i32 7, i32 24, i32 8, i1 false)
+  ; CHECK: [[BUF2:%[0-9]*]] = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 218, %dx.types.ResBind { i32 7, i32 30, i32 2, i8 0 }, i32 8, 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 tr...
[truncated]

@bogner
Copy link
Contributor Author

bogner commented Aug 14, 2024

Depends on #104246, #104247, and #104248

bogner added a commit to bogner/llvm-project that referenced this pull request Aug 14, 2024
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: llvm#104251
bogner added 2 commits August 15, 2024 12:22
Created using spr 1.3.5-bogner

[skip ci]
Created using spr 1.3.5-bogner
bogner added 2 commits August 20, 2024 10:54
Created using spr 1.3.5-bogner

[skip ci]
Created using spr 1.3.5-bogner
llvm/lib/Target/DirectX/DXIL.td Outdated Show resolved Hide resolved
llvm/lib/Target/DirectX/DXILOpLowering.cpp Outdated Show resolved Hide resolved
llvm/lib/Target/DirectX/DXILOpLowering.cpp Show resolved Hide resolved
llvm/lib/Target/DirectX/DXILOpLowering.cpp Show resolved Hide resolved
llvm/lib/Target/DirectX/DXILOpLowering.cpp Outdated Show resolved Hide resolved
llvm/test/CodeGen/DirectX/CreateHandle.ll Show resolved Hide resolved
bogner added 4 commits August 21, 2024 18:08
Created using spr 1.3.5-bogner

[skip ci]
Created using spr 1.3.5-bogner
Created using spr 1.3.5-bogner

[skip ci]
Created using spr 1.3.5-bogner
Copy link
Member

@hekota hekota left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

adrian-prantl and others added 2 commits August 23, 2024 12:11
Created using spr 1.3.5-bogner

[skip ci]
Copy link
Contributor

@dmpots dmpots left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, LGTM!

Created using spr 1.3.5-bogner
@bogner bogner changed the base branch from users/bogner/sprmain.directx-lower-llvmdxhandlefrombinding-to-dxil-ops to main August 23, 2024 19:58
@bogner bogner merged commit aa61925 into main Aug 23, 2024
6 of 8 checks passed
@bogner bogner deleted the users/bogner/sprdirectx-lower-llvmdxhandlefrombinding-to-dxil-ops branch August 23, 2024 19:58
5chmidti pushed a commit that referenced this pull request Aug 24, 2024
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: #104251
dmpolukhin pushed a commit to dmpolukhin/llvm-project that referenced this pull request Sep 2, 2024
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: llvm#104251
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[DirectX] Introduce llvm.dx.handle.fromBinding intrinsic and lowering
8 participants