https://github.com/hekota created https://github.com/llvm/llvm-project/pull/152454
Adds support for accessing individual resources from fixed-size resource arrays declared at global scope. When a global resource array is indexed to retrieve a specific resource, the codegen translates the `ArraySubscriptExpr` AST node to a constructor call for the corresponding resource record type and binding. Closes #145424 >From 86902233a96b26b710bd39c096cb581f252e09a4 Mon Sep 17 00:00:00 2001 From: Helena Kotas <heko...@microsoft.com> Date: Thu, 7 Aug 2025 01:30:36 -0700 Subject: [PATCH] [HLSL] Global resource arrays element access Adds support for accessing individual resources from fixed-size resource arrays declared at global scope. When a global resource array is indexed to retrieve a specific resource, the codegen translates the `ArraySubscriptExpr` into a constructor call for the corresponding resource record type and binding. Closes #145424 --- clang/include/clang/Sema/SemaHLSL.h | 9 +- clang/lib/CodeGen/CGExpr.cpp | 10 + clang/lib/CodeGen/CGHLSLRuntime.cpp | 223 +++++++++++++++++- clang/lib/CodeGen/CGHLSLRuntime.h | 6 + clang/lib/CodeGen/CodeGenModule.cpp | 4 +- clang/lib/Sema/SemaHLSL.cpp | 93 ++++++-- .../resources/res-array-global-multi-dim.hlsl | 32 +++ .../resources/res-array-global.hlsl | 59 +++++ clang/test/CodeGenHLSL/static-local-ctor.hlsl | 5 +- 9 files changed, 401 insertions(+), 40 deletions(-) create mode 100644 clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/res-array-global.hlsl diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 085c9ed9f3ebd..0c215c6e10013 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -229,10 +229,17 @@ class SemaHLSL : public SemaBase { void diagnoseAvailabilityViolations(TranslationUnitDecl *TU); - bool initGlobalResourceDecl(VarDecl *VD); uint32_t getNextImplicitBindingOrderID() { return ImplicitBindingNextOrderID++; } + + bool initGlobalResourceDecl(VarDecl *VD); + bool initGlobalResourceArrayDecl(VarDecl *VD); + void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName, + HLSLResourceBindingAttr *RBA, + HLSLVkBindingAttr *VkBinding, + uint32_t ArrayIndex, + llvm::SmallVector<Expr *> &Args); }; } // namespace clang diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index ed35a055d8a7f..8c34fb501a3b8 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -16,6 +16,7 @@ #include "CGCall.h" #include "CGCleanup.h" #include "CGDebugInfo.h" +#include "CGHLSLRuntime.h" #include "CGObjCRuntime.h" #include "CGOpenMPRuntime.h" #include "CGRecordLayout.h" @@ -4532,6 +4533,15 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, LHS.getBaseInfo(), TBAAAccessInfo()); } + // The HLSL runtime handle the subscript expression on global resource arrays. + if (getLangOpts().HLSL && (E->getType()->isHLSLResourceRecord() || + E->getType()->isHLSLResourceRecordArray())) { + std::optional<LValue> LV = + CGM.getHLSLRuntime().emitResourceArraySubscriptExpr(E, *this); + if (LV.has_value()) + return *LV; + } + // All the other cases basically behave like simple offsetting. // Handle the extvector case we ignored above. diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 918cb3e38448d..a09e540367a18 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -84,6 +84,124 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer, RootSignatureValMD->addOperand(MDVals); } +// If the specified expr is a simple decay from an array to pointer, +// return the array subexpression. Otherwise, return nullptr. +static const Expr *getSubExprFromArrayDecayOperand(const Expr *E) { + const auto *CE = dyn_cast<CastExpr>(E); + if (!CE || CE->getCastKind() != CK_ArrayToPointerDecay) + return nullptr; + return CE->getSubExpr(); +} + +// Find array variable declaration from nested array subscript AST nodes +static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) { + const Expr *E = nullptr; + while (ASE != nullptr) { + E = getSubExprFromArrayDecayOperand(ASE->getBase()); + if (!E) + return nullptr; + ASE = dyn_cast<ArraySubscriptExpr>(E); + } + if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E)) + return DRE->getDecl(); + return nullptr; +} + +// Get the total size of the array, or -1 if the array is unbounded. +static int getTotalArraySize(const clang::Type *Ty) { + assert(Ty->isArrayType() && "expected array type"); + if (Ty->isIncompleteArrayType()) + return -1; + int Size = 1; + while (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) { + Size *= CAT->getSExtSize(); + Ty = CAT->getArrayElementTypeNoTypeQual(); + } + return Size; +} + +// Find constructor decl for a specific resource record type and binding +// (implicit vs. explicit). The constructor has 6 parameters. +// For explicit binding the signature is: +// void(unsigned, unsigned, int, unsigned, const char *). +// For implicit binding the signature is: +// void(unsigned, int, unsigned, unsigned, const char *). +static CXXConstructorDecl *findResourceConstructorDecl(ASTContext &AST, + QualType ResTy, + bool ExplicitBinding) { + SmallVector<QualType> ExpParmTypes = { + AST.UnsignedIntTy, AST.UnsignedIntTy, AST.UnsignedIntTy, + AST.UnsignedIntTy, AST.getPointerType(AST.CharTy.withConst())}; + ExpParmTypes[ExplicitBinding ? 2 : 1] = AST.IntTy; + + CXXRecordDecl *ResDecl = ResTy->getAsCXXRecordDecl(); + for (auto *Ctor : ResDecl->ctors()) { + if (Ctor->getNumParams() != ExpParmTypes.size()) + continue; + ParmVarDecl **ParmIt = Ctor->param_begin(); + QualType *ExpTyIt = ExpParmTypes.begin(); + for (; ParmIt != Ctor->param_end() && ExpTyIt != ExpParmTypes.end(); + ++ParmIt, ++ExpTyIt) { + if ((*ParmIt)->getType() != *ExpTyIt) + break; + } + if (ParmIt == Ctor->param_end()) + return Ctor; + } + llvm_unreachable("did not find constructor for resource class"); +} + +static Value *buildNameForResource(llvm::StringRef BaseName, + CodeGenModule &CGM) { + std::string Str(BaseName); + std::string GlobalName(Str + ".str"); + return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer(); +} + +static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD, + llvm::Value *ThisPtr, llvm::Value *Range, + llvm::Value *Index, StringRef Name, + HLSLResourceBindingAttr *RBA, + HLSLVkBindingAttr *VkBinding, + CallArgList &Args) { + assert((VkBinding || RBA) && "at least one a binding attribute expected"); + + std::optional<uint32_t> RegisterSlot; + uint32_t SpaceNo = 0; + if (VkBinding) { + RegisterSlot = VkBinding->getBinding(); + SpaceNo = VkBinding->getSet(); + } else if (RBA) { + if (RBA->hasRegisterSlot()) + RegisterSlot = RBA->getSlotNumber(); + SpaceNo = RBA->getSpaceNumber(); + } + + ASTContext &AST = CD->getASTContext(); + Value *NameStr = buildNameForResource(Name, CGM); + Value *Space = llvm::ConstantInt::get(CGM.IntTy, SpaceNo); + + Args.add(RValue::get(ThisPtr), CD->getThisType()); + if (RegisterSlot.has_value()) { + // explicit binding + auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RegisterSlot.value()); + Args.add(RValue::get(RegSlot), AST.UnsignedIntTy); + Args.add(RValue::get(Space), AST.UnsignedIntTy); + Args.add(RValue::get(Range), AST.IntTy); + Args.add(RValue::get(Index), AST.UnsignedIntTy); + + } else { + // implicit binding + auto *OrderID = + llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID()); + Args.add(RValue::get(Space), AST.UnsignedIntTy); + Args.add(RValue::get(Range), AST.IntTy); + Args.add(RValue::get(Index), AST.UnsignedIntTy); + Args.add(RValue::get(OrderID), AST.UnsignedIntTy); + } + Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst())); +} + } // namespace llvm::Type * @@ -590,13 +708,6 @@ static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV, CGM.AddCXXGlobalInit(InitResFunc); } -static Value *buildNameForResource(llvm::StringRef BaseName, - CodeGenModule &CGM) { - std::string Str(BaseName); - std::string GlobalName(Str + ".str"); - return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer(); -} - void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *GV, HLSLVkBindingAttr *VkBinding) { @@ -624,17 +735,13 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl, auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0); auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1); auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber()); - Value *Name = nullptr; + Value *Name = buildNameForResource(BufDecl->getName(), CGM); llvm::Intrinsic::ID IntrinsicID = RBA->hasRegisterSlot() ? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic() : CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic(); - std::string Str(BufDecl->getName()); - std::string GlobalName(Str + ".str"); - Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer(); - // buffer with explicit binding if (RBA->hasRegisterSlot()) { auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber()); @@ -701,3 +808,95 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF, } } } + +std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr( + const ArraySubscriptExpr *ArraySubsExpr, CodeGenFunction &CGF) { + assert(ArraySubsExpr->getType()->isHLSLResourceRecord() || + ArraySubsExpr->getType()->isHLSLResourceRecordArray() && + "expected resource array subscript expression"); + + // let clang codegen handle local resource array subscrips + const VarDecl *ArrayDecl = dyn_cast<VarDecl>(getArrayDecl(ArraySubsExpr)); + if (!ArrayDecl || !ArrayDecl->hasGlobalStorage()) + return std::nullopt; + + // FIXME: this is not yet implemented (llvm/llvm-project#145426) + assert(!ArraySubsExpr->getType()->isArrayType() && + "indexing of array subsets it not supported yet"); + + // get total array size (= range size) + const Type *ResArrayTy = ArrayDecl->getType().getTypePtr(); + assert(ResArrayTy->isHLSLResourceRecordArray() && + "expected array of resource classes"); + llvm::Value *Range = + llvm::ConstantInt::get(CGM.IntTy, getTotalArraySize(ResArrayTy)); + + // Iterate through all nested array subscript expressions to calculate + // the index in the flattened resource array (if this is a multi- + // dimensional array). The index is calculated as a sum of all indices + // multiplied by the total size of the array at that level. + Value *Index = nullptr; + Value *Multiplier = nullptr; + const ArraySubscriptExpr *ASE = ArraySubsExpr; + while (ASE != nullptr) { + Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx()); + if (const auto *ArrayTy = + dyn_cast<ConstantArrayType>(ASE->getType().getTypePtr())) { + Value *SubMultiplier = + llvm::ConstantInt::get(CGM.IntTy, ArrayTy->getSExtSize()); + Multiplier = Multiplier ? CGF.Builder.CreateMul(Multiplier, SubMultiplier) + : SubMultiplier; + SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier); + } + + Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex; + ASE = dyn_cast<ArraySubscriptExpr>( + getSubExprFromArrayDecayOperand(ASE->getBase())); + } + + // find binding info for the resource array + // (for implicit binding an HLSLResourceBindingAttr should have been added by + // SemaHLSL) + QualType ResourceTy = ArraySubsExpr->getType(); + HLSLVkBindingAttr *VkBinding = ArrayDecl->getAttr<HLSLVkBindingAttr>(); + HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>(); + assert((VkBinding || RBA) && "resource array must have a binding attribute"); + + // lookup the resource class constructor based on the resource type and + // binding + CXXConstructorDecl *CD = + findResourceConstructorDecl(ArrayDecl->getASTContext(), ResourceTy, + VkBinding || RBA->hasRegisterSlot()); + + // create a temporary variable for the resource class instance (we need to + // return an LValue) + RawAddress TmpVar = CGF.CreateMemTemp(ResourceTy); + if (auto *Size = CGF.EmitLifetimeStart( + CGM.getDataLayout().getTypeAllocSize(TmpVar.getElementType()), + TmpVar.getPointer())) { + CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>( + NormalEHLifetimeMarker, TmpVar, Size); + } + AggValueSlot ValueSlot = AggValueSlot::forAddr( + TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true), + AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false), + AggValueSlot::MayOverlap); + + Address ThisAddress = ValueSlot.getAddress(); + llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo( + ThisAddress, CD->getThisType()->getPointeeType()); + + // assemble the constructor parameters + CallArgList Args; + createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(), + RBA, VkBinding, Args); + + // call the constructor + CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress, Args, + ValueSlot.mayOverlap(), + ArraySubsExpr->getExprLoc(), + ValueSlot.isSanitizerChecked()); + + return CGF.MakeAddrLValue(TmpVar, ArraySubsExpr->getType(), + AlignmentSource::Decl); +} diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 31d1728da9c56..b872f9ef0e9b6 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -68,6 +68,7 @@ class Type; class RecordType; class DeclContext; class HLSLPackOffsetAttr; +class ArraySubscriptExpr; class FunctionDecl; @@ -75,6 +76,7 @@ namespace CodeGen { class CodeGenModule; class CodeGenFunction; +class LValue; class CGHLSLRuntime { public: @@ -164,6 +166,10 @@ class CGHLSLRuntime { llvm::TargetExtType *LayoutTy); void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E); + std::optional<LValue> + emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E, + CodeGenFunction &CGF); + private: void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, llvm::GlobalVariable *BufGV); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 834b1c067d84c..1498c5d75fa53 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -5775,8 +5775,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, if (D->getType()->isReferenceType()) T = D->getType(); - if (getLangOpts().HLSL && - D->getType().getTypePtr()->isHLSLResourceRecord()) { + if (getLangOpts().HLSL && (D->getType()->isHLSLResourceRecord() || + D->getType()->isHLSLResourceRecordArray())) { Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy)); NeedsGlobalCtor = true; } else if (getLangOpts().CPlusPlus) { diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 6811f3f27603b..7de529fc898ad 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -357,6 +357,14 @@ getResourceArrayHandleType(VarDecl *VD) { return HLSLAttributedResourceType::findHandleTypeOnResource(Ty); } +// returns the element type of an array (including multi-dimensional array) +static QualType getArrayElementType(QualType Ty) { + assert(Ty->isArrayType() && "expected array type"); + while (const ArrayType *AT = dyn_cast<ArrayType>(Ty.getTypePtr())) + Ty = AT->getElementType(); + return Ty; +} + // Returns true if the type is a leaf element type that is not valid to be // included in HLSL Buffer, such as a resource class, empty struct, zero-sized // array, or a builtin intangible type. Returns false it is a valid leaf element @@ -3698,11 +3706,14 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD, return true; } -bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) { +void SemaHLSL::createResourceRecordCtorArgs(const Type *ResourceTy, + StringRef VarName, + HLSLResourceBindingAttr *RBA, + HLSLVkBindingAttr *VkBinding, + uint32_t ArrayIndex, + llvm::SmallVector<Expr *> &Args) { std::optional<uint32_t> RegisterSlot; uint32_t SpaceNo = 0; - HLSLVkBindingAttr *VkBinding = VD->getAttr<HLSLVkBindingAttr>(); - HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>(); if (VkBinding) { RegisterSlot = VkBinding->getBinding(); SpaceNo = VkBinding->getSet(); @@ -3717,12 +3728,12 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) { uint64_t IntTySize = AST.getTypeSize(AST.IntTy); IntegerLiteral *RangeSize = IntegerLiteral::Create( AST, llvm::APInt(IntTySize, 1), AST.IntTy, SourceLocation()); - IntegerLiteral *Index = IntegerLiteral::Create( - AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy, SourceLocation()); + IntegerLiteral *Index = + IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, ArrayIndex), + AST.UnsignedIntTy, SourceLocation()); IntegerLiteral *Space = IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo), AST.UnsignedIntTy, SourceLocation()); - StringRef VarName = VD->getName(); StringLiteral *Name = StringLiteral::Create( AST, VarName, StringLiteralKind::Ordinary, false, AST.getStringLiteralArrayType(AST.CharTy.withConst(), VarName.size()), @@ -3733,18 +3744,57 @@ bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) { IntegerLiteral *RegSlot = IntegerLiteral::Create( AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy, SourceLocation()); - Expr *Args[] = {RegSlot, Space, RangeSize, Index, Name}; - return initVarDeclWithCtor(SemaRef, VD, Args); + Args.append({RegSlot, Space, RangeSize, Index, Name}); + } else { + // resource with implicit binding + uint32_t OrderID = (RBA && RBA->hasImplicitBindingOrderID()) + ? RBA->getImplicitBindingOrderID() + : getNextImplicitBindingOrderID(); + IntegerLiteral *OrderId = + IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, OrderID), + AST.UnsignedIntTy, SourceLocation()); + Args.append({Space, RangeSize, Index, OrderId, Name}); } +} - // resource with implicit binding - IntegerLiteral *OrderId = IntegerLiteral::Create( - AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()), - AST.UnsignedIntTy, SourceLocation()); - Expr *Args[] = {Space, RangeSize, Index, OrderId, Name}; +bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) { + SmallVector<Expr *> Args; + createResourceRecordCtorArgs(VD->getType().getTypePtr(), VD->getName(), + VD->getAttr<HLSLResourceBindingAttr>(), + VD->getAttr<HLSLVkBindingAttr>(), 0, Args); return initVarDeclWithCtor(SemaRef, VD, Args); } +bool SemaHLSL::initGlobalResourceArrayDecl(VarDecl *VD) { + assert(VD->getType()->isHLSLResourceRecordArray() && + "expected array of resource records"); + + // Individual resources in a resource array are not initialized here. They + // are initialized later on during codegen when the individual resources are + // accessed. Codegen will emit a call to the resource constructor with the + // specified array index. We need to make sure though that the constructor + // for the specific resource type is instantiated, so codegen can emit a call + // to it when the array element is accessed. + SmallVector<Expr *> Args; + QualType ResElementTy = getArrayElementType(VD->getType()); + createResourceRecordCtorArgs(ResElementTy.getTypePtr(), VD->getName(), + VD->getAttr<HLSLResourceBindingAttr>(), + VD->getAttr<HLSLVkBindingAttr>(), 0, Args); + + SourceLocation Loc = VD->getLocation(); + InitializedEntity Entity = + InitializedEntity::InitializeTemporary(ResElementTy); + InitializationKind Kind = InitializationKind::CreateDirect(Loc, Loc, Loc); + InitializationSequence InitSeq(SemaRef, Entity, Kind, Args); + if (InitSeq.Failed()) + return false; + + // This takes care of instantiating and emitting of the constructor that will + // be called from codegen when the array is accessed. + ExprResult OneResInit = InitSeq.Perform(SemaRef, Entity, Kind, Args); + return !OneResInit.isInvalid(); +} + // Returns true if the initialization has been handled. // Returns false to use default initialization. bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) { @@ -3753,17 +3803,14 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) { if (VD->getType().getAddressSpace() == LangAS::hlsl_constant) return true; - // Initialize resources - if (!isResourceRecordTypeOrArrayOf(VD)) - return false; - - // FIXME: We currectly support only simple resources - no arrays of resources - // or resources in user defined structs. - // (llvm/llvm-project#133835, llvm/llvm-project#133837) // Initialize resources at the global scope - if (VD->hasGlobalStorage() && VD->getType()->isHLSLResourceRecord()) - return initGlobalResourceDecl(VD); - + if (VD->hasGlobalStorage()) { + const Type *Ty = VD->getType().getTypePtr(); + if (Ty->isHLSLResourceRecord()) + return initGlobalResourceDecl(VD); + if (Ty->isHLSLResourceRecordArray()) + return initGlobalResourceArrayDecl(VD); + } return false; } diff --git a/clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl b/clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl new file mode 100644 index 0000000000000..97df14769b132 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/res-array-global-multi-dim.hlsl @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -finclude-default-header -triple spirv-unknown-vulkan-compute \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s + +// CHECK: @[[BufB:.*]] = private unnamed_addr constant [2 x i8] c"B\00", align 1 +// CHECK: @[[BufC:.*]] = private unnamed_addr constant [2 x i8] c"C\00", align 1 +// CHECK: @[[BufD:.*]] = private unnamed_addr constant [2 x i8] c"D\00", align 1 + +RWBuffer<float> B[4][4] : register(u2); +RWBuffer<int> C[2][2][5] : register(u10, space1); +RWBuffer<uint> D[10][5]; // implicit binding -> u18, space0 + +RWStructuredBuffer<float> Out; + +[numthreads(4,1,1)] +void main() { + // CHECK: define internal{{.*}} void @_Z4mainv() + // CHECK: %[[Tmp0:.*]] = alloca %"class.hlsl::RWBuffer + // CHECK: %[[Tmp1:.*]] = alloca %"class.hlsl::RWBuffer + // CHECK: %[[Tmp2:.*]] = alloca %"class.hlsl::RWBuffer + + // Make sure that B[2][3] is translated to a RWBuffer<float> constructor call for explicit binding (u2, space0) with range 16 and index 14 + // CHECK: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], i32 noundef 2, i32 noundef 0, i32 noundef 16, i32 noundef 14, ptr noundef @[[BufB]]) + + // Make sure that C[1][0][3] is translated to a RWBuffer<int> constructor call for explicit binding (u10, space1) with range 20 and index 13 + // CHECK: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp1]], i32 noundef 10, i32 noundef 1, i32 noundef 20, i32 noundef 13, ptr noundef @[[BufC]]) + + // Make sure that D[9][2] is translated to a RWBuffer<uint> constructor call for implicit binding (u18, space0) with range 50 and index 47 + // CHECK: call void @_ZN4hlsl8RWBufferIjEC1EjijjPKc(ptr {{.*}} %[[Tmp2]], i32 noundef 0, i32 noundef 50, i32 noundef 47, i32 noundef 0, ptr noundef @[[BufD]]) + Out[0] = B[3][2][0] + (float)C[1][0][3][0] + (float)D[9][2][0]; +} diff --git a/clang/test/CodeGenHLSL/resources/res-array-global.hlsl b/clang/test/CodeGenHLSL/resources/res-array-global.hlsl new file mode 100644 index 0000000000000..26526e9a5c9d8 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/res-array-global.hlsl @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -check-prefixes=CHECK,DXIL +// RUN: %clang_cc1 -finclude-default-header -triple spirv-unknown-vulkan-compute \ +// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -check-prefixes=CHECK,SPV + +// CHECK: @[[BufA:.*]] = private unnamed_addr constant [2 x i8] c"A\00", align 1 +// CHECK: @[[BufB:.*]] = private unnamed_addr constant [2 x i8] c"B\00", align 1 +// CHECK: @[[BufC:.*]] = private unnamed_addr constant [2 x i8] c"C\00", align 1 +// CHECK: @[[BufD:.*]] = private unnamed_addr constant [2 x i8] c"D\00", align 1 + +// different explicit binding for DXIL and SPIR-V +[[vk::binding(12, 2)]] +RWBuffer<float> A[4] : register(u10, space1); + +[[vk::binding(13)]] // SPIR-V explicit binding 13, set 0 +RWBuffer<int> B[5]; // DXIL implicit binding in space0 + +// same explicit binding for both DXIL and SPIR-V +// (SPIR-V takes the binding from register annotation if there is no vk::binding attribute)) +RWBuffer<int> C[3] : register(u2); + +// implicit binding for both DXIL and SPIR-V in space/set 0 +RWBuffer<double> D[10]; + +RWStructuredBuffer<float> Out; + +[numthreads(4,1,1)] +void main() { + // CHECK: define internal{{.*}} void @_Z4mainv() + // CHECK: %[[Tmp0:.*]] = alloca %"class.hlsl::RWBuffer + // CHECK: %[[Tmp1:.*]] = alloca %"class.hlsl::RWBuffer + // CHECK: %[[Tmp2:.*]] = alloca %"class.hlsl::RWBuffer + // CHECK: %[[Tmp3:.*]] = alloca %"class.hlsl::RWBuffer + + // Make sure A[2] is translated to a RWBuffer<float> constructor call with range 4 and index 2 + // and DXIL explicit binding (u10, space1) + // and SPIR-V explicit binding (binding 12, set 2) + // DXIL: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], i32 noundef 10, i32 noundef 1, i32 noundef 4, i32 noundef 2, ptr noundef @[[BufA]]) + // SPV: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], i32 noundef 12, i32 noundef 2, i32 noundef 4, i32 noundef 2, ptr noundef @[[BufA]]) + + // Make sure B[3] is translated to a RWBuffer<int> constructor call with range 5 and index 3 + // and DXIL for implicit binding in space0, order id 0 + // and SPIR-V explicit binding (binding 13, set 0) + // DXIL: call void @_ZN4hlsl8RWBufferIiEC1EjijjPKc(ptr {{.*}} %[[Tmp1]], i32 noundef 0, i32 noundef 5, i32 noundef 3, i32 noundef 0, ptr noundef @[[BufB]]) + // SPV: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp1]], i32 noundef 13, i32 noundef 0, i32 noundef 5, i32 noundef 3, ptr noundef @[[BufB]]) + + // Make sure C[1] is translated to a RWBuffer<int> constructor call with range 3 and index 1 + // and DXIL explicit binding (u2, space0) + // and SPIR-V explicit binding (binding 2, set 0) + // DXIL: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp2]], i32 noundef 2, i32 noundef 0, i32 noundef 3, i32 noundef 1, ptr noundef @[[BufC]]) + // SPV: call void @_ZN4hlsl8RWBufferIiEC1EjjijPKc(ptr {{.*}} %[[Tmp2]], i32 noundef 2, i32 noundef 0, i32 noundef 3, i32 noundef 1, ptr noundef @[[BufC]]) + + // Make sure D[7] is translated to a RWBuffer<doublet> constructor call with range 10 and index 7 + // and DXIL for implicit binding in space0, order id 1 + // and SPIR-V explicit binding (binding 13, set 0), order id 0 + // DXIL: call void @_ZN4hlsl8RWBufferIdEC1EjijjPKc(ptr {{.*}} %[[Tmp3]], i32 noundef 0, i32 noundef 10, i32 noundef 7, i32 noundef 1, ptr noundef @[[BufD]]) + // SPV: call void @_ZN4hlsl8RWBufferIdEC1EjijjPKc(ptr {{.*}} %[[Tmp3]], i32 noundef 0, i32 noundef 10, i32 noundef 7, i32 noundef 0, ptr noundef @[[BufD]]) + Out[0] = A[2][0] + (float)B[3][0] + (float)C[1][0] + (float)D[7][0]; +} diff --git a/clang/test/CodeGenHLSL/static-local-ctor.hlsl b/clang/test/CodeGenHLSL/static-local-ctor.hlsl index 87f49b8bf7ac7..9a4bf66f030ed 100644 --- a/clang/test/CodeGenHLSL/static-local-ctor.hlsl +++ b/clang/test/CodeGenHLSL/static-local-ctor.hlsl @@ -2,7 +2,7 @@ // Verify that no per variable _Init_thread instructions are emitted for non-trivial static locals // These would normally be emitted by the MicrosoftCXXABI, but the DirectX backend should exlude them -// Instead, check for the guardvar oparations that should protect the constructor initialization should +// Instead, check for the guardvar operations that should protect the constructor initialization should // only take place once. RWBuffer<int> buf[10]; @@ -15,13 +15,14 @@ void InitBuf(RWBuffer<int> buf) { // CHECK-NOT: _Init_thread_epoch // CHECK: define internal void @_Z4mainv // CHECK-NEXT: entry: +// CHECK-NEXT: [[Tmp0:%.*]] = alloca %"class.hlsl::RWBuffer" // CHECK-NEXT: [[Tmp1:%.*]] = alloca %"class.hlsl::RWBuffer" // CHECK-NEXT: [[Tmp2:%.*]] = load i8, ptr @_ZGVZ4mainvE5mybuf // CHECK-NEXT: [[Tmp3:%.*]] = icmp eq i8 [[Tmp2]], 0 // CHECK-NEXT: br i1 [[Tmp3]] // CHECK-NOT: _Init_thread_header // CHECK: init.check: -// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1Ejijj +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1EjijjPKc( // CHECK-NEXT: store i8 1, ptr @_ZGVZ4mainvE5mybuf // CHECK-NOT: _Init_thread_footer _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits