https://github.com/Icohedron updated https://github.com/llvm/llvm-project/pull/211972
>From 676c00bafc02a2ef6eece6b6a596f1bfa77d0246 Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Thu, 23 Jul 2026 17:49:44 -0700 Subject: [PATCH 1/6] [HLSL] Add Texture2DMS Declares Texture2DMS<element_type, int sample_count = 0> with Load(location, sampleIndex[, offset]) and operator[], plus the resource.load.ms intrinsic for DirectX and SPIR-V. The sample count is a non-type template parameter, so the resource handle type now takes part in template instantiation, and lowers to the dx.MSTexture sample-count operand. A sample count of 0 means the count comes from the bound resource at runtime. --- clang/include/clang/AST/ASTContext.h | 3 +- clang/include/clang/AST/TypeBase.h | 42 +++---- clang/include/clang/AST/TypeProperties.td | 6 +- clang/include/clang/Basic/Builtins.td | 6 + clang/include/clang/Sema/SemaHLSL.h | 3 +- clang/lib/AST/ASTContext.cpp | 3 +- clang/lib/AST/ASTImporter.cpp | 3 +- clang/lib/AST/Type.cpp | 36 ++++++ clang/lib/CodeGen/CGHLSLBuiltins.cpp | 19 +++ clang/lib/CodeGen/CGHLSLRuntime.h | 1 + clang/lib/CodeGen/Targets/DirectX.cpp | 29 ++++- clang/lib/CodeGen/Targets/SPIR.cpp | 2 +- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 119 ++++++++++++++++-- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 12 +- clang/lib/Sema/HLSLExternalSemaSource.cpp | 31 +++++ clang/lib/Sema/SemaHLSL.cpp | 49 +++++++- clang/lib/Sema/TreeTransform.h | 16 ++- .../AST/HLSL/MultiSampledTextures-AST.hlsl | 97 ++++++++++++++ .../resources/MultiSampledTextures-Load.hlsl | 64 ++++++++++ ...pledTextures-default-explicit-binding.hlsl | 31 +++++ .../MultiSampledTextures-default.hlsl | 27 ++++ .../resources/MultiSampledTextures-pch.hlsl | 23 ++++ .../resources/Textures-Subscript.hlsl | 55 ++++---- .../Resources/MultiSampledTextures-Sema.hlsl | 55 ++++++++ llvm/include/llvm/IR/IntrinsicsDirectX.td | 6 + llvm/include/llvm/IR/IntrinsicsSPIRV.td | 6 + 26 files changed, 669 insertions(+), 75 deletions(-) create mode 100644 clang/test/AST/HLSL/MultiSampledTextures-AST.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/MultiSampledTextures-Load.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/MultiSampledTextures-default-explicit-binding.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/MultiSampledTextures-default.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl create mode 100644 clang/test/SemaHLSL/Resources/MultiSampledTextures-Sema.hlsl diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 7ed6509c3c16c..5a4a0840c7919 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -296,7 +296,8 @@ class ASTContext : public RefCountedBase<ASTContext> { DependentBitIntTypes; mutable llvm::FoldingSet<BTFTagAttributedType> BTFTagAttributedTypes; mutable llvm::FoldingSet<OverflowBehaviorType> OverflowBehaviorTypes; - llvm::FoldingSet<HLSLAttributedResourceType> HLSLAttributedResourceTypes; + mutable llvm::ContextualFoldingSet<HLSLAttributedResourceType, ASTContext &> + HLSLAttributedResourceTypes; llvm::FoldingSet<HLSLInlineSpirvType> HLSLInlineSpirvTypes; mutable llvm::FoldingSet<CountAttributedType> CountAttributedTypes; diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 00b6f1b20ad8e..370f129ce02ac 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -6850,14 +6850,18 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { LLVM_PREFERRED_TYPE(bool) uint8_t IsMultiSampled : 1; + /// The N in Texture2DMS<T, N>; null for every other resource. + Expr *SampleCountExpr; + Attributes(llvm::dxil::ResourceClass ResourceClass, llvm::dxil::ResourceDimension ResourceDimension, bool IsROV = false, bool RawBuffer = false, bool IsCounter = false, bool IsArray = false, - bool IsMultiSampled = false) + bool IsMultiSampled = false, Expr *SampleCountExpr = nullptr) : ResourceClass(ResourceClass), ResourceDimension(ResourceDimension), IsROV(IsROV), RawBuffer(RawBuffer), IsCounter(IsCounter), - IsArray(IsArray), IsMultiSampled(IsMultiSampled) {} + IsArray(IsArray), IsMultiSampled(IsMultiSampled), + SampleCountExpr(SampleCountExpr) {} Attributes(llvm::dxil::ResourceClass ResourceClass) : Attributes(ResourceClass, llvm::dxil::ResourceDimension::Unknown) {} @@ -6870,10 +6874,10 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { friend bool operator==(const Attributes &LHS, const Attributes &RHS) { return std::tie(LHS.ResourceClass, LHS.ResourceDimension, LHS.IsROV, LHS.RawBuffer, LHS.IsCounter, LHS.IsArray, - LHS.IsMultiSampled) == + LHS.IsMultiSampled, LHS.SampleCountExpr) == std::tie(RHS.ResourceClass, RHS.ResourceDimension, RHS.IsROV, RHS.RawBuffer, RHS.IsCounter, RHS.IsArray, - RHS.IsMultiSampled); + RHS.IsMultiSampled, RHS.SampleCountExpr); } friend bool operator!=(const Attributes &LHS, const Attributes &RHS) { return !(LHS == RHS); @@ -6888,16 +6892,17 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { const Attributes Attrs; HLSLAttributedResourceType(QualType Wrapped, QualType Contained, - const Attributes &Attrs) - : Type(HLSLAttributedResource, QualType(), - Contained.isNull() ? TypeDependence::None - : Contained->getDependence()), - WrappedType(Wrapped), ContainedType(Contained), Attrs(Attrs) {} + const Attributes &Attrs); + + /// WrappedType is always __hlsl_resource_t, so it never contributes. + static TypeDependence computeDependence(QualType Contained, + const Attributes &Attrs); public: QualType getWrappedType() const { return WrappedType; } QualType getContainedType() const { return ContainedType; } bool hasContainedType() const { return !ContainedType.isNull(); } + Expr *getSampleCountExpr() const { return Attrs.SampleCountExpr; } const Attributes &getAttrs() const { return Attrs; } bool isRaw() const { return Attrs.RawBuffer; } bool isStructured() const { return !ContainedType->isChar8Type(); } @@ -6905,22 +6910,13 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } - void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, WrappedType, ContainedType, Attrs); + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx) { + Profile(ID, Ctx, WrappedType, ContainedType, Attrs); } - static void Profile(llvm::FoldingSetNodeID &ID, QualType Wrapped, - QualType Contained, const Attributes &Attrs) { - ID.AddPointer(Wrapped.getAsOpaquePtr()); - ID.AddPointer(Contained.getAsOpaquePtr()); - ID.AddInteger(static_cast<uint32_t>(Attrs.ResourceClass)); - ID.AddInteger(static_cast<uint32_t>(Attrs.ResourceDimension)); - ID.AddBoolean(Attrs.IsROV); - ID.AddBoolean(Attrs.RawBuffer); - ID.AddBoolean(Attrs.IsCounter); - ID.AddBoolean(Attrs.IsArray); - ID.AddBoolean(Attrs.IsMultiSampled); - } + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx, + QualType Wrapped, QualType Contained, + const Attributes &Attrs); static bool classof(const Type *T) { return T->getTypeClass() == HLSLAttributedResource; diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index f202b889286bf..97eacae1a80b7 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -690,11 +690,15 @@ let Class = HLSLAttributedResourceType in { def : Property<"isMultiSampled", Bool> { let Read = [{ node->getAttrs().IsMultiSampled }]; } + def : Property<"sampleCountExpr", Optional<ExprRef>> { + let Read = [{ makeOptionalFromPointer(node->getSampleCountExpr()) }]; + } def : Creator<[{ HLSLAttributedResourceType::Attributes attrs( static_cast<llvm::dxil::ResourceClass>(resClass), static_cast<llvm::dxil::ResourceDimension>(resDimension), isROV, - rawBuffer, isCounter, isArray, isMultiSampled); + rawBuffer, isCounter, isArray, isMultiSampled, + makePointerFromOptional(sampleCountExpr)); return ctx.getHLSLAttributedResourceType(wrappedTy, containedTy, attrs); }]>; } diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 344a712ddc585..3239938aff9b3 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5335,6 +5335,12 @@ def HLSLResourceLoadLevel : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLResourceLoadMS : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_resource_load_ms"]; + let Attributes = [NoThrow]; + let Prototype = "void(...)"; +} + def HLSLResourceSample : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_resource_sample"]; let Attributes = [NoThrow]; diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 8928524e49783..fd9df822cd632 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -60,7 +60,8 @@ using llvm::dxil::ResourceClass; // longer need to create builtin buffer types in HLSLExternalSemaSource. bool CreateHLSLAttributedResourceType( Sema &S, QualType Wrapped, ArrayRef<const Attr *> AttrList, - QualType &ResType, HLSLAttributedResourceLocInfo *LocInfo = nullptr); + QualType &ResType, HLSLAttributedResourceLocInfo *LocInfo = nullptr, + Expr *SampleCountExpr = nullptr); enum class BindingType : uint8_t { NotAssigned, Explicit, Implicit }; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2228811546c0f..a5b775a60f401 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -938,6 +938,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()), DependentPackIndexingTypes(this_()), TemplateSpecializationTypes(this_()), AttributedTypes(this_()), DependentBitIntTypes(this_()), + HLSLAttributedResourceTypes(this_()), SubstTemplateTemplateParmPacks(this_()), DeducedTemplates(this_()), ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), @@ -5874,7 +5875,7 @@ QualType ASTContext::getHLSLAttributedResourceType( const HLSLAttributedResourceType::Attributes &Attrs) { llvm::FoldingSetNodeID ID; - HLSLAttributedResourceType::Profile(ID, Wrapped, Contained, Attrs); + HLSLAttributedResourceType::Profile(ID, *this, Wrapped, Contained, Attrs); void *InsertPos = nullptr; HLSLAttributedResourceType *Ty = diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index db7d223d56af3..8c0c314df97f0 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2036,9 +2036,10 @@ ExpectedType clang::ASTNodeImporter::VisitOverflowBehaviorType( ExpectedType clang::ASTNodeImporter::VisitHLSLAttributedResourceType( const clang::HLSLAttributedResourceType *T) { Error Err = Error::success(); - const HLSLAttributedResourceType::Attributes &ToAttrs = T->getAttrs(); + HLSLAttributedResourceType::Attributes ToAttrs = T->getAttrs(); QualType ToWrappedType = importChecked(Err, T->getWrappedType()); QualType ToContainedType = importChecked(Err, T->getContainedType()); + ToAttrs.SampleCountExpr = importChecked(Err, T->getSampleCountExpr()); if (Err) return std::move(Err); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 42d148715bc40..db3c94e13b7bc 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5952,6 +5952,42 @@ std::string FunctionEffectWithCondition::description() const { return Result; } +TypeDependence +HLSLAttributedResourceType::computeDependence(QualType Contained, + const Attributes &Attrs) { + TypeDependence Deps = TypeDependence::None; + if (!Contained.isNull()) + Deps |= Contained->getDependence(); + if (Attrs.SampleCountExpr) + Deps |= toTypeDependence(Attrs.SampleCountExpr->getDependence()); + return Deps; +} + +HLSLAttributedResourceType::HLSLAttributedResourceType(QualType Wrapped, + QualType Contained, + const Attributes &Attrs) + : Type(HLSLAttributedResource, QualType(), + computeDependence(Contained, Attrs)), + WrappedType(Wrapped), ContainedType(Contained), Attrs(Attrs) {} + +void HLSLAttributedResourceType::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &Ctx, + QualType Wrapped, QualType Contained, + const Attributes &Attrs) { + ID.AddPointer(Wrapped.getAsOpaquePtr()); + ID.AddPointer(Contained.getAsOpaquePtr()); + ID.AddInteger(static_cast<uint32_t>(Attrs.ResourceClass)); + ID.AddInteger(static_cast<uint32_t>(Attrs.ResourceDimension)); + ID.AddBoolean(Attrs.IsROV); + ID.AddBoolean(Attrs.RawBuffer); + ID.AddBoolean(Attrs.IsCounter); + ID.AddBoolean(Attrs.IsArray); + ID.AddBoolean(Attrs.IsMultiSampled); + ID.AddBoolean(Attrs.SampleCountExpr != nullptr); + if (Attrs.SampleCountExpr) + Attrs.SampleCountExpr->Profile(ID, Ctx, /*Canonical=*/true); +} + const HLSLAttributedResourceType * HLSLAttributedResourceType::findHandleTypeOnResource(const Type *RT) { // If the type RT is an HLSL resource class, the first field must diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 1bda113143e07..86f2c3be1d065 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -776,6 +776,25 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, return Builder.CreateIntrinsic( RetTy, CGM.getHLSLRuntime().getLoadLevelIntrinsic(), Args); } + case Builtin::BI__builtin_hlsl_resource_load_ms: { + Value *HandleOp = EmitScalarExpr(E->getArg(0)); + Value *CoordOp = EmitScalarExpr(E->getArg(1)); + Value *SampleOp = EmitScalarExpr(E->getArg(2)); + if (SampleOp->getType() != Builder.getInt32Ty()) + SampleOp = Builder.CreateIntCast(SampleOp, Builder.getInt32Ty(), + /*isSigned=*/true); + const HLSLAttributedResourceType *RT = getRequiredHandleType(E, 0); + + SmallVector<Value *, 4> Args; + Args.push_back(HandleOp); + Args.push_back(CoordOp); + Args.push_back(SampleOp); + Args.push_back(emitHlslOffset(*this, E, 3, getOffsetType(CGM, RT))); + + llvm::Type *RetTy = ConvertType(E->getType()); + return Builder.CreateIntrinsic( + RetTy, CGM.getHLSLRuntime().getLoadMSIntrinsic(), Args); + } case Builtin::BI__builtin_hlsl_resource_sample_cmp: { Value *HandleOp = EmitScalarExpr(E->getArg(0)); Value *SamplerOp = EmitScalarExpr(E->getArg(1)); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 0664eef464f98..4b8e4b6a96a24 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -210,6 +210,7 @@ class CGHLSLRuntime { GENERATE_HLSL_INTRINSIC_FUNCTION(GetDimensionsLevelsXY, resource_getdimensions_levels_xy) GENERATE_HLSL_INTRINSIC_FUNCTION(LoadLevel, resource_load_level) + GENERATE_HLSL_INTRINSIC_FUNCTION(LoadMS, resource_load_ms) GENERATE_HLSL_INTRINSIC_FUNCTION(CalculateLod, resource_calculate_lod) GENERATE_HLSL_INTRINSIC_FUNCTION(CalculateLodUnclamped, resource_calculate_lod_unclamped) diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index f483a7b867016..29998f6dfb16f 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -71,15 +71,30 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( ResAttrs.ResourceDimension != llvm::dxil::ResourceDimension::Unknown; assert((!IsRawBuffer || !IsTexture) && "A resource cannot be both a raw " "buffer and a texture."); + bool IsMultiSampledTexture = IsTexture && ResAttrs.IsMultiSampled; llvm::StringRef TypeName = "dx.TypedBuffer"; if (IsRawBuffer) TypeName = "dx.RawBuffer"; + else if (IsMultiSampledTexture) + TypeName = "dx.MSTexture"; else if (IsTexture) TypeName = "dx.Texture"; - SmallVector<unsigned, 4> Ints = {/*IsWriteable*/ ResAttrs.ResourceClass == - llvm::dxil::ResourceClass::UAV, - /*IsROV*/ ResAttrs.IsROV}; + // The second int operand is overloaded: dx.Texture holds IsROV there, + // dx.MSTexture holds the sample count. A sample count of 0 means the count + // comes from the bound resource at runtime, which is also what a + // Texture2DMS<T> written without an explicit N lowers to. + unsigned SampleCount = 0; + if (const Expr *SCE = ResAttrs.SampleCountExpr) { + std::optional<llvm::APSInt> Count = + SCE->getIntegerConstantExpr(CGM.getContext()); + if (Count && Count->isNonNegative()) + SampleCount = Count->getZExtValue(); + } + SmallVector<unsigned, 4> Ints = { + /*IsWriteable*/ ResAttrs.ResourceClass == + llvm::dxil::ResourceClass::UAV, + IsMultiSampledTexture ? SampleCount : /*IsROV*/ ResAttrs.IsROV}; if (!IsRawBuffer) { const clang::Type *ElemType = ContainedTy->getUnqualifiedDesugaredType(); if (ElemType->isVectorType()) @@ -97,10 +112,12 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( RK = llvm::dxil::ResourceKind::Texture1D; break; case llvm::dxil::ResourceDimension::Dim2D: - if (ResAttrs.IsArray) - RK = llvm::dxil::ResourceKind::Texture2DArray; + if (ResAttrs.IsMultiSampled) + RK = ResAttrs.IsArray ? llvm::dxil::ResourceKind::Texture2DMSArray + : llvm::dxil::ResourceKind::Texture2DMS; else - RK = llvm::dxil::ResourceKind::Texture2D; + RK = ResAttrs.IsArray ? llvm::dxil::ResourceKind::Texture2DArray + : llvm::dxil::ResourceKind::Texture2D; break; case llvm::dxil::ResourceDimension::Dim3D: RK = llvm::dxil::ResourceKind::Texture3D; diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index c8f7c03474be9..dba7d4fa73923 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -936,7 +936,7 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource( IntParams[2] = static_cast<unsigned>(attributes.IsArray); // MS - IntParams[3] = 0; + IntParams[3] = static_cast<unsigned>(attributes.IsMultiSampled); // Sampled IntParams[4] = diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index f8018729b4644..abe97d34ea72c 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -110,6 +110,10 @@ struct TemplateParameterListBuilder { TemplateParameterListBuilder & addTypeParameter(StringRef Name, QualType DefaultValue = QualType()); + TemplateParameterListBuilder & + addNonTypeParameter(StringRef Name, QualType Ty, + Expr *DefaultValue = nullptr); + ConceptSpecializationExpr * constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD); @@ -295,6 +299,28 @@ TemplateParameterListBuilder::addTypeParameter(StringRef Name, return *this; } +TemplateParameterListBuilder & +TemplateParameterListBuilder::addNonTypeParameter(StringRef Name, QualType Ty, + Expr *DefaultValue) { + assert(!Builder.Record->isCompleteDefinition() && + "record is already complete"); + ASTContext &AST = Builder.SemaRef.getASTContext(); + unsigned Position = static_cast<unsigned>(Params.size()); + auto *Decl = NonTypeTemplateParmDecl::Create( + AST, Builder.Record->getDeclContext(), SourceLocation(), SourceLocation(), + /* TemplateDepth */ 0, Position, + &AST.Idents.get(Name, tok::TokenKind::identifier), Ty, + /* ParameterPack */ false, AST.getTrivialTypeSourceInfo(Ty)); + if (DefaultValue) + Decl->setDefaultArgument( + AST, Builder.SemaRef.getTrivialTemplateArgumentLoc( + TemplateArgument(DefaultValue, /*IsCanonical=*/false), Ty, + SourceLocation())); + + Params.emplace_back(Decl); + return *this; +} + // The concept specialization expression (CSE) constructed in // constructConceptSpecializationExpr is constructed so that it // matches the CSE that is constructed when parsing the below C++ code: @@ -1039,18 +1065,18 @@ BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV, AccessSpecifier Access) { QualType ElementTy = getHandleElementType(); addHandleMember(RC, ResourceDimension::Unknown, IsROV, RawBuffer, - /*IsArray=*/false, ElementTy, Access); + /*IsArray=*/false, ElementTy, /*IsMultiSampled=*/false, + Access); if (HasCounter) addCounterHandleMember(RC, IsROV, RawBuffer, ElementTy, Access); return *this; } -BuiltinTypeDeclBuilder & -BuiltinTypeDeclBuilder::addTextureHandle(ResourceClass RC, bool IsROV, - bool IsArray, ResourceDimension RD, - AccessSpecifier Access) { +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addTextureHandle( + ResourceClass RC, bool IsROV, bool IsArray, ResourceDimension RD, + bool IsMultiSampled, AccessSpecifier Access) { addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, IsArray, - getHandleElementType(), Access); + getHandleElementType(), IsMultiSampled, Access); return *this; } @@ -1112,9 +1138,11 @@ CXXRecordDecl *BuiltinTypeDeclBuilder::addPrivateNestedRecord(StringRef Name) { BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember( ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, - bool IsArray, QualType ElementTy, AccessSpecifier Access) { + bool IsArray, QualType ElementTy, bool IsMultiSampled, + AccessSpecifier Access) { return addResourceMember("__handle", RC, RD, IsROV, RawBuffer, - /*IsCounter=*/false, IsArray, ElementTy, Access); + /*IsCounter=*/false, IsArray, ElementTy, + IsMultiSampled, Access); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( @@ -1122,13 +1150,14 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( AccessSpecifier Access) { return addResourceMember("__counter_handle", RC, ResourceDimension::Unknown, IsROV, RawBuffer, /*IsCounter=*/true, - /*IsArray=*/false, ElementTy, Access); + /*IsArray=*/false, ElementTy, + /*IsMultiSampled=*/false, Access); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember( StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, bool IsCounter, bool IsArray, QualType ElementTy, - AccessSpecifier Access) { + bool IsMultiSampled, AccessSpecifier Access) { assert(!Record->isCompleteDefinition() && "record is already complete"); ASTContext &Ctx = SemaRef.getASTContext(); @@ -1154,9 +1183,20 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember( Attrs.push_back(HLSLIsCounterAttr::CreateImplicit(Ctx)); if (IsArray) Attrs.push_back(HLSLIsArrayAttr::CreateImplicit(Ctx)); + Expr *SampleCountExpr = nullptr; + if (IsMultiSampled) { + Attrs.push_back(HLSLIsMultiSampledAttr::CreateImplicit(Ctx)); + ClassTemplateDecl *CTD = Record->getDescribedClassTemplate(); + assert(CTD && "multisampled texture must be a class template"); + auto *NTTP = cast<NonTypeTemplateParmDecl>( + CTD->getTemplateParameters()->getParam(1)); + SampleCountExpr = SemaRef.BuildDeclRefExpr(NTTP, NTTP->getType(), + VK_PRValue, SourceLocation()); + } if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs, - AttributedResTy)) + AttributedResTy, /*LocInfo=*/nullptr, + SampleCountExpr)) addMemberVariable(MemberName, AttributedResTy, {}, Access); return *this; } @@ -1466,7 +1506,7 @@ CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsSliceType(ResourceDimension Dim, .addHandleMember(getResourceAttrs().ResourceClass, Dim, getResourceAttrs().IsROV, /*RawBuffer=*/false, getResourceAttrs().IsArray, ReturnType, - AccessSpecifier::AS_public) + /*IsMultiSampled=*/false, AccessSpecifier::AS_public) .addMemberVariable("__level", IntTy, {}, AccessSpecifier::AS_public) .addDefaultHandleConstructor(AccessSpecifier::AS_protected) .addCopyConstructor(AccessSpecifier::AS_protected) @@ -1510,7 +1550,7 @@ CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsType(ResourceDimension Dim, .addHandleMember(getResourceAttrs().ResourceClass, Dim, getResourceAttrs().IsROV, /*RawBuffer=*/false, getResourceAttrs().IsArray, ReturnType, - AccessSpecifier::AS_public) + /*IsMultiSampled=*/false, AccessSpecifier::AS_public) .addDefaultHandleConstructor(AccessSpecifier::AS_protected) .addCopyConstructor(AccessSpecifier::AS_protected) .addCopyAssignmentOperator(AccessSpecifier::AS_protected); @@ -1589,6 +1629,39 @@ BuiltinTypeDeclBuilder::addTextureLoadMethods(ResourceDimension Dim, .finalize(); } +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addTextureLoadMSMethods(ResourceDimension Dim, + bool IsArray) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + ASTContext &AST = Record->getASTContext(); + uint32_t OffsetSize = getResourceDimensions(Dim); + // Multisampled textures use a plain location (no mip/LOD component). + uint32_t CoordSize = OffsetSize + (IsArray ? 1 : 0); + QualType IntTy = AST.IntTy; + QualType OffsetTy = AST.getExtVectorType(IntTy, OffsetSize); + QualType LocationTy = AST.getExtVectorType(IntTy, CoordSize); + QualType ReturnType = getHandleElementType(); + + using PH = BuiltinTypeMethodBuilder::PlaceHolder; + + // T Load(int2 location, int sampleIndex) + BuiltinTypeMethodBuilder(*this, "Load", ReturnType) + .addParam("Location", LocationTy) + .addParam("SampleIndex", IntTy) + .callBuiltin("__builtin_hlsl_resource_load_ms", ReturnType, PH::Handle, + PH::_0, PH::_1) + .finalize(); + + // T Load(int2 location, int sampleIndex, int2 offset) + return BuiltinTypeMethodBuilder(*this, "Load", ReturnType) + .addParam("Location", LocationTy) + .addParam("SampleIndex", IntTy) + .addParam("Offset", OffsetTy) + .callBuiltin("__builtin_hlsl_resource_load_ms", ReturnType, PH::Handle, + PH::_0, PH::_1, PH::_2) + .finalize(); +} + BuiltinTypeDeclBuilder & BuiltinTypeDeclBuilder::addByteAddressBufferLoadMethods() { assert(!Record->isCompleteDefinition() && "record is already complete"); @@ -2237,6 +2310,26 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names, return Builder.finalizeTemplateArgs(CD); } +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addMSTextureTemplateParams( + StringRef ElementName, StringRef SampleCountName, ConceptDecl *CD) { + if (Record->isCompleteDefinition()) { + assert(Template && "existing record it not a template"); + assert(Template->getTemplateParameters()->size() == 2 && + "template param count mismatch"); + return *this; + } + + ASTContext &AST = SemaRef.getASTContext(); + TemplateParameterListBuilder Builder = TemplateParameterListBuilder(*this); + // No default element type (`Texture2DMS` and `Texture2DMS<>` are errors). + // A sample count of 0 means the count comes from the bound resource rather + // than denoting zero samples. + Builder.addTypeParameter(ElementName); + Builder.addNonTypeParameter(SampleCountName, AST.IntTy, + getConstantIntExpr(0)); + return Builder.finalizeTemplateArgs(CD); +} + BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { using PH = BuiltinTypeMethodBuilder::PlaceHolder; QualType UnsignedIntTy = SemaRef.getASTContext().UnsignedIntTy; diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 09cf1fceca116..fdcf8a92c396b 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -68,6 +68,12 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder & addSimpleTemplateParams(ArrayRef<StringRef> Names, ArrayRef<QualType> DefaultTypes, ConceptDecl *CD); + // Adds `<typename element_type, int sample_count = 0>` for multisampled + // textures, with \p CD constraining the element type. Unlike the other + // textures the element type has no default argument. + BuiltinTypeDeclBuilder &addMSTextureTemplateParams(StringRef ElementName, + StringRef SampleCountName, + ConceptDecl *CD); CXXRecordDecl *finalizeForwardDeclaration() { return Record; } BuiltinTypeDeclBuilder &completeDefinition(); @@ -81,7 +87,7 @@ class BuiltinTypeDeclBuilder { AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder & addTextureHandle(ResourceClass RC, bool IsROV, bool IsArray, - ResourceDimension RD, + ResourceDimension RD, bool IsMultiSampled = false, AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder &addSamplerHandle(); BuiltinTypeDeclBuilder &addConstantBufferConversionToType(); @@ -104,6 +110,8 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder &addLoadMethods(); BuiltinTypeDeclBuilder &addTextureLoadMethods(ResourceDimension Dim, bool IsArray = false); + BuiltinTypeDeclBuilder &addTextureLoadMSMethods(ResourceDimension Dim, + bool IsArray = false); BuiltinTypeDeclBuilder &addByteAddressBufferLoadMethods(); BuiltinTypeDeclBuilder &addByteAddressBufferStoreMethods(); BuiltinTypeDeclBuilder &addSampleMethods(ResourceDimension Dim, @@ -150,6 +158,7 @@ class BuiltinTypeDeclBuilder { addResourceMember(StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, bool IsCounter, bool IsArray, QualType ElementTy, + bool IsMultiSampled = false, AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder &addFriend(CXXRecordDecl *Friend); CXXRecordDecl *addPrivateNestedRecord(StringRef Name); @@ -158,6 +167,7 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder & addHandleMember(ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, bool IsArray, QualType ElementTy, + bool IsMultiSampled = false, AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder & addCounterHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 7578f20a27f18..8ce9b2976fe7d 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -297,6 +297,24 @@ static BuiltinTypeDeclBuilder setupRWTextureType(CXXRecordDecl *Decl, Sema &S, .addStaticInitializationFunctions(false); } +/// Set up Texture2DMS (multisampled) type: SRV texture with only operator[] +/// and sample-indexed Load. It does not support sampling, gather, LOD, or mips. +static BuiltinTypeDeclBuilder setupMSTextureType(CXXRecordDecl *Decl, Sema &S, + bool IsArray, + ResourceDimension Dim) { + return BuiltinTypeDeclBuilder(S, Decl) + .addTextureHandle(ResourceClass::SRV, /*IsROV=*/false, IsArray, Dim, + /*IsMultiSampled=*/true) + .addTextureLoadMSMethods(Dim, IsArray) + .addArraySubscriptOperators(Dim, IsArray) + // TODO: Add MS-specific GetDimensions (with a NumberOfSamples output); + // the generic addGetDimensionsMethods is mip-based and unsuitable here. + .addDefaultHandleConstructor() + .addCopyConstructor() + .addCopyAssignmentOperator() + .addStaticInitializationFunctions(false); +} + // Add a partial specialization for a template. The `TextureTemplate` is // `Texture<element_type>`, and it will be specialized for vectors: // `Texture<vector<element_type, element_count>>`. @@ -765,6 +783,19 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { ResourceDimension::Dim2D) .completeDefinition(); }); + + // Texture2DMS — like Texture2D but multisampled, and the element type has no + // default argument. + Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2DMS") + .addMSTextureTemplateParams("element_type", "sample_count", + TypedBufferConcept) + .finalizeForwardDeclaration(); + + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + setupMSTextureType(Decl, *SemaPtr, /*IsArray=*/false, + ResourceDimension::Dim2D) + .completeDefinition(); + }); } // Build a single overload of an HLSL atomic intrinsic in the hlsl namespace. diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index ae5e022399e49..30635ac0dd000 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2096,7 +2096,8 @@ void SemaHLSL::handleShaderAttr(Decl *D, const ParsedAttr &AL) { bool clang::CreateHLSLAttributedResourceType( Sema &S, QualType Wrapped, ArrayRef<const Attr *> AttrList, - QualType &ResType, HLSLAttributedResourceLocInfo *LocInfo) { + QualType &ResType, HLSLAttributedResourceLocInfo *LocInfo, + Expr *SampleCountExpr) { assert(AttrList.size() && "expected list of resource attributes"); QualType ContainedTy = QualType(); @@ -2167,6 +2168,7 @@ bool clang::CreateHLSLAttributedResourceType( return false; } ResAttrs.IsMultiSampled = true; + ResAttrs.SampleCountExpr = SampleCountExpr; break; case attr::HLSLIsCounter: if (ResAttrs.IsCounter) { @@ -3950,6 +3952,49 @@ static bool CheckLoadLevelBuiltin(Sema &S, CallExpr *TheCall) { return false; } +static bool CheckLoadMSBuiltin(Sema &S, CallExpr *TheCall) { + if (S.checkArgCountRange(TheCall, 3, 4)) + return true; + + // Check the multisampled texture handle. + if (CheckResourceHandle(&S, TheCall, 0, + [](const HLSLAttributedResourceType *ResType) { + return !ResType->getAttrs().IsMultiSampled; + })) + return true; + + auto *ResourceTy = + TheCall->getArg(0)->getType()->castAs<HLSLAttributedResourceType>(); + + // Check the location (int2 for Texture2DMS, int3 for Texture2DMSArray). + // Unlike Load on regular textures, there is no mip/LOD component. + unsigned ResourceDim = + getResourceDimensions(ResourceTy->getAttrs().ResourceDimension); + unsigned LocationDim = ResourceDim + (ResourceTy->getAttrs().IsArray ? 1 : 0); + QualType LocationTy = TheCall->getArg(1)->getType(); + if (CheckVectorElementCount(&S, LocationTy, S.Context.IntTy, LocationDim, + TheCall->getArg(1)->getBeginLoc())) + return true; + + // Check the sample index operand (scalar int). + if (!TheCall->getArg(2)->getType()->isIntegerType()) { + S.Diag(TheCall->getArg(2)->getBeginLoc(), diag::err_typecheck_expect_int) + << TheCall->getArg(2)->getType(); + return true; + } + + // Check the offset operand (int2 for 2D textures; no array slice). + if (TheCall->getNumArgs() > 3) { + if (CheckVectorElementCount(&S, TheCall->getArg(3)->getType(), + S.Context.IntTy, ResourceDim, + TheCall->getArg(3)->getBeginLoc())) + return true; + } + + TheCall->setType(ResourceTy->getContainedType()); + return false; +} + static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) { unsigned MinArgs, MaxArgs; if (Kind == SampleKind::Sample) { @@ -4173,6 +4218,8 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { } case Builtin::BI__builtin_hlsl_resource_load_level: return CheckLoadLevelBuiltin(SemaRef, TheCall); + case Builtin::BI__builtin_hlsl_resource_load_ms: + return CheckLoadMSBuiltin(SemaRef, TheCall); case Builtin::BI__builtin_hlsl_resource_sample: return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::Sample); case Builtin::BI__builtin_hlsl_resource_sample_bias: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 8ed1dba3bf101..b5f4f1247025e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7824,11 +7824,21 @@ QualType TreeTransform<Derived>::TransformHLSLAttributedResourceType( ContainedTy = ContainedTSI->getType(); } + HLSLAttributedResourceType::Attributes Attrs = oldType->getAttrs(); + if (Attrs.SampleCountExpr) { + ExprResult SampleCountResult = + getDerived().TransformExpr(Attrs.SampleCountExpr); + if (SampleCountResult.isInvalid()) + return QualType(); + Attrs.SampleCountExpr = SampleCountResult.get(); + } + QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || WrappedTy != oldType->getWrappedType() || - ContainedTy != oldType->getContainedType()) { - Result = SemaRef.Context.getHLSLAttributedResourceType( - WrappedTy, ContainedTy, oldType->getAttrs()); + ContainedTy != oldType->getContainedType() || + Attrs.SampleCountExpr != oldType->getSampleCountExpr()) { + Result = SemaRef.Context.getHLSLAttributedResourceType(WrappedTy, + ContainedTy, Attrs); } HLSLAttributedResourceTypeLoc NewTL = diff --git a/clang/test/AST/HLSL/MultiSampledTextures-AST.hlsl b/clang/test/AST/HLSL/MultiSampledTextures-AST.hlsl new file mode 100644 index 0000000000000..bd2a3b17c61c7 --- /dev/null +++ b/clang/test/AST/HLSL/MultiSampledTextures-AST.hlsl @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -disable-llvm-passes -finclude-default-header -DTEXTURE=Texture2DMS -o - %s | FileCheck %s -DTEXTURE=Texture2DMS -DINDEX_SIZE=2 + +// The class template: an `element_type` type parameter followed by an `int` +// `sample_count` non-type parameter that defaults to 0. +// CHECK: ClassTemplateDecl {{.*}} [[TEXTURE]] +// CHECK: TemplateTypeParmDecl {{.*}} element_type +// CHECK: NonTypeTemplateParmDecl {{.*}} 'int' depth 0 index 1 sample_count +// CHECK: TemplateArgument expr '0' + +// The resource handle: an SRV whose handle additionally carries [[hlsl::is_ms]]. +// CHECK: CXXRecordDecl {{.*}} [[TEXTURE]] definition +// CHECK: FinalAttr {{.*}} Implicit final +// CHECK-NEXT: FieldDecl {{.*}} implicit __handle '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::is_ms]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] + +// Load(location, sampleIndex): the sample index is a separate scalar (there is +// no packed mip level), and the read lowers to __builtin_hlsl_resource_load_ms. +// CHECK: CXXMethodDecl {{.*}} Load 'element_type (vector<int, [[INDEX_SIZE]]>, int)' inline +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<int, [[INDEX_SIZE]]>' +// CHECK-NEXT: ParmVarDecl {{.*}} SampleIndex 'int' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: CStyleCastExpr {{.*}} 'element_type' <Dependent> +// CHECK-NEXT: CallExpr {{.*}} '<dependent type>' +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_ms' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::is_ms]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[TEXTURE]]<element_type, sample_count>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, [[INDEX_SIZE]]>' lvalue ParmVar {{.*}} 'Location' 'vector<int, [[INDEX_SIZE]]>' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'SampleIndex' 'int' +// CHECK-NEXT: AlwaysInlineAttr + +// Load(location, sampleIndex, offset): identical, with a trailing 2D offset. +// CHECK: CXXMethodDecl {{.*}} Load 'element_type (vector<int, [[INDEX_SIZE]]>, int, vector<int, 2>)' inline +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<int, [[INDEX_SIZE]]>' +// CHECK-NEXT: ParmVarDecl {{.*}} SampleIndex 'int' +// CHECK-NEXT: ParmVarDecl {{.*}} Offset 'vector<int, 2>' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: CStyleCastExpr {{.*}} 'element_type' <Dependent> +// CHECK-NEXT: CallExpr {{.*}} '<dependent type>' +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_ms' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[TEXTURE]]<element_type, sample_count>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, [[INDEX_SIZE]]>' lvalue ParmVar {{.*}} 'Location' 'vector<int, [[INDEX_SIZE]]>' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'SampleIndex' 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>' +// CHECK-NEXT: AlwaysInlineAttr + +// operator[] returns a const reference to sample 0 (same shape as the non-MS +// SRV subscript in Textures-AST.hlsl), via __builtin_hlsl_resource_getpointer. +// CHECK: CXXMethodDecl {{.*}} operator[] 'const hlsl_device element_type &(vector<unsigned int, [[INDEX_SIZE]]>) const' inline +// CHECK-NEXT: ParmVarDecl {{.*}} Index 'vector<unsigned int, [[INDEX_SIZE]]>' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' lvalue prefix '*' cannot overflow +// CHECK-NEXT: CStyleCastExpr {{.*}} 'hlsl_device element_type *' <Dependent> +// CHECK-NEXT: CallExpr {{.*}} '<dependent type>' +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'const hlsl::[[TEXTURE]]<element_type, sample_count>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<unsigned int, [[INDEX_SIZE]]>' lvalue ParmVar {{.*}} 'Index' 'vector<unsigned int, [[INDEX_SIZE]]>' +// CHECK-NEXT: AlwaysInlineAttr + +// TODO(MS GetDimensions): when the multisampled GetDimensions overload is +// implemented, dump its CHECK block here (out width/height[/elements]/ +// NumberOfSamples, on a samples-based getdimensions builtin), paralleling the +// GetDimensions blocks in Textures-AST.hlsl. + +TEXTURE<float> t; + +// An explicit, non-default sample count binds the non-type template parameter, +// producing a distinct specialization. +// CHECK: ClassTemplateSpecializationDecl {{.*}} class [[TEXTURE]] definition +// CHECK: TemplateArgument type 'vector<float, 4>' +// CHECK: TemplateArgument integral '4' +TEXTURE<float4, 4> tMS4; + +[numthreads(1, 1, 1)] +void main() { + uint2 i = uint2(0, 0); + float x = t[i]; + (void)x; + // TODO: enable once multisampled GetDimensions is implemented, paralleling + // the t.GetDimensions(w, h) call in Textures-AST.hlsl: + // uint w, h, n; + // t.GetDimensions(w, h, n); +} diff --git a/clang/test/CodeGenHLSL/resources/MultiSampledTextures-Load.hlsl b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-Load.hlsl new file mode 100644 index 0000000000000..f8cc6e2b72b62 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-Load.hlsl @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -o - %s | FileCheck %s --check-prefix=DXIL -DCOORD_DIM=2 -DKIND=3 +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -o - %s | FileCheck %s --check-prefix=SPIRV -DCOORD_DIM=2 -DARRAYED=0 + +// Load on a multisampled texture takes a separate sample index (rather than a +// packed mip level) and lowers to the MS-specific resource.load.ms intrinsic on +// a dx.MSTexture (DXIL) / multisampled spirv.Image (SPIR-V) handle. The correct +// per-element-type intrinsic overload is selected, and signed integer element +// types map to a SignedImage / IsSigned=1 handle. The resource type layout +// itself is covered by MultiSampledTextures-default.hlsl. + +TEXTURE<float4> T; +TEXTURE<float4, 4> TMS4; +TEXTURE<float> Tf; +TEXTURE<int> Ti; +TEXTURE<int4> Ti4; + +// CHECK-LABEL: define {{.*}} <4 x float> @{{.*}}test_load +// DXIL: call {{.*}} <4 x float> @llvm.dx.resource.load.ms.v4f32.{{.*}}(target("dx.MSTexture", <4 x float>, 0, 0, 0, [[KIND]]) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.load.ms.v4f32.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], 1, 1, 0) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +float4 test_load(LOCATION_TYPE loc, int sampleIndex) { + return T.Load(loc, sampleIndex); +} + +// The offset overload threads the constant offset through as the final operand. +// CHECK-LABEL: define {{.*}} <4 x float> @{{.*}}test_load_offset +// DXIL: call {{.*}} <4 x float> @llvm.dx.resource.load.ms.v4f32.{{.*}}(target("dx.MSTexture", <4 x float>, 0, 0, 0, [[KIND]]) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.load.ms.v4f32.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], 1, 1, 0) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +float4 test_load_offset(LOCATION_TYPE loc, int sampleIndex) { + return T.Load(loc, sampleIndex, int2(1, 1)); +} + +// An explicit compile-time sample count changes only the resource handle type +// (the dx.MSTexture sample-count operand becomes 4); the resource.load.ms call +// is otherwise identical to the default. +// CHECK-LABEL: define {{.*}} <4 x float> @{{.*}}test_explicit_count +// DXIL: call {{.*}} <4 x float> @llvm.dx.resource.load.ms.v4f32.{{.*}}(target("dx.MSTexture", <4 x float>, 0, 4, 0, [[KIND]]) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.load.ms.v4f32.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], 1, 1, 0) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +float4 test_explicit_count(LOCATION_TYPE loc, int sampleIndex) { + return TMS4.Load(loc, sampleIndex); +} + +// A scalar float element selects the f32 overload. +// CHECK-LABEL: define {{.*}} float @{{.*}}test_load_float +// DXIL: call {{.*}} float @llvm.dx.resource.load.ms.f32.{{.*}}(target("dx.MSTexture", float, 0, 0, 0, [[KIND]]) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: call {{.*}} float @llvm.spv.resource.load.ms.f32.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], 1, 1, 0) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +float test_load_float(LOCATION_TYPE loc, int sampleIndex) { + return Tf.Load(loc, sampleIndex); +} + +// A signed integer element maps to IsSigned=1 (DXIL) / spirv.SignedImage. +// CHECK-LABEL: define {{.*}} i32 @{{.*}}test_load_int +// DXIL: call i32 @llvm.dx.resource.load.ms.i32.{{.*}}(target("dx.MSTexture", i32, 0, 0, 1, [[KIND]]) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: call i32 @llvm.spv.resource.load.ms.i32.{{.*}}(target("spirv.SignedImage", i32, 1, 2, [[ARRAYED]], 1, 1, 0) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +int test_load_int(LOCATION_TYPE loc, int sampleIndex) { + return Ti.Load(loc, sampleIndex); +} + +// A signed integer vector element: v4i32 overload on a SignedImage handle. +// CHECK-LABEL: define {{.*}} <4 x i32> @{{.*}}test_load_int4 +// DXIL: call <4 x i32> @llvm.dx.resource.load.ms.v4i32.{{.*}}(target("dx.MSTexture", <4 x i32>, 0, 0, 1, [[KIND]]) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: call <4 x i32> @llvm.spv.resource.load.ms.v4i32.{{.*}}(target("spirv.SignedImage", i32, 1, 2, [[ARRAYED]], 1, 1, 0) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +int4 test_load_int4(LOCATION_TYPE loc, int sampleIndex) { + return Ti4.Load(loc, sampleIndex); +} diff --git a/clang/test/CodeGenHLSL/resources/MultiSampledTextures-default-explicit-binding.hlsl b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-default-explicit-binding.hlsl new file mode 100644 index 0000000000000..045174314a383 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-default-explicit-binding.hlsl @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -o - %s | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,DXIL -DTEXTURE=Texture2DMS -DCOORD_DIM=2 -DKIND=3 +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -o - %s | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,SPIRV -DTEXTURE=Texture2DMS -DCOORD_DIM=2 -DARRAYED=0 + +// A multisampled texture with an explicit register/space binding is initialized +// from that binding through __createFromBinding, identically to a non-MS +// texture; only the resource handle type differs (dx.MSTexture / a multisampled +// spirv.Image). Unlike non-multisampled textures, the element type must be +// stated explicitly (Texture2DMS and Texture2DMS<> are both errors), so the +// default-template / shorthand forms are not exercised here. + +TEXTURE<float4> explicit_binding : register(t1, space2); +TEXTURE<float4> implicit_template : register(t0, space1); + +// DXIL: %"class.hlsl::[[TEXTURE]]" = type { target("dx.MSTexture", <4 x float>, 0, 0, 0, [[KIND]]) } +// SPIRV: %"class.hlsl::[[TEXTURE]]" = type { target("spirv.Image", float, 1, 2, [[ARRAYED]], 1, 1, 0) } + +// CHECK: @{{.*}}explicit_binding = internal global %"class.hlsl::[[TEXTURE]]" poison, align {{[0-9]+}} +// CHECK: @{{.*}}implicit_template = internal global %"class.hlsl::[[TEXTURE]]" poison, align {{[0-9]+}} + +// Each texture is initialized from its explicit register/space binding: +// explicit_binding -> register(t1, space2) => registerNo 1, spaceNo 2 +// implicit_template -> register(t0, space1) => registerNo 0, spaceNo 1 +// CHECK: call {{.*}} @hlsl::[[TEXTURE]]<float vector[4]{{(, [0-9]+)?}}>::__createFromBinding{{.*}}(ptr {{.*}}@{{.*}}explicit_binding, i32 noundef 1, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @{{.*}}) +// CHECK: call {{.*}} @hlsl::[[TEXTURE]]<float vector[4]{{(, [0-9]+)?}}>::__createFromBinding{{.*}}(ptr {{.*}}@{{.*}}implicit_template, i32 noundef 0, i32 noundef 1, i32 noundef 1, i32 noundef 0, ptr noundef @{{.*}}) + +float4 main(LOCATION_TYPE loc : LOC, int sampleIndex : SI) : SV_Target { + // A multisampled texture is read through a sample-indexed Load, not Sample. + // DXIL: call {{.*}} <4 x float> @llvm.dx.resource.load.ms.v4f32.{{.*}}(target("dx.MSTexture", <4 x float>, 0, 0, 0, [[KIND]]) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) + // SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.load.ms.v4f32.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], 1, 1, 0) %{{.*}}, <[[COORD_DIM]] x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) + return implicit_template.Load(loc, sampleIndex); +} diff --git a/clang/test/CodeGenHLSL/resources/MultiSampledTextures-default.hlsl b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-default.hlsl new file mode 100644 index 0000000000000..e4980f3d2fb83 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-default.hlsl @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -DTEXTURE=Texture2DMS -o - %s | FileCheck %s --check-prefix=DXIL -DTEXTURE=Texture2DMS -DDXIL_ARGS="<4 x float>, 0, 0, 0, 3" -DDXIL_ARGS4="<4 x float>, 0, 4, 0, 3" +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -DTEXTURE=Texture2DMS -o - %s | FileCheck %s --check-prefix=SPIRV -DTEXTURE=Texture2DMS -DSPIRV_ARGS="float, 1, 2, 0, 1, 1, 0" + +// A multisampled texture lowers to a multisampled resource target type. For +// DXIL this is the dedicated `dx.MSTexture` type carrying the multisampled +// resource kind; for SPIR-V it is a `spirv.Image` with the Multisampled (MS) +// operand set to 1. +// +// The Texture2DMS<T, N> sample count N is the second integer operand of +// dx.MSTexture: it is 0 for the default (T, runtime-determined) and 4 for the +// explicit-count global (TMS4), producing two distinct resource types. SPIR-V +// does not encode the count, so both map to the same spirv.Image. + +// DXIL: %"class.hlsl::[[TEXTURE]]" = type { target("dx.MSTexture", [[DXIL_ARGS]]) } +// DXIL: %"class.hlsl::[[TEXTURE]].0" = type { target("dx.MSTexture", [[DXIL_ARGS4]]) } +// SPIRV: %"class.hlsl::[[TEXTURE]]" = type { target("spirv.Image", [[SPIRV_ARGS]]) } +// SPIRV: %"class.hlsl::[[TEXTURE]].0" = type { target("spirv.Image", [[SPIRV_ARGS]]) } + +// DXIL: @{{.*}}T = internal global %"class.hlsl::[[TEXTURE]]" poison +// SPIRV: @{{.*}}T = internal global %"class.hlsl::[[TEXTURE]]" poison +TEXTURE<float4> T; + +// DXIL: @{{.*}}TMS4 = internal global %"class.hlsl::[[TEXTURE]].0" poison +// SPIRV: @{{.*}}TMS4 = internal global %"class.hlsl::[[TEXTURE]].0" poison +TEXTURE<float4, 4> TMS4; + +void main() {} diff --git a/clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl new file mode 100644 index 0000000000000..7e3b5b981648c --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -emit-pch -o %t %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -include-pch %t -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -DTEXTURE=Texture2DMS -DKIND=3 + +// The Texture2DMS<T, N> sample count is part of the resource handle type, so it +// has to survive serialization: a deserialized handle still lowers to a +// dx.MSTexture with sample count 4. + +#ifndef HEADER +#define HEADER + +TEXTURE<float4, 4> TMS4; + +#else + +// CHECK: %"class.hlsl::[[TEXTURE]]" = type { target("dx.MSTexture", <4 x float>, 0, 4, 0, [[KIND]]) } + +[numthreads(1, 1, 1)] +void main() { + float4 V = TMS4.Load((LOCATION_TYPE)0, 0); + (void)V; +} + +#endif diff --git a/clang/test/CodeGenHLSL/resources/Textures-Subscript.hlsl b/clang/test/CodeGenHLSL/resources/Textures-Subscript.hlsl index 1d765ee9cd0a1..686df4195f68e 100644 --- a/clang/test/CodeGenHLSL/resources/Textures-Subscript.hlsl +++ b/clang/test/CodeGenHLSL/resources/Textures-Subscript.hlsl @@ -1,7 +1,18 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2D -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2D -DCOORD_DIM=2 --check-prefixes=CHECK,DXIL -DDXIL_TY=2 -DRW=0 -// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2D -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2D -DCOORD_DIM=2 --check-prefixes=CHECK,SPIRV -DARRAYED=0 -DSAMPLED=1 -DIMG_FMT=0 -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2DArray -DCOORD_TYPE=uint3 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2DArray -DCOORD_DIM=3 --check-prefixes=CHECK,DXIL -DDXIL_TY=7 -DRW=0 -// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2DArray -DCOORD_TYPE=uint3 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2DArray -DCOORD_DIM=3 --check-prefixes=CHECK,SPIRV -DARRAYED=1 -DSAMPLED=1 -DIMG_FMT=0 +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2D -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2D -DCOORD_DIM=2 --check-prefixes=CHECK,DXIL -DROV_OR_COUNT=0 -DDXIL_HANDLE=dx.Texture -DDXIL_TY=2 -DRW=0 +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2D -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2D -DCOORD_DIM=2 --check-prefixes=CHECK,SPIRV -DARRAYED=0 -DMS=0 -DSAMPLED=1 -DIMG_FMT=0 +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2DArray -DCOORD_TYPE=uint3 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2DArray -DCOORD_DIM=3 --check-prefixes=CHECK,DXIL -DROV_OR_COUNT=0 -DDXIL_HANDLE=dx.Texture -DDXIL_TY=7 -DRW=0 +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2DArray -DCOORD_TYPE=uint3 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2DArray -DCOORD_DIM=3 --check-prefixes=CHECK,SPIRV -DARRAYED=1 -DMS=0 -DSAMPLED=1 -DIMG_FMT=0 + +// Texture2DMS reuses the same operator[] codegen; only the resource handle type +// differs (dx.MSTexture / a multisampled spirv.Image). It reads sample 0. +// +// ROV_OR_COUNT is the overloaded second int operand of the DXIL handle type: +// IsROV for dx.Texture, the sample count for dx.MSTexture. It is 0 for every +// texture below (none are ROVs, and Texture2DMS<T> defaults to a runtime +// sample count), but a ROV or an explicit Texture2DMS<T, N> would need its own +// value here. +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2DMS -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2DMS -DCOORD_DIM=2 --check-prefixes=CHECK,DXIL -DROV_OR_COUNT=0 -DDXIL_HANDLE=dx.MSTexture -DDXIL_TY=3 -DRW=0 +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -Wno-sign-conversion -DTEXTURE=Texture2DMS -DCOORD_TYPE=uint2 -o - %s | llvm-cxxfilt | FileCheck %s -DTEXTURE=Texture2DMS -DCOORD_DIM=2 --check-prefixes=CHECK,SPIRV -DARRAYED=0 -DMS=1 -DSAMPLED=1 -DIMG_FMT=0 TEXTURE<float4> Tex : register(t0); TEXTURE<float> Tex2 : register(t1); @@ -21,56 +32,56 @@ void main(COORD_TYPE DTid : SV_DispatchThreadID) { // CHECK: %[[VAL3:.*]] = alloca <3 x i32> // CHECK: store <[[COORD_DIM]] x i32> %[[DTID]], ptr %[[DTID_ADDR]] // CHECK: %[[DTID_VAL:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[DTID_ADDR]] -// CHECK: %[[CALL1:.*]] = call noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float vector[4]>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @Tex, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL]]) +// CHECK: %[[CALL1:.*]] = call noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float vector[4]{{(, [0-9]+)?}}>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @Tex, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL]]) // CHECK: %[[LOAD_VAL:.*]] = load <4 x float>, ptr{{.*}} %[[CALL1]] // CHECK: store <4 x float> %[[LOAD_VAL]], ptr %[[VAL]] // CHECK: %[[DTID_VAL2:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[DTID_ADDR]] -// CHECK: %[[CALL2:.*]] = call noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @Tex2, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL2]]) +// CHECK: %[[CALL2:.*]] = call noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float{{(, [0-9]+)?}}>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @Tex2, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL2]]) // CHECK: %[[LOAD_VAL2:.*]] = load float, ptr{{.*}} %[[CALL2]] // CHECK: store float %[[LOAD_VAL2]], ptr %[[VAL2]] // CHECK: %[[DTID_VAL3:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[DTID_ADDR]] -// CHECK: %[[CALL3:.*]] = call noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<int vector[3]>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @Tex3, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL3]]) +// CHECK: %[[CALL3:.*]] = call noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<int vector[3]{{(, [0-9]+)?}}>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @Tex3, <[[COORD_DIM]] x i32> noundef %[[DTID_VAL3]]) // CHECK: %[[LOAD_VAL3:.*]] = load <3 x i32>, ptr{{.*}} %[[CALL3]] // CHECK: store <3 x i32> %[[LOAD_VAL3]], ptr %[[VAL3]] -// CHECK: define linkonce_odr hidden noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float vector[4]>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %[[THIS:.*]], <[[COORD_DIM]] x i32> noundef %[[INDEX:.*]]) +// CHECK: define linkonce_odr hidden noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float vector[4]{{(, [0-9]+)?}}>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %[[THIS:.*]], <[[COORD_DIM]] x i32> noundef %[[INDEX:.*]]) // CHECK: %[[THIS_ADDR:.*]] = alloca ptr // CHECK: %[[INDEX_ADDR:.*]] = alloca <[[COORD_DIM]] x i32> // CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] // CHECK: store <[[COORD_DIM]] x i32> %[[INDEX]], ptr %[[INDEX_ADDR]] // CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]] // CHECK: %[[HANDLE_PTR:.*]] = getelementptr {{.*}} %"class.hlsl::[[TEXTURE]]", ptr %[[THIS1]], i32 0, i32 0 -// DXIL: %[[HANDLE:.*]] = load target("dx.Texture", <4 x float>, [[RW]], 0, 0, [[DXIL_TY]]), ptr %[[HANDLE_PTR]] -// SPIRV: %[[HANDLE:.*]] = load target("spirv.Image", float, 1, 2, [[ARRAYED]], 0, [[SAMPLED]], [[IMG_FMT]]), ptr %[[HANDLE_PTR]] +// DXIL: %[[HANDLE:.*]] = load target("[[DXIL_HANDLE]]", <4 x float>, [[RW]], [[ROV_OR_COUNT]], 0, [[DXIL_TY]]), ptr %[[HANDLE_PTR]] +// SPIRV: %[[HANDLE:.*]] = load target("spirv.Image", float, 1, 2, [[ARRAYED]], [[MS]], [[SAMPLED]], [[IMG_FMT]]), ptr %[[HANDLE_PTR]] // CHECK: %[[INDEX_VAL:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[INDEX_ADDR]] -// DXIL: %[[PTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.{{.*}}(target("dx.Texture", <4 x float>, [[RW]], 0, 0, [[DXIL_TY]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) -// SPIRV: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], 0, [[SAMPLED]], [[IMG_FMT]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) +// DXIL: %[[PTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.{{.*}}(target("[[DXIL_HANDLE]]", <4 x float>, [[RW]], [[ROV_OR_COUNT]], 0, [[DXIL_TY]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) +// SPIRV: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], [[MS]], [[SAMPLED]], [[IMG_FMT]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) // CHECK: ret ptr {{.*}}%[[PTR]] -// CHECK: define linkonce_odr hidden noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %[[THIS:.*]], <[[COORD_DIM]] x i32> noundef %[[INDEX:.*]]) +// CHECK: define linkonce_odr hidden noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<float{{(, [0-9]+)?}}>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %[[THIS:.*]], <[[COORD_DIM]] x i32> noundef %[[INDEX:.*]]) // CHECK: %[[THIS_ADDR:.*]] = alloca ptr // CHECK: %[[INDEX_ADDR:.*]] = alloca <[[COORD_DIM]] x i32> // CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] // CHECK: store <[[COORD_DIM]] x i32> %[[INDEX]], ptr %[[INDEX_ADDR]] // CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]] // CHECK: %[[HANDLE_PTR:.*]] = getelementptr {{.*}} %"class.hlsl::[[TEXTURE]].0", ptr %[[THIS1]], i32 0, i32 0 -// DXIL: %[[HANDLE:.*]] = load target("dx.Texture", float, [[RW]], 0, 0, [[DXIL_TY]]), ptr %[[HANDLE_PTR]] -// SPIRV: %[[HANDLE:.*]] = load target("spirv.Image", float, 1, 2, [[ARRAYED]], 0, [[SAMPLED]], [[IMG_FMT]]), ptr %[[HANDLE_PTR]] +// DXIL: %[[HANDLE:.*]] = load target("[[DXIL_HANDLE]]", float, [[RW]], [[ROV_OR_COUNT]], 0, [[DXIL_TY]]), ptr %[[HANDLE_PTR]] +// SPIRV: %[[HANDLE:.*]] = load target("spirv.Image", float, 1, 2, [[ARRAYED]], [[MS]], [[SAMPLED]], [[IMG_FMT]]), ptr %[[HANDLE_PTR]] // CHECK: %[[INDEX_VAL:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[INDEX_ADDR]] -// DXIL: %[[PTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.{{.*}}(target("dx.Texture", float, [[RW]], 0, 0, [[DXIL_TY]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) -// SPIRV: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], 0, [[SAMPLED]], [[IMG_FMT]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) +// DXIL: %[[PTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.{{.*}}(target("[[DXIL_HANDLE]]", float, [[RW]], [[ROV_OR_COUNT]], 0, [[DXIL_TY]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) +// SPIRV: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.{{.*}}(target("spirv.Image", float, 1, 2, [[ARRAYED]], [[MS]], [[SAMPLED]], [[IMG_FMT]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) // CHECK: ret ptr {{.*}}%[[PTR]] -// CHECK: define linkonce_odr hidden noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<int vector[3]>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %[[THIS:.*]], <[[COORD_DIM]] x i32> noundef %[[INDEX:.*]]) +// CHECK: define linkonce_odr hidden noundef {{.*}}ptr{{.*}} @hlsl::[[TEXTURE]]<int vector[3]{{(, [0-9]+)?}}>::operator[](unsigned int vector[[[COORD_DIM]]]) const(ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %[[THIS:.*]], <[[COORD_DIM]] x i32> noundef %[[INDEX:.*]]) // CHECK: %[[THIS_ADDR:.*]] = alloca ptr // CHECK: %[[INDEX_ADDR:.*]] = alloca <[[COORD_DIM]] x i32> // CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] // CHECK: store <[[COORD_DIM]] x i32> %[[INDEX]], ptr %[[INDEX_ADDR]] // CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]] // CHECK: %[[HANDLE_PTR:.*]] = getelementptr {{.*}} %"class.hlsl::[[TEXTURE]].1", ptr %[[THIS1]], i32 0, i32 0 -// DXIL: %[[HANDLE:.*]] = load target("dx.Texture", <3 x i32>, [[RW]], 0, 1, [[DXIL_TY]]), ptr %[[HANDLE_PTR]] -// SPIRV: %[[HANDLE:.*]] = load target("spirv.SignedImage", i32, 1, 2, [[ARRAYED]], 0, [[SAMPLED]], [[IMG_FMT]]), ptr %[[HANDLE_PTR]] +// DXIL: %[[HANDLE:.*]] = load target("[[DXIL_HANDLE]]", <3 x i32>, [[RW]], [[ROV_OR_COUNT]], 1, [[DXIL_TY]]), ptr %[[HANDLE_PTR]] +// SPIRV: %[[HANDLE:.*]] = load target("spirv.SignedImage", i32, 1, 2, [[ARRAYED]], [[MS]], [[SAMPLED]], [[IMG_FMT]]), ptr %[[HANDLE_PTR]] // CHECK: %[[INDEX_VAL:.*]] = load <[[COORD_DIM]] x i32>, ptr %[[INDEX_ADDR]] -// DXIL: %[[PTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.{{.*}}(target("dx.Texture", <3 x i32>, [[RW]], 0, 1, [[DXIL_TY]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) -// SPIRV: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.{{.*}}(target("spirv.SignedImage", i32, 1, 2, [[ARRAYED]], 0, [[SAMPLED]], [[IMG_FMT]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) +// DXIL: %[[PTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.{{.*}}(target("[[DXIL_HANDLE]]", <3 x i32>, [[RW]], [[ROV_OR_COUNT]], 1, [[DXIL_TY]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) +// SPIRV: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.{{.*}}(target("spirv.SignedImage", i32, 1, 2, [[ARRAYED]], [[MS]], [[SAMPLED]], [[IMG_FMT]]) %[[HANDLE]], <[[COORD_DIM]] x i32> %[[INDEX_VAL]]) // CHECK: ret ptr {{.*}}%[[PTR]] diff --git a/clang/test/SemaHLSL/Resources/MultiSampledTextures-Sema.hlsl b/clang/test/SemaHLSL/Resources/MultiSampledTextures-Sema.hlsl new file mode 100644 index 0000000000000..40236507d85e2 --- /dev/null +++ b/clang/test/SemaHLSL/Resources/MultiSampledTextures-Sema.hlsl @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -fsyntax-only -verify -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -DINDEX_TYPE=uint2 %s + +// A multisampled texture supports the sample-indexed Load and operator[], but +// does not support Sample/SampleBias/SampleGrad/SampleLevel/SampleCmp, Gather, +// CalculateLevelOfDetail, or mips. + +TEXTURE<float4> T; +SamplerState S; + +// The sample_count template parameter is a non-type 'int' parameter, so it must +// be an integer constant expression. There is currently no HLSL-specific +// validation of the value itself (0, negative, non-power-of-2, and out-of-range +// counts are all accepted); the value is forwarded to the generated resource +// type as the multisampled sample count. +TEXTURE<float4, 4> TExplicit; // explicit valid count +static const int SC = 8; +TEXTURE<float4, SC> TConst; // constant-expression count + +int NonConst; +TEXTURE<float4, NonConst> TBad; // expected-error {{non-type template argument is not a constant expression}} +// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}} +// expected-note@-3 {{declared here}} +TEXTURE<float4, 1.5> TFloat; // expected-error {{conversion from 'float' to 'int' is not allowed in a converted constant expression}} + +// The element type has no default argument: a multisampled texture must be +// declared with an explicit element type, matching DXC. Both the bare +// template-name (shorthand) form and an empty template argument list are +// errors. (Non-multisampled textures such as Texture2D do allow these forms.) +TEXTURE TBare; // expected-error-re {{use of class template 'Texture2DMS{{(Array)?}}' requires template arguments}} +// expected-note@*:* {{template declaration from hidden source}} +TEXTURE<> TEmpty; // expected-error-re {{too few template arguments for class template 'Texture2DMS{{(Array)?}}'}} +// expected-note@*:* {{template declaration from hidden source}} + +void valid() { + // Sample-indexed Load and its offset overload. + float4 a = T.Load((LOCATION_TYPE)0, 0); + float4 b = T.Load((LOCATION_TYPE)0, 0, int2(1, 1)); + // operator[] reads sample 0. + float4 c = T[(INDEX_TYPE)0]; +} + +void unsupported() { + T.Sample(S, float2(0, 0)); // expected-error-re {{no member named 'Sample' in 'hlsl::Texture2DMS{{(Array)?}}<vector<float, 4>>'}} + T.SampleLevel(S, float2(0, 0), 0); // expected-error-re {{no member named 'SampleLevel' in 'hlsl::Texture2DMS{{(Array)?}}<vector<float, 4>>'}} + T.Gather(S, float2(0, 0)); // expected-error-re {{no member named 'Gather' in 'hlsl::Texture2DMS{{(Array)?}}<vector<float, 4>>'}} + T.CalculateLevelOfDetail(S, float2(0, 0)); // expected-error-re {{no member named 'CalculateLevelOfDetail' in 'hlsl::Texture2DMS{{(Array)?}}<vector<float, 4>>'}} + T.mips[0][(LOCATION_TYPE)0]; // expected-error-re {{no member named 'mips' in 'hlsl::Texture2DMS{{(Array)?}}<vector<float, 4>>'}} +} + +void bad_load() { + // Load on a multisampled texture requires a sample index. + T.Load((LOCATION_TYPE)0); // expected-error {{no matching member function for call to 'Load'}} + // expected-note@*:* {{candidate function not viable: requires 2 arguments, but 1 was provided}} + // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 1 was provided}} +} diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td index 4dd86270f0d01..81f390cc4fd8e 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -134,6 +134,12 @@ def int_dx_resource_load_level llvm_any_ty], [IntrReadMem]>; +def int_dx_resource_load_ms + : DefaultAttrsIntrinsic<[llvm_any_ty], + [llvm_any_ty, llvm_any_ty, llvm_i32_ty, + llvm_any_ty], + [IntrReadMem]>; + def int_dx_resource_calculate_lod : DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_any_ty, llvm_any_ty, llvm_any_ty], diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index d948ef78b9584..448f9664fc545 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -290,6 +290,12 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty] llvm_any_ty], [IntrReadMem]>; + def int_spv_resource_load_ms + : DefaultAttrsIntrinsic<[llvm_any_ty], + [llvm_any_ty, llvm_any_ty, llvm_i32_ty, + llvm_any_ty], + [IntrReadMem]>; + def int_spv_resource_samplecmp : DefaultAttrsIntrinsic<[llvm_any_ty], [llvm_any_ty, llvm_any_ty, llvm_any_ty, >From d8f6aeb2f5955cacf075a6bf10a41144a9fa26f8 Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Tue, 28 Jul 2026 14:56:25 -0700 Subject: [PATCH 2/6] Remove unecessary -pch test --- .../resources/MultiSampledTextures-pch.hlsl | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl diff --git a/clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl b/clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl deleted file mode 100644 index 7e3b5b981648c..0000000000000 --- a/clang/test/CodeGenHLSL/resources/MultiSampledTextures-pch.hlsl +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -emit-pch -o %t %s -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -DTEXTURE=Texture2DMS -DLOCATION_TYPE=int2 -include-pch %t -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -DTEXTURE=Texture2DMS -DKIND=3 - -// The Texture2DMS<T, N> sample count is part of the resource handle type, so it -// has to survive serialization: a deserialized handle still lowers to a -// dx.MSTexture with sample count 4. - -#ifndef HEADER -#define HEADER - -TEXTURE<float4, 4> TMS4; - -#else - -// CHECK: %"class.hlsl::[[TEXTURE]]" = type { target("dx.MSTexture", <4 x float>, 0, 4, 0, [[KIND]]) } - -[numthreads(1, 1, 1)] -void main() { - float4 V = TMS4.Load((LOCATION_TYPE)0, 0); - (void)V; -} - -#endif >From 6fc08d07032b46ff69c23db81235aa1d2dbf9240 Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Tue, 28 Jul 2026 14:57:44 -0700 Subject: [PATCH 3/6] Add sema test for the sample count template parameter --- .../SampleCount-template-arg-errors.hlsl | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 clang/test/SemaHLSL/Resources/SampleCount-template-arg-errors.hlsl diff --git a/clang/test/SemaHLSL/Resources/SampleCount-template-arg-errors.hlsl b/clang/test/SemaHLSL/Resources/SampleCount-template-arg-errors.hlsl new file mode 100644 index 0000000000000..96c5f1ba45529 --- /dev/null +++ b/clang/test/SemaHLSL/Resources/SampleCount-template-arg-errors.hlsl @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -fsyntax-only -verify %s + +// The `sample_count` non-type template parameter only exists on multisampled +// texture types. Every other resource type takes just an `element_type`, so +// providing a second template argument must be diagnosed as too many template +// arguments. + +struct S { + float4 F; +}; + +// Multisampled textures accept the sample count. +Texture2DMS<float4> MSDefaultCount; +Texture2DMS<float4, 4> MSExplicitCount; + +// ...but not more than that. +Texture2DMS<float4, 4, 8> MSTooMany; +// expected-error@-1 {{too many template arguments for class template 'Texture2DMS'}} +// expected-note@*:* {{template declaration from hidden source}} + +// Typed buffers. +Buffer<float4, 4> B; +// expected-error@-1 {{too many template arguments for class template 'Buffer'}} +// expected-note@*:* {{template declaration from hidden source}} +RWBuffer<float4, 4> RWB; +// expected-error@-1 {{too many template arguments for class template 'RWBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} +RasterizerOrderedBuffer<float4, 4> ROB; +// expected-error@-1 {{too many template arguments for class template 'RasterizerOrderedBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} + +// Structured buffers. +StructuredBuffer<S, 4> SB; +// expected-error@-1 {{too many template arguments for class template 'StructuredBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} +RWStructuredBuffer<S, 4> RWSB; +// expected-error@-1 {{too many template arguments for class template 'RWStructuredBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} +AppendStructuredBuffer<S, 4> ASB; +// expected-error@-1 {{too many template arguments for class template 'AppendStructuredBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} +ConsumeStructuredBuffer<S, 4> CSB; +// expected-error@-1 {{too many template arguments for class template 'ConsumeStructuredBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} +RasterizerOrderedStructuredBuffer<S, 4> ROSB; +// expected-error@-1 {{too many template arguments for class template 'RasterizerOrderedStructuredBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} + +// Constant buffers. +ConstantBuffer<S, 4> CB; +// expected-error@-1 {{too many template arguments for class template 'ConstantBuffer'}} +// expected-note@*:* {{template declaration from hidden source}} + +// Non-multisampled textures. +Texture2D<float4, 4> T2D; +// expected-error@-1 {{too many template arguments for class template 'Texture2D'}} +// expected-note@*:* {{template declaration from hidden source}} +RWTexture2D<float4, 4> RWT2D; +// expected-error@-1 {{too many template arguments for class template 'RWTexture2D'}} +// expected-note@*:* {{template declaration from hidden source}} +Texture2DArray<float4, 4> T2DA; +// expected-error@-1 {{too many template arguments for class template 'Texture2DArray'}} +// expected-note@*:* {{template declaration from hidden source}} +RWTexture2DArray<float4, 4> RWT2DA; +// expected-error@-1 {{too many template arguments for class template 'RWTexture2DArray'}} +// expected-note@*:* {{template declaration from hidden source}} + +// Resource types that are not templates at all cannot take a sample count +// either. +ByteAddressBuffer<4> BAB; // expected-error {{expected unqualified-id}} +RWByteAddressBuffer<4> RWBAB; // expected-error {{expected unqualified-id}} +RasterizerOrderedByteAddressBuffer<4> ROBAB; // expected-error {{expected unqualified-id}} +SamplerState<4> Samp; // expected-error {{expected unqualified-id}} +SamplerComparisonState<4> SampCmp; // expected-error {{expected unqualified-id}} >From 4703ca6a4b8a652f9310e74b310d431ef0c8b6d2 Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Tue, 18 Aug 2026 11:47:42 -0700 Subject: [PATCH 4/6] Remove IsMultiSampled field and fix ASTStructuralEquivalence logic with SampleCountExpr --- clang/include/clang/AST/TypeBase.h | 23 ++++++------- clang/include/clang/AST/TypeProperties.td | 5 +-- clang/lib/AST/ASTStructuralEquivalence.cpp | 15 +++++++-- clang/lib/AST/ItaniumMangle.cpp | 2 +- clang/lib/AST/Type.cpp | 1 - clang/lib/AST/TypePrinter.cpp | 2 +- clang/lib/CodeGen/Targets/DirectX.cpp | 4 +-- clang/lib/CodeGen/Targets/SPIR.cpp | 2 +- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 33 +++++++------------ clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 5 ++- clang/lib/Sema/HLSLExternalSemaSource.cpp | 11 ++++++- clang/lib/Sema/SemaHLSL.cpp | 13 +++++--- 12 files changed, 63 insertions(+), 53 deletions(-) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 791b014779a05..aae5e9f82635d 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -6882,37 +6882,37 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { LLVM_PREFERRED_TYPE(bool) uint8_t IsArray : 1; - LLVM_PREFERRED_TYPE(bool) - uint8_t IsMultiSampled : 1; - - /// The N in Texture2DMS<T, N>; null for every other resource. + /// The N in Texture2DMS<T, N>; null for every resource that is not + /// multisampled. A multisampled resource always carries a sample count, + /// defaulting to 0, which means the count comes from the bound resource + /// at runtime rather than denoting zero samples. Expr *SampleCountExpr; Attributes(llvm::dxil::ResourceClass ResourceClass, llvm::dxil::ResourceDimension ResourceDimension, bool IsROV = false, bool RawBuffer = false, bool IsCounter = false, bool IsArray = false, - bool IsMultiSampled = false, Expr *SampleCountExpr = nullptr) + Expr *SampleCountExpr = nullptr) : ResourceClass(ResourceClass), ResourceDimension(ResourceDimension), IsROV(IsROV), RawBuffer(RawBuffer), IsCounter(IsCounter), - IsArray(IsArray), IsMultiSampled(IsMultiSampled), - SampleCountExpr(SampleCountExpr) {} + IsArray(IsArray), SampleCountExpr(SampleCountExpr) {} Attributes(llvm::dxil::ResourceClass ResourceClass) : Attributes(ResourceClass, llvm::dxil::ResourceDimension::Unknown) {} Attributes() : Attributes(llvm::dxil::ResourceClass::UAV, - llvm::dxil::ResourceDimension::Unknown, false, false, - false, false, false) {} + llvm::dxil::ResourceDimension::Unknown) {} + + bool isMultiSampled() const { return SampleCountExpr != nullptr; } friend bool operator==(const Attributes &LHS, const Attributes &RHS) { return std::tie(LHS.ResourceClass, LHS.ResourceDimension, LHS.IsROV, LHS.RawBuffer, LHS.IsCounter, LHS.IsArray, - LHS.IsMultiSampled, LHS.SampleCountExpr) == + LHS.SampleCountExpr) == std::tie(RHS.ResourceClass, RHS.ResourceDimension, RHS.IsROV, RHS.RawBuffer, RHS.IsCounter, RHS.IsArray, - RHS.IsMultiSampled, RHS.SampleCountExpr); + RHS.SampleCountExpr); } friend bool operator!=(const Attributes &LHS, const Attributes &RHS) { return !(LHS == RHS); @@ -6938,6 +6938,7 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { QualType getContainedType() const { return ContainedType; } bool hasContainedType() const { return !ContainedType.isNull(); } Expr *getSampleCountExpr() const { return Attrs.SampleCountExpr; } + bool isMultiSampled() const { return Attrs.isMultiSampled(); } const Attributes &getAttrs() const { return Attrs; } bool isRaw() const { return Attrs.RawBuffer; } bool isStructured() const { return !ContainedType->isChar8Type(); } diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 55fafa3acfa4a..054e7a7357970 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -693,9 +693,6 @@ let Class = HLSLAttributedResourceType in { def : Property<"isArray", Bool> { let Read = [{ node->getAttrs().IsArray }]; } - def : Property<"isMultiSampled", Bool> { - let Read = [{ node->getAttrs().IsMultiSampled }]; - } def : Property<"sampleCountExpr", Optional<ExprRef>> { let Read = [{ makeOptionalFromPointer(node->getSampleCountExpr()) }]; } @@ -703,7 +700,7 @@ let Class = HLSLAttributedResourceType in { HLSLAttributedResourceType::Attributes attrs( static_cast<llvm::dxil::ResourceClass>(resClass), static_cast<llvm::dxil::ResourceDimension>(resDimension), isROV, - rawBuffer, isCounter, isArray, isMultiSampled, + rawBuffer, isCounter, isArray, makePointerFromOptional(sampleCountExpr)); return ctx.getHLSLAttributedResourceType(wrappedTy, containedTy, attrs); }]>; diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index d8bbfbe5dac72..0ebb3c0d55974 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1238,9 +1238,18 @@ bool ASTStructuralEquivalence::isEquivalent( Context, cast<HLSLAttributedResourceType>(T1)->getContainedType(), cast<HLSLAttributedResourceType>(T2)->getContainedType())) return false; - if (cast<HLSLAttributedResourceType>(T1)->getAttrs() != - cast<HLSLAttributedResourceType>(T2)->getAttrs()) - return false; + { + const auto *Res1 = cast<HLSLAttributedResourceType>(T1); + const auto *Res2 = cast<HLSLAttributedResourceType>(T2); + if (!IsStructurallyEquivalent(Context, Res1->getSampleCountExpr(), + Res2->getSampleCountExpr())) + return false; + HLSLAttributedResourceType::Attributes Attrs1 = Res1->getAttrs(); + HLSLAttributedResourceType::Attributes Attrs2 = Res2->getAttrs(); + Attrs1.SampleCountExpr = Attrs2.SampleCountExpr = nullptr; + if (Attrs1 != Attrs2) + return false; + } break; case Type::HLSLInlineSpirv: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index f8e6b898be250..40fff45cbe781 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4691,7 +4691,7 @@ void CXXNameMangler::mangleType(const HLSLAttributedResourceType *T) { Str += "_Counter"; if (Attrs.IsArray) Str += "_Array"; - if (Attrs.IsMultiSampled) + if (Attrs.isMultiSampled()) Str += "_MS"; if (T->hasContainedType()) Str += "_CT"; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index db3c94e13b7bc..6c33e25f5d513 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5982,7 +5982,6 @@ void HLSLAttributedResourceType::Profile(llvm::FoldingSetNodeID &ID, ID.AddBoolean(Attrs.RawBuffer); ID.AddBoolean(Attrs.IsCounter); ID.AddBoolean(Attrs.IsArray); - ID.AddBoolean(Attrs.IsMultiSampled); ID.AddBoolean(Attrs.SampleCountExpr != nullptr); if (Attrs.SampleCountExpr) Attrs.SampleCountExpr->Profile(ID, Ctx, /*Canonical=*/true); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 60964d2859790..a7ceec29f7158 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2213,7 +2213,7 @@ void TypePrinter::printHLSLAttributedResourceAfter( OS << " [[hlsl::is_counter]]"; if (Attrs.IsArray) OS << " [[hlsl::is_array]]"; - if (Attrs.IsMultiSampled) + if (Attrs.isMultiSampled()) OS << " [[hlsl::is_ms]]"; QualType ContainedTy = T->getContainedType(); diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index 29998f6dfb16f..6240a64b6a539 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -71,7 +71,7 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( ResAttrs.ResourceDimension != llvm::dxil::ResourceDimension::Unknown; assert((!IsRawBuffer || !IsTexture) && "A resource cannot be both a raw " "buffer and a texture."); - bool IsMultiSampledTexture = IsTexture && ResAttrs.IsMultiSampled; + bool IsMultiSampledTexture = IsTexture && ResAttrs.isMultiSampled(); llvm::StringRef TypeName = "dx.TypedBuffer"; if (IsRawBuffer) TypeName = "dx.RawBuffer"; @@ -112,7 +112,7 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( RK = llvm::dxil::ResourceKind::Texture1D; break; case llvm::dxil::ResourceDimension::Dim2D: - if (ResAttrs.IsMultiSampled) + if (ResAttrs.isMultiSampled()) RK = ResAttrs.IsArray ? llvm::dxil::ResourceKind::Texture2DMSArray : llvm::dxil::ResourceKind::Texture2DMS; else diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index dba7d4fa73923..152d6214526df 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -936,7 +936,7 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource( IntParams[2] = static_cast<unsigned>(attributes.IsArray); // MS - IntParams[3] = static_cast<unsigned>(attributes.IsMultiSampled); + IntParams[3] = static_cast<unsigned>(attributes.isMultiSampled()); // Sampled IntParams[4] = diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 7b778bd362d58..70ece482ae2e7 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -1065,8 +1065,7 @@ BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV, AccessSpecifier Access) { QualType ElementTy = getHandleElementType(); addHandleMember(RC, ResourceDimension::Unknown, IsROV, RawBuffer, - /*IsArray=*/false, ElementTy, /*IsMultiSampled=*/false, - Access); + /*IsArray=*/false, ElementTy, Access); if (HasCounter) addCounterHandleMember(RC, IsROV, RawBuffer, ElementTy, Access); return *this; @@ -1074,9 +1073,10 @@ BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV, BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addTextureHandle( ResourceClass RC, bool IsROV, bool IsArray, ResourceDimension RD, - bool IsMultiSampled, AccessSpecifier Access) { - addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, IsArray, - getHandleElementType(), IsMultiSampled, Access); + Expr *SampleCountExpr, AccessSpecifier Access) { + addResourceMember("__handle", RC, RD, IsROV, /*RawBuffer=*/false, + /*IsCounter=*/false, IsArray, getHandleElementType(), + SampleCountExpr, Access); return *this; } @@ -1138,11 +1138,10 @@ CXXRecordDecl *BuiltinTypeDeclBuilder::addPrivateNestedRecord(StringRef Name) { BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember( ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, - bool IsArray, QualType ElementTy, bool IsMultiSampled, - AccessSpecifier Access) { + bool IsArray, QualType ElementTy, AccessSpecifier Access) { return addResourceMember("__handle", RC, RD, IsROV, RawBuffer, /*IsCounter=*/false, IsArray, ElementTy, - IsMultiSampled, Access); + /*SampleCountExpr=*/nullptr, Access); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( @@ -1151,13 +1150,13 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( return addResourceMember("__counter_handle", RC, ResourceDimension::Unknown, IsROV, RawBuffer, /*IsCounter=*/true, /*IsArray=*/false, ElementTy, - /*IsMultiSampled=*/false, Access); + /*SampleCountExpr=*/nullptr, Access); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember( StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, bool IsCounter, bool IsArray, QualType ElementTy, - bool IsMultiSampled, AccessSpecifier Access) { + Expr *SampleCountExpr, AccessSpecifier Access) { assert(!Record->isCompleteDefinition() && "record is already complete"); ASTContext &Ctx = SemaRef.getASTContext(); @@ -1183,16 +1182,8 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember( Attrs.push_back(HLSLIsCounterAttr::CreateImplicit(Ctx)); if (IsArray) Attrs.push_back(HLSLIsArrayAttr::CreateImplicit(Ctx)); - Expr *SampleCountExpr = nullptr; - if (IsMultiSampled) { + if (SampleCountExpr) Attrs.push_back(HLSLIsMultiSampledAttr::CreateImplicit(Ctx)); - ClassTemplateDecl *CTD = Record->getDescribedClassTemplate(); - assert(CTD && "multisampled texture must be a class template"); - auto *NTTP = cast<NonTypeTemplateParmDecl>( - CTD->getTemplateParameters()->getParam(1)); - SampleCountExpr = SemaRef.BuildDeclRefExpr(NTTP, NTTP->getType(), - VK_PRValue, SourceLocation()); - } if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs, AttributedResTy, /*LocInfo=*/nullptr, @@ -1506,7 +1497,7 @@ CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsSliceType(ResourceDimension Dim, .addHandleMember(getResourceAttrs().ResourceClass, Dim, getResourceAttrs().IsROV, /*RawBuffer=*/false, getResourceAttrs().IsArray, ReturnType, - /*IsMultiSampled=*/false, AccessSpecifier::AS_public) + AccessSpecifier::AS_public) .addMemberVariable("__level", IntTy, {}, AccessSpecifier::AS_public) .addDefaultHandleConstructor(AccessSpecifier::AS_protected) .addCopyConstructor(AccessSpecifier::AS_protected) @@ -1550,7 +1541,7 @@ CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsType(ResourceDimension Dim, .addHandleMember(getResourceAttrs().ResourceClass, Dim, getResourceAttrs().IsROV, /*RawBuffer=*/false, getResourceAttrs().IsArray, ReturnType, - /*IsMultiSampled=*/false, AccessSpecifier::AS_public) + AccessSpecifier::AS_public) .addDefaultHandleConstructor(AccessSpecifier::AS_protected) .addCopyConstructor(AccessSpecifier::AS_protected) .addCopyAssignmentOperator(AccessSpecifier::AS_protected); diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 2b527765ce527..a7521cd8480fd 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -87,7 +87,7 @@ class BuiltinTypeDeclBuilder { AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder & addTextureHandle(ResourceClass RC, bool IsROV, bool IsArray, - ResourceDimension RD, bool IsMultiSampled = false, + ResourceDimension RD, Expr *SampleCountExpr = nullptr, AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder &addSamplerHandle(); BuiltinTypeDeclBuilder &addConstantBufferConversionToType(); @@ -162,7 +162,7 @@ class BuiltinTypeDeclBuilder { addResourceMember(StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, bool IsCounter, bool IsArray, QualType ElementTy, - bool IsMultiSampled = false, + Expr *SampleCountExpr = nullptr, AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder &addFriend(CXXRecordDecl *Friend); CXXRecordDecl *addPrivateNestedRecord(StringRef Name); @@ -171,7 +171,6 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder & addHandleMember(ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer, bool IsArray, QualType ElementTy, - bool IsMultiSampled = false, AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder & addCounterHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index c666134b83304..18822ce7f92b1 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -302,9 +302,18 @@ static BuiltinTypeDeclBuilder setupRWTextureType(CXXRecordDecl *Decl, Sema &S, static BuiltinTypeDeclBuilder setupMSTextureType(CXXRecordDecl *Decl, Sema &S, bool IsArray, ResourceDimension Dim) { + ClassTemplateDecl *CTD = Decl->getDescribedClassTemplate(); + assert(CTD && "multisampled texture must be a class template"); + + // Parameter 1 is the N in Texture2DMS<T, N>. + auto *NTTP = + cast<NonTypeTemplateParmDecl>(CTD->getTemplateParameters()->getParam(1)); + Expr *SampleCountExpr = + S.BuildDeclRefExpr(NTTP, NTTP->getType(), VK_PRValue, SourceLocation()); + return BuiltinTypeDeclBuilder(S, Decl) .addTextureHandle(ResourceClass::SRV, /*IsROV=*/false, IsArray, Dim, - /*IsMultiSampled=*/true) + SampleCountExpr) .addTextureLoadMSMethods(Dim, IsArray) .addArraySubscriptOperators(Dim, IsArray) // TODO: Add MS-specific GetDimensions (with a NumberOfSamples output); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index b3126324fae6c..1615ca3129e51 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2163,12 +2163,17 @@ bool clang::CreateHLSLAttributedResourceType( ResAttrs.IsArray = true; break; case attr::HLSLIsMultiSampled: - if (ResAttrs.IsMultiSampled) { + if (ResAttrs.SampleCountExpr) { S.Diag(A->getLocation(), diag::warn_duplicate_attribute_exact) << A; return false; } - ResAttrs.IsMultiSampled = true; - ResAttrs.SampleCountExpr = SampleCountExpr; + // A bare [[hlsl::is_ms]] carries no count, so default it to 0, the same + // value Texture2DMS<T> gets from its template parameter. + ResAttrs.SampleCountExpr = + SampleCountExpr + ? SampleCountExpr + : IntegerLiteral::Create(S.Context, llvm::APInt(32, 0), + S.Context.IntTy, A->getLocation()); break; case attr::HLSLIsCounter: if (ResAttrs.IsCounter) { @@ -3959,7 +3964,7 @@ static bool CheckLoadMSBuiltin(Sema &S, CallExpr *TheCall) { // Check the multisampled texture handle. if (CheckResourceHandle(&S, TheCall, 0, [](const HLSLAttributedResourceType *ResType) { - return !ResType->getAttrs().IsMultiSampled; + return !ResType->isMultiSampled(); })) return true; >From 789aa94228c8593474a2c1028a9d24a3baa62838 Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Tue, 18 Aug 2026 18:20:15 -0700 Subject: [PATCH 5/6] Make texture load methods return *this like the buffer loads --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 70ece482ae2e7..4a9b7b1b56a64 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -1612,12 +1612,14 @@ BuiltinTypeDeclBuilder::addTextureLoadMethods(ResourceDimension Dim, .finalize(); // T Load(int3 location, int2 offset) - return BuiltinTypeMethodBuilder(*this, "Load", ReturnType) + BuiltinTypeMethodBuilder(*this, "Load", ReturnType) .addParam("Location", LocationTy) .addParam("Offset", OffsetTy) .callBuiltin("__builtin_hlsl_resource_load_level", ReturnType, PH::Handle, PH::_0, PH::_1) .finalize(); + + return *this; } BuiltinTypeDeclBuilder & @@ -1644,13 +1646,15 @@ BuiltinTypeDeclBuilder::addTextureLoadMSMethods(ResourceDimension Dim, .finalize(); // T Load(int2 location, int sampleIndex, int2 offset) - return BuiltinTypeMethodBuilder(*this, "Load", ReturnType) + BuiltinTypeMethodBuilder(*this, "Load", ReturnType) .addParam("Location", LocationTy) .addParam("SampleIndex", IntTy) .addParam("Offset", OffsetTy) .callBuiltin("__builtin_hlsl_resource_load_ms", ReturnType, PH::Handle, PH::_0, PH::_1, PH::_2) .finalize(); + + return *this; } BuiltinTypeDeclBuilder & >From b15300b8fbb9a9dcc4f8e196941e2366f9f5d276 Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Tue, 18 Aug 2026 18:29:18 -0700 Subject: [PATCH 6/6] Link appropriate issue for TODO on MS-specific GetDimensions --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 18822ce7f92b1..d8a697041ae7d 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -318,6 +318,7 @@ static BuiltinTypeDeclBuilder setupMSTextureType(CXXRecordDecl *Decl, Sema &S, .addArraySubscriptOperators(Dim, IsArray) // TODO: Add MS-specific GetDimensions (with a NumberOfSamples output); // the generic addGetDimensionsMethods is mip-based and unsuitable here. + // https://github.com/llvm/wg-hlsl/issues/347 .addDefaultHandleConstructor() .addCopyConstructor() .addCopyAssignmentOperator() _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
