Skip to content

Commit 7dbfa7b

Browse files
authored
[HLSL] Add handle initialization for simple resource declarations (#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 #110327 and #111203. Part 1 of #105076
1 parent 7106de9 commit 7dbfa7b

File tree

9 files changed

+170
-44
lines changed

9 files changed

+170
-44
lines changed

clang/include/clang/AST/Type.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6320,6 +6320,10 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode {
63206320
static bool classof(const Type *T) {
63216321
return T->getTypeClass() == HLSLAttributedResource;
63226322
}
6323+
6324+
// Returns handle type from HLSL resource, if the type is a resource
6325+
static const HLSLAttributedResourceType *
6326+
findHandleTypeOnResource(const Type *RT);
63236327
};
63246328

63256329
class TemplateTypeParmType : public Type, public llvm::FoldingSetNode {

clang/lib/AST/Type.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5335,3 +5335,18 @@ std::string FunctionEffectWithCondition::description() const {
53355335
Result += "(expr)";
53365336
return Result;
53375337
}
5338+
5339+
const HLSLAttributedResourceType *
5340+
HLSLAttributedResourceType::findHandleTypeOnResource(const Type *RT) {
5341+
// If the type RT is an HLSL resource class, the first field must
5342+
// be the resource handle of type HLSLAttributedResourceType
5343+
const clang::Type *Ty = RT->getUnqualifiedDesugaredType();
5344+
if (const RecordDecl *RD = Ty->getAsCXXRecordDecl()) {
5345+
if (!RD->fields().empty()) {
5346+
const auto &FirstFD = RD->fields().begin();
5347+
return dyn_cast<HLSLAttributedResourceType>(
5348+
FirstFD->getType().getTypePtr());
5349+
}
5350+
}
5351+
return nullptr;
5352+
}

clang/lib/CodeGen/CGDeclCXX.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,14 @@ CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn,
11211121
if (Decls[i])
11221122
EmitRuntimeCall(Decls[i]);
11231123

1124+
if (getLangOpts().HLSL) {
1125+
CGHLSLRuntime &CGHLSL = CGM.getHLSLRuntime();
1126+
if (CGHLSL.needsResourceBindingInitFn()) {
1127+
llvm::Function *ResInitFn = CGHLSL.createResourceBindingInitFn();
1128+
Builder.CreateCall(llvm::FunctionCallee(ResInitFn), {});
1129+
}
1130+
}
1131+
11241132
Scope.ForceCleanup();
11251133

11261134
if (ExitBlock) {

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
#include "TargetInfo.h"
1919
#include "clang/AST/Decl.h"
2020
#include "clang/Basic/TargetOptions.h"
21+
#include "llvm/IR/GlobalVariable.h"
22+
#include "llvm/IR/LLVMContext.h"
2123
#include "llvm/IR/Metadata.h"
2224
#include "llvm/IR/Module.h"
25+
#include "llvm/IR/Value.h"
26+
#include "llvm/Support/Alignment.h"
27+
2328
#include "llvm/Support/FormatVariadic.h"
2429

2530
using namespace clang;
@@ -489,3 +494,88 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
489494
GV->eraseFromParent();
490495
}
491496
}
497+
498+
void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
499+
llvm::GlobalVariable *GV) {
500+
// If the global variable has resource binding, add it to the list of globals
501+
// that need resource binding initialization.
502+
const HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
503+
if (!RBA)
504+
return;
505+
506+
if (!HLSLAttributedResourceType::findHandleTypeOnResource(
507+
VD->getType().getTypePtr()))
508+
// FIXME: Only simple declarations of resources are supported for now.
509+
// Arrays of resources or resources in user defined classes are
510+
// not implemented yet.
511+
return;
512+
513+
ResourcesToBind.emplace_back(VD, GV);
514+
}
515+
516+
bool CGHLSLRuntime::needsResourceBindingInitFn() {
517+
return !ResourcesToBind.empty();
518+
}
519+
520+
llvm::Function *CGHLSLRuntime::createResourceBindingInitFn() {
521+
// No resources to bind
522+
assert(needsResourceBindingInitFn() && "no resources to bind");
523+
524+
LLVMContext &Ctx = CGM.getLLVMContext();
525+
llvm::Type *Int1Ty = llvm::Type::getInt1Ty(Ctx);
526+
527+
llvm::Function *InitResBindingsFunc =
528+
llvm::Function::Create(llvm::FunctionType::get(CGM.VoidTy, false),
529+
llvm::GlobalValue::InternalLinkage,
530+
"_init_resource_bindings", CGM.getModule());
531+
532+
llvm::BasicBlock *EntryBB =
533+
llvm::BasicBlock::Create(Ctx, "entry", InitResBindingsFunc);
534+
CGBuilderTy Builder(CGM, Ctx);
535+
const DataLayout &DL = CGM.getModule().getDataLayout();
536+
Builder.SetInsertPoint(EntryBB);
537+
538+
for (const auto &[VD, GV] : ResourcesToBind) {
539+
for (Attr *A : VD->getAttrs()) {
540+
HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
541+
if (!RBA)
542+
continue;
543+
544+
const HLSLAttributedResourceType *AttrResType =
545+
HLSLAttributedResourceType::findHandleTypeOnResource(
546+
VD->getType().getTypePtr());
547+
548+
// FIXME: Only simple declarations of resources are supported for now.
549+
// Arrays of resources or resources in user defined classes are
550+
// not implemented yet.
551+
assert(AttrResType != nullptr &&
552+
"Resource class must have a handle of HLSLAttributedResourceType");
553+
554+
llvm::Type *TargetTy =
555+
CGM.getTargetCodeGenInfo().getHLSLType(CGM, AttrResType);
556+
assert(TargetTy != nullptr &&
557+
"Failed to convert resource handle to target type");
558+
559+
auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber());
560+
auto *Slot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
561+
// FIXME: resource arrays are not yet implemented
562+
auto *Range = llvm::ConstantInt::get(CGM.IntTy, 1);
563+
auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0);
564+
// FIXME: NonUniformResourceIndex bit is not yet implemented
565+
auto *NonUniform = llvm::ConstantInt::get(Int1Ty, false);
566+
llvm::Value *Args[] = {Space, Slot, Range, Index, NonUniform};
567+
568+
llvm::Value *CreateHandle = Builder.CreateIntrinsic(
569+
/*ReturnType=*/TargetTy, getCreateHandleFromBindingIntrinsic(), Args,
570+
nullptr, Twine(VD->getName()).concat("_h"));
571+
572+
llvm::Value *HandleRef =
573+
Builder.CreateStructGEP(GV->getValueType(), GV, 0);
574+
Builder.CreateAlignedStore(CreateHandle, HandleRef,
575+
HandleRef->getPointerAlignment(DL));
576+
}
577+
}
578+
579+
Builder.CreateRetVoid();
580+
return InitResBindingsFunc;
581+
}

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class CGHLSLRuntime {
9292
GENERATE_HLSL_INTRINSIC_FUNCTION(WaveIsFirstLane, wave_is_first_lane)
9393
GENERATE_HLSL_INTRINSIC_FUNCTION(WaveReadLaneAt, wave_readlane)
9494

95+
GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding, handle_fromBinding)
96+
9597
//===----------------------------------------------------------------------===//
9698
// End of reserved area for HLSL intrinsic getters.
9799
//===----------------------------------------------------------------------===//
@@ -137,6 +139,10 @@ class CGHLSLRuntime {
137139

138140
void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
139141
void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
142+
void handleGlobalVarDefinition(const VarDecl *VD, llvm::GlobalVariable *Var);
143+
144+
bool needsResourceBindingInitFn();
145+
llvm::Function *createResourceBindingInitFn();
140146

141147
private:
142148
void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
@@ -148,6 +154,9 @@ class CGHLSLRuntime {
148154
void addBufferDecls(const DeclContext *DC, Buffer &CB);
149155
llvm::Triple::ArchType getArch();
150156
llvm::SmallVector<Buffer> Buffers;
157+
158+
llvm::SmallVector<std::pair<const VarDecl *, llvm::GlobalVariable *>>
159+
ResourcesToBind;
151160
};
152161

153162
} // namespace CodeGen

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5634,6 +5634,9 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
56345634
getCUDARuntime().handleVarRegistration(D, *GV);
56355635
}
56365636

5637+
if (LangOpts.HLSL)
5638+
getHLSLRuntime().handleGlobalVarDefinition(D, GV);
5639+
56375640
GV->setInitializer(Init);
56385641
if (emitter)
56395642
emitter->finalize(GV);

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,21 +1039,6 @@ SemaHLSL::TakeLocForHLSLAttribute(const HLSLAttributedResourceType *RT) {
10391039
return LocInfo;
10401040
}
10411041

1042-
// Returns handle type of a resource, if the type is a resource
1043-
static const HLSLAttributedResourceType *
1044-
findHandleTypeOnResource(const Type *Ty) {
1045-
// If Ty is a resource class, the first field must
1046-
// be the resource handle of type HLSLAttributedResourceType
1047-
if (RecordDecl *RD = Ty->getAsCXXRecordDecl()) {
1048-
if (!RD->fields().empty()) {
1049-
const auto &FirstFD = RD->fields().begin();
1050-
return dyn_cast<HLSLAttributedResourceType>(
1051-
FirstFD->getType().getTypePtr());
1052-
}
1053-
}
1054-
return nullptr;
1055-
}
1056-
10571042
// Walks though the global variable declaration, collects all resource binding
10581043
// requirements and adds them to Bindings
10591044
void SemaHLSL::collectResourcesOnUserRecordDecl(const VarDecl *VD,
@@ -1075,7 +1060,7 @@ void SemaHLSL::collectResourcesOnUserRecordDecl(const VarDecl *VD,
10751060
continue;
10761061

10771062
if (const HLSLAttributedResourceType *AttrResType =
1078-
findHandleTypeOnResource(Ty)) {
1063+
HLSLAttributedResourceType::findHandleTypeOnResource(Ty)) {
10791064
// Add a new DeclBindingInfo to Bindings if it does not already exist
10801065
ResourceClass RC = AttrResType->getAttrs().ResourceClass;
10811066
DeclBindingInfo *DBI = Bindings.getDeclBindingInfo(VD, RC);
@@ -1126,7 +1111,8 @@ static bool DiagnoseLocalRegisterBinding(Sema &S, SourceLocation &ArgLoc,
11261111

11271112
// Resource
11281113
if (const HLSLAttributedResourceType *AttrResType =
1129-
findHandleTypeOnResource(VD->getType().getTypePtr())) {
1114+
HLSLAttributedResourceType::findHandleTypeOnResource(
1115+
VD->getType().getTypePtr())) {
11301116
if (RegType == getRegisterType(AttrResType->getAttrs().ResourceClass))
11311117
return true;
11321118

@@ -2369,7 +2355,7 @@ void SemaHLSL::collectResourcesOnVarDecl(VarDecl *VD) {
23692355

23702356
// Resource (or array of resources)
23712357
if (const HLSLAttributedResourceType *AttrResType =
2372-
findHandleTypeOnResource(Ty)) {
2358+
HLSLAttributedResourceType::findHandleTypeOnResource(Ty)) {
23732359
Bindings.addDeclBindingInfo(VD, AttrResType->getAttrs().ResourceClass);
23742360
return;
23752361
}
Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1-
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
2-
// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV
1+
// 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
2+
// FIXME: SPIR-V codegen of llvm.spv.handle.fromBinding is not yet implemented
3+
// RUN-DISABLED: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
34

4-
// XFAIL: *
5-
// This expectedly fails because create.handle is no longer called
6-
// from RWBuffer constructor and the replacement has not been
7-
// implemented yet. This test should be updated to expect
8-
// dx.create.handleFromBinding as part of issue #105076.
5+
// NOTE: SPIRV codegen for resource types is not yet implemented
96

10-
RWBuffer<float> Buf;
7+
RWBuffer<float> Buf : register(u5, space3);
118

12-
// CHECK: define linkonce_odr noundef ptr @"??0?$RWBuffer@M@hlsl@@QAA@XZ"
9+
// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0), float }
10+
// CHECK: @Buf = global %"class.hlsl::RWBuffer" zeroinitializer, align 4
11+
12+
// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this)
1313
// CHECK-NEXT: entry:
1414

15-
// CHECK: %[[HandleRes:[0-9]+]] = call ptr @llvm.dx.create.handle(i8 1)
16-
// CHECK: store ptr %[[HandleRes]], ptr %h, align 4
15+
// CHECK: define internal void @_GLOBAL__sub_I_RWBuffer_constructor.hlsl()
16+
// CHECK-NEXT: entry:
17+
// CHECK-NEXT: call void @__cxx_global_var_init()
18+
// CHECK-NEXT: call void @_init_resource_bindings()
1719

18-
// CHECK-SPIRV: %[[HandleRes:[0-9]+]] = call ptr @llvm.spv.create.handle(i8 1)
19-
// CHECK-SPIRV: store ptr %[[HandleRes]], ptr %h, align 8
20+
// CHECK: define internal void @_init_resource_bindings() {
21+
// CHECK-NEXT: entry:
22+
// 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)
23+
// CHECK-DXIL-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %Buf_h, ptr @Buf, align 4
24+
// 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)
25+
// CHECK-SPIRV-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %Buf_h, ptr @Buf, align 4
Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
2-
// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV
1+
// 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
2+
// RUN-DISABLED: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
33

4-
// XFAIL: *
5-
// This expectedly fails because create.handle is no longer invoked
6-
// from StructuredBuffer constructor and the replacement has not been
7-
// implemented yet. This test should be updated to expect
8-
// dx.create.handleFromBinding as part of issue #105076.
4+
// NOTE: SPIRV codegen for resource types is not yet implemented
95

10-
StructuredBuffer<float> Buf;
6+
StructuredBuffer<float> Buf : register(u10);
117

12-
// CHECK: define linkonce_odr noundef ptr @"??0?$StructuredBuffer@M@hlsl@@QAA@XZ"
8+
// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), float }
9+
// CHECK: @Buf = global %"class.hlsl::StructuredBuffer" zeroinitializer, align 4
10+
11+
// CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this)
1312
// CHECK-NEXT: entry:
1413

15-
// CHECK: %[[HandleRes:[0-9]+]] = call ptr @llvm.dx.create.handle(i8 1)
16-
// CHECK: store ptr %[[HandleRes]], ptr %h, align 4
14+
// CHECK: define internal void @_GLOBAL__sub_I_StructuredBuffer_constructor.hlsl()
15+
// CHECK-NEXT: entry:
16+
// CHECK-NEXT: call void @__cxx_global_var_init()
17+
// CHECK-NEXT: call void @_init_resource_bindings()
1718

18-
// CHECK-SPIRV: %[[HandleRes:[0-9]+]] = call ptr @llvm.spv.create.handle(i8 1)
19-
// CHECK-SPIRV: store ptr %[[HandleRes]], ptr %h, align 8
19+
// CHECK: define internal void @_init_resource_bindings() {
20+
// CHECK-NEXT: entry:
21+
// 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)
22+
// CHECK-DXIL-NEXT: store target("dx.RawBuffer", float, 1, 0) %Buf_h, ptr @Buf, align 4
23+
// 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)
24+
// CHECK-SPIRV-NEXT: store target("dx.RawBuffer", float, 1, 0) %Buf_h, ptr @Buf", align 4

0 commit comments

Comments
 (0)