From 9736c86391d9b293be38e20bba5b144edb43c1cc Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Wed, 27 Aug 2025 14:54:28 -0400 Subject: [PATCH 1/4] [HLSL] [SPIR-V] Add initial support for typed buffer counters This is part 1 of implementing the typed buffer counters proposal: https://github.com/llvm/wg-hlsl/blob/main/proposals/0023-typed-buffer-counters.md This patch adds the initial plumbing for supporting counter variables associated with structured buffers for the SPIR-V backend. It introduces an `IsCounter` attribute to `HLSLAttributedResourceType` and threads it through the AST, type printing, and mangling. It also adds a `__counter_handle` member to the relevant buffer types in `HLSLBuiltinTypeDeclBuilder`. --- clang/include/clang/AST/TypeBase.h | 17 +- clang/include/clang/AST/TypeProperties.td | 5 +- clang/include/clang/Basic/Attr.td | 6 + clang/lib/AST/ItaniumMangle.cpp | 2 + clang/lib/AST/TypePrinter.cpp | 3 + clang/lib/CodeGen/Targets/SPIR.cpp | 6 + clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 150 ++++++++++++++++-- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 7 + clang/lib/Sema/HLSLExternalSemaSource.cpp | 2 +- clang/lib/Sema/SemaHLSL.cpp | 11 ++ .../test/AST/HLSL/StructuredBuffers-AST.hlsl | 23 ++- .../RWStructuredBuffer-elementtype.hlsl | 34 ++-- ...erOrderedStructuredBuffer-elementtype.hlsl | 26 +-- .../StructuredBuffers-constructors.hlsl | 4 +- .../StructuredBuffers-methods-lib.hlsl | 6 +- .../StructuredBuffers-methods-ps.hlsl | 2 +- .../resources/resource-bindings.hlsl | 2 +- 17 files changed, 247 insertions(+), 59 deletions(-) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index b02d9c7499fe5..b60ef6911d84a 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -6700,15 +6700,21 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { LLVM_PREFERRED_TYPE(bool) uint8_t RawBuffer : 1; + LLVM_PREFERRED_TYPE(bool) + uint8_t IsCounter : 1; + Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV = false, - bool RawBuffer = false) - : ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer) {} + bool RawBuffer = false, bool IsCounter = false) + : ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer), + IsCounter(IsCounter) {} - Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, false, false) {} + Attributes() + : Attributes(llvm::dxil::ResourceClass::UAV, false, false, false) {} friend bool operator==(const Attributes &LHS, const Attributes &RHS) { - return std::tie(LHS.ResourceClass, LHS.IsROV, LHS.RawBuffer) == - std::tie(RHS.ResourceClass, RHS.IsROV, RHS.RawBuffer); + return std::tie(LHS.ResourceClass, LHS.IsROV, LHS.RawBuffer, + LHS.IsCounter) == std::tie(RHS.ResourceClass, RHS.IsROV, + RHS.RawBuffer, RHS.IsCounter); } friend bool operator!=(const Attributes &LHS, const Attributes &RHS) { return !(LHS == RHS); @@ -6749,6 +6755,7 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { ID.AddInteger(static_cast(Attrs.ResourceClass)); ID.AddBoolean(Attrs.IsROV); ID.AddBoolean(Attrs.RawBuffer); + ID.AddBoolean(Attrs.IsCounter); } static bool classof(const Type *T) { diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index b3932a67db69d..9dc85fb88e267 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -662,6 +662,9 @@ let Class = HLSLAttributedResourceType in { def : Property<"rawBuffer", Bool> { let Read = [{ node->getAttrs().RawBuffer }]; } + def : Property<"isCounter", Bool> { + let Read = [{ node->getAttrs().IsCounter }]; + } def : Property<"wrappedTy", QualType> { let Read = [{ node->getWrappedType() }]; } @@ -669,7 +672,7 @@ let Class = HLSLAttributedResourceType in { let Read = [{ node->getContainedType() }]; } def : Creator<[{ - HLSLAttributedResourceType::Attributes attrs(static_cast(resClass), isROV, rawBuffer); + HLSLAttributedResourceType::Attributes attrs(static_cast(resClass), isROV, rawBuffer, isCounter); return ctx.getHLSLAttributedResourceType(wrappedTy, containedTy, attrs); }]>; } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 2623f9ff6972f..1d6f4e809a1fa 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5059,6 +5059,12 @@ def HLSLRawBuffer : TypeAttr { let Documentation = [InternalOnly]; } +def HLSLIsCounter : TypeAttr { + let Spellings = [CXX11<"hlsl", "is_counter">]; + let LangOpts = [HLSL]; + let Documentation = [InternalOnly]; +} + def HLSLGroupSharedAddressSpace : TypeAttr { let Spellings = [CustomKeyword<"groupshared">]; let Subjects = SubjectList<[Var]>; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 2173aed5b45af..844db79f18a4a 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4624,6 +4624,8 @@ void CXXNameMangler::mangleType(const HLSLAttributedResourceType *T) { Str += "_ROV"; if (Attrs.RawBuffer) Str += "_Raw"; + if (Attrs.IsCounter) + Str += "_Counter"; if (T->hasContainedType()) Str += "_CT"; mangleVendorQualifier(Str); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index cd59678d67f2f..b2ba569843897 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2033,6 +2033,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::HLSLROV: case attr::HLSLRawBuffer: case attr::HLSLContainedType: + case attr::HLSLIsCounter: llvm_unreachable("HLSL resource type attributes handled separately"); case attr::OpenCLPrivateAddressSpace: @@ -2181,6 +2182,8 @@ void TypePrinter::printHLSLAttributedResourceAfter( OS << " [[hlsl::is_rov]]"; if (Attrs.RawBuffer) OS << " [[hlsl::raw_buffer]]"; + if (Attrs.IsCounter) + OS << " [[hlsl::is_counter]]"; QualType ContainedTy = T->getContainedType(); if (!ContainedTy.isNull()) { diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index 2e3fc53c58edc..4aa63143a66cd 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -486,6 +486,12 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType( return getSPIRVImageTypeFromHLSLResource(ResAttrs, ContainedTy, CGM); } + if (ResAttrs.IsCounter) { + llvm::Type *ElemType = llvm::Type::getInt32Ty(Ctx); + uint32_t StorageClass = /* StorageBuffer storage class */ 12; + return llvm::TargetExtType::get(Ctx, "spirv.VulkanBuffer", {ElemType}, + {StorageClass, true}); + } llvm::Type *ElemType = CGM.getTypes().ConvertTypeForMem(ContainedTy); llvm::ArrayType *RuntimeArrayType = llvm::ArrayType::get(ElemType, 0); uint32_t StorageClass = /* StorageBuffer storage class */ 12; diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 5eafd03d89efe..d910f89176bfb 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -57,6 +57,15 @@ CXXConstructorDecl *lookupCopyConstructor(QualType ResTy) { return CD; return nullptr; } + +/// Set up common members and attributes for buffer types +static bool resourceHasCounter(const CXXRecordDecl *Decl) { + StringRef Name = Decl->getName(); + return Name == "RWStructuredBuffer" || Name == "AppendStructuredBuffer" || + Name == "ConsumeStructuredBuffer" || + Name == "RasterizerOrderedStructuredBuffer"; +} + } // namespace // Builder for template arguments of builtin types. Used internally @@ -138,7 +147,16 @@ struct BuiltinTypeMethodBuilder { // LastStmt - refers to the last statement in the method body; referencing // LastStmt will remove the statement from the method body since // it will be linked from the new expression being constructed. - enum class PlaceHolder { _0, _1, _2, _3, _4, Handle = 128, LastStmt }; + enum class PlaceHolder { + _0, + _1, + _2, + _3, + _4, + Handle = 128, + CounterHandle, + LastStmt + }; Expr *convertPlaceholder(PlaceHolder PH); Expr *convertPlaceholder(LocalVar &Var); @@ -178,10 +196,17 @@ struct BuiltinTypeMethodBuilder { template BuiltinTypeMethodBuilder &setHandleFieldOnResource(ResourceT ResourceRecord, ValueT HandleValue); + template + BuiltinTypeMethodBuilder & + accessCounterHandleFieldOnResource(T ResourceRecord); + template + BuiltinTypeMethodBuilder & + setCounterHandleFieldOnResource(ResourceT ResourceRecord, ValueT HandleValue); template BuiltinTypeMethodBuilder &returnValue(T ReturnValue); BuiltinTypeMethodBuilder &returnThis(); BuiltinTypeDeclBuilder &finalize(); Expr *getResourceHandleExpr(); + Expr *getResourceCounterHandleExpr(); private: void createDecl(); @@ -346,6 +371,8 @@ TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) { Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) { if (PH == PlaceHolder::Handle) return getResourceHandleExpr(); + if (PH == PlaceHolder::CounterHandle) + return getResourceCounterHandleExpr(); if (PH == PlaceHolder::LastStmt) { assert(!StmtsList.empty() && "no statements in the list"); @@ -467,6 +494,18 @@ Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() { OK_Ordinary); } +Expr *BuiltinTypeMethodBuilder::getResourceCounterHandleExpr() { + ensureCompleteDecl(); + + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + CXXThisExpr *This = CXXThisExpr::Create( + AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); + FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField(); + return MemberExpr::CreateImplicit(AST, This, false, HandleField, + HandleField->getType(), VK_LValue, + OK_Ordinary); +} + BuiltinTypeMethodBuilder & BuiltinTypeMethodBuilder::declareLocalVar(LocalVar &Var) { ensureCompleteDecl(); @@ -583,6 +622,44 @@ BuiltinTypeMethodBuilder::setHandleFieldOnResource(ResourceT ResourceRecord, return *this; } +template +BuiltinTypeMethodBuilder & +BuiltinTypeMethodBuilder::accessCounterHandleFieldOnResource(T ResourceRecord) { + ensureCompleteDecl(); + + Expr *ResourceExpr = convertPlaceholder(ResourceRecord); + + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField(); + MemberExpr *HandleExpr = MemberExpr::CreateImplicit( + AST, ResourceExpr, false, HandleField, HandleField->getType(), VK_LValue, + OK_Ordinary); + StmtsList.push_back(HandleExpr); + return *this; +} + +template +BuiltinTypeMethodBuilder & +BuiltinTypeMethodBuilder::setCounterHandleFieldOnResource( + ResourceT ResourceRecord, ValueT HandleValue) { + ensureCompleteDecl(); + + Expr *ResourceExpr = convertPlaceholder(ResourceRecord); + Expr *HandleValueExpr = convertPlaceholder(HandleValue); + + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField(); + MemberExpr *HandleMemberExpr = MemberExpr::CreateImplicit( + AST, ResourceExpr, false, HandleField, HandleField->getType(), VK_LValue, + OK_Ordinary); + Stmt *AssignStmt = BinaryOperator::Create( + DeclBuilder.SemaRef.getASTContext(), HandleMemberExpr, HandleValueExpr, + BO_Assign, HandleMemberExpr->getType(), ExprValueKind::VK_PRValue, + ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride()); + StmtsList.push_back(AssignStmt); + return *this; +} + template BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnValue(T ReturnValue) { ensureCompleteDecl(); @@ -722,6 +799,15 @@ BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type, return *this; } +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMembers( + ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { + addHandleMember(RC, IsROV, RawBuffer, Access); + if (resourceHasCounter(Record)) { + addCounterHandleMember(RC, IsROV, RawBuffer, Access); + } + return *this; +} + BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember( ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { assert(!Record->isCompleteDefinition() && "record is already complete"); @@ -745,6 +831,30 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember( return *this; } +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( + ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + + ASTContext &Ctx = SemaRef.getASTContext(); + TypeSourceInfo *ElementTypeInfo = + Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation()); + + // add handle member with resource type attributes + QualType AttributedResTy = QualType(); + SmallVector Attrs = { + HLSLResourceClassAttr::CreateImplicit(Ctx, RC), + IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr, + RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr, + ElementTypeInfo + ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) + : nullptr, + HLSLIsCounterAttr::CreateImplicit(Ctx)}; + if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs, + AttributedResTy)) + addMemberVariable("__counter_handle", AttributedResTy, {}, Access); + return *this; +} + // Adds default constructor to the resource class: // Resource::Resource() BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() { @@ -848,12 +958,18 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() { using PH = BuiltinTypeMethodBuilder::PlaceHolder; - return BuiltinTypeMethodBuilder(*this, /*Name=*/"", AST.VoidTy, - /*IsConst=*/false, /*IsCtor=*/true) - .addParam("other", ConstRecordRefType) + BuiltinTypeMethodBuilder MMB(*this, /*Name=*/"", AST.VoidTy, + /*IsConst=*/false, /*IsCtor=*/true); + MMB.addParam("other", ConstRecordRefType) .accessHandleFieldOnResource(PH::_0) - .assign(PH::Handle, PH::LastStmt) - .finalize(); + .assign(PH::Handle, PH::LastStmt); + + if (getResourceCounterHandleField()) { + MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle, + PH::LastStmt); + } + + return MMB.finalize(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() { @@ -868,12 +984,17 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() { using PH = BuiltinTypeMethodBuilder::PlaceHolder; DeclarationName Name = AST.DeclarationNames.getCXXOperatorName(OO_Equal); - return BuiltinTypeMethodBuilder(*this, Name, RecordRefType) - .addParam("other", ConstRecordRefType) + BuiltinTypeMethodBuilder MMB(*this, Name, RecordRefType); + MMB.addParam("other", ConstRecordRefType) .accessHandleFieldOnResource(PH::_0) - .assign(PH::Handle, PH::LastStmt) - .returnThis() - .finalize(); + .assign(PH::Handle, PH::LastStmt); + + if (getResourceCounterHandleField()) { + MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle, + PH::LastStmt); + } + + return MMB.returnThis().finalize(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() { @@ -909,6 +1030,13 @@ FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() const { return I->second; } +FieldDecl *BuiltinTypeDeclBuilder::getResourceCounterHandleField() const { + auto I = Fields.find("__counter_handle"); + if (I == Fields.end()) + return nullptr; + return I->second; +} + QualType BuiltinTypeDeclBuilder::getFirstTemplateTypeParam() { assert(Template && "record it not a template"); if (const auto *TTD = dyn_cast( diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 9448af13530cb..8ae73af4d9154 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -72,8 +72,14 @@ class BuiltinTypeDeclBuilder { AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder & + addHandleMembers(ResourceClass RC, bool IsROV, bool RawBuffer, + AccessSpecifier Access = AccessSpecifier::AS_private); + BuiltinTypeDeclBuilder & addHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access = AccessSpecifier::AS_private); + BuiltinTypeDeclBuilder & + addCounterHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, + AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder &addArraySubscriptOperators(); // Builtin types constructors @@ -96,6 +102,7 @@ class BuiltinTypeDeclBuilder { private: FieldDecl *getResourceHandleField() const; + FieldDecl *getResourceCounterHandleField() const; QualType getFirstTemplateTypeParam(); QualType getHandleElementType(); Expr *getConstantIntExpr(int value); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 781f0445d0b61..af3755a3cb5e2 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -130,7 +130,7 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, ResourceClass RC, bool IsROV, bool RawBuffer) { return BuiltinTypeDeclBuilder(S, Decl) - .addHandleMember(RC, IsROV, RawBuffer) + .addHandleMembers(RC, IsROV, RawBuffer) .addDefaultHandleConstructor() .addCopyConstructor() .addCopyAssignmentOperator() diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 55be036207eec..190d2dec8758a 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1811,6 +1811,13 @@ bool clang::CreateHLSLAttributedResourceType( } ResAttrs.RawBuffer = true; break; + case attr::HLSLIsCounter: + if (ResAttrs.IsCounter) { + S.Diag(A->getLocation(), diag::warn_duplicate_attribute_exact) << A; + return false; + } + ResAttrs.IsCounter = true; + break; case attr::HLSLContainedType: { const HLSLContainedTypeAttr *CTAttr = cast(A); QualType Ty = CTAttr->getType(); @@ -1903,6 +1910,10 @@ bool SemaHLSL::handleResourceTypeAttr(QualType T, const ParsedAttr &AL) { A = HLSLRawBufferAttr::Create(getASTContext(), ACI); break; + case ParsedAttr::AT_HLSLIsCounter: + A = HLSLIsCounterAttr::Create(getASTContext(), ACI); + break; + case ParsedAttr::AT_HLSLContainedType: { if (AL.getNumArgs() != 1 && !AL.hasParsedType()) { Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl index a490b22ab437b..6779abb10bec4 100644 --- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl @@ -12,7 +12,7 @@ // // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump \ // RUN: -DRESOURCE=RWStructuredBuffer %s | FileCheck -DRESOURCE=RWStructuredBuffer \ -// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-SUBSCRIPT,CHECK-SUBSCRIPT-UAV,CHECK-COUNTER,CHECK-LOAD %s +// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-SUBSCRIPT,CHECK-SUBSCRIPT-UAV,CHECK-COUNTER,CHECK-LOAD,CHECK-COUNTER-HANDLE %s // // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -DEMPTY \ // RUN: -DRESOURCE=AppendStructuredBuffer %s | FileCheck -DRESOURCE=AppendStructuredBuffer \ @@ -20,7 +20,7 @@ // // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump \ // RUN: -DRESOURCE=AppendStructuredBuffer %s | FileCheck -DRESOURCE=AppendStructuredBuffer \ -// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-NOSUBSCRIPT,CHECK-APPEND %s +// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-NOSUBSCRIPT,CHECK-APPEND,CHECK-COUNTER-HANDLE %s // // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -DEMPTY \ // RUN: -DRESOURCE=ConsumeStructuredBuffer %s | FileCheck -DRESOURCE=ConsumeStructuredBuffer \ @@ -28,7 +28,7 @@ // // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump \ // RUN: -DRESOURCE=ConsumeStructuredBuffer %s | FileCheck -DRESOURCE=ConsumeStructuredBuffer \ -// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-NOSUBSCRIPT,CHECK-CONSUME %s +// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-NOSUBSCRIPT,CHECK-CONSUME,CHECK-COUNTER-HANDLE %s // // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -DEMPTY \ // RUN: -DRESOURCE=RasterizerOrderedStructuredBuffer %s | FileCheck -DRESOURCE=RasterizerOrderedStructuredBuffer \ @@ -36,7 +36,7 @@ // // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump \ // RUN: -DRESOURCE=RasterizerOrderedStructuredBuffer %s | FileCheck -DRESOURCE=RasterizerOrderedStructuredBuffer \ -// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-ROV,CHECK-SUBSCRIPT,CHECK-SUBSCRIPT-UAV,CHECK-LOAD %s +// RUN: -check-prefixes=CHECK,CHECK-UAV,CHECK-ROV,CHECK-SUBSCRIPT,CHECK-SUBSCRIPT-UAV,CHECK-LOAD,CHECK-COUNTER-HANDLE %s // This test tests two different AST generations for each structured buffer. // The "EMPTY" test mode verifies the AST generated by forward declaration @@ -113,6 +113,11 @@ RESOURCE Buffer; // CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this // CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle // CHECK-NEXT: DeclRefExpr {{.*}} 'const hlsl::[[RESOURCE]]' ParmVar {{.*}} 'other' 'const hlsl::[[RESOURCE]] &' +// CHECK-COUNTER-HANDLE-NEXT: BinaryOperator {{.*}} '=' +// CHECK-COUNTER-HANDLE-NEXT: MemberExpr {{.*}} lvalue .__counter_handle +// CHECK-COUNTER-HANDLE-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this +// CHECK-COUNTER-HANDLE-NEXT: MemberExpr {{.*}} lvalue .__counter_handle +// CHECK-COUNTER-HANDLE-NEXT: DeclRefExpr {{.*}} 'const hlsl::[[RESOURCE]]' ParmVar {{.*}} 'other' 'const hlsl::[[RESOURCE]] &' // CHECK-NEXT: AlwaysInlineAttr // operator= @@ -125,6 +130,11 @@ RESOURCE Buffer; // CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this // CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle // CHECK-NEXT: DeclRefExpr {{.*}} 'const hlsl::[[RESOURCE]]' ParmVar {{.*}} 'other' 'const hlsl::[[RESOURCE]] &' +// CHECK-COUNTER-HANDLE: BinaryOperator {{.*}} '=' +// CHECK-COUNTER-HANDLE: MemberExpr {{.*}} lvalue .__counter_handle +// CHECK-COUNTER-HANDLE: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this +// CHECK-COUNTER-HANDLE: MemberExpr {{.*}} lvalue .__counter_handle +// CHECK-COUNTER-HANDLE: DeclRefExpr {{.*}} 'const hlsl::[[RESOURCE]]' ParmVar {{.*}} 'other' 'const hlsl::[[RESOURCE]] &' // CHECK-NEXT: ReturnStmt // CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this // CHECK-NEXT: AlwaysInlineAttr @@ -334,3 +344,8 @@ RESOURCE Buffer; // CHECK-ROV-SAME{LITERAL}: [[hlsl::is_rov]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(float)]] +// CHECK-COUNTER-HANDLE: FieldDecl {{.*}} implicit referenced __counter_handle '__hlsl_resource_t +// CHECK-COUNTER-HANDLE-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] +// CHECK-COUNTER-HANDLE-SAME{LITERAL}: [[hlsl::raw_buffer]] +// CHECK-COUNTER-HANDLE-SAME{LITERAL}: [[hlsl::is_counter]] +// CHECK-COUNTER-HANDLE-SAME{LITERAL}: [[hlsl::contained_type(float)]] diff --git a/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl b/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl index 472b9a8dae2ae..1ab67644c30ad 100644 --- a/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl +++ b/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl @@ -1,23 +1,23 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-compute -finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-compute -finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s -check-prefixes=SPV -// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", i16, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.0" = type { target("dx.RawBuffer", i16, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.1" = type { target("dx.RawBuffer", i32, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.2" = type { target("dx.RawBuffer", i32, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.3" = type { target("dx.RawBuffer", i64, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.4" = type { target("dx.RawBuffer", i64, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.5" = type { target("dx.RawBuffer", half, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.6" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.7" = type { target("dx.RawBuffer", double, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.8" = type { target("dx.RawBuffer", <4 x i16>, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.9" = type { target("dx.RawBuffer", <3 x i32>, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.10" = type { target("dx.RawBuffer", <2 x half>, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.11" = type { target("dx.RawBuffer", <3 x float>, 1, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer.12" = type { target("dx.RawBuffer", i32, 1, 0) } -// SPV: %"class.hlsl::RWStructuredBuffer.12" = type { target("spirv.VulkanBuffer", [0 x i32], 12, 1) -// CHECK: %"class.hlsl::RWStructuredBuffer.13" = type { target("dx.RawBuffer", <4 x i32>, 1, 0) } -// SPV: %"class.hlsl::RWStructuredBuffer.13" = type { target("spirv.VulkanBuffer", [0 x <4 x i32>], 12, 1) +// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", i16, 1, 0), target("dx.RawBuffer", i16, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.0" = type { target("dx.RawBuffer", i16, 1, 0), target("dx.RawBuffer", i16, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.1" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.2" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.3" = type { target("dx.RawBuffer", i64, 1, 0), target("dx.RawBuffer", i64, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.4" = type { target("dx.RawBuffer", i64, 1, 0), target("dx.RawBuffer", i64, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.5" = type { target("dx.RawBuffer", half, 1, 0), target("dx.RawBuffer", half, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.6" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.7" = type { target("dx.RawBuffer", double, 1, 0), target("dx.RawBuffer", double, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.8" = type { target("dx.RawBuffer", <4 x i16>, 1, 0), target("dx.RawBuffer", <4 x i16>, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.9" = type { target("dx.RawBuffer", <3 x i32>, 1, 0), target("dx.RawBuffer", <3 x i32>, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.10" = type { target("dx.RawBuffer", <2 x half>, 1, 0), target("dx.RawBuffer", <2 x half>, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.11" = type { target("dx.RawBuffer", <3 x float>, 1, 0), target("dx.RawBuffer", <3 x float>, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer.12" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.12" = type { target("spirv.VulkanBuffer", [0 x i32], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } +// CHECK: %"class.hlsl::RWStructuredBuffer.13" = type { target("dx.RawBuffer", <4 x i32>, 1, 0), target("dx.RawBuffer", <4 x i32>, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.13" = type { target("spirv.VulkanBuffer", [0 x <4 x i32>], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } RWStructuredBuffer BufI16; RWStructuredBuffer BufU16; diff --git a/clang/test/CodeGenHLSL/resources/RasterizerOrderedStructuredBuffer-elementtype.hlsl b/clang/test/CodeGenHLSL/resources/RasterizerOrderedStructuredBuffer-elementtype.hlsl index 6c5a705d5cf2e..c97ad4237000f 100644 --- a/clang/test/CodeGenHLSL/resources/RasterizerOrderedStructuredBuffer-elementtype.hlsl +++ b/clang/test/CodeGenHLSL/resources/RasterizerOrderedStructuredBuffer-elementtype.hlsl @@ -5,19 +5,19 @@ struct MyStruct { int2 b; }; -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer" = type { target("dx.RawBuffer", i16, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.0" = type { target("dx.RawBuffer", i16, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.1" = type { target("dx.RawBuffer", i32, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.2" = type { target("dx.RawBuffer", i32, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.3" = type { target("dx.RawBuffer", i64, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.4" = type { target("dx.RawBuffer", i64, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.5" = type { target("dx.RawBuffer", half, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.6" = type { target("dx.RawBuffer", float, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.7" = type { target("dx.RawBuffer", double, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.8" = type { target("dx.RawBuffer", <4 x i16>, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.9" = type { target("dx.RawBuffer", <3 x i32>, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.10" = type { target("dx.RawBuffer", <2 x half>, 1, 1) } -// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.11" = type { target("dx.RawBuffer", <3 x float>, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer" = type { target("dx.RawBuffer", i16, 1, 1), target("dx.RawBuffer", i16, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.0" = type { target("dx.RawBuffer", i16, 1, 1), target("dx.RawBuffer", i16, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.1" = type { target("dx.RawBuffer", i32, 1, 1), target("dx.RawBuffer", i32, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.2" = type { target("dx.RawBuffer", i32, 1, 1), target("dx.RawBuffer", i32, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.3" = type { target("dx.RawBuffer", i64, 1, 1), target("dx.RawBuffer", i64, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.4" = type { target("dx.RawBuffer", i64, 1, 1), target("dx.RawBuffer", i64, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.5" = type { target("dx.RawBuffer", half, 1, 1), target("dx.RawBuffer", half, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.6" = type { target("dx.RawBuffer", float, 1, 1), target("dx.RawBuffer", float, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.7" = type { target("dx.RawBuffer", double, 1, 1), target("dx.RawBuffer", double, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.8" = type { target("dx.RawBuffer", <4 x i16>, 1, 1), target("dx.RawBuffer", <4 x i16>, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.9" = type { target("dx.RawBuffer", <3 x i32>, 1, 1), target("dx.RawBuffer", <3 x i32>, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.10" = type { target("dx.RawBuffer", <2 x half>, 1, 1), target("dx.RawBuffer", <2 x half>, 1, 1) } +// DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer.11" = type { target("dx.RawBuffer", <3 x float>, 1, 1), target("dx.RawBuffer", <3 x float>, 1, 1) } // DXIL: %struct.MyStruct = type <{ <4 x float>, <2 x i32> }> RasterizerOrderedStructuredBuffer BufI16; diff --git a/clang/test/CodeGenHLSL/resources/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/resources/StructuredBuffers-constructors.hlsl index 4f005eab5c71a..89a66b047a3bd 100644 --- a/clang/test/CodeGenHLSL/resources/StructuredBuffers-constructors.hlsl +++ b/clang/test/CodeGenHLSL/resources/StructuredBuffers-constructors.hlsl @@ -21,8 +21,8 @@ export void foo() { } // CHECK-DXIL: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 0, 0) } -// CHECK-DXIL: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK-DXIL: %"class.hlsl::AppendStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } +// CHECK-DXIL: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } +// CHECK-DXIL: %"class.hlsl::AppendStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } // CHECK: @Buf1 = internal global %"class.hlsl::StructuredBuffer" poison, align 4 // CHECK: @[[Buf1Str:.*]] = private unnamed_addr constant [5 x i8] c"Buf1\00", align 1 diff --git a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl index 93aa218f63ecf..43ddd2e768ea0 100644 --- a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl +++ b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl @@ -10,9 +10,9 @@ AppendStructuredBuffer ASB : register(u2); ConsumeStructuredBuffer CSB : register(u3); // CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 0, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::AppendStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::ConsumeStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } +// CHECK: %"class.hlsl::AppendStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } +// CHECK: %"class.hlsl::ConsumeStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } export int TestIncrementCounter() { return RWSB1.IncrementCounter(); diff --git a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl index b513963d72474..9e08a6d0d7ae0 100644 --- a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl +++ b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl @@ -6,7 +6,7 @@ RWStructuredBuffer RWSB1, RWSB2; RasterizerOrderedStructuredBuffer ROSB1, ROSB2; -// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } export void TestIncrementCounter() { // CHECK: define void @_Z20TestIncrementCounterv() diff --git a/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl index 4ffa7cfc84e17..1d85048db87a8 100644 --- a/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl +++ b/clang/test/CodeGenHLSL/resources/resource-bindings.hlsl @@ -4,7 +4,7 @@ // CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", <4 x float>, 1, 0, 0) } // CHECK: %"class.hlsl::RWBuffer.0" = type { target("dx.TypedBuffer", float, 1, 0, 0) } // CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", i32, 0, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", %struct.S, 1, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", %struct.S, 1, 0), target("dx.RawBuffer", %struct.S, 1, 0) } // CHECK: %"class.hlsl::RWBuffer.1" = type { target("dx.TypedBuffer", double, 1, 0, 0) } // CHECK: @_ZL4U0S0 = internal global %"class.hlsl::RWBuffer" poison, align 4 From 912c232faa22f6b4a0e208f0d41ee9950e26e9c8 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 2 Oct 2025 09:59:57 -0400 Subject: [PATCH 2/4] Fixes from code review. --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 53 ++++++++----------- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 19 ++++--- clang/lib/Sema/HLSLExternalSemaSource.cpp | 26 ++++----- .../RWStructuredBuffer-elementtype.hlsl | 13 +++++ 4 files changed, 61 insertions(+), 50 deletions(-) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 625206972df1d..63f61e6264f68 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -799,10 +799,12 @@ BuiltinTypeDeclBuilder::addMemberVariable(StringRef Name, QualType Type, return *this; } -BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMembers( - ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV, + bool RawBuffer, bool HasCounter, + AccessSpecifier Access) { addHandleMember(RC, IsROV, RawBuffer, Access); - if (resourceHasCounter(Record)) { + if (HasCounter) { addCounterHandleMember(RC, IsROV, RawBuffer, Access); } return *this; @@ -810,29 +812,19 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMembers( BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember( ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { - assert(!Record->isCompleteDefinition() && "record is already complete"); - - ASTContext &Ctx = SemaRef.getASTContext(); - TypeSourceInfo *ElementTypeInfo = - Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation()); - - // add handle member with resource type attributes - QualType AttributedResTy = QualType(); - SmallVector Attrs = { - HLSLResourceClassAttr::CreateImplicit(Ctx, RC), - IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr, - RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr, - ElementTypeInfo - ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) - : nullptr}; - if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs, - AttributedResTy)) - addMemberVariable("__handle", AttributedResTy, {}, Access); - return *this; + return addResourceMember("__handle", RC, IsROV, RawBuffer, + /*IsCounter=*/false, Access); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { + return addResourceMember("__counter_handle", RC, IsROV, RawBuffer, + /*IsCounter=*/true, Access); +} + +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember( + StringRef MemberName, ResourceClass RC, bool IsROV, bool RawBuffer, + bool IsCounter, AccessSpecifier Access) { assert(!Record->isCompleteDefinition() && "record is already complete"); ASTContext &Ctx = SemaRef.getASTContext(); @@ -847,11 +839,13 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr, ElementTypeInfo ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) - : nullptr, - HLSLIsCounterAttr::CreateImplicit(Ctx)}; + : nullptr}; + if (IsCounter) + Attrs.push_back(HLSLIsCounterAttr::CreateImplicit(Ctx)); + if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs, AttributedResTy)) - addMemberVariable("__counter_handle", AttributedResTy, {}, Access); + addMemberVariable(MemberName, AttributedResTy, {}, Access); return *this; } @@ -960,10 +954,9 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() { .accessHandleFieldOnResource(PH::_0) .assign(PH::Handle, PH::LastStmt); - if (getResourceCounterHandleField()) { + if (getResourceCounterHandleField()) MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle, PH::LastStmt); - } return MMB.finalize(); } @@ -984,10 +977,9 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() { .accessHandleFieldOnResource(PH::_0) .assign(PH::Handle, PH::LastStmt); - if (getResourceCounterHandleField()) { + if (getResourceCounterHandleField()) MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle, PH::LastStmt); - } return MMB.returnThis().finalize(); } @@ -1026,7 +1018,8 @@ FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() const { FieldDecl *BuiltinTypeDeclBuilder::getResourceCounterHandleField() const { auto I = Fields.find("__counter_handle"); - if (I == Fields.end()) + if (I == Fields.end() || + !I->second->getType()->isHLSLAttributedResourceType()) return nullptr; return I->second; } diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 8ae73af4d9154..a981602a50461 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -72,14 +72,9 @@ class BuiltinTypeDeclBuilder { AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder & - addHandleMembers(ResourceClass RC, bool IsROV, bool RawBuffer, + addBufferHandles(ResourceClass RC, bool IsROV, bool RawBuffer, + bool HasCounter, AccessSpecifier Access = AccessSpecifier::AS_private); - BuiltinTypeDeclBuilder & - addHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, - AccessSpecifier Access = AccessSpecifier::AS_private); - BuiltinTypeDeclBuilder & - addCounterHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, - AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder &addArraySubscriptOperators(); // Builtin types constructors @@ -101,6 +96,16 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder &addConsumeMethod(); private: + BuiltinTypeDeclBuilder &addResourceMember(StringRef MemberName, + ResourceClass RC, bool IsROV, + bool RawBuffer, bool IsCounter, + AccessSpecifier Access); + BuiltinTypeDeclBuilder & + addHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, + AccessSpecifier Access = AccessSpecifier::AS_private); + BuiltinTypeDeclBuilder & + addCounterHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, + AccessSpecifier Access = AccessSpecifier::AS_private); FieldDecl *getResourceHandleField() const; FieldDecl *getResourceCounterHandleField() const; QualType getFirstTemplateTypeParam(); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 9f3664f114a6d..cc43e9474ea79 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -230,9 +230,9 @@ void HLSLExternalSemaSource::defineTrivialHLSLTypes() { /// Set up common members and attributes for buffer types static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, ResourceClass RC, bool IsROV, - bool RawBuffer) { + bool RawBuffer, bool HasCounter) { return BuiltinTypeDeclBuilder(S, Decl) - .addHandleMembers(RC, IsROV, RawBuffer) + .addBufferHandles(RC, IsROV, RawBuffer, HasCounter) .addDefaultHandleConstructor() .addCopyConstructor() .addCopyAssignmentOperator() @@ -377,7 +377,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false, - /*RawBuffer=*/false) + /*RawBuffer=*/false, /*HasCounter=*/false) .addArraySubscriptOperators() .addLoadMethods() .completeDefinition(); @@ -389,7 +389,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false, - /*RawBuffer=*/false) + /*RawBuffer=*/false, /*HasCounter=*/false) .addArraySubscriptOperators() .addLoadMethods() .completeDefinition(); @@ -401,7 +401,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/true, - /*RawBuffer=*/false) + /*RawBuffer=*/false, /*HasCounter=*/false) .addArraySubscriptOperators() .addLoadMethods() .completeDefinition(); @@ -412,7 +412,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/false) .addArraySubscriptOperators() .addLoadMethods() .completeDefinition(); @@ -423,7 +423,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/true) .addArraySubscriptOperators() .addLoadMethods() .addIncrementCounterMethod() @@ -437,7 +437,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/true) .addAppendMethod() .completeDefinition(); }); @@ -448,7 +448,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/true) .addConsumeMethod() .completeDefinition(); }); @@ -459,7 +459,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/true, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/true) .addArraySubscriptOperators() .addLoadMethods() .addIncrementCounterMethod() @@ -471,14 +471,14 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, /*IsROV=*/false, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/false) .completeDefinition(); }); Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWByteAddressBuffer") .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/false, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/false) .completeDefinition(); }); Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, @@ -486,7 +486,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .finalizeForwardDeclaration(); onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/true, - /*RawBuffer=*/true) + /*RawBuffer=*/true, /*HasCounter=*/false) .completeDefinition(); }); } diff --git a/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl b/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl index 1ab67644c30ad..9f0a5b79ee6e4 100644 --- a/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl +++ b/clang/test/CodeGenHLSL/resources/RWStructuredBuffer-elementtype.hlsl @@ -2,18 +2,31 @@ // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-compute -finclude-default-header -fnative-half-type -emit-llvm -o - %s | FileCheck %s -check-prefixes=SPV // CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", i16, 1, 0), target("dx.RawBuffer", i16, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer" = type { target("spirv.VulkanBuffer", [0 x i16], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.0" = type { target("dx.RawBuffer", i16, 1, 0), target("dx.RawBuffer", i16, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.0" = type { target("spirv.VulkanBuffer", [0 x i16], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.1" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.1" = type { target("spirv.VulkanBuffer", [0 x i32], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.2" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.2" = type { target("spirv.VulkanBuffer", [0 x i32], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.3" = type { target("dx.RawBuffer", i64, 1, 0), target("dx.RawBuffer", i64, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.3" = type { target("spirv.VulkanBuffer", [0 x i64], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.4" = type { target("dx.RawBuffer", i64, 1, 0), target("dx.RawBuffer", i64, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.4" = type { target("spirv.VulkanBuffer", [0 x i64], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.5" = type { target("dx.RawBuffer", half, 1, 0), target("dx.RawBuffer", half, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.5" = type { target("spirv.VulkanBuffer", [0 x half], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.6" = type { target("dx.RawBuffer", float, 1, 0), target("dx.RawBuffer", float, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.6" = type { target("spirv.VulkanBuffer", [0 x float], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.7" = type { target("dx.RawBuffer", double, 1, 0), target("dx.RawBuffer", double, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.7" = type { target("spirv.VulkanBuffer", [0 x double], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.8" = type { target("dx.RawBuffer", <4 x i16>, 1, 0), target("dx.RawBuffer", <4 x i16>, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.8" = type { target("spirv.VulkanBuffer", [0 x <4 x i16>], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.9" = type { target("dx.RawBuffer", <3 x i32>, 1, 0), target("dx.RawBuffer", <3 x i32>, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.9" = type { target("spirv.VulkanBuffer", [0 x <3 x i32>], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.10" = type { target("dx.RawBuffer", <2 x half>, 1, 0), target("dx.RawBuffer", <2 x half>, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.10" = type { target("spirv.VulkanBuffer", [0 x <2 x half>], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.11" = type { target("dx.RawBuffer", <3 x float>, 1, 0), target("dx.RawBuffer", <3 x float>, 1, 0) } +// SPV: %"class.hlsl::RWStructuredBuffer.11" = type { target("spirv.VulkanBuffer", [0 x <3 x float>], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.12" = type { target("dx.RawBuffer", i32, 1, 0), target("dx.RawBuffer", i32, 1, 0) } // SPV: %"class.hlsl::RWStructuredBuffer.12" = type { target("spirv.VulkanBuffer", [0 x i32], 12, 1), target("spirv.VulkanBuffer", i32, 12, 1) } // CHECK: %"class.hlsl::RWStructuredBuffer.13" = type { target("dx.RawBuffer", <4 x i32>, 1, 0), target("dx.RawBuffer", <4 x i32>, 1, 0) } From ff554b4b410c91db1433e77eb1a81c83a206edab Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 2 Oct 2025 11:31:38 -0400 Subject: [PATCH 3/4] Remove unused function. --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 63f61e6264f68..674bd52b8dbf4 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -57,15 +57,6 @@ CXXConstructorDecl *lookupCopyConstructor(QualType ResTy) { return CD; return nullptr; } - -/// Set up common members and attributes for buffer types -static bool resourceHasCounter(const CXXRecordDecl *Decl) { - StringRef Name = Decl->getName(); - return Name == "RWStructuredBuffer" || Name == "AppendStructuredBuffer" || - Name == "ConsumeStructuredBuffer" || - Name == "RasterizerOrderedStructuredBuffer"; -} - } // namespace // Builder for template arguments of builtin types. Used internally From 5440471d2df75d0b6550f9bb18d8d798ac8932fe Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 2 Oct 2025 13:12:00 -0400 Subject: [PATCH 4/4] Fixes from code review. --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 674bd52b8dbf4..3c20ccd799b2d 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -190,9 +190,6 @@ struct BuiltinTypeMethodBuilder { template BuiltinTypeMethodBuilder & accessCounterHandleFieldOnResource(T ResourceRecord); - template - BuiltinTypeMethodBuilder & - setCounterHandleFieldOnResource(ResourceT ResourceRecord, ValueT HandleValue); template BuiltinTypeMethodBuilder &returnValue(T ReturnValue); BuiltinTypeMethodBuilder &returnThis(); BuiltinTypeDeclBuilder &finalize(); @@ -629,28 +626,6 @@ BuiltinTypeMethodBuilder::accessCounterHandleFieldOnResource(T ResourceRecord) { return *this; } -template -BuiltinTypeMethodBuilder & -BuiltinTypeMethodBuilder::setCounterHandleFieldOnResource( - ResourceT ResourceRecord, ValueT HandleValue) { - ensureCompleteDecl(); - - Expr *ResourceExpr = convertPlaceholder(ResourceRecord); - Expr *HandleValueExpr = convertPlaceholder(HandleValue); - - ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); - FieldDecl *HandleField = DeclBuilder.getResourceCounterHandleField(); - MemberExpr *HandleMemberExpr = MemberExpr::CreateImplicit( - AST, ResourceExpr, false, HandleField, HandleField->getType(), VK_LValue, - OK_Ordinary); - Stmt *AssignStmt = BinaryOperator::Create( - DeclBuilder.SemaRef.getASTContext(), HandleMemberExpr, HandleValueExpr, - BO_Assign, HandleMemberExpr->getType(), ExprValueKind::VK_PRValue, - ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride()); - StmtsList.push_back(AssignStmt); - return *this; -} - template BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnValue(T ReturnValue) { ensureCompleteDecl(); @@ -795,9 +770,8 @@ BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV, bool RawBuffer, bool HasCounter, AccessSpecifier Access) { addHandleMember(RC, IsROV, RawBuffer, Access); - if (HasCounter) { + if (HasCounter) addCounterHandleMember(RC, IsROV, RawBuffer, Access); - } return *this; }