-
Notifications
You must be signed in to change notification settings - Fork 12.3k
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
Conversation
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.
@llvm/pr-subscribers-clang @llvm/pr-subscribers-hlsl Author: Helena Kotas (hekota) ChangesAdds 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:
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]>;
}
|
clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl
Outdated
Show resolved
Hide resolved
…te-handle-from-binding-pr
…vm#111207) 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](llvm/wg-hlsl#7) 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 llvm#110327 and llvm#111203. Part 1 of llvm#105076
…vm#111207) 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](llvm/wg-hlsl#7) 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 llvm#110327 and llvm#111203. Part 1 of llvm#105076
Adds
@_init_resource_bindings()
function to module initialization that includeshandle.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