[llvm] [SPIRV] Implement handle_fromBinding intrinsic. (PR #111052)

Steven Perron via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 8 06:24:25 PDT 2024


https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/111052

>From dc691ca0181f8dc14f1523617941597533d3af00 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 20 Sep 2024 11:02:12 -0400
Subject: [PATCH 1/5] [SPIRV] Implement image_fromBinding intrinsic.

Implement the intrinsic `llvm.spv.handle.fromBinding`, which returns the
handle for a global resource. This involves creating a global variable
that matches the return-type, set, and binding in the call, and
returning the handle to that resource.

This commit implements the scalar version. It does not handle arrays of
resources yet. It also does not handle storage buffers yet. We do not
have the type for the storage buffers designed yet.
---
 llvm/docs/SPIRVUsage.rst                      |  7 +++
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       | 10 ++++
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 23 ++++++++
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h   |  3 ++
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 53 +++++++++++++++++++
 llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll     | 28 ++++++++++
 6 files changed, 124 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll

diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index 5485bb6195c3d4..f3031c809b40bd 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -381,6 +381,13 @@ SPIR-V backend, along with their descriptions and argument details.
      - Pointer
      - `[8-bit Integer]`
      - Creates a resource handle for graphics or compute resources. Facilitates the management and use of resources in shaders.
+   * - `int_spv_handle_fromBinding`
+     - spirv.Image
+     - `[32-bit Integer set, 32-bit Integer binding, 32-bit Integer arraySize, 32-bit Integer index, bool isUniformIndex]`
+     - Returns the handle for the resource at the given set and binding.\
+       If `ararySize > 1`, then the binding represents and array of resources\
+       of the given size, and the handle for the resource at the given index is returned.\
+       If the index is possibly non-uniform, then `isUniformIndex` must get set to true.
 
 .. _spirv-builtin-functions:
 
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 88059aa8378140..92d2f67399d263 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -85,4 +85,14 @@ let TargetPrefix = "spv" in {
   def int_spv_wave_is_first_lane : DefaultAttrsIntrinsic<[llvm_i1_ty], [], [IntrConvergent]>;
   def int_spv_sign : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_any_ty], [IntrNoMem]>;
   def int_spv_radians : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
+
+  // Create resource handle given the binding information. Returns a 
+  // type appropriate for the kind of resource given the set id, binding id,
+  // array size of the binding, as well as an index and an indicator
+  // whether that index may be non-uniform.
+  def int_spv_handle_fromBinding
+      : DefaultAttrsIntrinsic<
+            [llvm_any_ty],
+            [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
+            [IntrNoMem]>;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index f35c2435e60a4d..c5cede7745ac52 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -713,6 +713,29 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
   return Reg;
 }
 
+Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
+    const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
+    MachineIRBuilder &MIRBuilder) {
+  SPIRVType *VarPointerTypeReg = getOrCreateSPIRVPointerType(
+      VarType, MIRBuilder, SPIRV::StorageClass::UniformConstant);
+  Register VarReg =
+      MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
+
+  // TODO: The name should come from the llvm-ir, but how that name will be
+  // passed from the HLSL to the backend has not been decided. Using this place
+  // holder for now. We use the result register of the type in the name.
+  std::string name = ("__resource_" + Twine(VarType->getOperand(0).getReg()) +
+                      "_" + Twine(Set) + "_" + Twine(Binding))
+                         .str();
+  buildGlobalVariable(VarReg, VarPointerTypeReg, name, nullptr,
+                      SPIRV::StorageClass::UniformConstant, nullptr, false,
+                      false, SPIRV::LinkageType::Import, MIRBuilder, false);
+
+  buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::DescriptorSet, {Set});
+  buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::Binding, {Binding});
+  return VarReg;
+}
+
 SPIRVType *SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
                                                SPIRVType *ElemType,
                                                MachineIRBuilder &MIRBuilder,
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index d301e119e16c8e..a95b488960c4c3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -519,6 +519,9 @@ class SPIRVGlobalRegistry {
                                SPIRV::LinkageType::LinkageType LinkageType,
                                MachineIRBuilder &MIRBuilder,
                                bool IsInstSelector);
+  Register getOrCreateGlobalVariableWithBinding(const SPIRVType *VarType,
+                                                uint32_t Set, uint32_t Binding,
+                                                MachineIRBuilder &MIRBuilder);
 
   // Convenient helpers for getting types with check for duplicates.
   SPIRVType *getOrCreateSPIRVIntegerType(unsigned BitWidth,
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 468e34a365826a..224fb143b9d336 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -44,6 +44,18 @@ using ExtInstList =
 
 namespace {
 
+uint64_t getUnsignedConstantValueFromReg(llvm::Register Reg,
+                                         const llvm::MachineRegisterInfo &MRI) {
+  llvm::SPIRVType *ConstTy = MRI.getVRegDef(Reg);
+  assert(ConstTy && ConstTy->getOpcode() == llvm::SPIRV::ASSIGN_TYPE &&
+         ConstTy->getOperand(1).isReg());
+  llvm::Register ConstReg = ConstTy->getOperand(1).getReg();
+  const llvm::MachineInstr *Const = MRI.getVRegDef(ConstReg);
+  assert(Const && Const->getOpcode() == llvm::TargetOpcode::G_CONSTANT);
+  const llvm::APInt &Val = Const->getOperand(1).getCImm()->getValue();
+  return Val.getZExtValue();
+}
+
 #define GET_GLOBALISEL_PREDICATE_BITSET
 #include "SPIRVGenGlobalISel.inc"
 #undef GET_GLOBALISEL_PREDICATE_BITSET
@@ -232,6 +244,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
 
   bool selectUnmergeValues(MachineInstr &I) const;
 
+  void selectHandleFromBinding(Register &ResVReg, const SPIRVType *ResType,
+                               MachineInstr &I) const;
+
   // Utilities
   Register buildI32Constant(uint32_t Val, MachineInstr &I,
                             const SPIRVType *ResType = nullptr) const;
@@ -252,6 +267,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
                                           uint32_t Opcode) const;
   MachineInstrBuilder buildConstGenericPtr(MachineInstr &I, Register SrcPtr,
                                            SPIRVType *SrcPtrTy) const;
+  Register buildPointerToResource(const SPIRVType *ResType, uint32_t Set,
+                                  uint32_t Binding, uint32_t ArraySize,
+                                  MachineIRBuilder MIRBuilder) const;
 };
 
 } // end anonymous namespace
@@ -2549,6 +2567,10 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
   // Discard internal intrinsics.
   case Intrinsic::spv_value_md:
     break;
+  case Intrinsic::spv_handle_fromBinding: {
+    selectHandleFromBinding(ResVReg, ResType, I);
+    return true;
+  }
   default: {
     std::string DiagMsg;
     raw_string_ostream OS(DiagMsg);
@@ -2560,6 +2582,37 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
   return true;
 }
 
+void SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
+                                                       const SPIRVType *ResType,
+                                                       MachineInstr &I) const {
+
+  uint32_t Set =
+      getUnsignedConstantValueFromReg(I.getOperand(2).getReg(), *MRI);
+  uint32_t Binding =
+      getUnsignedConstantValueFromReg(I.getOperand(3).getReg(), *MRI);
+  uint32_t ArraySize =
+      getUnsignedConstantValueFromReg(I.getOperand(4).getReg(), *MRI);
+
+  MachineIRBuilder MIRBuilder(I);
+  Register VarReg =
+      buildPointerToResource(ResType, Set, Binding, ArraySize, MIRBuilder);
+
+  // TODO: For now we assume the resource is an image, which needs to be
+  // loaded to get the handle. That will not be true for storage buffers.
+  BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addUse(VarReg);
+}
+
+Register SPIRVInstructionSelector::buildPointerToResource(
+    const SPIRVType *ResType, uint32_t Set, uint32_t Binding,
+    uint32_t ArraySize, MachineIRBuilder MIRBuilder) const {
+  assert(ArraySize == 1 && "Resource arrays are not implemented yet.");
+  return GR.getOrCreateGlobalVariableWithBinding(ResType, Set, Binding,
+                                                 MIRBuilder);
+}
+
 bool SPIRVInstructionSelector::selectAllocaArray(Register ResVReg,
                                                  const SPIRVType *ResType,
                                                  MachineInstr &I) const {
diff --git a/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll b/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
new file mode 100644
index 00000000000000..90ec6997c9cf73
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
@@ -0,0 +1,28 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-vulkan-library %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-library %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpDecorate [[BufferVar:%[0-9]+]] DescriptorSet 16
+; CHECK-DAG: OpDecorate [[BufferVar]] Binding 7
+
+; CHECK: [[float:%[0-9]+]] = OpTypeFloat 32
+; CHECK: [[RWBufferType:%[0-9]+]] = OpTypeImage [[float]] Buffer 2 0 0 2 R32i {{$}}
+; CHECK: [[BufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferType]]
+; CHECK: [[BufferVar]] = OpVariable [[BufferPtrType]] UniformConstant
+
+; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
+; CHECK-NEXT: OpLabel
+define void @RWBufferLoad() #0 {
+; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferType]] [[BufferVar]]
+  %buffer0 = call target("spirv.Image", float, 5, 2, 0, 0, 2, 24)
+      @llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_24(
+          i32 16, i32 7, i32 1, i32 0, i1 false)
+
+; Make sure we use the same variable with multiple loads.
+; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferType]] [[BufferVar]]
+  %buffer1 = call target("spirv.Image", float, 5, 2, 0, 0, 2, 24)
+      @llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_24(
+          i32 16, i32 7, i32 1, i32 0, i1 false)
+  ret void
+}
+
+attributes #0 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
\ No newline at end of file

>From 59a2ca49317f1771665e181be3a86f11046918c7 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 4 Oct 2024 09:47:17 -0400
Subject: [PATCH 2/5] Fixs from code review.

---
 llvm/docs/SPIRVUsage.rst                      |  2 +-
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp |  4 ++--
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 19 +++++++------------
 llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll     |  2 +-
 4 files changed, 11 insertions(+), 16 deletions(-)

diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index f3031c809b40bd..5a5b26b7b3232f 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -385,7 +385,7 @@ SPIR-V backend, along with their descriptions and argument details.
      - spirv.Image
      - `[32-bit Integer set, 32-bit Integer binding, 32-bit Integer arraySize, 32-bit Integer index, bool isUniformIndex]`
      - Returns the handle for the resource at the given set and binding.\
-       If `ararySize > 1`, then the binding represents and array of resources\
+       If `arraySize > 1`, then the binding represents and array of resources\
        of the given size, and the handle for the resource at the given index is returned.\
        If the index is possibly non-uniform, then `isUniformIndex` must get set to true.
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index c5cede7745ac52..8a9a0ad3a6126d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -724,10 +724,10 @@ Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
   // TODO: The name should come from the llvm-ir, but how that name will be
   // passed from the HLSL to the backend has not been decided. Using this place
   // holder for now. We use the result register of the type in the name.
-  std::string name = ("__resource_" + Twine(VarType->getOperand(0).getReg()) +
+  std::string Name = ("__resource_" + Twine(VarType->getOperand(0).getReg()) +
                       "_" + Twine(Set) + "_" + Twine(Binding))
                          .str();
-  buildGlobalVariable(VarReg, VarPointerTypeReg, name, nullptr,
+  buildGlobalVariable(VarReg, VarPointerTypeReg, Name, nullptr,
                       SPIRV::StorageClass::UniformConstant, nullptr, false,
                       false, SPIRV::LinkageType::Import, MIRBuilder, false);
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 224fb143b9d336..ebb6e97c7a6377 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -47,10 +47,12 @@ namespace {
 uint64_t getUnsignedConstantValueFromReg(llvm::Register Reg,
                                          const llvm::MachineRegisterInfo &MRI) {
   llvm::SPIRVType *ConstTy = MRI.getVRegDef(Reg);
-  assert(ConstTy && ConstTy->getOpcode() == llvm::SPIRV::ASSIGN_TYPE &&
-         ConstTy->getOperand(1).isReg());
-  llvm::Register ConstReg = ConstTy->getOperand(1).getReg();
-  const llvm::MachineInstr *Const = MRI.getVRegDef(ConstReg);
+  assert(ConstTy);
+  if (ConstTy->getOpcode() == llvm::SPIRV::ASSIGN_TYPE) {
+    assert(ConstTy->getOperand(1).isReg());
+    Reg = ConstTy->getOperand(1).getReg();
+  }
+  const llvm::MachineInstr *Const = MRI.getVRegDef(Reg);
   assert(Const && Const->getOpcode() == llvm::TargetOpcode::G_CONSTANT);
   const llvm::APInt &Val = Const->getOperand(1).getCImm()->getValue();
   return Val.getZExtValue();
@@ -2917,14 +2919,7 @@ bool SPIRVInstructionSelector::selectSpvThreadId(Register ResVReg,
   // wrapped in a type assignment.
   assert(I.getOperand(2).isReg());
   Register ThreadIdReg = I.getOperand(2).getReg();
-  SPIRVType *ConstTy = this->MRI->getVRegDef(ThreadIdReg);
-  assert(ConstTy && ConstTy->getOpcode() == SPIRV::ASSIGN_TYPE &&
-         ConstTy->getOperand(1).isReg());
-  Register ConstReg = ConstTy->getOperand(1).getReg();
-  const MachineInstr *Const = this->MRI->getVRegDef(ConstReg);
-  assert(Const && Const->getOpcode() == TargetOpcode::G_CONSTANT);
-  const llvm::APInt &Val = Const->getOperand(1).getCImm()->getValue();
-  const uint32_t ThreadId = Val.getZExtValue();
+  const uint32_t ThreadId = getUnsignedConstantValueFromReg(ThreadIdReg, *MRI);
 
   // Extract the thread ID from the loaded vector value.
   MachineBasicBlock &BB = *I.getParent();
diff --git a/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll b/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
index 90ec6997c9cf73..8c1777598f4c71 100644
--- a/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
+++ b/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
@@ -25,4 +25,4 @@ define void @RWBufferLoad() #0 {
   ret void
 }
 
-attributes #0 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
\ No newline at end of file
+attributes #0 = { convergent noinline norecurse "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }

>From 39e39f64077c10a8bccc2e48127b49805dfd7966 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 4 Oct 2024 10:24:08 -0400
Subject: [PATCH 3/5] Use foldImm

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 26 +++----------------
 1 file changed, 4 insertions(+), 22 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index ebb6e97c7a6377..e8b769b6fd6900 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -44,20 +44,6 @@ using ExtInstList =
 
 namespace {
 
-uint64_t getUnsignedConstantValueFromReg(llvm::Register Reg,
-                                         const llvm::MachineRegisterInfo &MRI) {
-  llvm::SPIRVType *ConstTy = MRI.getVRegDef(Reg);
-  assert(ConstTy);
-  if (ConstTy->getOpcode() == llvm::SPIRV::ASSIGN_TYPE) {
-    assert(ConstTy->getOperand(1).isReg());
-    Reg = ConstTy->getOperand(1).getReg();
-  }
-  const llvm::MachineInstr *Const = MRI.getVRegDef(Reg);
-  assert(Const && Const->getOpcode() == llvm::TargetOpcode::G_CONSTANT);
-  const llvm::APInt &Val = Const->getOperand(1).getCImm()->getValue();
-  return Val.getZExtValue();
-}
-
 #define GET_GLOBALISEL_PREDICATE_BITSET
 #include "SPIRVGenGlobalISel.inc"
 #undef GET_GLOBALISEL_PREDICATE_BITSET
@@ -2588,12 +2574,9 @@ void SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg,
                                                        const SPIRVType *ResType,
                                                        MachineInstr &I) const {
 
-  uint32_t Set =
-      getUnsignedConstantValueFromReg(I.getOperand(2).getReg(), *MRI);
-  uint32_t Binding =
-      getUnsignedConstantValueFromReg(I.getOperand(3).getReg(), *MRI);
-  uint32_t ArraySize =
-      getUnsignedConstantValueFromReg(I.getOperand(4).getReg(), *MRI);
+  uint32_t Set = foldImm(I.getOperand(2), MRI);
+  uint32_t Binding = foldImm(I.getOperand(3), MRI);
+  uint32_t ArraySize = foldImm(I.getOperand(4), MRI);
 
   MachineIRBuilder MIRBuilder(I);
   Register VarReg =
@@ -2918,8 +2901,7 @@ bool SPIRVInstructionSelector::selectSpvThreadId(Register ResVReg,
   // Get Thread ID index. Expecting operand is a constant immediate value,
   // wrapped in a type assignment.
   assert(I.getOperand(2).isReg());
-  Register ThreadIdReg = I.getOperand(2).getReg();
-  const uint32_t ThreadId = getUnsignedConstantValueFromReg(ThreadIdReg, *MRI);
+  const uint32_t ThreadId = foldImm(I.getOperand(2), MRI);
 
   // Extract the thread ID from the loaded vector value.
   MachineBasicBlock &BB = *I.getParent();

>From a0c5f9bdbf1bc7ea8d3cd34ede35c42ed5aac500 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 4 Oct 2024 11:41:26 -0400
Subject: [PATCH 4/5] Redo the name for the resource.

---
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 28 ++++++++++-
 llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll     | 50 +++++++++++++++----
 2 files changed, 66 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 8a9a0ad3a6126d..64fde8bf67ab91 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -713,6 +713,30 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
   return Reg;
 }
 
+static std::string buildSpirvTypeName(const SPIRVType *Type,
+                                      MachineIRBuilder &MIRBuilder) {
+  switch (Type->getOpcode()) {
+  case SPIRV::OpTypeImage: {
+    Register SampledTypeReg = Type->getOperand(1).getReg();
+    auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
+    std::string TypeName =
+        "image_" + buildSpirvTypeName(SampledType, MIRBuilder);
+    for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
+      TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
+    }
+    return TypeName;
+  }
+  case SPIRV::OpTypeFloat:
+    return ("f" + Twine(Type->getOperand(1).getImm())).str();
+  case SPIRV::OpTypeInt:
+    if (Type->getOperand(2).getImm())
+      return ("i" + Twine(Type->getOperand(1).getImm())).str();
+    return ("u" + Twine(Type->getOperand(1).getImm())).str();
+  default:
+    llvm_unreachable("Trying to the the name of an unknown type.");
+  }
+}
+
 Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
     const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
     MachineIRBuilder &MIRBuilder) {
@@ -723,8 +747,8 @@ Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
 
   // TODO: The name should come from the llvm-ir, but how that name will be
   // passed from the HLSL to the backend has not been decided. Using this place
-  // holder for now. We use the result register of the type in the name.
-  std::string Name = ("__resource_" + Twine(VarType->getOperand(0).getReg()) +
+  // holder for now.
+  std::string Name = ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder) +
                       "_" + Twine(Set) + "_" + Twine(Binding))
                          .str();
   buildGlobalVariable(VarReg, VarPointerTypeReg, Name, nullptr,
diff --git a/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll b/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
index 8c1777598f4c71..fe960f0d6f2f9a 100644
--- a/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
+++ b/llvm/test/CodeGen/SPIRV/HlslBufferLoad.ll
@@ -1,25 +1,55 @@
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-vulkan-library %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-library %s -o - -filetype=obj | spirv-val %}
 
-; CHECK-DAG: OpDecorate [[BufferVar:%[0-9]+]] DescriptorSet 16
-; CHECK-DAG: OpDecorate [[BufferVar]] Binding 7
+; CHECK-DAG: OpDecorate [[IntBufferVar:%[0-9]+]] DescriptorSet 16
+; CHECK-DAG: OpDecorate [[IntBufferVar]] Binding 7
+; CHECK-DAG: OpDecorate [[FloatBufferVar:%[0-9]+]] DescriptorSet 16
+; CHECK-DAG: OpDecorate [[FloatBufferVar]] Binding 7
 
-; CHECK: [[float:%[0-9]+]] = OpTypeFloat 32
-; CHECK: [[RWBufferType:%[0-9]+]] = OpTypeImage [[float]] Buffer 2 0 0 2 R32i {{$}}
-; CHECK: [[BufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferType]]
-; CHECK: [[BufferVar]] = OpVariable [[BufferPtrType]] UniformConstant
+; CHECK-DAG: [[float:%[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: [[int:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[RWBufferTypeInt:%[0-9]+]] = OpTypeImage [[int]] Buffer 2 0 0 2 R32i {{$}}
+; CHECK-DAG: [[RWBufferTypeFloat:%[0-9]+]] = OpTypeImage [[float]] Buffer 2 0 0 2 R32f {{$}}
+; CHECK-DAG: [[IntBufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferTypeInt]]
+; CHECK-DAG: [[FloatBufferPtrType:%[0-9]+]] = OpTypePointer UniformConstant [[RWBufferTypeFloat]]
+; CHECK-DAG: [[IntBufferVar]] = OpVariable [[IntBufferPtrType]] UniformConstant
+; CHECK-DAG: [[FloatBufferVar]] = OpVariable [[FloatBufferPtrType]] UniformConstant
 
 ; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
 ; CHECK-NEXT: OpLabel
 define void @RWBufferLoad() #0 {
-; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferType]] [[BufferVar]]
-  %buffer0 = call target("spirv.Image", float, 5, 2, 0, 0, 2, 24)
+; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
+  %buffer0 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 24)
       @llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_24(
           i32 16, i32 7, i32 1, i32 0, i1 false)
 
 ; Make sure we use the same variable with multiple loads.
-; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferType]] [[BufferVar]]
-  %buffer1 = call target("spirv.Image", float, 5, 2, 0, 0, 2, 24)
+; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
+  %buffer1 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 24)
+      @llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_24(
+          i32 16, i32 7, i32 1, i32 0, i1 false)
+  ret void
+}
+
+; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
+; CHECK-NEXT: OpLabel
+define void @UseDifferentGlobalVar() #0 {
+; Make sure we use a different variable from the first function. They have
+; different types.
+; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeFloat]] [[FloatBufferVar]]
+  %buffer0 = call target("spirv.Image", float, 5, 2, 0, 0, 2, 3)
+      @llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_3(
+          i32 16, i32 7, i32 1, i32 0, i1 false)
+  ret void
+}
+
+; CHECK: {{%[0-9]+}} = OpFunction {{%[0-9]+}} DontInline {{%[0-9]+}}
+; CHECK-NEXT: OpLabel
+define void @ReuseGlobalVarFromFirstFunction() #0 {
+; Make sure we use the same variable as the first function. They should be the
+; same in case one function calls the other.
+; CHECK-NEXT: [[buffer:%[0-9]+]] = OpLoad [[RWBufferTypeInt]] [[IntBufferVar]]
+  %buffer1 = call target("spirv.Image", i32, 5, 2, 0, 0, 2, 24)
       @llvm.spv.handle.fromBinding.tspirv.Image_f32_5_2_0_0_2_24(
           i32 16, i32 7, i32 1, i32 0, i1 false)
   ret void

>From b9cba8d441f321fb657b9ae25f9dae7e3c7c6e49 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Tue, 8 Oct 2024 09:24:08 -0400
Subject: [PATCH 5/5] Fix typo

---
 llvm/docs/SPIRVUsage.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index 5a5b26b7b3232f..5c31b060cebbfa 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -385,7 +385,7 @@ SPIR-V backend, along with their descriptions and argument details.
      - spirv.Image
      - `[32-bit Integer set, 32-bit Integer binding, 32-bit Integer arraySize, 32-bit Integer index, bool isUniformIndex]`
      - Returns the handle for the resource at the given set and binding.\
-       If `arraySize > 1`, then the binding represents and array of resources\
+       If `arraySize > 1`, then the binding represents an array of resources\
        of the given size, and the handle for the resource at the given index is returned.\
        If the index is possibly non-uniform, then `isUniformIndex` must get set to true.
 



More information about the llvm-commits mailing list