[llvm-branch-commits] [clang] [HLSL] Global resource arrays element access (PR #152454)
Helena Kotas via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Aug 7 01:33:38 PDT 2025
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 <hekotas at 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
More information about the llvm-branch-commits
mailing list