-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[HLSL] Global resource arrays element access #152454
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
base: main
Are you sure you want to change the base?
Changes from 6 commits
10a06e3
4e153a4
ac99f5a
b5ca61c
e12a7a9
8690223
f08e213
c8fd0b4
227f6ae
8bc614a
f0d05cd
ac62921
1600414
00341fa
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 |
---|---|---|
|
@@ -84,6 +84,124 @@ void addRootSignature(llvm::dxbc::RootSignatureVersion RootSigVer, | |
RootSignatureValMD->addOperand(MDVals); | ||
} | ||
|
||
// If the specified expr is a simple decay from an array to pointer, | ||
// return the array subexpression. Otherwise, return nullptr. | ||
static const Expr *getSubExprFromArrayDecayOperand(const Expr *E) { | ||
const auto *CE = dyn_cast<CastExpr>(E); | ||
if (!CE || CE->getCastKind() != CK_ArrayToPointerDecay) | ||
return nullptr; | ||
return CE->getSubExpr(); | ||
} | ||
|
||
// Find array variable declaration from nested array subscript AST nodes | ||
static const ValueDecl *getArrayDecl(const ArraySubscriptExpr *ASE) { | ||
const Expr *E = nullptr; | ||
while (ASE != nullptr) { | ||
E = getSubExprFromArrayDecayOperand(ASE->getBase()); | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!E) | ||
return nullptr; | ||
ASE = dyn_cast<ArraySubscriptExpr>(E); | ||
} | ||
if (const DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E)) | ||
return DRE->getDecl(); | ||
return nullptr; | ||
} | ||
|
||
// Get the total size of the array, or -1 if the array is unbounded. | ||
static int getTotalArraySize(const clang::Type *Ty) { | ||
bob80905 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert(Ty->isArrayType() && "expected array type"); | ||
if (Ty->isIncompleteArrayType()) | ||
return -1; | ||
int Size = 1; | ||
while (const auto *CAT = dyn_cast<ConstantArrayType>(Ty)) { | ||
Size *= CAT->getSExtSize(); | ||
Ty = CAT->getArrayElementTypeNoTypeQual(); | ||
} | ||
return Size; | ||
} | ||
|
||
// Find constructor decl for a specific resource record type and binding | ||
// (implicit vs. explicit). The constructor has 6 parameters. | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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) { | ||
SmallVector<QualType> 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()) { | ||
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. I wish we had a good way to resolve this with overload resolution and have it captured in the AST... that said I'm not sure I have a good idea on how to do that. This does seem to do the correct thing. |
||
if (Ctor->getNumParams() != ExpParmTypes.size()) | ||
bob80905 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
continue; | ||
ParmVarDecl **ParmIt = Ctor->param_begin(); | ||
QualType *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) { | ||
std::string Str(BaseName); | ||
std::string GlobalName(Str + ".str"); | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return CGM.GetAddrOfConstantCString(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) { | ||
assert((VkBinding || RBA) && "at least one a binding attribute expected"); | ||
|
||
std::optional<uint32_t> RegisterSlot; | ||
uint32_t SpaceNo = 0; | ||
if (VkBinding) { | ||
RegisterSlot = VkBinding->getBinding(); | ||
SpaceNo = VkBinding->getSet(); | ||
} else if (RBA) { | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (RBA->hasRegisterSlot()) | ||
RegisterSlot = RBA->getSlotNumber(); | ||
SpaceNo = RBA->getSpaceNumber(); | ||
} | ||
|
||
ASTContext &AST = CD->getASTContext(); | ||
Value *NameStr = buildNameForResource(Name, CGM); | ||
Value *Space = llvm::ConstantInt::get(CGM.IntTy, SpaceNo); | ||
|
||
Args.add(RValue::get(ThisPtr), CD->getThisType()); | ||
if (RegisterSlot.has_value()) { | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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); | ||
|
||
} else { | ||
// implicit binding | ||
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); | ||
} | ||
Args.add(RValue::get(NameStr), AST.getPointerType(AST.CharTy.withConst())); | ||
} | ||
|
||
} // namespace | ||
|
||
llvm::Type * | ||
|
@@ -103,13 +221,6 @@ llvm::Triple::ArchType CGHLSLRuntime::getArch() { | |
return CGM.getTarget().getTriple().getArch(); | ||
} | ||
|
||
// Returns true if the type is an HLSL resource class or an array of them | ||
static bool isResourceRecordTypeOrArrayOf(const clang::Type *Ty) { | ||
while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) | ||
Ty = CAT->getArrayElementTypeNoTypeQual(); | ||
return Ty->isHLSLResourceRecord(); | ||
} | ||
|
||
// Emits constant global variables for buffer constants declarations | ||
// and creates metadata linking the constant globals with the buffer global. | ||
void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, | ||
|
@@ -146,7 +257,7 @@ void CGHLSLRuntime::emitBufferGlobalsAndMetadata(const HLSLBufferDecl *BufDecl, | |
if (VDTy.getAddressSpace() != LangAS::hlsl_constant) { | ||
if (VD->getStorageClass() == SC_Static || | ||
VDTy.getAddressSpace() == LangAS::hlsl_groupshared || | ||
isResourceRecordTypeOrArrayOf(VDTy.getTypePtr())) { | ||
VDTy->isHLSLResourceRecord() || VDTy->isHLSLResourceRecordArray()) { | ||
// Emit static and groupshared variables and resource classes inside | ||
// cbuffer as regular globals | ||
CGM.EmitGlobal(VD); | ||
|
@@ -597,13 +708,6 @@ static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV, | |
CGM.AddCXXGlobalInit(InitResFunc); | ||
} | ||
|
||
static Value *buildNameForResource(llvm::StringRef BaseName, | ||
CodeGenModule &CGM) { | ||
std::string Str(BaseName); | ||
std::string GlobalName(Str + ".str"); | ||
return CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer(); | ||
} | ||
|
||
void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl, | ||
llvm::GlobalVariable *GV, | ||
HLSLVkBindingAttr *VkBinding) { | ||
|
@@ -631,17 +735,13 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl, | |
auto *Index = llvm::ConstantInt::get(CGM.IntTy, 0); | ||
auto *RangeSize = llvm::ConstantInt::get(CGM.IntTy, 1); | ||
auto *Space = llvm::ConstantInt::get(CGM.IntTy, RBA->getSpaceNumber()); | ||
Value *Name = nullptr; | ||
Value *Name = buildNameForResource(BufDecl->getName(), CGM); | ||
|
||
llvm::Intrinsic::ID IntrinsicID = | ||
RBA->hasRegisterSlot() | ||
? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic() | ||
: CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic(); | ||
|
||
std::string Str(BufDecl->getName()); | ||
std::string GlobalName(Str + ".str"); | ||
Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer(); | ||
|
||
// buffer with explicit binding | ||
if (RBA->hasRegisterSlot()) { | ||
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber()); | ||
|
@@ -708,3 +808,95 @@ void CGHLSLRuntime::emitInitListOpaqueValues(CodeGenFunction &CGF, | |
} | ||
} | ||
} | ||
|
||
std::optional<LValue> CGHLSLRuntime::emitResourceArraySubscriptExpr( | ||
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. Could this function be better named? I don't think it emits a resource array subscript expr? 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. I believe the name is quite descriptive - it is emitting code for subscript expression on resource arrays, or in other words, emitting code for |
||
const ArraySubscriptExpr *ArraySubsExpr, CodeGenFunction &CGF) { | ||
assert(ArraySubsExpr->getType()->isHLSLResourceRecord() || | ||
ArraySubsExpr->getType()->isHLSLResourceRecordArray() && | ||
"expected resource array subscript expression"); | ||
|
||
// let clang codegen handle local resource array subscrips | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const VarDecl *ArrayDecl = dyn_cast<VarDecl>(getArrayDecl(ArraySubsExpr)); | ||
if (!ArrayDecl || !ArrayDecl->hasGlobalStorage()) | ||
return std::nullopt; | ||
|
||
// FIXME: this is not yet implemented (llvm/llvm-project#145426) | ||
assert(!ArraySubsExpr->getType()->isArrayType() && | ||
"indexing of array subsets it not supported yet"); | ||
|
||
// get total array size (= range size) | ||
const Type *ResArrayTy = ArrayDecl->getType().getTypePtr(); | ||
assert(ResArrayTy->isHLSLResourceRecordArray() && | ||
bob80905 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"expected array of resource classes"); | ||
llvm::Value *Range = | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
llvm::ConstantInt::get(CGM.IntTy, getTotalArraySize(ResArrayTy)); | ||
|
||
// Iterate through all nested array subscript expressions to calculate | ||
// the index in the flattened resource array (if this is a multi- | ||
// dimensional array). The index is calculated as a sum of all indices | ||
// multiplied by the total size of the array at that level. | ||
Value *Index = nullptr; | ||
Value *Multiplier = nullptr; | ||
const ArraySubscriptExpr *ASE = ArraySubsExpr; | ||
while (ASE != nullptr) { | ||
Value *SubIndex = CGF.EmitScalarExpr(ASE->getIdx()); | ||
bob80905 marked this conversation as resolved.
Show resolved
Hide resolved
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. Could SubIndex ever be null? 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. I don't think so. The result of |
||
if (const auto *ArrayTy = | ||
dyn_cast<ConstantArrayType>(ASE->getType().getTypePtr())) { | ||
Value *SubMultiplier = | ||
llvm::ConstantInt::get(CGM.IntTy, ArrayTy->getSExtSize()); | ||
Multiplier = Multiplier ? CGF.Builder.CreateMul(Multiplier, SubMultiplier) | ||
: SubMultiplier; | ||
SubIndex = CGF.Builder.CreateMul(SubIndex, Multiplier); | ||
} | ||
|
||
Index = Index ? CGF.Builder.CreateAdd(Index, SubIndex) : SubIndex; | ||
ASE = dyn_cast<ArraySubscriptExpr>( | ||
getSubExprFromArrayDecayOperand(ASE->getBase())); | ||
} | ||
|
||
// find binding info for the resource array | ||
// (for implicit binding an HLSLResourceBindingAttr should have been added by | ||
// SemaHLSL) | ||
QualType ResourceTy = ArraySubsExpr->getType(); | ||
HLSLVkBindingAttr *VkBinding = ArrayDecl->getAttr<HLSLVkBindingAttr>(); | ||
HLSLResourceBindingAttr *RBA = ArrayDecl->getAttr<HLSLResourceBindingAttr>(); | ||
assert((VkBinding || RBA) && "resource array must have a binding attribute"); | ||
|
||
// lookup the resource class constructor based on the resource type and | ||
// binding | ||
CXXConstructorDecl *CD = | ||
findResourceConstructorDecl(ArrayDecl->getASTContext(), ResourceTy, | ||
VkBinding || RBA->hasRegisterSlot()); | ||
|
||
// create a temporary variable for the resource class instance (we need to | ||
// return an LValue) | ||
RawAddress TmpVar = CGF.CreateMemTemp(ResourceTy); | ||
if (auto *Size = CGF.EmitLifetimeStart( | ||
CGM.getDataLayout().getTypeAllocSize(TmpVar.getElementType()), | ||
TmpVar.getPointer())) { | ||
CGF.pushFullExprCleanup<CodeGenFunction::CallLifetimeEnd>( | ||
NormalEHLifetimeMarker, TmpVar, Size); | ||
} | ||
AggValueSlot ValueSlot = AggValueSlot::forAddr( | ||
TmpVar, Qualifiers(), AggValueSlot::IsDestructed_t(true), | ||
AggValueSlot::DoesNotNeedGCBarriers, AggValueSlot::IsAliased_t(false), | ||
AggValueSlot::MayOverlap); | ||
hekota marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Address ThisAddress = ValueSlot.getAddress(); | ||
llvm::Value *ThisPtr = CGF.getAsNaturalPointerTo( | ||
ThisAddress, CD->getThisType()->getPointeeType()); | ||
|
||
// 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, ThisAddress, Args, | ||
ValueSlot.mayOverlap(), | ||
ArraySubsExpr->getExprLoc(), | ||
ValueSlot.isSanitizerChecked()); | ||
|
||
return CGF.MakeAddrLValue(TmpVar, ArraySubsExpr->getType(), | ||
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.
In what situation would we call in here where
E->getType()->isHLSLResourceRecord()
is true butE->getType()->isHLSLResourceRecordArray()
is false?Uh oh!
There was an error while loading. Please reload this page.
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.
E->getType()
is result type of theArraySubscriptExpr
. If the result of the indexing into a resource array is a single resource,E->getType()->isHLSLResourceRecord()
will be true andE->getType()->isHLSLResourceRecordArray()
will be false. If the result is a sub-array of a multi-dimensinal array, then it is going to be the other way around.