diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 30efb9f39e4f4..a9fa4a8f07454 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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 { diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 17f17f8114373..23eef069cdf95 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -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) { @@ -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(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 @@ -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 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 @@ -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(); @@ -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) @@ -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); @@ -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()) { + HLSLResourceBindingAttr *RBA = VD->getAttr(); + if (!RBA || !RBA->hasRegisterSlot()) { + uint32_t OrderID = getNextImplicitBindingOrderID(); + if (RBA) + RBA->setImplicitBindingOrderID(OrderID); + else + addImplicitBindingAttrToDecl( + SemaRef, VD, getRegisterType(getResourceArrayHandleType(VD)), + OrderID); + } + } + } } deduceAddressSpace(VD); diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl index c073cd4dc1476..c6d93b991fbfc 100644 --- a/clang/test/AST/HLSL/resource_binding_attr.hlsl +++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl @@ -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 @@ -34,6 +35,10 @@ RWBuffer UAV1 : register(u2), UAV2 : register(u4); // CHECK: HLSLResourceBindingAttr {{.*}} "" "space5" RWBuffer UAV3 : register(space5); +// CHECK: VarDecl {{.*}} UAV_Array 'RWBuffer[10]' +// CHECK: HLSLResourceBindingAttr {{.*}} "u10" "space6" +RWBuffer UAV_Array[10] : register(u10, space6); + // // Default constants ($Globals) layout annotations @@ -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[10]' +// CHECK: HLSLResourceBindingAttr {{.*}} Implicit "" "0" +StructuredBuffer SB[10]; + +// CHECK: VarDecl {{.*}} SB2 'StructuredBuffer[10]' +// DXIL: HLSLResourceBindingAttr {{.*}} Implicit +// DXIL-NOT: HLSLVkBindingAttr +// SPV: HLSLVkBindingAttr {{.*}} 2 0 +// SPV-NOT: HLSLResourceBindingAttr {{.*}} Implicit +[[vk::binding(2)]] +StructuredBuffer SB2[10]; + +// $Globals should have implicit binding attribute added by SemaHLSL +// CHECK: HLSLBufferDecl {{.*}} implicit cbuffer $Globals +// CHECK: HLSLResourceBindingAttr {{.*}} Implicit "" "0"