[clang] [HLSL] Add support for fixed-size global resource arrays (PR #152209)
Helena Kotas via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 5 14:51:06 PDT 2025
https://github.com/hekota created https://github.com/llvm/llvm-project/pull/152209
Adds support for fixed-size resource arrays declared at the global scope.
When a global resource array is indexed to access an individual resource, codegen will translate the `ArraySubscriptExpr` into a constructor call for the specific resource record type and binding.
Closes #145424
>From 10c2803970a2174d92068c1ab16587ba6ef55083 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 5 Aug 2025 14:49:35 -0700
Subject: [PATCH] [HLSL] Add support for fixed-size global resource arrays
Adds support for fixed-size resource arrays declared at the global scope.
When a global resource array is indexed to access an individual resource,
codegen will translate the `ArraySubscriptExpr` into a constructor call for
the specific resource record type and binding.
Closes #145424
---
clang/include/clang/AST/Type.h | 1 +
clang/include/clang/Sema/SemaHLSL.h | 8 +-
clang/lib/AST/Type.cpp | 9 +
clang/lib/CodeGen/CGExpr.cpp | 10 +
clang/lib/CodeGen/CGHLSLRuntime.cpp | 204 +++++++++++++++++-
clang/lib/CodeGen/CGHLSLRuntime.h | 6 +
clang/lib/CodeGen/CodeGenModule.cpp | 4 +-
clang/lib/Sema/SemaHLSL.cpp | 163 +++++++++-----
.../resources/res-array-global-multi-dim.hlsl | 32 +++
.../resources/res-array-global.hlsl | 26 +++
clang/test/CodeGenHLSL/static-local-ctor.hlsl | 5 +-
11 files changed, 405 insertions(+), 63 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/AST/Type.h b/clang/include/clang/AST/Type.h
index 98810fbea726e..a7e82bbb905dd 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2724,6 +2724,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isHLSLAttributedResourceType() const;
bool isHLSLInlineSpirvType() const;
bool isHLSLResourceRecord() const;
+ bool isHLSLResourceRecordArray() const;
bool isHLSLIntangibleType()
const; // Any HLSL intangible type (builtin, array, class)
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index c0da80a70bb82..8510d44098cc6 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -228,10 +228,16 @@ 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,
+ uint32_t ArrayIndex,
+ llvm::SmallVector<Expr *> &Args);
};
} // namespace clang
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 7444a2f90c5dd..3adaee9a565de 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5246,6 +5246,15 @@ bool Type::isHLSLResourceRecord() const {
return HLSLAttributedResourceType::findHandleTypeOnResource(this) != nullptr;
}
+bool Type::isHLSLResourceRecordArray() const {
+ const Type *Ty = getUnqualifiedDesugaredType();
+ if (!Ty->isArrayType())
+ return false;
+ while (isa<ConstantArrayType>(Ty))
+ Ty = Ty->getArrayElementTypeNoTypeQual();
+ return Ty->isHLSLResourceRecord();
+}
+
bool Type::isHLSLIntangibleType() const {
const Type *Ty = getUnqualifiedDesugaredType();
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 5a3d4e447b229..d43bd0fa78ff7 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"
@@ -4515,6 +4516,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 a47d1cc22980d..f0c88a8eea2a7 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -18,6 +18,7 @@
#include "CodeGenModule.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
@@ -35,6 +36,7 @@
#include "llvm/Support/Alignment.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
+#include <optional>
using namespace clang;
using namespace CodeGen;
@@ -84,6 +86,110 @@ 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,
+ CallArgList &Args) {
+ ASTContext &AST = CD->getASTContext();
+ Value *NameStr = buildNameForResource(Name, CGM);
+ Value *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber());
+
+ Args.add(RValue::get(ThisPtr), CD->getThisType());
+ if (RBA->hasRegisterSlot()) {
+ // explicit binding
+ auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
+ 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 *
@@ -103,13 +209,6 @@ llvm::Triple::ArchType CGHLSLRuntime::getArch() {
return CGM.getTarget().getTriple().getArch();
}
-// Returns true if the type is an HLSL resource class or an array of them
-static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) {
- while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
- Ty = CAT->getArrayElementTypeNoTypeQual();
- return Ty->isHLSLResourceRecord();
-}
-
// Emits constant global variables for buffer constants declarations
// and creates metadata linking the constant globals with the buffer global.
void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
@@ -146,7 +245,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
if (VDTy.getAddressSpace() != LangAS::hlsl_constant) {
if (VD->getStorageClass() == SC_Static ||
VDTy.getAddressSpace() == LangAS::hlsl_groupshared ||
- isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) {
+ VDTy->isHLSLResourceRecord() || VDTy->isHLSLResourceRecordArray()) {
// Emit static and groupshared variables and resource classes inside
// cbuffer as regular globals
CGM.EmitGlobal(VD);
@@ -679,3 +778,92 @@ 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 it should have been by SemaHLSL)
+ QualType ResourceTy = ArraySubsExpr->getType();
+ HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>();
+ assert(RBA && "resource array is missing HLSLResourceBindingAttr attribute");
+
+ // lookup the resource class constructor based on the resource type and
+ // binding
+ CXXConstructorDecl *CD = findResourceConstructorDecl(
+ ArrayDecl->getASTContext(), ResourceTy, 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, 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 89d2aff85d913..3f9866d1ebdaf 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -67,6 +67,7 @@ class Type;
class RecordType;
class DeclContext;
class HLSLPackOffsetAttr;
+class ArraySubscriptExpr;
class FunctionDecl;
@@ -74,6 +75,7 @@ namespace CodeGen {
class CodeGenModule;
class CodeGenFunction;
+class LValue;
class CGHLSLRuntime {
public:
@@ -163,6 +165,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 9276554bebf9d..81014a665516d 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -70,6 +70,10 @@ static RegisterType getRegisterType(ResourceClass RC) {
llvm_unreachable("unexpected ResourceClass value");
}
+static RegisterType getRegisterType(const HLSLAttributedResourceType *ResTy) {
+ return getRegisterType(ResTy->getAttrs().ResourceClass);
+}
+
// Converts the first letter of string Slot to RegisterType.
// Returns false if the letter does not correspond to a valid register type.
static bool convertToRegisterType(StringRef Slot, RegisterType *RT) {
@@ -336,16 +340,28 @@ static bool isZeroSizedArray(const ConstantArrayType *CAT) {
return CAT != nullptr;
}
-// Returns true if the record type is an HLSL resource class or an array of
-// resource classes
-static bool isResourceRecordTypeOrArrayOf(const Type *Ty) {
- while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
- Ty = CAT->getArrayElementTypeNoTypeQual();
- return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
+static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
+ const Type *Ty = VD->getType().getTypePtr();
+ return Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray();
}
-static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
- return isResourceRecordTypeOrArrayOf(VD->getType().getTypePtr());
+static const HLSLAttributedResourceType *
+getResourceArrayHandleType(VarDecl *VD) {
+ assert(VD->getType()->isHLSLResourceRecordArray() &&
+ "expected array of resource records");
+ const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
+ while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) {
+ Ty = CAT->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType();
+ }
+ 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
@@ -354,7 +370,7 @@ static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
// type or if it is a record type that needs to be inspected further.
static bool isInvalidConstantBufferLeafElementType(const Type *Ty) {
Ty = Ty->getUnqualifiedDesugaredType();
- if (isResourceRecordTypeOrArrayOf(Ty))
+ if (Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray())
return true;
if (Ty->isRecordType())
return Ty->getAsCXXRecordDecl()->isEmpty();
@@ -574,16 +590,13 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
BufDecl->addLayoutStruct(LS);
}
-static void addImplicitBindingAttrToBuffer(Sema &S, HLSLBufferDecl *BufDecl,
- uint32_t ImplicitBindingOrderID) {
- RegisterType RT =
- BufDecl->isCBuffer() ? RegisterType::CBuffer : RegisterType::SRV;
+static void addImplicitBindingAttrToDecl(Sema &S, Decl *D, RegisterType RT,
+ uint32_t ImplicitBindingOrderID) {
auto *Attr =
HLSLResourceBindingAttr::CreateImplicit(S.getASTContext(), "", "0", {});
- std::optional<unsigned> RegSlot;
- Attr->setBinding(RT, RegSlot, 0);
+ Attr->setBinding(RT, std::nullopt, 0);
Attr->setImplicitBindingOrderID(ImplicitBindingOrderID);
- BufDecl->addAttr(Attr);
+ D->addAttr(Attr);
}
// Handle end of cbuffer/tbuffer declaration
@@ -605,7 +618,10 @@ void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) {
if (RBA)
RBA->setImplicitBindingOrderID(OrderID);
else
- addImplicitBindingAttrToBuffer(SemaRef, BufDecl, OrderID);
+ addImplicitBindingAttrToDecl(SemaRef, BufDecl,
+ BufDecl->isCBuffer() ? RegisterType::CBuffer
+ : RegisterType::SRV,
+ OrderID);
}
SemaRef.PopDeclContext();
@@ -2411,8 +2427,8 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
HLSLBufferDecl *DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
SemaRef.getASTContext(), SemaRef.getCurLexicalContext(),
DefaultCBufferDecls);
- addImplicitBindingAttrToBuffer(SemaRef, DefaultCBuffer,
- getNextImplicitBindingOrderID());
+ addImplicitBindingAttrToDecl(SemaRef, DefaultCBuffer, RegisterType::CBuffer,
+ getNextImplicitBindingOrderID());
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);
@@ -3562,7 +3578,7 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
return;
// Resource handles.
- if (isResourceRecordTypeOrArrayOf(Type->getUnqualifiedDesugaredType()))
+ if (Type->isHLSLResourceRecord() || Type->isHLSLResourceRecordArray())
return;
// Only static globals belong to the Private address space.
@@ -3598,14 +3614,7 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
DefaultCBufferDecls.push_back(VD);
}
- // find all resources bindings on decl
- if (VD->getType()->isHLSLIntangibleType())
- collectResourceBindingsOnVarDecl(VD);
-
- const Type *VarType = VD->getType().getTypePtr();
- while (VarType->isArrayType())
- VarType = VarType->getArrayElementTypeNoTypeQual();
- if (VarType->isHLSLResourceRecord() ||
+ if (isResourceRecordTypeOrArrayOf(VD) ||
VD->hasAttr<HLSLVkConstantIdAttr>()) {
// Make the variable for resources static. The global externally visible
// storage is accessed through the handle, which is a member. The variable
@@ -3613,6 +3622,26 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
VD->setStorageClass(StorageClass::SC_Static);
}
+ if (VD->getType()->isHLSLResourceRecordArray()) {
+ // If the resource array does not have an explicit binding attribute,
+ // create an implicit one. It will be used to transfer implicit binding
+ // order_ID to codegen.
+ HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
+ if (!RBA || !RBA->hasRegisterSlot()) {
+ uint32_t OrderID = getNextImplicitBindingOrderID();
+ if (RBA)
+ RBA->setImplicitBindingOrderID(OrderID);
+ else
+ addImplicitBindingAttrToDecl(
+ SemaRef, VD, getRegisterType(getResourceArrayHandleType(VD)),
+ OrderID);
+ }
+ }
+
+ // find all resources bindings on decl
+ if (VD->getType()->isHLSLIntangibleType())
+ collectResourceBindingsOnVarDecl(VD);
+
// process explicit bindings
processExplicitBindingsOnDecl(VD);
}
@@ -3640,10 +3669,13 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
return true;
}
-bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
+void SemaHLSL::createResourceRecordCtorArgs(const Type *ResourceTy,
+ StringRef VarName,
+ HLSLResourceBindingAttr *RBA,
+ uint32_t ArrayIndex,
+ llvm::SmallVector<Expr *> &Args) {
std::optional<uint32_t> RegisterSlot;
uint32_t SpaceNo = 0;
- HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
if (RBA) {
if (RBA->hasRegisterSlot())
RegisterSlot = RBA->getSlotNumber();
@@ -3655,12 +3687,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()),
@@ -3671,18 +3703,52 @@ 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
+ IntegerLiteral *OrderId = IntegerLiteral::Create(
+ AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()),
+ 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>(), 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>(), 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) {
@@ -3691,17 +3757,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..73d9f28e4e9e2
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/res-array-global.hlsl
@@ -0,0 +1,26 @@
+// 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: @[[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
+
+RWBuffer<float> A[4] : register(u10, space1);
+RWBuffer<int> B[5]; // implicit binding -> u0, 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
+
+ // Make sure that A[2] is translated to a RWBuffer<float> constructor call for explicit binding (u10, space1) with range 4 and index 2
+ // CHECK: call void @_ZN4hlsl8RWBufferIfEC1EjjijPKc(ptr {{.*}} %[[Tmp0]], i32 noundef 10, i32 noundef 1, i32 noundef 4, i32 noundef 2, ptr noundef @[[BufA]])
+
+ // Make sure that A[3] is translated to a RWBuffer<int> constructor call for implicit binding (u0, space0) with range 5 and index 3
+ // CHECK: call void @_ZN4hlsl8RWBufferIiEC1EjijjPKc(ptr {{.*}} %[[Tmp1]], i32 noundef 0, i32 noundef 5, i32 noundef 3, i32 noundef 0, ptr noundef @[[BufB]])
+
+ Out[0] = A[2][0] + (float)B[3][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 cfe-commits
mailing list