-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[HLSL] Use static create methods to initialize resources in arrays #157005
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7a0d3ca
dcbbda4
02342c9
b13a530
a0cfc72
1e6f4de
dfc07bd
a41cf8a
e7641e1
6028194
ea93f39
e7d6ee6
7e9d4c5
84883bc
7782eea
f64525e
b7e5126
6669bd7
0d1bc12
75e6b0f
dfa4144
c7b35b9
fd7b37a
152471e
886ab3c
3e85c31
acd45af
363d463
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ | |
//===----------------------------------------------------------------------===// | ||
|
||
#include "CGHLSLRuntime.h" | ||
#include "Address.h" | ||
#include "CGDebugInfo.h" | ||
#include "CodeGenFunction.h" | ||
#include "CodeGenModule.h" | ||
|
@@ -39,6 +40,7 @@ | |
#include "llvm/Support/ErrorHandling.h" | ||
#include "llvm/Support/FormatVariadic.h" | ||
#include <cstdint> | ||
#include <optional> | ||
|
||
using namespace clang; | ||
using namespace CodeGen; | ||
|
@@ -111,52 +113,29 @@ static int getTotalArraySize(ASTContext &AST, const clang::Type *Ty) { | |
return AST.getConstantArrayElementCount(cast<ConstantArrayType>(Ty)); | ||
} | ||
|
||
// Find constructor decl for a specific resource record type and binding | ||
// (implicit vs. explicit). The constructor has 5 parameters. | ||
// For explicit binding the signature is: | ||
// void(unsigned, unsigned, int, unsigned, const char *). | ||
// For implicit binding the signature is: | ||
// void(unsigned, int, unsigned, unsigned, const char *). | ||
static CXXConstructorDecl *findResourceConstructorDecl(ASTContext &AST, | ||
QualType ResTy, | ||
bool ExplicitBinding) { | ||
std::array<QualType, 5> ExpParmTypes = { | ||
AST.UnsignedIntTy, AST.UnsignedIntTy, AST.UnsignedIntTy, | ||
AST.UnsignedIntTy, AST.getPointerType(AST.CharTy.withConst())}; | ||
ExpParmTypes[ExplicitBinding ? 2 : 1] = AST.IntTy; | ||
|
||
CXXRecordDecl *ResDecl = ResTy->getAsCXXRecordDecl(); | ||
for (auto *Ctor : ResDecl->ctors()) { | ||
if (Ctor->getNumParams() != ExpParmTypes.size()) | ||
continue; | ||
auto *ParmIt = Ctor->param_begin(); | ||
auto ExpTyIt = ExpParmTypes.begin(); | ||
for (; ParmIt != Ctor->param_end() && ExpTyIt != ExpParmTypes.end(); | ||
++ParmIt, ++ExpTyIt) { | ||
if ((*ParmIt)->getType() != *ExpTyIt) | ||
break; | ||
} | ||
if (ParmIt == Ctor->param_end()) | ||
return Ctor; | ||
} | ||
llvm_unreachable("did not find constructor for resource class"); | ||
} | ||
|
||
static Value *buildNameForResource(llvm::StringRef BaseName, | ||
CodeGenModule &CGM) { | ||
llvm::SmallString<64> GlobalName = {BaseName, ".str"}; | ||
return CGM.GetAddrOfConstantCString(BaseName.str(), GlobalName.c_str()) | ||
.getPointer(); | ||
} | ||
|
||
static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD, | ||
llvm::Value *ThisPtr, llvm::Value *Range, | ||
llvm::Value *Index, StringRef Name, | ||
HLSLResourceBindingAttr *RBA, | ||
HLSLVkBindingAttr *VkBinding, | ||
CallArgList &Args) { | ||
static CXXMethodDecl *lookupMethod(CXXRecordDecl *Record, StringRef Name, | ||
StorageClass SC = SC_None) { | ||
for (auto *Method : Record->methods()) { | ||
if (Method->getStorageClass() == SC && Method->getName() == Name) | ||
return Method; | ||
} | ||
return nullptr; | ||
} | ||
|
||
static CXXMethodDecl *lookupResourceInitMethodAndSetupArgs( | ||
CodeGenModule &CGM, CXXRecordDecl *ResourceDecl, llvm::Value *Range, | ||
llvm::Value *Index, StringRef Name, HLSLResourceBindingAttr *RBA, | ||
HLSLVkBindingAttr *VkBinding, CallArgList &Args) { | ||
assert((VkBinding || RBA) && "at least one a binding attribute expected"); | ||
|
||
ASTContext &AST = CGM.getContext(); | ||
std::optional<uint32_t> RegisterSlot; | ||
uint32_t SpaceNo = 0; | ||
if (VkBinding) { | ||
|
@@ -168,44 +147,57 @@ static void createResourceCtorArgs(CodeGenModule &CGM, CXXConstructorDecl *CD, | |
SpaceNo = RBA->getSpaceNumber(); | ||
} | ||
|
||
ASTContext &AST = CD->getASTContext(); | ||
CXXMethodDecl *CreateMethod = nullptr; | ||
Value *NameStr = buildNameForResource(Name, CGM); | ||
Value *Space = llvm::ConstantInt::get(CGM.IntTy, SpaceNo); | ||
|
||
Args.add(RValue::get(ThisPtr), CD->getThisType()); | ||
if (RegisterSlot.has_value()) { | ||
// explicit binding | ||
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RegisterSlot.value()); | ||
Args.add(RValue::get(RegSlot), AST.UnsignedIntTy); | ||
Args.add(RValue::get(Space), AST.UnsignedIntTy); | ||
Args.add(RValue::get(Range), AST.IntTy); | ||
Args.add(RValue::get(Index), AST.UnsignedIntTy); | ||
|
||
CreateMethod = lookupMethod(ResourceDecl, "__createFromBinding", SC_Static); | ||
} else { | ||
// implicit binding | ||
assert(RBA && "missing implicit binding attribute"); | ||
auto *OrderID = | ||
llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID()); | ||
Args.add(RValue::get(Space), AST.UnsignedIntTy); | ||
Args.add(RValue::get(Range), AST.IntTy); | ||
Args.add(RValue::get(Index), AST.UnsignedIntTy); | ||
Args.add(RValue::get(OrderID), AST.UnsignedIntTy); | ||
CreateMethod = | ||
lookupMethod(ResourceDecl, "__createFromImplicitBinding", SC_Static); | ||
} | ||
Args.add(RValue::get(Space), AST.UnsignedIntTy); | ||
Args.add(RValue::get(Range), AST.IntTy); | ||
Args.add(RValue::get(Index), AST.UnsignedIntTy); | ||
Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst())); | ||
|
||
return CreateMethod; | ||
} | ||
|
||
static void callResourceInitMethod(CodeGenFunction &CGF, | ||
CXXMethodDecl *CreateMethod, | ||
CallArgList &Args, Address ReturnAddress) { | ||
llvm::Constant *CalleeFn = CGF.CGM.GetAddrOfFunction(CreateMethod); | ||
const FunctionProtoType *Proto = | ||
CreateMethod->getType()->getAs<FunctionProtoType>(); | ||
const CGFunctionInfo &FnInfo = | ||
CGF.CGM.getTypes().arrangeFreeFunctionCall(Args, Proto, false); | ||
ReturnValueSlot ReturnValue(ReturnAddress, false); | ||
CGCallee Callee(CGCalleeInfo(Proto), CalleeFn); | ||
CGF.EmitCall(FnInfo, Callee, ReturnValue, Args, nullptr); | ||
} | ||
|
||
// Initializes local resource array variable. For multi-dimensional arrays it | ||
// calls itself recursively to initialize its sub-arrays. The Index used in the | ||
// resource constructor calls will begin at StartIndex and will be incremented | ||
// for each array element. The last used resource Index is returned to the | ||
// caller. | ||
static Value *initializeLocalResourceArray( | ||
CodeGenFunction &CGF, AggValueSlot &ValueSlot, | ||
const ConstantArrayType *ArrayTy, CXXConstructorDecl *CD, | ||
// caller. If the function returns std::nullopt, it indicates an error. | ||
static std::optional<llvm::Value *> initializeLocalResourceArray( | ||
CodeGenFunction &CGF, CXXRecordDecl *ResourceDecl, | ||
const ConstantArrayType *ArrayTy, AggValueSlot &ValueSlot, | ||
llvm::Value *Range, llvm::Value *StartIndex, StringRef ResourceName, | ||
HLSLResourceBindingAttr *RBA, HLSLVkBindingAttr *VkBinding, | ||
ArrayRef<llvm::Value *> PrevGEPIndices, SourceLocation ArraySubsExprLoc) { | ||
|
||
ASTContext &AST = CGF.getContext(); | ||
llvm::IntegerType *IntTy = CGF.CGM.IntTy; | ||
llvm::Value *Index = StartIndex; | ||
llvm::Value *One = llvm::ConstantInt::get(IntTy, 1); | ||
|
@@ -226,16 +218,19 @@ static Value *initializeLocalResourceArray( | |
Index = CGF.Builder.CreateAdd(Index, One); | ||
GEPIndices.back() = llvm::ConstantInt::get(IntTy, I); | ||
} | ||
Index = initializeLocalResourceArray( | ||
CGF, ValueSlot, SubArrayTy, CD, Range, Index, ResourceName, RBA, | ||
VkBinding, GEPIndices, ArraySubsExprLoc); | ||
std::optional<llvm::Value *> MaybeIndex = initializeLocalResourceArray( | ||
CGF, ResourceDecl, SubArrayTy, ValueSlot, Range, Index, ResourceName, | ||
RBA, VkBinding, GEPIndices, ArraySubsExprLoc); | ||
if (!MaybeIndex) | ||
return std::nullopt; | ||
Index = *MaybeIndex; | ||
} | ||
return Index; | ||
} | ||
|
||
// For array of resources, initialize each resource in the array. | ||
llvm::Type *Ty = CGF.ConvertTypeForMem(ElemType); | ||
CharUnits ElemSize = CD->getASTContext().getTypeSizeInChars(ElemType); | ||
CharUnits ElemSize = AST.getTypeSizeInChars(ElemType); | ||
CharUnits Align = | ||
TmpArrayAddr.getAlignment().alignmentOfArrayElement(ElemSize); | ||
|
||
|
@@ -244,16 +239,21 @@ static Value *initializeLocalResourceArray( | |
Index = CGF.Builder.CreateAdd(Index, One); | ||
GEPIndices.back() = llvm::ConstantInt::get(IntTy, I); | ||
} | ||
Address ThisAddress = | ||
Address ReturnAddress = | ||
CGF.Builder.CreateGEP(TmpArrayAddr, GEPIndices, Ty, Align); | ||
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(ThisAddress, ElemType); | ||
|
||
CallArgList Args; | ||
createResourceCtorArgs(CGF.CGM, CD, ThisPtr, Range, Index, ResourceName, | ||
RBA, VkBinding, Args); | ||
CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, ThisAddress, | ||
Args, ValueSlot.mayOverlap(), ArraySubsExprLoc, | ||
ValueSlot.isSanitizerChecked()); | ||
CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs( | ||
CGF.CGM, ResourceDecl, Range, Index, ResourceName, RBA, VkBinding, | ||
Args); | ||
|
||
if (!CreateMethod) | ||
// This can happen if someone creates an array of structs that looks like | ||
// an HLSL resource record array but it does not have the required static | ||
// create method. No binding will be generated for it. | ||
return std::nullopt; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to surface an error if this happens? Can this happen? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can happen when someone has an array of fake/custom(?) resource classes - array of structs with a __hlsl_resource_t member - and the struct does not have the create method. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Chatted with @hekota offline. Seems like this happens for manually written structs containing handles, and the outcome here is that they just don't get bindings generated. I think not supporting automatically binding handles for people writing their own types against internal compiler details is completely reasonable. |
||
|
||
callResourceInitMethod(CGF, CreateMethod, Args, ReturnAddress); | ||
} | ||
return Index; | ||
} | ||
|
@@ -969,11 +969,6 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr( | |
QualType ResourceTy = | ||
ResultTy->isArrayType() ? AST.getBaseElementType(ResultTy) : ResultTy; | ||
|
||
// Lookup the resource class constructor based on the resource type and | ||
// binding. | ||
CXXConstructorDecl *CD = findResourceConstructorDecl( | ||
AST, ResourceTy, VkBinding || RBA->hasRegisterSlot()); | ||
|
||
// Create a temporary variable for the result, which is either going | ||
// to be a single resource instance or a local array of resources (we need to | ||
// return an LValue). | ||
|
@@ -986,7 +981,6 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr( | |
TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true), | ||
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false), | ||
AggValueSlot::DoesNotOverlap); | ||
Address TmpVarAddress = ValueSlot.getAddress(); | ||
|
||
// Calculate total array size (= range size). | ||
llvm::Value *Range = | ||
|
@@ -995,27 +989,30 @@ std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr( | |
// If the result of the subscript operation is a single resource, call the | ||
// constructor. | ||
if (ResultTy == ResourceTy) { | ||
QualType ThisType = CD->getThisType()->getPointeeType(); | ||
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo(TmpVarAddress, ThisType); | ||
|
||
// Assemble the constructor parameters. | ||
CallArgList Args; | ||
createResourceCtorArgs(CGM, CD, ThisPtr, Range, Index, ArrayDecl->getName(), | ||
RBA, VkBinding, Args); | ||
// Call the constructor. | ||
CGF.EmitCXXConstructorCall(CD, Ctor_Complete, false, false, TmpVarAddress, | ||
Args, ValueSlot.mayOverlap(), | ||
ArraySubsExpr->getExprLoc(), | ||
ValueSlot.isSanitizerChecked()); | ||
CXXMethodDecl *CreateMethod = lookupResourceInitMethodAndSetupArgs( | ||
CGF.CGM, ResourceTy->getAsCXXRecordDecl(), Range, Index, | ||
ArrayDecl->getName(), RBA, VkBinding, Args); | ||
|
||
if (!CreateMethod) | ||
// This can happen if someone creates an array of structs that looks like | ||
// an HLSL resource record array but it does not have the required static | ||
// create method. No binding will be generated for it. | ||
return std::nullopt; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, what happens if this happens? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above. |
||
|
||
callResourceInitMethod(CGF, CreateMethod, Args, ValueSlot.getAddress()); | ||
|
||
} else { | ||
// The result of the subscript operation is a local resource array which | ||
// needs to be initialized. | ||
const ConstantArrayType *ArrayTy = | ||
cast<ConstantArrayType>(ResultTy.getTypePtr()); | ||
initializeLocalResourceArray(CGF, ValueSlot, ArrayTy, CD, Range, Index, | ||
ArrayDecl->getName(), RBA, VkBinding, | ||
{llvm::ConstantInt::get(CGM.IntTy, 0)}, | ||
ArraySubsExpr->getExprLoc()); | ||
std::optional<llvm::Value *> EndIndex = initializeLocalResourceArray( | ||
CGF, ResourceTy->getAsCXXRecordDecl(), ArrayTy, ValueSlot, Range, Index, | ||
ArrayDecl->getName(), RBA, VkBinding, | ||
{llvm::ConstantInt::get(CGM.IntTy, 0)}, ArraySubsExpr->getExprLoc()); | ||
if (!EndIndex) | ||
return std::nullopt; | ||
} | ||
return CGF.MakeAddrLValue(TmpVar, ResultTy, AlignmentSource::Decl); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably do this with
Sema::LookupSingleName
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this is in CodeGen and we do not have access to Sema anymore.
I can use Sema lookup in my previous PR though (#156544), so I will update it at least there.