diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index deda5b3f70f34..40e617bf8f3b8 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -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 { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 6f23a1a13d051..5232efae4e363 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -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( + FirstFD->getType().getTypePtr()); + } + } + return nullptr; +} diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 8dcb5f6100619..b4f1a68cfe87f 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -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) { diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 3237d93ca31ce..2cce2936fe5ae 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,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(); + 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(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; +} diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index f7621ee20b124..ff7df41b5c62e 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -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. //===----------------------------------------------------------------------===// @@ -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, @@ -148,6 +154,9 @@ class CGHLSLRuntime { void addBufferDecls(const DeclContext *DC, Buffer &CB); llvm::Triple::ArchType getArch(); llvm::SmallVector Buffers; + + llvm::SmallVector> + ResourcesToBind; }; } // namespace CodeGen diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b3e805a67768a..9a84a11973b1a 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -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); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index efb0fbaa432d7..1d18a6308e2a5 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -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( - 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, @@ -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); @@ -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; @@ -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; } diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl index 19699dcf14d9f..3949f7b943cfe 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl @@ -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 Buf; +RWBuffer 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 diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl index f65090410ce66..4dbca9bc0a4d9 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffer-constructor.hlsl @@ -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 Buf; +StructuredBuffer 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