Skip to content

Commit

Permalink
[HLSL] Add handle initialization for simple resource declarations (#1…
Browse files Browse the repository at this point in the history
…11207)

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
  • Loading branch information
hekota authored Oct 18, 2024
1 parent 7106de9 commit 7dbfa7b
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 44 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -6320,6 +6320,10 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode {
static bool classof(const Type *T) {
return T->getTypeClass() == HLSLAttributedResource;
}

// Returns handle type from HLSL resource, if the type is a resource
static const HLSLAttributedResourceType *
findHandleTypeOnResource(const Type *RT);
};

class TemplateTypeParmType : public Type, public llvm::FoldingSetNode {
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5335,3 +5335,18 @@ std::string FunctionEffectWithCondition::description() const {
Result += "(expr)";
return Result;
}

const HLSLAttributedResourceType *
HLSLAttributedResourceType::findHandleTypeOnResource(const Type *RT) {
// If the type RT is an HLSL resource class, the first field must
// be the resource handle of type HLSLAttributedResourceType
const clang::Type *Ty = RT->getUnqualifiedDesugaredType();
if (const RecordDecl *RD = Ty->getAsCXXRecordDecl()) {
if (!RD->fields().empty()) {
const auto &FirstFD = RD->fields().begin();
return dyn_cast<HLSLAttributedResourceType>(
FirstFD->getType().getTypePtr());
}
}
return nullptr;
}
8 changes: 8 additions & 0 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,14 @@ CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn,
if (Decls[i])
EmitRuntimeCall(Decls[i]);

if (getLangOpts().HLSL) {
CGHLSLRuntime &CGHLSL = CGM.getHLSLRuntime();
if (CGHLSL.needsResourceBindingInitFn()) {
llvm::Function *ResInitFn = CGHLSL.createResourceBindingInitFn();
Builder.CreateCall(llvm::FunctionCallee(ResInitFn), {});
}
}

Scope.ForceCleanup();

if (ExitBlock) {
Expand Down
90 changes: 90 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -489,3 +494,88 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
GV->eraseFromParent();
}
}

void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
llvm::GlobalVariable *GV) {
// 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;

if (!HLSLAttributedResourceType::findHandleTypeOnResource(
VD->getType().getTypePtr()))
// FIXME: Only simple declarations of resources are supported for now.
// Arrays of resources or resources in user defined classes are
// not implemented yet.
return;

ResourcesToBind.emplace_back(VD, GV);
}

bool CGHLSLRuntime::needsResourceBindingInitFn() {
return !ResourcesToBind.empty();
}

llvm::Function *CGHLSLRuntime::createResourceBindingInitFn() {
// No resources to bind
assert(needsResourceBindingInitFn() && "no resources to bind");

LLVMContext &Ctx = CGM.getLLVMContext();
llvm::Type *Int1Ty = llvm::Type::getInt1Ty(Ctx);

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 (const auto &[VD, GV] : ResourcesToBind) {
for (Attr *A : VD->getAttrs()) {
HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
if (!RBA)
continue;

const HLSLAttributedResourceType *AttrResType =
HLSLAttributedResourceType::findHandleTypeOnResource(
VD->getType().getTypePtr());

// FIXME: Only simple declarations of resources are supported for now.
// Arrays of resources or resources in user defined classes are
// not implemented yet.
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");

auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber());
auto *Slot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
// FIXME: resource arrays are not yet implemented
auto *Range = llvm::ConstantInt::get(CGM.IntTy, 1);
auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0);
// FIXME: NonUniformResourceIndex bit is not yet implemented
auto *NonUniform = llvm::ConstantInt::get(Int1Ty, false);
llvm::Value *Args[] = {Space, Slot, Range, Index, NonUniform};

llvm::Value *CreateHandle = Builder.CreateIntrinsic(
/*ReturnType=*/TargetTy, getCreateHandleFromBindingIntrinsic(), Args,
nullptr, Twine(VD->getName()).concat("_h"));

llvm::Value *HandleRef =
Builder.CreateStructGEP(GV->getValueType(), GV, 0);
Builder.CreateAlignedStore(CreateHandle, HandleRef,
HandleRef->getPointerAlignment(DL));
}
}

Builder.CreateRetVoid();
return InitResBindingsFunc;
}
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class CGHLSLRuntime {
GENERATE_HLSL_INTRINSIC_FUNCTION(WaveIsFirstLane, wave_is_first_lane)
GENERATE_HLSL_INTRINSIC_FUNCTION(WaveReadLaneAt, wave_readlane)

GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding, handle_fromBinding)

//===----------------------------------------------------------------------===//
// End of reserved area for HLSL intrinsic getters.
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -137,6 +139,10 @@ 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);

bool needsResourceBindingInitFn();
llvm::Function *createResourceBindingInitFn();

private:
void addBufferResourceAnnotation(llvm::GlobalVariable *GV,
Expand All @@ -148,6 +154,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
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5634,6 +5634,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);
Expand Down
22 changes: 4 additions & 18 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1039,21 +1039,6 @@ SemaHLSL::TakeLocForHLSLAttribute(const HLSLAttributedResourceType *RT) {
return LocInfo;
}

// Returns handle type of a resource, if the type is a resource
static const HLSLAttributedResourceType *
findHandleTypeOnResource(const Type *Ty) {
// If Ty is a resource class, the first field must
// be the resource handle of type HLSLAttributedResourceType
if (RecordDecl *RD = Ty->getAsCXXRecordDecl()) {
if (!RD->fields().empty()) {
const auto &FirstFD = RD->fields().begin();
return dyn_cast<HLSLAttributedResourceType>(
FirstFD->getType().getTypePtr());
}
}
return nullptr;
}

// Walks though the global variable declaration, collects all resource binding
// requirements and adds them to Bindings
void SemaHLSL::collectResourcesOnUserRecordDecl(const VarDecl *VD,
Expand All @@ -1075,7 +1060,7 @@ void SemaHLSL::collectResourcesOnUserRecordDecl(const VarDecl *VD,
continue;

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

// Resource
if (const HLSLAttributedResourceType *AttrResType =
findHandleTypeOnResource(VD->getType().getTypePtr())) {
HLSLAttributedResourceType::findHandleTypeOnResource(
VD->getType().getTypePtr())) {
if (RegType == getRegisterType(AttrResType->getAttrs().ResourceClass))
return true;

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

// Resource (or array of resources)
if (const HLSLAttributedResourceType *AttrResType =
findHandleTypeOnResource(Ty)) {
HLSLAttributedResourceType::findHandleTypeOnResource(Ty)) {
Bindings.addDeclBindingInfo(VD, AttrResType->getAttrs().ResourceClass);
return;
}
Expand Down
32 changes: 19 additions & 13 deletions clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
// 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
// FIXME: SPIR-V codegen of llvm.spv.handle.fromBinding is not yet implemented
// 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: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0), float }
// CHECK: @Buf = global %"class.hlsl::RWBuffer" zeroinitializer, align 4

// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this)
// 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 @__cxx_global_var_init()
// 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, 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, align 4
31 changes: 18 additions & 13 deletions clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// 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.
// NOTE: SPIRV codegen for resource types is not yet implemented

StructuredBuffer<float> Buf;
StructuredBuffer<float> Buf : register(u10);

// CHECK: define linkonce_odr noundef ptr @"??0?$StructuredBuffer@M@hlsl@@QAA@XZ"
// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), float }
// CHECK: @Buf = global %"class.hlsl::StructuredBuffer" zeroinitializer, align 4

// CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this)
// 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 @__cxx_global_var_init()
// 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, 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", align 4

0 comments on commit 7dbfa7b

Please sign in to comment.