Author: Tim Corringham Date: 2026-06-12T08:54:58+01:00 New Revision: b086e198e241fdcde57a3f9961e7d8d3c3ca4a99
URL: https://github.com/llvm/llvm-project/commit/b086e198e241fdcde57a3f9961e7d8d3c3ca4a99 DIFF: https://github.com/llvm/llvm-project/commit/b086e198e241fdcde57a3f9961e7d8d3c3ca4a99.diff LOG: [HLSL] Add IsArray resource attribute (#201163) First part of changes to add support for the IsArray resource attribute, which will be used by the resource array types (Texture2DArray etc.). These types will be implemented in subsequent changes. This implements the attribute parsing and associated tests, and implements the first part of #194910. The Sema builtin argument checking and CodeGen will be included in a subsequent PR which will implement a resource array type which will allow those changes to be tested. --------- Co-authored-by: Tim Corringham <[email protected]> Added: clang/test/ParserHLSL/hlsl_is_array_attr.hlsl clang/test/ParserHLSL/hlsl_is_array_attr_error.hlsl Modified: clang/include/clang/AST/TypeBase.h clang/include/clang/AST/TypeProperties.td clang/include/clang/Basic/Attr.td clang/lib/AST/ItaniumMangle.cpp clang/lib/AST/TypePrinter.cpp clang/lib/CodeGen/CGHLSLBuiltins.cpp clang/lib/Sema/SemaHLSL.cpp clang/lib/Sema/SemaType.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index e3141a82b54d2..e3844d0cefa78 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -6842,12 +6842,16 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { LLVM_PREFERRED_TYPE(bool) uint8_t IsCounter : 1; + LLVM_PREFERRED_TYPE(bool) + uint8_t IsArray : 1; + Attributes(llvm::dxil::ResourceClass ResourceClass, llvm::dxil::ResourceDimension ResourceDimension, bool IsROV = false, bool RawBuffer = false, - bool IsCounter = false) + bool IsCounter = false, bool IsArray = false) : ResourceClass(ResourceClass), ResourceDimension(ResourceDimension), - IsROV(IsROV), RawBuffer(RawBuffer), IsCounter(IsCounter) {} + IsROV(IsROV), RawBuffer(RawBuffer), IsCounter(IsCounter), + IsArray(IsArray) {} Attributes(llvm::dxil::ResourceClass ResourceClass) : Attributes(ResourceClass, llvm::dxil::ResourceDimension::Unknown) {} @@ -6855,13 +6859,13 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, llvm::dxil::ResourceDimension::Unknown, false, false, - false) {} + false, false) {} friend bool operator==(const Attributes &LHS, const Attributes &RHS) { return std::tie(LHS.ResourceClass, LHS.ResourceDimension, LHS.IsROV, - LHS.RawBuffer, LHS.IsCounter) == + LHS.RawBuffer, LHS.IsCounter, LHS.IsArray) == std::tie(RHS.ResourceClass, RHS.ResourceDimension, RHS.IsROV, - RHS.RawBuffer, RHS.IsCounter); + RHS.RawBuffer, RHS.IsCounter, RHS.IsArray); } friend bool operator!=(const Attributes &LHS, const Attributes &RHS) { return !(LHS == RHS); @@ -6906,6 +6910,7 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { ID.AddBoolean(Attrs.IsROV); ID.AddBoolean(Attrs.RawBuffer); ID.AddBoolean(Attrs.IsCounter); + ID.AddBoolean(Attrs.IsArray); } static bool classof(const Type *T) { diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 0f3722b36774a..f16c10da430f9 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -681,11 +681,14 @@ let Class = HLSLAttributedResourceType in { def : Property<"containedTy", QualType> { let Read = [{ node->getContainedType() }]; } + def : Property<"isArray", Bool> { + let Read = [{ node->getAttrs().IsArray }]; + } def : Creator<[{ HLSLAttributedResourceType::Attributes attrs( static_cast<llvm::dxil::ResourceClass>(resClass), static_cast<llvm::dxil::ResourceDimension>(resDimension), isROV, - rawBuffer, isCounter); + rawBuffer, isCounter, isArray); return ctx.getHLSLAttributedResourceType(wrappedTy, containedTy, attrs); }]>; } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 7f7e9489782a7..a1e02e1478df1 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5178,6 +5178,12 @@ def HLSLShader : InheritableAttr { }]; } +def HLSLIsArray : TypeAttr { + let Spellings = [CXX11<"hlsl", "is_array">]; + let LangOpts = [HLSL]; + let Documentation = [InternalOnly]; +} + def HLSLROV : TypeAttr { let Spellings = [CXX11<"hlsl", "is_rov">]; let LangOpts = [HLSL]; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 2e74aeb558e3d..e5cdd6f31c507 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4677,6 +4677,8 @@ void CXXNameMangler::mangleType(const HLSLAttributedResourceType *T) { Str += "_Raw"; if (Attrs.IsCounter) Str += "_Counter"; + if (Attrs.IsArray) + Str += "_Array"; if (T->hasContainedType()) Str += "_CT"; mangleVendorQualifier(Str); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index d85ed244f643a..53b869e019074 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1990,6 +1990,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::HLSLContainedType: case attr::HLSLIsCounter: case attr::HLSLResourceDimension: + case attr::HLSLIsArray: llvm_unreachable("HLSL resource type attributes handled separately"); case attr::OpenCLPrivateAddressSpace: @@ -2169,6 +2170,8 @@ void TypePrinter::printHLSLAttributedResourceAfter( OS << " [[hlsl::raw_buffer]]"; if (Attrs.IsCounter) OS << " [[hlsl::is_counter]]"; + if (Attrs.IsArray) + OS << " [[hlsl::is_array]]"; QualType ContainedTy = T->getContainedType(); if (!ContainedTy.isNull()) { diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 920816bd3d3f8..95b736ea60bc2 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -13,6 +13,7 @@ #include "CGBuiltin.h" #include "CGHLSLRuntime.h" #include "CodeGenFunction.h" +#include "clang/AST/HLSLResource.h" #include "clang/AST/MatrixUtils.h" #include "llvm/IR/MatrixBuilder.h" @@ -457,11 +458,32 @@ static std::string getSpecConstantFunctionName(clang::QualType SpecConstantType, return Name; } -static llvm::Type *getOffsetType(CodeGenModule &CGM, llvm::Type *CoordTy) { +static const HLSLAttributedResourceType * +getHandleAttributedType(QualType HandleQT) { + if (const auto *RT = HandleQT->getAs<HLSLAttributedResourceType>()) + return RT; + // If the expr is a texture/sampler record (or similar), peel to __handle. + if (const HLSLAttributedResourceType *RT = + HLSLAttributedResourceType::findHandleTypeOnResource( + HandleQT.getTypePtr())) + return RT; + llvm_unreachable("attributed handle type not found"); +} + +static const HLSLAttributedResourceType * +getRequiredHandleType(const CallExpr *E, unsigned ArgNo) { + return getHandleAttributedType(E->getArg(ArgNo)->getType()); +} + +static llvm::Type *getOffsetType(CodeGenModule &CGM, + const HLSLAttributedResourceType *RT) { + const auto &Attrs = RT->getAttrs(); + unsigned OffsetSize = + clang::hlsl::getResourceDimensions(Attrs.ResourceDimension); llvm::Type *Int32Ty = CGM.Int32Ty; - if (auto *VT = dyn_cast<llvm::FixedVectorType>(CoordTy)) - return llvm::FixedVectorType::get(Int32Ty, VT->getNumElements()); - return Int32Ty; + if (OffsetSize == 1) + return Int32Ty; + return llvm::FixedVectorType::get(Int32Ty, OffsetSize); } static Value *emitHlslOffset(CodeGenFunction &CGF, const CallExpr *E, @@ -607,13 +629,13 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Value *HandleOp = EmitScalarExpr(E->getArg(0)); Value *SamplerOp = EmitScalarExpr(E->getArg(1)); Value *CoordOp = EmitScalarExpr(E->getArg(2)); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 4> Args; Args.push_back(HandleOp); Args.push_back(SamplerOp); Args.push_back(CoordOp); - Args.push_back( - emitHlslOffset(*this, E, 3, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 3, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); if (E->getNumArgs() <= 4) { @@ -632,14 +654,14 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Value *BiasOp = EmitScalarExpr(E->getArg(3)); if (BiasOp->getType() != Builder.getFloatTy()) BiasOp = Builder.CreateFPCast(BiasOp, Builder.getFloatTy()); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 6> Args; // Max 6 arguments for SampleBias Args.push_back(HandleOp); Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(BiasOp); - Args.push_back( - emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 4, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); if (E->getNumArgs() <= 5) @@ -656,6 +678,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Value *CoordOp = EmitScalarExpr(E->getArg(2)); Value *DDXOp = EmitScalarExpr(E->getArg(3)); Value *DDYOp = EmitScalarExpr(E->getArg(4)); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 7> Args; Args.push_back(HandleOp); @@ -663,8 +686,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(CoordOp); Args.push_back(DDXOp); Args.push_back(DDYOp); - Args.push_back( - emitHlslOffset(*this, E, 5, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 5, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); @@ -684,14 +706,14 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Value *LODOp = EmitScalarExpr(E->getArg(3)); if (LODOp->getType() != Builder.getFloatTy()) LODOp = Builder.CreateFPCast(LODOp, Builder.getFloatTy()); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 5> Args; // Max 5 arguments for SampleLevel Args.push_back(HandleOp); Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(LODOp); - Args.push_back( - emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 4, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( @@ -714,13 +736,13 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Builder.CreateShuffleVector(CoordLODOp, Mask, "hlsl.load.coord"); Value *LODOp = Builder.CreateExtractElement(CoordLODOp, NumElts - 1, "hlsl.load.lod"); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 4> Args; Args.push_back(HandleOp); Args.push_back(CoordOp); Args.push_back(LODOp); - Args.push_back( - emitHlslOffset(*this, E, 2, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 2, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( @@ -733,14 +755,14 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Value *CmpOp = EmitScalarExpr(E->getArg(3)); if (CmpOp->getType() != Builder.getFloatTy()) CmpOp = Builder.CreateFPCast(CmpOp, Builder.getFloatTy()); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 6> Args; // Max 6 arguments for SampleCmp Args.push_back(HandleOp); Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(CmpOp); - Args.push_back( - emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 4, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); if (E->getNumArgs() <= 5) { @@ -759,15 +781,14 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Value *CmpOp = EmitScalarExpr(E->getArg(3)); if (CmpOp->getType() != Builder.getFloatTy()) CmpOp = Builder.CreateFPCast(CmpOp, Builder.getFloatTy()); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 5> Args; Args.push_back(HandleOp); Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(CmpOp); - - Args.push_back( - emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 4, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( @@ -801,14 +822,14 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, if (ComponentOp->getType() != Builder.getInt32Ty()) ComponentOp = Builder.CreateIntCast(ComponentOp, Builder.getInt32Ty(), /*isSigned=*/false); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); SmallVector<Value *, 5> Args; Args.push_back(HandleOp); Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(ComponentOp); - Args.push_back( - emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); + Args.push_back(emitHlslOffset(*this, E, 4, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( @@ -836,8 +857,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(ComponentOp); } - Args.push_back( - emitHlslOffset(*this, E, 5, getOffsetType(CGM, CoordOp->getType()))); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); + Args.push_back(emitHlslOffset(*this, E, 5, getOffsetType(CGM, RT))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 30aef43f7150f..0602402fb99f8 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2152,6 +2152,13 @@ bool clang::CreateHLSLAttributedResourceType( } ResAttrs.RawBuffer = true; break; + case attr::HLSLIsArray: + if (ResAttrs.IsArray) { + S.Diag(A->getLocation(), diag::warn_duplicate_attribute_exact) << A; + return false; + } + ResAttrs.IsArray = true; + break; case attr::HLSLIsCounter: if (ResAttrs.IsCounter) { S.Diag(A->getLocation(), diag::warn_duplicate_attribute_exact) << A; @@ -2273,6 +2280,10 @@ bool SemaHLSL::handleResourceTypeAttr(QualType T, const ParsedAttr &AL) { A = HLSLIsCounterAttr::Create(getASTContext(), ACI); break; + case ParsedAttr::AT_HLSLIsArray: + A = HLSLIsArrayAttr::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/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index ea4a6fb208dc3..d2bb312feadc1 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9356,6 +9356,7 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, case ParsedAttr::AT_HLSLResourceDimension: case ParsedAttr::AT_HLSLROV: case ParsedAttr::AT_HLSLRawBuffer: + case ParsedAttr::AT_HLSLIsArray: case ParsedAttr::AT_HLSLContainedType: { // Only collect HLSL resource type attributes that are in // decl-specifier-seq; do not collect attributes on declarations or those diff --git a/clang/test/ParserHLSL/hlsl_is_array_attr.hlsl b/clang/test/ParserHLSL/hlsl_is_array_attr.hlsl new file mode 100644 index 0000000000000..618d5d9a86c33 --- /dev/null +++ b/clang/test/ParserHLSL/hlsl_is_array_attr.hlsl @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s | FileCheck %s + +// CHECK: CXXRecordDecl 0x{{[0-9a-f]+}} {{.*}} struct MyBuffer definition +// CHECK: FieldDecl 0x{{[0-9a-f]+}} <line:[[# @LINE + 4]]:3, col:70> col:70 h '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] +// CHECK-SAME{LITERAL}: [[hlsl::is_array]] +struct MyBuffer { + __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::is_array]] h; +}; + +// CHECK: VarDecl 0x{{[0-9a-f]+}} <line:[[# @LINE + 3]]:1, col:68> col:68 res '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::is_array]] +__hlsl_resource_t [[hlsl::is_array]] [[hlsl::resource_class(SRV)]] res; + +// CHECK: FunctionDecl 0x{{[0-9a-f]+}} <line:[[# @LINE + 4]]:1, line:[[# @LINE + 6]]:1> line:[[# @LINE + 4]]:6 f 'void () +// CHECK: VarDecl 0x{{[0-9a-f]+}} <col:3, col:74> col:74 r '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(Sampler)]] +// CHECK-SAME{LITERAL}: [[hlsl::is_array]] +void f() { + __hlsl_resource_t [[hlsl::resource_class(Sampler)]] [[hlsl::is_array]] r; +} diff --git a/clang/test/ParserHLSL/hlsl_is_array_attr_error.hlsl b/clang/test/ParserHLSL/hlsl_is_array_attr_error.hlsl new file mode 100644 index 0000000000000..1d19822c974da --- /dev/null +++ b/clang/test/ParserHLSL/hlsl_is_array_attr_error.hlsl @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -o - %s -verify + +// expected-error@+1{{'hlsl::is_array' attribute cannot be applied to a declaration}} +[[hlsl::is_array]] __hlsl_resource_t res0; + +// expected-error@+1{{HLSL resource needs to have [[hlsl::resource_class()]] attribute}} +__hlsl_resource_t [[hlsl::is_array]] res1; + +// expected-error@+1{{'hlsl::is_array' attribute takes no arguments}} +__hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::is_array(3)]] res2; + +// expected-error@+1{{use of undeclared identifier 'gibberish'}} +__hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::is_array(gibberish)]] res3; + +// expected-warning@+1{{attribute 'hlsl::is_array' is already applied}} +__hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::is_array]] [[hlsl::is_array]] res4; + +// expected-error@+2{{attribute 'hlsl::resource_class' can be used only on HLSL intangible type '__hlsl_resource_t'}} +// expected-error@+1{{attribute 'hlsl::is_array' can be used only on HLSL intangible type '__hlsl_resource_t'}} +float [[hlsl::resource_class(UAV)]] [[hlsl::is_array]] res5; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
