Skip to content

Commit 10c2803

Browse files
committed
[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 llvm#145424
1 parent 82f00ea commit 10c2803

File tree

11 files changed

+405
-63
lines changed

11 files changed

+405
-63
lines changed

clang/include/clang/AST/Type.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2724,6 +2724,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
27242724
bool isHLSLAttributedResourceType() const;
27252725
bool isHLSLInlineSpirvType() const;
27262726
bool isHLSLResourceRecord() const;
2727+
bool isHLSLResourceRecordArray() const;
27272728
bool isHLSLIntangibleType()
27282729
const; // Any HLSL intangible type (builtin, array, class)
27292730

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,16 @@ class SemaHLSL : public SemaBase {
228228

229229
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
230230

231-
bool initGlobalResourceDecl(VarDecl *VD);
232231
uint32_t getNextImplicitBindingOrderID() {
233232
return ImplicitBindingNextOrderID++;
234233
}
234+
235+
bool initGlobalResourceDecl(VarDecl *VD);
236+
bool initGlobalResourceArrayDecl(VarDecl *VD);
237+
void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName,
238+
HLSLResourceBindingAttr *RBA,
239+
uint32_t ArrayIndex,
240+
llvm::SmallVector<Expr *> &Args);
235241
};
236242

237243
} // namespace clang

clang/lib/AST/Type.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5246,6 +5246,15 @@ bool Type::isHLSLResourceRecord() const {
52465246
return HLSLAttributedResourceType::findHandleTypeOnResource(this) != nullptr;
52475247
}
52485248

5249+
bool Type::isHLSLResourceRecordArray() const {
5250+
const Type *Ty = getUnqualifiedDesugaredType();
5251+
if (!Ty->isArrayType())
5252+
return false;
5253+
while (isa<ConstantArrayType>(Ty))
5254+
Ty = Ty->getArrayElementTypeNoTypeQual();
5255+
return Ty->isHLSLResourceRecord();
5256+
}
5257+
52495258
bool Type::isHLSLIntangibleType() const {
52505259
const Type *Ty = getUnqualifiedDesugaredType();
52515260

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"
@@ -4515,6 +4516,15 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
45154516
LHS.getBaseInfo(), TBAAAccessInfo());
45164517
}
45174518

4519+
// The HLSL runtime handle the subscript expression on global resource arrays.
4520+
if (getLangOpts().HLSL && (E->getType()->isHLSLResourceRecord() ||
4521+
E->getType()->isHLSLResourceRecordArray())) {
4522+
std::optional<LValue> LV =
4523+
CGM.getHLSLRuntime().emitResourceArraySubscriptExpr(E, *this);
4524+
if (LV.has_value())
4525+
return *LV;
4526+
}
4527+
45184528
// All the other cases basically behave like simple offsetting.
45194529

45204530
// Handle the extvector case we ignored above.

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 196 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "CodeGenModule.h"
1919
#include "TargetInfo.h"
2020
#include "clang/AST/ASTContext.h"
21+
#include "clang/AST/Attrs.inc"
2122
#include "clang/AST/Decl.h"
2223
#include "clang/AST/RecursiveASTVisitor.h"
2324
#include "clang/AST/Type.h"
@@ -35,6 +36,7 @@
3536
#include "llvm/Support/Alignment.h"
3637
#include "llvm/Support/ErrorHandling.h"
3738
#include "llvm/Support/FormatVariadic.h"
39+
#include <optional>
3840

3941
using namespace clang;
4042
using namespace CodeGen;
@@ -84,6 +86,110 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer,
8486
RootSignatureValMD->addOperand(MDVals);
8587
}
8688

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

89195
llvm::Type *
@@ -103,13 +209,6 @@ llvm::Triple::ArchType CGHLSLRuntime::getArch() {
103209
return CGM.getTarget().getTriple().getArch();
104210
}
105211

106-
// Returns true if the type is an HLSL resource class or an array of them
107-
static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) {
108-
while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
109-
Ty = CAT->getArrayElementTypeNoTypeQual();
110-
return Ty->isHLSLResourceRecord();
111-
}
112-
113212
// Emits constant global variables for buffer constants declarations
114213
// and creates metadata linking the constant globals with the buffer global.
115214
void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
@@ -146,7 +245,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
146245
if (VDTy.getAddressSpace() != LangAS::hlsl_constant) {
147246
if (VD->getStorageClass() == SC_Static ||
148247
VDTy.getAddressSpace() == LangAS::hlsl_groupshared ||
149-
isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) {
248+
VDTy->isHLSLResourceRecord() || VDTy->isHLSLResourceRecordArray()) {
150249
// Emit static and groupshared variables and resource classes inside
151250
// cbuffer as regular globals
152251
CGM.EmitGlobal(VD);
@@ -679,3 +778,92 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
679778
}
680779
}
681780
}
781+
782+
std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
783+
const ArraySubscriptExpr *ArraySubsExpr, CodeGenFunction &CGF) {
784+
assert(ArraySubsExpr->getType()->isHLSLResourceRecord() ||
785+
ArraySubsExpr->getType()->isHLSLResourceRecordArray() &&
786+
"expected resource array subscript expression");
787+
788+
// let clang codegen handle local resource array subscrips
789+
const VarDecl *ArrayDecl = dyn_cast<VarDecl>(getArrayDecl(ArraySubsExpr));
790+
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage())
791+
return std::nullopt;
792+
793+
// FIXME: this is not yet implemented (llvm/llvm-project#145426)
794+
assert(!ArraySubsExpr->getType()->isArrayType() &&
795+
"indexing of array subsets it not supported yet");
796+
797+
// get total array size (= range size)
798+
const Type *ResArrayTy = ArrayDecl->getType().getTypePtr();
799+
assert(ResArrayTy->isHLSLResourceRecordArray() &&
800+
"expected array of resource classes");
801+
llvm::Value *Range =
802+
llvm::ConstantInt::get(CGM.IntTy, getTotalArraySize(ResArrayTy));
803+
804+
// Iterate through all nested array subscript expressions to calculate
805+
// the index in the flattened resource array (if this is a multi-
806+
// dimensional array). The index is calculated as a sum of all indices
807+
// multiplied by the total size of the array at that level.
808+
Value *Index = nullptr;
809+
Value *Multiplier = nullptr;
810+
const ArraySubscriptExpr *ASE = ArraySubsExpr;
811+
while (ASE != nullptr) {
812+
Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx());
813+
if (const auto *ArrayTy =
814+
dyn_cast<ConstantArrayType>(ASE->getType().getTypePtr())) {
815+
Value *SubMultiplier =
816+
llvm::ConstantInt::get(CGM.IntTy, ArrayTy->getSExtSize());
817+
Multiplier = Multiplier ? CGF.Builder.CreateMul(Multiplier, SubMultiplier)
818+
: SubMultiplier;
819+
SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier);
820+
}
821+
822+
Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex;
823+
ASE = dyn_cast<ArraySubscriptExpr>(
824+
getSubExprFromArrayDecayOperand(ASE->getBase()));
825+
}
826+
827+
// find binding info for the resource array
828+
// (for implicit binding it should have been by SemaHLSL)
829+
QualType ResourceTy = ArraySubsExpr->getType();
830+
HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>();
831+
assert(RBA && "resource array is missing HLSLResourceBindingAttr attribute");
832+
833+
// lookup the resource class constructor based on the resource type and
834+
// binding
835+
CXXConstructorDecl *CD = findResourceConstructorDecl(
836+
ArrayDecl->getASTContext(), ResourceTy, RBA->hasRegisterSlot());
837+
838+
// create a temporary variable for the resource class instance (we need to
839+
// return an LValue)
840+
RawAddress TmpVar = CGF.CreateMemTemp(ResourceTy);
841+
if (auto *Size = CGF.EmitLifetimeStart(
842+
CGM.getDataLayout().getTypeAllocSize(TmpVar.getElementType()),
843+
TmpVar.getPointer())) {
844+
CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>(
845+
NormalEHLifetimeMarker, TmpVar, Size);
846+
}
847+
AggValueSlot ValueSlot = AggValueSlot::forAddr(
848+
TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true),
849+
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false),
850+
AggValueSlot::MayOverlap);
851+
852+
Address ThisAddress = ValueSlot.getAddress();
853+
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(
854+
ThisAddress, CD->getThisType()->getPointeeType());
855+
856+
// assemble the constructor parameters
857+
CallArgList Args;
858+
createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(),
859+
RBA, Args);
860+
861+
// call the constructor
862+
CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress, Args,
863+
ValueSlot.mayOverlap(),
864+
ArraySubsExpr->getExprLoc(),
865+
ValueSlot.isSanitizerChecked());
866+
867+
return CGF.MakeAddrLValue(TmpVar, ArraySubsExpr->getType(),
868+
AlignmentSource::Decl);
869+
}

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@ class Type;
6767
class RecordType;
6868
class DeclContext;
6969
class HLSLPackOffsetAttr;
70+
class ArraySubscriptExpr;
7071

7172
class FunctionDecl;
7273

7374
namespace CodeGen {
7475

7576
class CodeGenModule;
7677
class CodeGenFunction;
78+
class LValue;
7779

7880
class CGHLSLRuntime {
7981
public:
@@ -163,6 +165,10 @@ class CGHLSLRuntime {
163165
llvm::TargetExtType *LayoutTy);
164166
void emitInitListOpaqueValues(CodeGenFunction &CGF, InitListExpr *E);
165167

168+
std::optional<LValue>
169+
emitResourceArraySubscriptExpr(const ArraySubscriptExpr *E,
170+
CodeGenFunction &CGF);
171+
166172
private:
167173
void emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
168174
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)