1313// ===----------------------------------------------------------------------===//
1414
1515#include " CGHLSLRuntime.h"
16+ #include " Address.h"
1617#include " CGDebugInfo.h"
1718#include " CodeGenFunction.h"
1819#include " CodeGenModule.h"
3940#include " llvm/Support/ErrorHandling.h"
4041#include " llvm/Support/FormatVariadic.h"
4142#include < cstdint>
43+ #include < optional>
4244
4345using namespace clang ;
4446using namespace CodeGen ;
@@ -111,52 +113,29 @@ static int getTotalArraySize(ASTContext &AST, const clang::Type *Ty) {
111113 return AST.getConstantArrayElementCount (cast<ConstantArrayType>(Ty));
112114}
113115
114- // Find constructor decl for a specific resource record type and binding
115- // (implicit vs. explicit). The constructor has 5 parameters.
116- // For explicit binding the signature is:
117- // void(unsigned, unsigned, int, unsigned, const char *).
118- // For implicit binding the signature is:
119- // void(unsigned, int, unsigned, unsigned, const char *).
120- static CXXConstructorDecl *findResourceConstructorDecl (ASTContext &AST,
121- QualType ResTy,
122- bool ExplicitBinding) {
123- std::array<QualType, 5 > ExpParmTypes = {
124- AST.UnsignedIntTy , AST.UnsignedIntTy , AST.UnsignedIntTy ,
125- AST.UnsignedIntTy , AST.getPointerType (AST.CharTy .withConst ())};
126- ExpParmTypes[ExplicitBinding ? 2 : 1 ] = AST.IntTy ;
127-
128- CXXRecordDecl *ResDecl = ResTy->getAsCXXRecordDecl ();
129- for (auto *Ctor : ResDecl->ctors ()) {
130- if (Ctor->getNumParams () != ExpParmTypes.size ())
131- continue ;
132- auto *ParmIt = Ctor->param_begin ();
133- auto ExpTyIt = ExpParmTypes.begin ();
134- for (; ParmIt != Ctor->param_end () && ExpTyIt != ExpParmTypes.end ();
135- ++ParmIt, ++ExpTyIt) {
136- if ((*ParmIt)->getType () != *ExpTyIt)
137- break ;
138- }
139- if (ParmIt == Ctor->param_end ())
140- return Ctor;
141- }
142- llvm_unreachable (" did not find constructor for resource class" );
143- }
144-
145116static Value *buildNameForResource (llvm::StringRef BaseName,
146117 CodeGenModule &CGM) {
147118 llvm::SmallString<64 > GlobalName = {BaseName, " .str" };
148119 return CGM.GetAddrOfConstantCString (BaseName.str (), GlobalName.c_str ())
149120 .getPointer ();
150121}
151122
152- static void createResourceCtorArgs (CodeGenModule &CGM, CXXConstructorDecl *CD,
153- llvm::Value *ThisPtr, llvm::Value *Range,
154- llvm::Value *Index, StringRef Name,
155- HLSLResourceBindingAttr *RBA,
156- HLSLVkBindingAttr *VkBinding,
157- CallArgList &Args) {
123+ static CXXMethodDecl *lookupMethod (CXXRecordDecl *Record, StringRef Name,
124+ StorageClass SC = SC_None) {
125+ for (auto *Method : Record->methods ()) {
126+ if (Method->getStorageClass () == SC && Method->getName () == Name)
127+ return Method;
128+ }
129+ return nullptr ;
130+ }
131+
132+ static CXXMethodDecl *lookupResourceInitMethodAndSetupArgs (
133+ CodeGenModule &CGM, CXXRecordDecl *ResourceDecl, llvm::Value *Range,
134+ llvm::Value *Index, StringRef Name, HLSLResourceBindingAttr *RBA,
135+ HLSLVkBindingAttr *VkBinding, CallArgList &Args) {
158136 assert ((VkBinding || RBA) && " at least one a binding attribute expected" );
159137
138+ ASTContext &AST = CGM.getContext ();
160139 std::optional<uint32_t > RegisterSlot;
161140 uint32_t SpaceNo = 0 ;
162141 if (VkBinding) {
@@ -168,44 +147,57 @@ static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD,
168147 SpaceNo = RBA->getSpaceNumber ();
169148 }
170149
171- ASTContext &AST = CD-> getASTContext () ;
150+ CXXMethodDecl *CreateMethod = nullptr ;
172151 Value *NameStr = buildNameForResource (Name, CGM);
173152 Value *Space = llvm::ConstantInt::get (CGM.IntTy , SpaceNo);
174153
175- Args.add (RValue::get (ThisPtr), CD->getThisType ());
176154 if (RegisterSlot.has_value ()) {
177155 // explicit binding
178156 auto *RegSlot = llvm::ConstantInt::get (CGM.IntTy , RegisterSlot.value ());
179157 Args.add (RValue::get (RegSlot), AST.UnsignedIntTy );
180- Args.add (RValue::get (Space), AST.UnsignedIntTy );
181- Args.add (RValue::get (Range), AST.IntTy );
182- Args.add (RValue::get (Index), AST.UnsignedIntTy );
183-
158+ CreateMethod = lookupMethod (ResourceDecl, " __createFromBinding" , SC_Static);
184159 } else {
185160 // implicit binding
186- assert (RBA && " missing implicit binding attribute" );
187161 auto *OrderID =
188162 llvm::ConstantInt::get (CGM.IntTy , RBA->getImplicitBindingOrderID ());
189- Args.add (RValue::get (Space), AST.UnsignedIntTy );
190- Args.add (RValue::get (Range), AST.IntTy );
191- Args.add (RValue::get (Index), AST.UnsignedIntTy );
192163 Args.add (RValue::get (OrderID), AST.UnsignedIntTy );
164+ CreateMethod =
165+ lookupMethod (ResourceDecl, " __createFromImplicitBinding" , SC_Static);
193166 }
167+ Args.add (RValue::get (Space), AST.UnsignedIntTy );
168+ Args.add (RValue::get (Range), AST.IntTy );
169+ Args.add (RValue::get (Index), AST.UnsignedIntTy );
194170 Args.add (RValue::get (NameStr), AST.getPointerType (AST.CharTy .withConst ()));
171+
172+ return CreateMethod;
173+ }
174+
175+ static void callResourceInitMethod (CodeGenFunction &CGF,
176+ CXXMethodDecl *CreateMethod,
177+ CallArgList &Args, Address ReturnAddress) {
178+ llvm::Constant *CalleeFn = CGF.CGM .GetAddrOfFunction (CreateMethod);
179+ const FunctionProtoType *Proto =
180+ CreateMethod->getType ()->getAs <FunctionProtoType>();
181+ const CGFunctionInfo &FnInfo =
182+ CGF.CGM .getTypes ().arrangeFreeFunctionCall (Args, Proto, false );
183+ ReturnValueSlot ReturnValue (ReturnAddress, false );
184+ CGCallee Callee (CGCalleeInfo (Proto), CalleeFn);
185+ CGF.EmitCall (FnInfo, Callee, ReturnValue, Args, nullptr );
195186}
196187
197188// Initializes local resource array variable. For multi-dimensional arrays it
198189// calls itself recursively to initialize its sub-arrays. The Index used in the
199190// resource constructor calls will begin at StartIndex and will be incremented
200191// for each array element. The last used resource Index is returned to the
201- // caller.
202- static Value *initializeLocalResourceArray (
203- CodeGenFunction &CGF, AggValueSlot &ValueSlot ,
204- const ConstantArrayType *ArrayTy, CXXConstructorDecl *CD ,
192+ // caller. If the function returns std::nullopt, it indicates an error.
193+ static std::optional<llvm:: Value *> initializeLocalResourceArray (
194+ CodeGenFunction &CGF, CXXRecordDecl *ResourceDecl ,
195+ const ConstantArrayType *ArrayTy, AggValueSlot &ValueSlot ,
205196 llvm::Value *Range, llvm::Value *StartIndex, StringRef ResourceName,
206197 HLSLResourceBindingAttr *RBA, HLSLVkBindingAttr *VkBinding,
207198 ArrayRef<llvm::Value *> PrevGEPIndices, SourceLocation ArraySubsExprLoc) {
208199
200+ ASTContext &AST = CGF.getContext ();
209201 llvm::IntegerType *IntTy = CGF.CGM .IntTy ;
210202 llvm::Value *Index = StartIndex;
211203 llvm::Value *One = llvm::ConstantInt::get (IntTy, 1 );
@@ -226,16 +218,19 @@ static Value *initializeLocalResourceArray(
226218 Index = CGF.Builder .CreateAdd (Index, One);
227219 GEPIndices.back () = llvm::ConstantInt::get (IntTy, I);
228220 }
229- Index = initializeLocalResourceArray (
230- CGF, ValueSlot, SubArrayTy, CD, Range, Index, ResourceName, RBA,
231- VkBinding, GEPIndices, ArraySubsExprLoc);
221+ std::optional<llvm::Value *> MaybeIndex = initializeLocalResourceArray (
222+ CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index, ResourceName,
223+ RBA, VkBinding, GEPIndices, ArraySubsExprLoc);
224+ if (!MaybeIndex)
225+ return std::nullopt ;
226+ Index = *MaybeIndex;
232227 }
233228 return Index;
234229 }
235230
236231 // For array of resources, initialize each resource in the array.
237232 llvm::Type *Ty = CGF.ConvertTypeForMem (ElemType);
238- CharUnits ElemSize = CD-> getASTContext () .getTypeSizeInChars (ElemType);
233+ CharUnits ElemSize = AST .getTypeSizeInChars (ElemType);
239234 CharUnits Align =
240235 TmpArrayAddr.getAlignment ().alignmentOfArrayElement (ElemSize);
241236
@@ -244,16 +239,21 @@ static Value *initializeLocalResourceArray(
244239 Index = CGF.Builder .CreateAdd (Index, One);
245240 GEPIndices.back () = llvm::ConstantInt::get (IntTy, I);
246241 }
247- Address ThisAddress =
242+ Address ReturnAddress =
248243 CGF.Builder .CreateGEP (TmpArrayAddr, GEPIndices, Ty, Align);
249- llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo (ThisAddress, ElemType);
250244
251245 CallArgList Args;
252- createResourceCtorArgs (CGF.CGM , CD, ThisPtr, Range, Index, ResourceName,
253- RBA, VkBinding, Args);
254- CGF.EmitCXXConstructorCall (CD, Ctor_Complete, false , false , ThisAddress,
255- Args, ValueSlot.mayOverlap (), ArraySubsExprLoc,
256- ValueSlot.isSanitizerChecked ());
246+ CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs (
247+ CGF.CGM , ResourceDecl, Range, Index, ResourceName, RBA, VkBinding,
248+ Args);
249+
250+ if (!CreateMethod)
251+ // This can happen if someone creates an array of structs that looks like
252+ // an HLSL resource record array but it does not have the required static
253+ // create method. No binding will be generated for it.
254+ return std::nullopt ;
255+
256+ callResourceInitMethod (CGF, CreateMethod, Args, ReturnAddress);
257257 }
258258 return Index;
259259}
@@ -969,11 +969,6 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
969969 QualType ResourceTy =
970970 ResultTy->isArrayType () ? AST.getBaseElementType (ResultTy) : ResultTy;
971971
972- // Lookup the resource class constructor based on the resource type and
973- // binding.
974- CXXConstructorDecl *CD = findResourceConstructorDecl (
975- AST, ResourceTy, VkBinding || RBA->hasRegisterSlot ());
976-
977972 // Create a temporary variable for the result, which is either going
978973 // to be a single resource instance or a local array of resources (we need to
979974 // return an LValue).
@@ -986,7 +981,6 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
986981 TmpVar, Qualifiers (), AggValueSlot::IsDestructed_t (true ),
987982 AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t (false ),
988983 AggValueSlot::DoesNotOverlap);
989- Address TmpVarAddress = ValueSlot.getAddress ();
990984
991985 // Calculate total array size (= range size).
992986 llvm::Value *Range =
@@ -995,27 +989,30 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr(
995989 // If the result of the subscript operation is a single resource, call the
996990 // constructor.
997991 if (ResultTy == ResourceTy) {
998- QualType ThisType = CD->getThisType ()->getPointeeType ();
999- llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo (TmpVarAddress, ThisType);
1000-
1001- // Assemble the constructor parameters.
1002992 CallArgList Args;
1003- createResourceCtorArgs (CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName (),
1004- RBA, VkBinding, Args);
1005- // Call the constructor.
1006- CGF.EmitCXXConstructorCall (CD, Ctor_Complete, false , false , TmpVarAddress,
1007- Args, ValueSlot.mayOverlap (),
1008- ArraySubsExpr->getExprLoc (),
1009- ValueSlot.isSanitizerChecked ());
993+ CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs (
994+ CGF.CGM , ResourceTy->getAsCXXRecordDecl (), Range, Index,
995+ ArrayDecl->getName (), RBA, VkBinding, Args);
996+
997+ if (!CreateMethod)
998+ // This can happen if someone creates an array of structs that looks like
999+ // an HLSL resource record array but it does not have the required static
1000+ // create method. No binding will be generated for it.
1001+ return std::nullopt ;
1002+
1003+ callResourceInitMethod (CGF, CreateMethod, Args, ValueSlot.getAddress ());
1004+
10101005 } else {
10111006 // The result of the subscript operation is a local resource array which
10121007 // needs to be initialized.
10131008 const ConstantArrayType *ArrayTy =
10141009 cast<ConstantArrayType>(ResultTy.getTypePtr ());
1015- initializeLocalResourceArray (CGF, ValueSlot, ArrayTy, CD, Range, Index,
1016- ArrayDecl->getName (), RBA, VkBinding,
1017- {llvm::ConstantInt::get (CGM.IntTy , 0 )},
1018- ArraySubsExpr->getExprLoc ());
1010+ std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray (
1011+ CGF, ResourceTy->getAsCXXRecordDecl (), ArrayTy, ValueSlot, Range, Index,
1012+ ArrayDecl->getName (), RBA, VkBinding,
1013+ {llvm::ConstantInt::get (CGM.IntTy , 0 )}, ArraySubsExpr->getExprLoc ());
1014+ if (!EndIndex)
1015+ return std::nullopt ;
10191016 }
10201017 return CGF.MakeAddrLValue (TmpVar, ResultTy, AlignmentSource::Decl);
10211018}
0 commit comments