Skip to content

Commit 8690223

Browse files
committed
[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
1 parent e12a7a9 commit 8690223

File tree

9 files changed

+401
-40
lines changed

9 files changed

+401
-40
lines changed

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,17 @@ class SemaHLSL : public SemaBase {
229229

230230
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
231231

232-
bool initGlobalResourceDecl(VarDecl *VD);
233232
uint32_t getNextImplicitBindingOrderID() {
234233
return ImplicitBindingNextOrderID++;
235234
}
235+
236+
bool initGlobalResourceDecl(VarDecl *VD);
237+
bool initGlobalResourceArrayDecl(VarDecl *VD);
238+
void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName,
239+
HLSLResourceBindingAttr *RBA,
240+
HLSLVkBindingAttr *VkBinding,
241+
uint32_t ArrayIndex,
242+
llvm::SmallVector<Expr *> &Args);
236243
};
237244

238245
} // namespace clang

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "CGCall.h"
1717
#include "CGCleanup.h"
1818
#include "CGDebugInfo.h"
19+
#include "CGHLSLRuntime.h"
1920
#include "CGObjCRuntime.h"
2021
#include "CGOpenMPRuntime.h"
2122
#include "CGRecordLayout.h"
@@ -4532,6 +4533,15 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
45324533
LHS.getBaseInfo(), TBAAAccessInfo());
45334534
}
45344535

4536+
// The HLSL runtime handle the subscript expression on global resource arrays.
4537+
if (getLangOpts().HLSL && (E->getType()->isHLSLResourceRecord() ||
4538+
E->getType()->isHLSLResourceRecordArray())) {
4539+
std::optional<LValue> LV =
4540+
CGM.getHLSLRuntime().emitResourceArraySubscriptExpr(E, *this);
4541+
if (LV.has_value())
4542+
return *LV;
4543+
}
4544+
45354545
// All the other cases basically behave like simple offsetting.
45364546

45374547
// Handle the extvector case we ignored above.

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 211 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,124 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer,
8484
RootSignatureValMD->addOperand(MDVals);
8585
}
8686

87+
// If the specified expr is a simple decay from an array to pointer,
88+
// return the array subexpression. Otherwise, return nullptr.
89+
static const Expr *getSubExprFromArrayDecayOperand(const Expr *E) {
90+
const auto *CE = dyn_cast<CastExpr>(E);
91+
if (!CE || CE->getCastKind() != CK_ArrayToPointerDecay)
92+
return nullptr;
93+
return CE->getSubExpr();
94+
}
95+
96+
// Find array variable declaration from nested array subscript AST nodes
97+
static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) {
98+
const Expr *E = nullptr;
99+
while (ASE != nullptr) {
100+
E = getSubExprFromArrayDecayOperand(ASE->getBase());
101+
if (!E)
102+
return nullptr;
103+
ASE = dyn_cast<ArraySubscriptExpr>(E);
104+
}
105+
if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E))
106+
return DRE->getDecl();
107+
return nullptr;
108+
}
109+
110+
// Get the total size of the array, or -1 if the array is unbounded.
111+
static int getTotalArraySize(const clang::Type *Ty) {
112+
assert(Ty->isArrayType() && "expected array type");
113+
if (Ty->isIncompleteArrayType())
114+
return -1;
115+
int Size = 1;
116+
while (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) {
117+
Size *= CAT->getSExtSize();
118+
Ty = CAT->getArrayElementTypeNoTypeQual();
119+
}
120+
return Size;
121+
}
122+
123+
// Find constructor decl for a specific resource record type and binding
124+
// (implicit vs. explicit). The constructor has 6 parameters.
125+
// For explicit binding the signature is:
126+
// void(unsigned, unsigned, int, unsigned, const char *).
127+
// For implicit binding the signature is:
128+
// void(unsigned, int, unsigned, unsigned, const char *).
129+
static CXXConstructorDecl *findResourceConstructorDecl(ASTContext &AST,
130+
QualType ResTy,
131+
bool ExplicitBinding) {
132+
SmallVector<QualType> ExpParmTypes = {
133+
AST.UnsignedIntTy, AST.UnsignedIntTy, AST.UnsignedIntTy,
134+
AST.UnsignedIntTy, AST.getPointerType(AST.CharTy.withConst())};
135+
ExpParmTypes[ExplicitBinding ? 2 : 1] = AST.IntTy;
136+
137+
CXXRecordDecl *ResDecl = ResTy->getAsCXXRecordDecl();
138+
for (auto *Ctor : ResDecl->ctors()) {
139+
if (Ctor->getNumParams() != ExpParmTypes.size())
140+
continue;
141+
ParmVarDecl **ParmIt = Ctor->param_begin();
142+
QualType *ExpTyIt = ExpParmTypes.begin();
143+
for (; ParmIt != Ctor->param_end() && ExpTyIt != ExpParmTypes.end();
144+
++ParmIt, ++ExpTyIt) {
145+
if ((*ParmIt)->getType() != *ExpTyIt)
146+
break;
147+
}
148+
if (ParmIt == Ctor->param_end())
149+
return Ctor;
150+
}
151+
llvm_unreachable("did not find constructor for resource class");
152+
}
153+
154+
static Value *buildNameForResource(llvm::StringRef BaseName,
155+
CodeGenModule &CGM) {
156+
std::string Str(BaseName);
157+
std::string GlobalName(Str + ".str");
158+
return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
159+
}
160+
161+
static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD,
162+
llvm::Value *ThisPtr, llvm::Value *Range,
163+
llvm::Value *Index, StringRef Name,
164+
HLSLResourceBindingAttr *RBA,
165+
HLSLVkBindingAttr *VkBinding,
166+
CallArgList &Args) {
167+
assert((VkBinding || RBA) && "at least one a binding attribute expected");
168+
169+
std::optional<uint32_t> RegisterSlot;
170+
uint32_t SpaceNo = 0;
171+
if (VkBinding) {
172+
RegisterSlot = VkBinding->getBinding();
173+
SpaceNo = VkBinding->getSet();
174+
} else if (RBA) {
175+
if (RBA->hasRegisterSlot())
176+
RegisterSlot = RBA->getSlotNumber();
177+
SpaceNo = RBA->getSpaceNumber();
178+
}
179+
180+
ASTContext &AST = CD->getASTContext();
181+
Value *NameStr = buildNameForResource(Name, CGM);
182+
Value *Space = llvm::ConstantInt::get(CGM.IntTy, SpaceNo);
183+
184+
Args.add(RValue::get(ThisPtr), CD->getThisType());
185+
if (RegisterSlot.has_value()) {
186+
// explicit binding
187+
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RegisterSlot.value());
188+
Args.add(RValue::get(RegSlot), AST.UnsignedIntTy);
189+
Args.add(RValue::get(Space), AST.UnsignedIntTy);
190+
Args.add(RValue::get(Range), AST.IntTy);
191+
Args.add(RValue::get(Index), AST.UnsignedIntTy);
192+
193+
} else {
194+
// implicit binding
195+
auto *OrderID =
196+
llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID());
197+
Args.add(RValue::get(Space), AST.UnsignedIntTy);
198+
Args.add(RValue::get(Range), AST.IntTy);
199+
Args.add(RValue::get(Index), AST.UnsignedIntTy);
200+
Args.add(RValue::get(OrderID), AST.UnsignedIntTy);
201+
}
202+
Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst()));
203+
}
204+
87205
} // namespace
88206

89207
llvm::Type *
@@ -590,13 +708,6 @@ static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV,
590708
CGM.AddCXXGlobalInit(InitResFunc);
591709
}
592710

593-
static Value *buildNameForResource(llvm::StringRef BaseName,
594-
CodeGenModule &CGM) {
595-
std::string Str(BaseName);
596-
std::string GlobalName(Str + ".str");
597-
return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
598-
}
599-
600711
void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
601712
llvm::GlobalVariable *GV,
602713
HLSLVkBindingAttr *VkBinding) {
@@ -624,17 +735,13 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
624735
auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0);
625736
auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1);
626737
auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber());
627-
Value *Name = nullptr;
738+
Value *Name = buildNameForResource(BufDecl->getName(), CGM);
628739

629740
llvm::Intrinsic::ID IntrinsicID =
630741
RBA->hasRegisterSlot()
631742
? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic()
632743
: CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();
633744

634-
std::string Str(BufDecl->getName());
635-
std::string GlobalName(Str + ".str");
636-
Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
637-
638745
// buffer with explicit binding
639746
if (RBA->hasRegisterSlot()) {
640747
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
@@ -701,3 +808,95 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
701808
}
702809
}
703810
}
811+
812+
std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
813+
const ArraySubscriptExpr *ArraySubsExpr, CodeGenFunction &CGF) {
814+
assert(ArraySubsExpr->getType()->isHLSLResourceRecord() ||
815+
ArraySubsExpr->getType()->isHLSLResourceRecordArray() &&
816+
"expected resource array subscript expression");
817+
818+
// let clang codegen handle local resource array subscrips
819+
const VarDecl *ArrayDecl = dyn_cast<VarDecl>(getArrayDecl(ArraySubsExpr));
820+
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
821+
return std::nullopt;
822+
823+
// FIXME: this is not yet implemented (llvm/llvm-project#145426)
824+
assert(!ArraySubsExpr->getType()->isArrayType() &&
825+
"indexing of array subsets it not supported yet");
826+
827+
// get total array size (= range size)
828+
const Type *ResArrayTy = ArrayDecl->getType().getTypePtr();
829+
assert(ResArrayTy->isHLSLResourceRecordArray() &&
830+
"expected array of resource classes");
831+
llvm::Value *Range =
832+
llvm::ConstantInt::get(CGM.IntTy, getTotalArraySize(ResArrayTy));
833+
834+
// Iterate through all nested array subscript expressions to calculate
835+
// the index in the flattened resource array (if this is a multi-
836+
// dimensional array). The index is calculated as a sum of all indices
837+
// multiplied by the total size of the array at that level.
838+
Value *Index = nullptr;
839+
Value *Multiplier = nullptr;
840+
const ArraySubscriptExpr *ASE = ArraySubsExpr;
841+
while (ASE != nullptr) {
842+
Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx());
843+
if (const auto *ArrayTy =
844+
dyn_cast<ConstantArrayType>(ASE->getType().getTypePtr())) {
845+
Value *SubMultiplier =
846+
llvm::ConstantInt::get(CGM.IntTy, ArrayTy->getSExtSize());
847+
Multiplier = Multiplier ? CGF.Builder.CreateMul(Multiplier, SubMultiplier)
848+
: SubMultiplier;
849+
SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier);
850+
}
851+
852+
Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex;
853+
ASE = dyn_cast<ArraySubscriptExpr>(
854+
getSubExprFromArrayDecayOperand(ASE->getBase()));
855+
}
856+
857+
// find binding info for the resource array
858+
// (for implicit binding an HLSLResourceBindingAttr should have been added by
859+
// SemaHLSL)
860+
QualType ResourceTy = ArraySubsExpr->getType();
861+
HLSLVkBindingAttr *VkBinding = ArrayDecl->getAttr<HLSLVkBindingAttr>();
862+
HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>();
863+
assert((VkBinding || RBA) && "resource array must have a binding attribute");
864+
865+
// lookup the resource class constructor based on the resource type and
866+
// binding
867+
CXXConstructorDecl *CD =
868+
findResourceConstructorDecl(ArrayDecl->getASTContext(), ResourceTy,
869+
VkBinding || RBA->hasRegisterSlot());
870+
871+
// create a temporary variable for the resource class instance (we need to
872+
// return an LValue)
873+
RawAddress TmpVar = CGF.CreateMemTemp(ResourceTy);
874+
if (auto *Size = CGF.EmitLifetimeStart(
875+
CGM.getDataLayout().getTypeAllocSize(TmpVar.getElementType()),
876+
TmpVar.getPointer())) {
877+
CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>(
878+
NormalEHLifetimeMarker, TmpVar, Size);
879+
}
880+
AggValueSlot ValueSlot = AggValueSlot::forAddr(
881+
TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true),
882+
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
883+
AggValueSlot::MayOverlap);
884+
885+
Address ThisAddress = ValueSlot.getAddress();
886+
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(
887+
ThisAddress, CD->getThisType()->getPointeeType());
888+
889+
// assemble the constructor parameters
890+
CallArgList Args;
891+
createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(),
892+
RBA, VkBinding, Args);
893+
894+
// call the constructor
895+
CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress, Args,
896+
ValueSlot.mayOverlap(),
897+
ArraySubsExpr->getExprLoc(),
898+
ValueSlot.isSanitizerChecked());
899+
900+
return CGF.MakeAddrLValue(TmpVar, ArraySubsExpr->getType(),
901+
AlignmentSource::Decl);
902+
}

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ class Type;
6868
class RecordType;
6969
class DeclContext;
7070
class HLSLPackOffsetAttr;
71+
class ArraySubscriptExpr;
7172

7273
class FunctionDecl;
7374

7475
namespace CodeGen {
7576

7677
class CodeGenModule;
7778
class CodeGenFunction;
79+
class LValue;
7880

7981
class CGHLSLRuntime {
8082
public:
@@ -164,6 +166,10 @@ class CGHLSLRuntime {
164166
llvm::TargetExtType *LayoutTy);
165167
void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E);
166168

169+
std::optional<LValue>
170+
emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
171+
CodeGenFunction &CGF);
172+
167173
private:
168174
void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
169175
llvm::GlobalVariable *BufGV);

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5775,8 +5775,8 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
57755775
if (D->getType()->isReferenceType())
57765776
T = D->getType();
57775777

5778-
if (getLangOpts().HLSL &&
5779-
D->getType().getTypePtr()->isHLSLResourceRecord()) {
5778+
if (getLangOpts().HLSL && (D->getType()->isHLSLResourceRecord() ||
5779+
D->getType()->isHLSLResourceRecordArray())) {
57805780
Init = llvm::PoisonValue::get(getTypes().ConvertType(ASTTy));
57815781
NeedsGlobalCtor = true;
57825782
} else if (getLangOpts().CPlusPlus) {

0 commit comments

Comments
 (0)