Skip to content

[HLSL] Add implicit binding attribute to resource arrays #152452

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

Merged
merged 7 commits into from
Aug 11, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2724,6 +2724,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isHLSLAttributedResourceType() const;
bool isHLSLInlineSpirvType() const;
bool isHLSLResourceRecord() const;
bool isHLSLResourceRecordArray() const;
bool isHLSLIntangibleType()
const; // Any HLSL intangible type (builtin, array, class)

Expand Down
9 changes: 9 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5246,6 +5246,15 @@ bool Type::isHLSLResourceRecord() const {
return HLSLAttributedResourceType::findHandleTypeOnResource(this) != nullptr;
}

bool Type::isHLSLResourceRecordArray() const {
const Type *Ty = getUnqualifiedDesugaredType();
if (!Ty->isArrayType())
return false;
while (isa<ConstantArrayType>(Ty))
Ty = Ty->getArrayElementTypeNoTypeQual();
return Ty->isHLSLResourceRecord();
}

bool Type::isHLSLIntangibleType() const {
const Type *Ty = getUnqualifiedDesugaredType();

Expand Down
9 changes: 1 addition & 8 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,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,
Expand Down Expand Up @@ -146,7 +139,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);
Expand Down
73 changes: 48 additions & 25 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ static RegisterType getRegisterType(ResourceClass RC) {
llvm_unreachable("unexpected ResourceClass value");
}

static RegisterType getRegisterType(const HLSLAttributedResourceType *ResTy) {
return getRegisterType(ResTy->getAttrs().ResourceClass);
}

// Converts the first letter of string Slot to RegisterType.
// Returns false if the letter does not correspond to a valid register type.
static bool convertToRegisterType(StringRef Slot, RegisterType *RT) {
Expand Down Expand Up @@ -337,16 +341,20 @@ static bool isZeroSizedArray(const ConstantArrayType *CAT) {
return CAT != nullptr;
}

// Returns true if the record type is an HLSL resource class or an array of
// resource classes
static bool isResourceRecordTypeOrArrayOf(const Type *Ty) {
while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty))
Ty = CAT->getArrayElementTypeNoTypeQual();
return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr;
static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
const Type *Ty = VD->getType().getTypePtr();
return Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray();
}

static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
return isResourceRecordTypeOrArrayOf(VD->getType().getTypePtr());
static const HLSLAttributedResourceType *
getResourceArrayHandleType(VarDecl *VD) {
assert(VD->getType()->isHLSLResourceRecordArray() &&
"expected array of resource records");
const Type *Ty = VD->getType()->getUnqualifiedDesugaredType();
while (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(Ty)) {
Ty = CAT->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType();
}
return HLSLAttributedResourceType::findHandleTypeOnResource(Ty);
}

// Returns true if the type is a leaf element type that is not valid to be
Expand All @@ -355,7 +363,7 @@ static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
// type or if it is a record type that needs to be inspected further.
static bool isInvalidConstantBufferLeafElementType(const Type *Ty) {
Ty = Ty->getUnqualifiedDesugaredType();
if (isResourceRecordTypeOrArrayOf(Ty))
if (Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray())
return true;
if (Ty->isRecordType())
return Ty->getAsCXXRecordDecl()->isEmpty();
Expand Down Expand Up @@ -575,16 +583,13 @@ void createHostLayoutStructForBuffer(Sema &S, HLSLBufferDecl *BufDecl) {
BufDecl->addLayoutStruct(LS);
}

static void addImplicitBindingAttrToBuffer(Sema &S, HLSLBufferDecl *BufDecl,
uint32_t ImplicitBindingOrderID) {
RegisterType RT =
BufDecl->isCBuffer() ? RegisterType::CBuffer : RegisterType::SRV;
static void addImplicitBindingAttrToDecl(Sema &S, Decl *D, RegisterType RT,
uint32_t ImplicitBindingOrderID) {
auto *Attr =
HLSLResourceBindingAttr::CreateImplicit(S.getASTContext(), "", "0", {});
std::optional<unsigned> RegSlot;
Attr->setBinding(RT, RegSlot, 0);
Attr->setBinding(RT, std::nullopt, 0);
Attr->setImplicitBindingOrderID(ImplicitBindingOrderID);
BufDecl->addAttr(Attr);
D->addAttr(Attr);
}

// Handle end of cbuffer/tbuffer declaration
Expand All @@ -607,7 +612,10 @@ void SemaHLSL::ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace) {
if (RBA)
RBA->setImplicitBindingOrderID(OrderID);
else
addImplicitBindingAttrToBuffer(SemaRef, BufDecl, OrderID);
addImplicitBindingAttrToDecl(SemaRef, BufDecl,
BufDecl->isCBuffer() ? RegisterType::CBuffer
: RegisterType::SRV,
OrderID);
}

SemaRef.PopDeclContext();
Expand Down Expand Up @@ -1913,7 +1921,7 @@ static bool DiagnoseLocalRegisterBinding(Sema &S, SourceLocation &ArgLoc,
if (const HLSLAttributedResourceType *AttrResType =
HLSLAttributedResourceType::findHandleTypeOnResource(
VD->getType().getTypePtr())) {
if (RegType == getRegisterType(AttrResType->getAttrs().ResourceClass))
if (RegType == getRegisterType(AttrResType))
return true;

S.Diag(D->getLocation(), diag::err_hlsl_binding_type_mismatch)
Expand Down Expand Up @@ -2446,8 +2454,8 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
HLSLBufferDecl *DefaultCBuffer = HLSLBufferDecl::CreateDefaultCBuffer(
SemaRef.getASTContext(), SemaRef.getCurLexicalContext(),
DefaultCBufferDecls);
addImplicitBindingAttrToBuffer(SemaRef, DefaultCBuffer,
getNextImplicitBindingOrderID());
addImplicitBindingAttrToDecl(SemaRef, DefaultCBuffer, RegisterType::CBuffer,
getNextImplicitBindingOrderID());
SemaRef.getCurLexicalContext()->addDecl(DefaultCBuffer);
createHostLayoutStructForBuffer(SemaRef, DefaultCBuffer);

Expand Down Expand Up @@ -3597,7 +3605,7 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
return;

// Resource handles.
if (isResourceRecordTypeOrArrayOf(Type->getUnqualifiedDesugaredType()))
if (Type->isHLSLResourceRecord() || Type->isHLSLResourceRecordArray())
return;

// Only static globals belong to the Private address space.
Expand Down Expand Up @@ -3637,10 +3645,7 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
if (VD->getType()->isHLSLIntangibleType())
collectResourceBindingsOnVarDecl(VD);

const Type *VarType = VD->getType().getTypePtr();
while (VarType->isArrayType())
VarType = VarType->getArrayElementTypeNoTypeQual();
if (VarType->isHLSLResourceRecord() ||
if (isResourceRecordTypeOrArrayOf(VD) ||
VD->hasAttr<HLSLVkConstantIdAttr>()) {
// Make the variable for resources static. The global externally visible
// storage is accessed through the handle, which is a member. The variable
Expand All @@ -3650,6 +3655,24 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {

// process explicit bindings
processExplicitBindingsOnDecl(VD);

if (VD->getType()->isHLSLResourceRecordArray()) {
// If the resource array does not have an explicit binding attribute,
// create an implicit one. It will be used to transfer implicit binding
// order_ID to codegen.
if (!VD->hasAttr<HLSLVkBindingAttr>()) {
HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
if (!RBA || !RBA->hasRegisterSlot()) {
uint32_t OrderID = getNextImplicitBindingOrderID();
if (RBA)
RBA->setImplicitBindingOrderID(OrderID);
else
addImplicitBindingAttrToDecl(
SemaRef, VD, getRegisterType(getResourceArrayHandleType(VD)),
OrderID);
}
}
}
}

deduceAddressSpace(VD);
Expand Down
20 changes: 20 additions & 0 deletions clang/test/AST/HLSL/resource_binding_attr.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ RWBuffer<float> UAV1 : register(u2), UAV2 : register(u4);
// CHECK: HLSLResourceBindingAttr {{.*}} "" "space5"
RWBuffer<float> UAV3 : register(space5);

// CHECK: VarDecl {{.*}} UAV_Array 'RWBuffer<float>[10]'
// CHECK: HLSLResourceBindingAttr {{.*}} "u10" "space6"
RWBuffer<float> UAV_Array[10] : register(u10, space6);

//
// Default constants ($Globals) layout annotations

Expand All @@ -56,3 +60,19 @@ struct S {
// CHECK: VarDecl {{.*}} s 'hlsl_constant S'
// CHECK: HLSLResourceBindingAttr {{.*}} "c10" "space0
S s : register(c10);

//
// Implicit binding

// Constant buffers should have implicit binding attribute added by SemaHLSL
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB2
// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK-NEXT: HLSLResourceBindingAttr {{.*}} Implicit "" "0"
cbuffer CB2 {
float4 c;
}

// Resource arrays should have implicit binding attribute added by SemaHLSL
// CHECK: VarDecl {{.*}} SB 'StructuredBuffer<float>[10]'
// CHECK: HLSLResourceBindingAttr {{.*}} Implicit "" "0"
StructuredBuffer<float> SB[10];