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 all 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/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -4932,6 +4932,7 @@ def HLSLResourceBinding: InheritableAttr {
return SpaceNumber;
}
void setImplicitBindingOrderID(uint32_t Value) {
assert(!hasImplicitBindingOrderID() && "attribute already has implicit binding order id");
ImplicitBindingOrderID = Value;
}
bool hasImplicitBindingOrderID() const {
Expand Down
54 changes: 43 additions & 11 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 @@ -342,6 +346,16 @@ static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) {
return Ty->isHLSLResourceRecord() || Ty->isHLSLResourceRecordArray();
}

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
// included in HLSL Buffer, such as a resource class, empty struct, zero-sized
// array, or a builtin intangible type. Returns false it is a valid leaf element
Expand Down Expand Up @@ -568,16 +582,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 @@ -600,7 +611,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 @@ -1906,7 +1920,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 @@ -2439,8 +2453,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 @@ -3640,6 +3654,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
48 changes: 47 additions & 1 deletion clang/test/AST/HLSL/resource_binding_attr.hlsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -ast-dump -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -ast-dump -o - %s | FileCheck %s -check-prefixes=CHECK,DXIL
// RUN: %clang_cc1 -triple spirv-unknown-vulkan-library -finclude-default-header -ast-dump -o - %s | FileCheck %s -check-prefixes=CHECK,SPV

// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 4]]:9 cbuffer CB
// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer
Expand Down Expand Up @@ -34,6 +35,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 +61,44 @@ 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,
// unless the target is SPIR-V and there is [[vk::binding]] attribute.
// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 3]]:9 cbuffer CB2
// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// CHECK-NEXT: HLSLResourceBindingAttr {{.*}} Implicit "" "0"
cbuffer CB2 {
float4 c;
}

// CHECK: HLSLBufferDecl {{.*}} line:[[# @LINE + 7]]:9 cbuffer CB3
// CHECK-NEXT: HLSLResourceClassAttr {{.*}} Implicit CBuffer
// DXIL: HLSLResourceBindingAttr {{.*}} Implicit
// DXIL-NOT: HLSLVkBindingAttr
// SPV: HLSLVkBindingAttr {{.*}} 1 0
// SPV-NOT: HLSLResourceBindingAttr {{.*}} Implicit
[[vk::binding(1)]]
cbuffer CB3 {
float2 d;
}

// Resource arrays should have implicit binding attribute added by SemaHLSL,
// unless the target is SPIR-V and there is [[vk::binding]] attribute.
// CHECK: VarDecl {{.*}} SB 'StructuredBuffer<float>[10]'
// CHECK: HLSLResourceBindingAttr {{.*}} Implicit "" "0"
StructuredBuffer<float> SB[10];

// CHECK: VarDecl {{.*}} SB2 'StructuredBuffer<float>[10]'
// DXIL: HLSLResourceBindingAttr {{.*}} Implicit
// DXIL-NOT: HLSLVkBindingAttr
// SPV: HLSLVkBindingAttr {{.*}} 2 0
// SPV-NOT: HLSLResourceBindingAttr {{.*}} Implicit
[[vk::binding(2)]]
StructuredBuffer<float> SB2[10];

// $Globals should have implicit binding attribute added by SemaHLSL
// CHECK: HLSLBufferDecl {{.*}} implicit cbuffer $Globals
// CHECK: HLSLResourceBindingAttr {{.*}} Implicit "" "0"