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

[HLSL] Add handle initialization for simple resource declarations #111207

Merged
merged 9 commits into from
Oct 18, 2024

Conversation

hekota
Copy link
Member

@hekota hekota commented Oct 4, 2024

Adds @_init_resource_bindings() function to module initialization that includes handle.fromBinding intrinsic calls for simple resource declarations. Arrays of resources or resources inside user defined types are not supported yet.

While this unblocks our progress on Compile a runnable shader from clang milestone, this is probably not the way we would like to handle resource binding initialization going forward. Ideally, it should be done via the resource class constructors in order to support dynamic resource binding or unbounded arrays if resources.

Depends on PRs #110327 and #111203.

Part 1 of #105076

Adds `@_init_resource_bindings()` function to module initialization that
includes `handle.fromBinding` intrinsic calls for simple resource declarations.
Arrays of resources or resources inside user defined types are not supported yet.
@hekota hekota marked this pull request as ready for review October 4, 2024 20:35
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen HLSL HLSL Language Support llvm:ir labels Oct 4, 2024
@hekota hekota marked this pull request as draft October 4, 2024 20:35
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 4, 2024

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-hlsl

Author: Helena Kotas (hekota)

Changes

Adds @<!-- -->_init_resource_bindings() function to module initialization that includes handle.fromBinding intrinsic calls for simple resource declarations. Arrays of resources or resources inside user defined types are not supported yet.

While this unblocks our progress on Compile a runnable shader from clang milestone, this is probably not the way we would like to handle resource binding initialization going forward. Ideally, it should be done via the resource class constructors in order to support dynamic resource binding or unbounded arrays if resources.

Depends on PRs #110327 and #111203.

Part 1 of #105076


Full diff: https://github.com/llvm/llvm-project/pull/111207.diff

7 Files Affected:

  • (modified) clang/lib/CodeGen/CGDeclCXX.cpp (+5)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.cpp (+111)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.h (+7)
  • (modified) clang/lib/CodeGen/CodeGenModule.cpp (+3)
  • (modified) clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl (+14-12)
  • (modified) clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl (+14-13)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+10)
diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index 8dcb5f61006196..834c5b2d65db42 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -1121,6 +1121,11 @@ CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn,
       if (Decls[i])
         EmitRuntimeCall(Decls[i]);
 
+    if (getLangOpts().HLSL)
+      if (llvm::Function *ResInitFn =
+              CGM.getHLSLRuntime().createResourceBindingInitFn())
+        Builder.CreateCall(llvm::FunctionCallee(ResInitFn), {});
+
     Scope.ForceCleanup();
 
     if (ExitBlock) {
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3237d93ca31ceb..23ed24eaf5cb27 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -18,8 +18,13 @@
 #include "TargetInfo.h"
 #include "clang/AST/Decl.h"
 #include "clang/Basic/TargetOptions.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Support/Alignment.h"
+
 #include "llvm/Support/FormatVariadic.h"
 
 using namespace clang;
@@ -489,3 +494,109 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
       GV->eraseFromParent();
   }
 }
+
+// Returns handle type of a resource, if the VarDecl is a resource
+// or an array of resources
+static const HLSLAttributedResourceType *
+findHandleTypeOnResource(const VarDecl *VD) {
+  // If VarDecl is a resource class, the first field must
+  // be the resource handle of type HLSLAttributedResourceType
+  assert(VD != nullptr && "expected VarDecl");
+  const clang::Type *Ty = VD->getType()->getPointeeOrArrayElementType();
+  if (RecordDecl *RD = Ty->getAsCXXRecordDecl()) {
+    if (!RD->fields().empty()) {
+      const auto &FirstFD = RD->fields().begin();
+      return dyn_cast<HLSLAttributedResourceType>(
+          FirstFD->getType().getTypePtr());
+    }
+  }
+  return nullptr;
+}
+
+void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
+                                              llvm::GlobalVariable *Var) {
+  // If the global variable has resource binding, add it to the list of globals
+  // that need resource binding initialization.
+  const HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
+  if (!RBA)
+    return;
+
+  // FIXME: support for resource arrays or resource fields on user defined
+  // classes is not yet implemented
+  if (RBA->ResourceField != nullptr || VD->getType()->isArrayType())
+    return;
+
+  ResourcesToBind.emplace_back(std::make_pair(VD, Var));
+}
+
+llvm::Function *CGHLSLRuntime::createResourceBindingInitFn() {
+  // No resources to bind
+  if (ResourcesToBind.empty())
+    return nullptr;
+
+  LLVMContext &Ctx = CGM.getLLVMContext();
+
+  llvm::Function *InitResBindingsFunc =
+      llvm::Function::Create(llvm::FunctionType::get(CGM.VoidTy, false),
+                             llvm::GlobalValue::InternalLinkage,
+                             "_init_resource_bindings", CGM.getModule());
+
+  llvm::BasicBlock *EntryBB =
+      llvm::BasicBlock::Create(Ctx, "entry", InitResBindingsFunc);
+  CGBuilderTy Builder(CGM, Ctx);
+  const DataLayout &DL = CGM.getModule().getDataLayout();
+  Builder.SetInsertPoint(EntryBB);
+
+  for (auto I : ResourcesToBind) {
+    const VarDecl *VD = I.first;
+    llvm::GlobalVariable *Var = I.second;
+
+    for (Attr *A : VD->getAttrs()) {
+      HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
+      if (!RBA)
+        continue;
+
+      if (RBA->getResourceField() != nullptr) {
+        // FIXME: Register bindings inside user defined struct are not yet
+        // supported
+        llvm_unreachable("Register bindings inside user defined struct are not "
+                         "implemented yet");
+        continue;
+      }
+
+      const HLSLAttributedResourceType *AttrResType =
+          findHandleTypeOnResource(VD);
+      assert(AttrResType != nullptr &&
+             "Resource class must have a handle of HLSLAttributedResourceType");
+
+      llvm::Type *TargetTy =
+          CGM.getTargetCodeGenInfo().getHLSLType(CGM, AttrResType);
+      assert(TargetTy != nullptr &&
+             "Failed to convert resource handle to target type");
+
+      llvm::Value *Args[] = {
+          llvm::ConstantInt::get(CGM.IntTy,
+                                 RBA->getSpaceNumber()), /*RegisterSpace*/
+          llvm::ConstantInt::get(CGM.IntTy,
+                                 RBA->getSlotNumber()), /*RegisterSlot*/
+          // FIXME: resource arrays are not yet implemented
+          llvm::ConstantInt::get(CGM.IntTy, 1), /*Range*/
+          llvm::ConstantInt::get(CGM.IntTy, 0), /*Index*/
+          // FIXME: NonUniformResourceIndex bit is not yet implemented
+          llvm::ConstantInt::get(llvm::Type::getInt1Ty(Ctx),
+                                 false) /*Non-uniform*/
+      };
+      llvm::Value *CreateHandle = Builder.CreateIntrinsic(
+          /*ReturnType=*/TargetTy, getCreateHandleFromBindingIntrinsic(), Args,
+          nullptr, Twine(VD->getName()).concat("_h"));
+
+      llvm::Value *HandleRef =
+          Builder.CreateStructGEP(Var->getValueType(), Var, 0);
+      Builder.CreateAlignedStore(CreateHandle, HandleRef,
+                                 HandleRef->getPointerAlignment(DL));
+    }
+  }
+
+  Builder.CreateRetVoid();
+  return InitResBindingsFunc;
+}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 6722d2c7c50a2b..0b9d2f165f322b 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -89,6 +89,8 @@ class CGHLSLRuntime {
   GENERATE_HLSL_INTRINSIC_FUNCTION(UDot, udot)
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveIsFirstLane, wave_is_first_lane)
 
+  GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding, handle_fromBinding)
+
   //===----------------------------------------------------------------------===//
   // End of reserved area for HLSL intrinsic getters.
   //===----------------------------------------------------------------------===//
@@ -134,6 +136,8 @@ class CGHLSLRuntime {
 
   void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
   void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
+  void handleGlobalVarDefinition(const VarDecl *VD, llvm::GlobalVariable *Var);
+  llvm::Function *createResourceBindingInitFn();
 
 private:
   void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
@@ -145,6 +149,9 @@ class CGHLSLRuntime {
   void addBufferDecls(const DeclContext *DC, Buffer &CB);
   llvm::Triple::ArchType getArch();
   llvm::SmallVector<Buffer> Buffers;
+
+  llvm::SmallVector<std::pair<const VarDecl *, llvm::GlobalVariable *>>
+      ResourcesToBind;
 };
 
 } // namespace CodeGen
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 25c1c496a4f27f..1dd969fb0a4187 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -5617,6 +5617,9 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
     getCUDARuntime().handleVarRegistration(D, *GV);
   }
 
+  if (LangOpts.HLSL)
+    getHLSLRuntime().handleGlobalVarDefinition(D, GV);
+
   GV->setInitializer(Init);
   if (emitter)
     emitter->finalize(GV);
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
index 19699dcf14d9f4..844edea3d0f319 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
@@ -1,19 +1,21 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
+// RUN-DISABLED: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
 
-// XFAIL: *
-// This expectedly fails because create.handle is no longer called
-// from RWBuffer constructor and the replacement has not been
-// implemented yet. This test should be updated to expect
-// dx.create.handleFromBinding as part of issue #105076.
+// NOTE: SPIRV codegen for resource types is not yet implemented
 
-RWBuffer<float> Buf;
+RWBuffer<float> Buf : register(u5, space3);
 
 // CHECK: define linkonce_odr noundef ptr @"??0?$RWBuffer@M@hlsl@@QAA@XZ"
 // CHECK-NEXT: entry:
 
-// CHECK: %[[HandleRes:[0-9]+]] = call ptr @llvm.dx.create.handle(i8 1)
-// CHECK: store ptr %[[HandleRes]], ptr %h, align 4
+// CHECK: define internal void @_GLOBAL__sub_I_RWBuffer_constructor.hlsl()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @"??__EBuf@@YAXXZ"()
+// CHECK-NEXT: call void @_init_resource_bindings()
 
-// CHECK-SPIRV: %[[HandleRes:[0-9]+]] = call ptr @llvm.spv.create.handle(i8 1)
-// CHECK-SPIRV: store ptr %[[HandleRes]], ptr %h, align 8
+// CHECK: define internal void @_init_resource_bindings() {
+// CHECK-NEXT: entry:
+// CHECK-DXIL-NEXT: %Buf_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.handle.fromBinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
+// CHECK-DXIL-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %Buf_h, ptr @"?Buf@@3V?$RWBuffer@M@hlsl@@A", align 4
+// CHECK-SPIRV-NEXT: %Buf_h = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.spv.handle.fromBinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false)
+// CHECK-SPIRV-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %Buf_h, ptr @"?Buf@@3V?$RWBuffer@M@hlsl@@A", align 4
diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl
index 178332d03e6404..5014d6959d7973 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl
@@ -1,19 +1,20 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
+// RUN-DISABLED: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
 
-// XFAIL: *
-// This expectedly fails because create.handle is no longer invoked
-// from StructuredBuffer constructor and the replacement has not been
-// implemented yet. This test should be updated to expect
-// dx.create.handleFromBinding as part of issue #105076.
-
-StructuredBuffer<float> Buf;
+// NOTE: SPIRV codegen for resource types is not yet implemented
+StructuredBuffer<float> Buf : register(u10);
 
 // CHECK: define linkonce_odr noundef ptr @"??0?$StructuredBuffer@M@hlsl@@QAA@XZ"
 // CHECK-NEXT: entry:
 
-// CHECK: %[[HandleRes:[0-9]+]] = call ptr @llvm.dx.create.handle(i8 1)
-// CHECK: store ptr %[[HandleRes]], ptr %h, align 4
+// CHECK: define internal void @_GLOBAL__sub_I_StructuredBuffer_constructor.hlsl()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: call void @"??__EBuf@@YAXXZ"()
+// CHECK-NEXT: call void @_init_resource_bindings()
 
-// CHECK-SPIRV: %[[HandleRes:[0-9]+]] = call ptr @llvm.spv.create.handle(i8 1)
-// CHECK-SPIRV: store ptr %[[HandleRes]], ptr %h, align 8
+// CHECK: define internal void @_init_resource_bindings() {
+// CHECK-NEXT: entry:
+// CHECK-DXIL-NEXT: %Buf_h = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.handle.fromBinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 10, i32 1, i32 0, i1 false)
+// CHECK-DXIL-NEXT: store target("dx.RawBuffer", float, 1, 0) %Buf_h, ptr @"?Buf@@3V?$StructuredBuffer@M@hlsl@@A", align 4
+// CHECK-SPIRV-NEXT: %Buf_h = call target("dx.RawBuffer", float, 1, 0) @llvm.spv.handle.fromBinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 10, i32 1, i32 0, i1 false)
+// CHECK-SPIRV-NEXT: store target("dx.RawBuffer", float, 1, 0) %Buf_h, ptr @"?Buf@@3V?$StructuredBuffer@M@hlsl@@A", align 4
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 88059aa8378140..bc09d1f34503b5 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 binding information. Returns a target
+// type appropriate for the kind of resource given a register space ID, lower
+// bound and range 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]>;  
 }

@hekota hekota marked this pull request as ready for review October 4, 2024 20:43
@hekota hekota marked this pull request as draft October 7, 2024 17:44
@hekota hekota marked this pull request as ready for review October 10, 2024 15:45
clang/lib/CodeGen/CGDeclCXX.cpp Outdated Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Outdated Show resolved Hide resolved
clang/lib/CodeGen/CodeGenModule.cpp Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Outdated Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Outdated Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Outdated Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Outdated Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Outdated Show resolved Hide resolved
@llvmbot llvmbot added the clang:frontend Language frontend issues, e.g. anything involving "Sema" label Oct 16, 2024
clang/lib/AST/Type.cpp Outdated Show resolved Hide resolved
clang/lib/CodeGen/CGHLSLRuntime.cpp Outdated Show resolved Hide resolved
@hekota hekota merged commit 7dbfa7b into llvm:main Oct 18, 2024
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support llvm:ir
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

5 participants