18
18
#include " CodeGenModule.h"
19
19
#include " TargetInfo.h"
20
20
#include " clang/AST/ASTContext.h"
21
+ #include " clang/AST/Attrs.inc"
21
22
#include " clang/AST/Decl.h"
22
23
#include " clang/AST/RecursiveASTVisitor.h"
23
24
#include " clang/AST/Type.h"
35
36
#include " llvm/Support/Alignment.h"
36
37
#include " llvm/Support/ErrorHandling.h"
37
38
#include " llvm/Support/FormatVariadic.h"
39
+ #include < optional>
38
40
39
41
using namespace clang ;
40
42
using namespace CodeGen ;
@@ -84,6 +86,110 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer,
84
86
RootSignatureValMD->addOperand (MDVals);
85
87
}
86
88
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
+
87
193
} // namespace
88
194
89
195
llvm::Type *
@@ -103,13 +209,6 @@ llvm::Triple::ArchType CGHLSLRuntime::getArch() {
103
209
return CGM.getTarget ().getTriple ().getArch ();
104
210
}
105
211
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
-
113
212
// Emits constant global variables for buffer constants declarations
114
213
// and creates metadata linking the constant globals with the buffer global.
115
214
void CGHLSLRuntime::emitBufferGlobalsAndMetadata (const HLSLBufferDecl *BufDecl,
@@ -146,7 +245,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl,
146
245
if (VDTy.getAddressSpace () != LangAS::hlsl_constant) {
147
246
if (VD->getStorageClass () == SC_Static ||
148
247
VDTy.getAddressSpace () == LangAS::hlsl_groupshared ||
149
- isResourceRecordTypeOrArrayOf ( VDTy. getTypePtr () )) {
248
+ VDTy-> isHLSLResourceRecord () || VDTy-> isHLSLResourceRecordArray ( )) {
150
249
// Emit static and groupshared variables and resource classes inside
151
250
// cbuffer as regular globals
152
251
CGM.EmitGlobal (VD);
@@ -679,3 +778,92 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF,
679
778
}
680
779
}
681
780
}
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
+ }
0 commit comments