Skip to content
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't RBA have to be non-nullptr here?

Copy link
Member Author

@hekota hekota Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually no. The condition !RBA || !RBA->hasRegisterSlot() is true when we either don't have an RBA, or when we have an RBA but without a register slot. In both cases we need to assign implicit binding, but in case that we don't have an RBA we need to create one.

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];