Skip to content

Commit e87e028

Browse files
authored
[HLSL] Use static create methods to initialize resources in arrays (#157005)
Use static methods `__createFromBinding` and `__createFromImplicitBinding` to initialize resources in resource arrays instead of calling resource constructors with binding information, per proposal update llvm/wg-hlsl#336. Test updates include the use of the `llvm-cxxfilt` tool which takes care of demangling of function names for a more readable test baseline.
1 parent 9855d54 commit e87e028

10 files changed

+279
-272
lines changed

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,6 @@ class SemaHLSL : public SemaBase {
260260

261261
bool initGlobalResourceDecl(VarDecl *VD);
262262
bool initGlobalResourceArrayDecl(VarDecl *VD);
263-
void createResourceRecordCtorArgs(const Type *ResourceTy, StringRef VarName,
264-
HLSLResourceBindingAttr *RBA,
265-
HLSLVkBindingAttr *VkBinding,
266-
uint32_t ArrayIndex,
267-
llvm::SmallVectorImpl<Expr *> &Args);
268263
};
269264

270265
} // namespace clang

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 80 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
#include "CGHLSLRuntime.h"
16+
#include "Address.h"
1617
#include "CGDebugInfo.h"
1718
#include "CodeGenFunction.h"
1819
#include "CodeGenModule.h"
@@ -39,6 +40,7 @@
3940
#include "llvm/Support/ErrorHandling.h"
4041
#include "llvm/Support/FormatVariadic.h"
4142
#include <cstdint>
43+
#include <optional>
4244

4345
using namespace clang;
4446
using 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-
145116
static 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
}

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 28 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3793,55 +3793,6 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
37933793
deduceAddressSpace(VD);
37943794
}
37953795

3796-
void SemaHLSL::createResourceRecordCtorArgs(
3797-
const Type *ResourceTy, StringRef VarName, HLSLResourceBindingAttr *RBA,
3798-
HLSLVkBindingAttr *VkBinding, uint32_t ArrayIndex,
3799-
llvm::SmallVectorImpl<Expr *> &Args) {
3800-
std::optional<uint32_t> RegisterSlot;
3801-
uint32_t SpaceNo = 0;
3802-
if (VkBinding) {
3803-
RegisterSlot = VkBinding->getBinding();
3804-
SpaceNo = VkBinding->getSet();
3805-
} else if (RBA) {
3806-
if (RBA->hasRegisterSlot())
3807-
RegisterSlot = RBA->getSlotNumber();
3808-
SpaceNo = RBA->getSpaceNumber();
3809-
}
3810-
3811-
ASTContext &AST = SemaRef.getASTContext();
3812-
uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy);
3813-
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
3814-
IntegerLiteral *RangeSize = IntegerLiteral::Create(
3815-
AST, llvm::APInt(IntTySize, 1), AST.IntTy, SourceLocation());
3816-
IntegerLiteral *Index =
3817-
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, ArrayIndex),
3818-
AST.UnsignedIntTy, SourceLocation());
3819-
IntegerLiteral *Space =
3820-
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo),
3821-
AST.UnsignedIntTy, SourceLocation());
3822-
StringLiteral *Name = StringLiteral::Create(
3823-
AST, VarName, StringLiteralKind::Ordinary, false,
3824-
AST.getStringLiteralArrayType(AST.CharTy.withConst(), VarName.size()),
3825-
SourceLocation());
3826-
3827-
// resource with explicit binding
3828-
if (RegisterSlot.has_value()) {
3829-
IntegerLiteral *RegSlot = IntegerLiteral::Create(
3830-
AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy,
3831-
SourceLocation());
3832-
Args.append({RegSlot, Space, RangeSize, Index, Name});
3833-
} else {
3834-
// resource with implicit binding
3835-
uint32_t OrderID = (RBA && RBA->hasImplicitBindingOrderID())
3836-
? RBA->getImplicitBindingOrderID()
3837-
: getNextImplicitBindingOrderID();
3838-
IntegerLiteral *OrderId =
3839-
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, OrderID),
3840-
AST.UnsignedIntTy, SourceLocation());
3841-
Args.append({Space, RangeSize, Index, OrderId, Name});
3842-
}
3843-
}
3844-
38453796
bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) {
38463797
assert(VD->getType()->isHLSLResourceRecord() &&
38473798
"expected resource record type");
@@ -3951,28 +3902,39 @@ bool SemaHLSL::initGlobalResourceArrayDecl(VarDecl *VD) {
39513902

39523903
// Individual resources in a resource array are not initialized here. They
39533904
// are initialized later on during codegen when the individual resources are
3954-
// accessed. Codegen will emit a call to the resource constructor with the
3955-
// specified array index. We need to make sure though that the constructor
3905+
// accessed. Codegen will emit a call to the resource initialization method
3906+
// with the specified array index. We need to make sure though that the method
39563907
// for the specific resource type is instantiated, so codegen can emit a call
39573908
// to it when the array element is accessed.
3958-
SmallVector<Expr *> Args;
3959-
QualType ResElementTy = VD->getASTContext().getBaseElementType(VD->getType());
3960-
createResourceRecordCtorArgs(ResElementTy.getTypePtr(), VD->getName(),
3961-
VD->getAttr<HLSLResourceBindingAttr>(),
3962-
VD->getAttr<HLSLVkBindingAttr>(), 0, Args);
39633909

3964-
SourceLocation Loc = VD->getLocation();
3965-
InitializedEntity Entity =
3966-
InitializedEntity::InitializeTemporary(ResElementTy);
3967-
InitializationKind Kind = InitializationKind::CreateDirect(Loc, Loc, Loc);
3968-
InitializationSequence InitSeq(SemaRef, Entity, Kind, Args);
3969-
if (InitSeq.Failed())
3910+
// Find correct initialization method based on the resource binding
3911+
// information.
3912+
ASTContext &AST = SemaRef.getASTContext();
3913+
QualType ResElementTy = AST.getBaseElementType(VD->getType());
3914+
CXXRecordDecl *ResourceDecl = ResElementTy->getAsCXXRecordDecl();
3915+
3916+
HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
3917+
HLSLVkBindingAttr *VkBinding = VD->getAttr<HLSLVkBindingAttr>();
3918+
CXXMethodDecl *CreateMethod = nullptr;
3919+
3920+
if (VkBinding || (RBA && RBA->hasRegisterSlot()))
3921+
// Resource has explicit binding.
3922+
CreateMethod = lookupMethod(SemaRef, ResourceDecl, "__createFromBinding",
3923+
VD->getLocation());
3924+
else
3925+
// Resource has implicit binding.
3926+
CreateMethod =
3927+
lookupMethod(SemaRef, ResourceDecl, "__createFromImplicitBinding",
3928+
VD->getLocation());
3929+
3930+
if (!CreateMethod)
39703931
return false;
39713932

3972-
// This takes care of instantiating and emitting of the constructor that will
3973-
// be called from codegen when the array is accessed.
3974-
ExprResult OneResInit = InitSeq.Perform(SemaRef, Entity, Kind, Args);
3975-
return !OneResInit.isInvalid();
3933+
// Make sure the create method template is instantiated and emitted.
3934+
if (!CreateMethod->isDefined() && CreateMethod->isTemplateInstantiation())
3935+
SemaRef.InstantiateFunctionDefinition(VD->getLocation(), CreateMethod,
3936+
true);
3937+
return true;
39763938
}
39773939

39783940
// Returns true if the initialization has been handled.

0 commit comments

Comments
 (0)