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
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.
damyanp marked this conversation as resolved.
Show resolved Hide resolved
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);
damyanp marked this conversation as resolved.
Show resolved Hide resolved

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

hekota marked this conversation as resolved.
Show resolved Hide resolved
// 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
Loading