https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/177240
>From a27a614c29d8109ad8c34c50be9ad6eb6aca5ee3 Mon Sep 17 00:00:00 2001 From: Steven Perron <[email protected]> Date: Wed, 21 Jan 2026 13:35:18 -0500 Subject: [PATCH] [HLSL] Implement Texture2D type and Sample method in Clang This patch implements the `Texture2D` resource type and its `Sample` member function in Clang. It includes the necessary AST and Sema changes to support the new type and its built-in methods, as well as CodeGen support for both DirectX and SPIR-V targets. Key changes: - Added `ResourceDimension` to `HLSLAttributedResourceType` and `HLSLResourceDimension` attribute. - Implemented `Texture2D` and `SamplerState` in `HLSLExternalSemaSource`. - Added `__builtin_hlsl_resource_sample` and associated Sema checking. - Updated `DirectXTargetCodeGenInfo` and `CommonSPIRTargetCodeGenInfo` to handle texture types. - Added AST, Sema, and CodeGen tests for `Texture2D`. --- clang/include/clang/AST/TypeBase.h | 27 ++-- clang/include/clang/AST/TypeProperties.td | 8 +- clang/include/clang/Basic/Attr.td | 23 +++- clang/include/clang/Basic/Builtins.td | 6 + clang/lib/AST/TypePrinter.cpp | 8 ++ clang/lib/CodeGen/CGHLSLBuiltins.cpp | 39 ++++++ clang/lib/CodeGen/CGHLSLRuntime.h | 2 + clang/lib/CodeGen/Targets/DirectX.cpp | 40 +++++- clang/lib/CodeGen/Targets/SPIR.cpp | 19 ++- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 128 ++++++++++++++++-- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 12 +- clang/lib/Sema/HLSLExternalSemaSource.cpp | 40 ++++++ clang/lib/Sema/SemaHLSL.cpp | 88 ++++++++++++ clang/test/AST/HLSL/Texture2D-AST.hlsl | 93 +++++++++++++ .../test/CodeGenHLSL/builtins/Texture2D.hlsl | 90 ++++++++++++ .../ByteAddressBuffers-constructors.hlsl | 4 +- clang/test/SemaHLSL/Texture2D-Sema.hlsl | 43 ++++++ .../include/llvm/Frontend/HLSL/HLSLResource.h | 1 + llvm/include/llvm/Support/DXILABI.h | 8 ++ 19 files changed, 641 insertions(+), 38 deletions(-) create mode 100644 clang/test/AST/HLSL/Texture2D-AST.hlsl create mode 100644 clang/test/CodeGenHLSL/builtins/Texture2D.hlsl create mode 100644 clang/test/SemaHLSL/Texture2D-Sema.hlsl diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 279b75f14d7b8..e62ca4a3eb2c0 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -6698,6 +6698,7 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { struct Attributes { // Data gathered from HLSL resource attributes llvm::dxil::ResourceClass ResourceClass; + llvm::dxil::ResourceDimension ResourceDimension; LLVM_PREFERRED_TYPE(bool) uint8_t IsROV : 1; @@ -6708,18 +6709,27 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { LLVM_PREFERRED_TYPE(bool) uint8_t IsCounter : 1; - Attributes(llvm::dxil::ResourceClass ResourceClass, bool IsROV = false, - bool RawBuffer = false, bool IsCounter = false) - : ResourceClass(ResourceClass), IsROV(IsROV), RawBuffer(RawBuffer), - IsCounter(IsCounter) {} + Attributes(llvm::dxil::ResourceClass ResourceClass, + llvm::dxil::ResourceDimension ResourceDimension, + bool IsROV = false, bool RawBuffer = false, + bool IsCounter = false) + : ResourceClass(ResourceClass), ResourceDimension(ResourceDimension), + IsROV(IsROV), RawBuffer(RawBuffer), IsCounter(IsCounter) {} + + Attributes(llvm::dxil::ResourceClass ResourceClass) + : Attributes(ResourceClass, + llvm::dxil::ResourceDimension::DimensionUnknown) {} Attributes() - : Attributes(llvm::dxil::ResourceClass::UAV, false, false, false) {} + : Attributes(llvm::dxil::ResourceClass::UAV, + llvm::dxil::ResourceDimension::DimensionUnknown, false, + false, false) {} friend bool operator==(const Attributes &LHS, const Attributes &RHS) { - return std::tie(LHS.ResourceClass, LHS.IsROV, LHS.RawBuffer, - LHS.IsCounter) == std::tie(RHS.ResourceClass, RHS.IsROV, - RHS.RawBuffer, RHS.IsCounter); + return std::tie(LHS.ResourceClass, LHS.ResourceDimension, LHS.IsROV, + LHS.RawBuffer, LHS.IsCounter) == + std::tie(RHS.ResourceClass, RHS.ResourceDimension, RHS.IsROV, + RHS.RawBuffer, RHS.IsCounter); } friend bool operator!=(const Attributes &LHS, const Attributes &RHS) { return !(LHS == RHS); @@ -6758,6 +6768,7 @@ class HLSLAttributedResourceType : public Type, public llvm::FoldingSetNode { 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); diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index b150ac2e1cbe3..8c1e9f209e5ad 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -659,6 +659,9 @@ let Class = HLSLAttributedResourceType in { def : Property<"resClass", UInt32> { let Read = [{ static_cast<uint32_t>(node->getAttrs().ResourceClass) }]; } + def : Property<"resDimension", UInt32> { + let Read = [{ static_cast<uint32_t>(node->getAttrs().ResourceDimension) }]; + } def : Property<"isROV", Bool> { let Read = [{ node->getAttrs().IsROV }]; } @@ -675,7 +678,10 @@ let Class = HLSLAttributedResourceType in { let Read = [{ node->getContainedType() }]; } def : Creator<[{ - HLSLAttributedResourceType::Attributes attrs(static_cast<llvm::dxil::ResourceClass>(resClass), isROV, rawBuffer, isCounter); + HLSLAttributedResourceType::Attributes attrs( + static_cast<llvm::dxil::ResourceClass>(resClass), + static_cast<llvm::dxil::ResourceDimension>(resDimension), isROV, + rawBuffer, isCounter); return ctx.getHLSLAttributedResourceType(wrappedTy, containedTy, attrs); }]>; } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index ba44266d22c8c..16a7334d20f81 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5093,13 +5093,22 @@ def HLSLROV : TypeAttr { def HLSLResourceClass : TypeAttr { let Spellings = [CXX11<"hlsl", "resource_class">]; - let LangOpts = [HLSL]; - let Args = [ - EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass", - /*is_string=*/true, ["SRV", "UAV", "CBuffer", "Sampler"], - ["SRV", "UAV", "CBuffer", "Sampler"], - /*opt=*/0, /*fake=*/0, /*isExternalType=*/1> - ]; + let Args = [EnumArgument< + "ResourceClass", "llvm::hlsl::ResourceClass", + /*is_string=*/true, ["SRV", "UAV", "CBuffer", "Sampler"], + ["SRV", "UAV", "CBuffer", "Sampler"], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1>]; + let Documentation = [InternalOnly]; +} + +def HLSLResourceDimension : TypeAttr { + let Spellings = [CXX11<"hlsl", "dimension">]; + let Args = [EnumArgument< + "Dimension", "llvm::hlsl::ResourceDimension", + /*is_string=*/true, ["Unknown", "1D", "2D", "3D", "Cube"], + ["DimensionUnknown", "Dimension1D", "Dimension2D", "Dimension3D", + "DimensionCube"], + /*opt=*/0, /*fake=*/0, /*isExternalType=*/1>]; let Documentation = [InternalOnly]; } diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index bc8f1474493b0..a9e865eed0818 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5001,6 +5001,12 @@ def HLSLResourceLoadWithStatus : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLResourceSample : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_resource_sample"]; + let Attributes = [NoThrow]; + let Prototype = "void(...)"; +} + def HLSLResourceUninitializedHandle : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_resource_uninitializedhandle"]; let Attributes = [NoThrow]; diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 5a0cbc47e7c1a..cd43982bee4e0 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1972,6 +1972,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::HLSLRawBuffer: case attr::HLSLContainedType: case attr::HLSLIsCounter: + case attr::HLSLResourceDimension: llvm_unreachable("HLSL resource type attributes handled separately"); case attr::OpenCLPrivateAddressSpace: @@ -2127,6 +2128,13 @@ void TypePrinter::printHLSLAttributedResourceAfter( printAfter(ContainedTy, OS); OS << ")]]"; } + + if (Attrs.ResourceDimension != + llvm::dxil::ResourceDimension::DimensionUnknown) + OS << " [[hlsl::resource_dimension(" + << HLSLResourceDimensionAttr::ConvertResourceDimensionToStr( + Attrs.ResourceDimension) + << ")]]"; } void TypePrinter::printHLSLInlineSpirvBefore(const HLSLInlineSpirvType *T, diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 36691c7b72efe..e7597371f01fa 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -440,6 +440,45 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, RetTy, CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(), ArrayRef<Value *>{HandleOp, IndexOp}); } + case Builtin::BI__builtin_hlsl_resource_sample: { + Value *HandleOp = EmitScalarExpr(E->getArg(0)); + Value *SamplerOp = EmitScalarExpr(E->getArg(1)); + Value *CoordOp = EmitScalarExpr(E->getArg(2)); + + SmallVector<Value *, 4> Args; + Args.push_back(HandleOp); + Args.push_back(SamplerOp); + Args.push_back(CoordOp); + if (E->getNumArgs() > 3) { + Args.push_back(EmitScalarExpr(E->getArg(3))); + } else { + // Default offset is 0. + // We need to know the type of the offset. It should be a vector of i32 + // with the same number of elements as the coordinate, or scalar i32. + llvm::Type *CoordTy = CoordOp->getType(); + llvm::Type *Int32Ty = Builder.getInt32Ty(); + llvm::Type *OffsetTy = Int32Ty; + if (auto *VT = dyn_cast<llvm::FixedVectorType>(CoordTy)) + OffsetTy = llvm::FixedVectorType::get(Int32Ty, VT->getNumElements()); + Args.push_back(llvm::Constant::getNullValue(OffsetTy)); + } + + llvm::Type *RetTy = ConvertType(E->getType()); + if (E->getNumArgs() <= 4) { + return Builder.CreateIntrinsic( + RetTy, CGM.getHLSLRuntime().getSampleIntrinsic(), Args); + } + + llvm::Value *Clamp = EmitScalarExpr(E->getArg(4)); + // The builtin is defined with variadic arguments, so the clamp parameter + // might have been promoted to double. The intrinsic requires a 32-bit + // float. + if (Clamp->getType() != Builder.getFloatTy()) + Clamp = Builder.CreateFPCast(Clamp, Builder.getFloatTy()); + Args.push_back(Clamp); + return Builder.CreateIntrinsic( + RetTy, CGM.getHLSLRuntime().getSampleClampIntrinsic(), Args); + } case Builtin::BI__builtin_hlsl_resource_load_with_status: { Value *HandleOp = EmitScalarExpr(E->getArg(0)); Value *IndexOp = EmitScalarExpr(E->getArg(1)); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index ba2ca2c358388..03a64ed0fa6bb 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -159,6 +159,8 @@ class CGHLSLRuntime { GENERATE_HLSL_INTRINSIC_FUNCTION(CreateResourceGetPointer, resource_getpointer) + GENERATE_HLSL_INTRINSIC_FUNCTION(Sample, resource_sample) + GENERATE_HLSL_INTRINSIC_FUNCTION(SampleClamp, resource_sample_clamp) GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding, resource_handlefrombinding) GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromImplicitBinding, diff --git a/clang/lib/CodeGen/Targets/DirectX.cpp b/clang/lib/CodeGen/Targets/DirectX.cpp index a007c90881ab2..13c6862191dd2 100644 --- a/clang/lib/CodeGen/Targets/DirectX.cpp +++ b/clang/lib/CodeGen/Targets/DirectX.cpp @@ -58,7 +58,7 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( switch (ResAttrs.ResourceClass) { case llvm::dxil::ResourceClass::UAV: case llvm::dxil::ResourceClass::SRV: { - // TypedBuffer and RawBuffer both need element type + // TypedBuffer, RawBuffer and Texture all need element type QualType ContainedTy = ResType->getContainedType(); if (ContainedTy.isNull()) return nullptr; @@ -66,12 +66,17 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( // convert element type llvm::Type *ElemType = CGM.getTypes().ConvertTypeForMem(ContainedTy); - llvm::StringRef TypeName = - ResAttrs.RawBuffer ? "dx.RawBuffer" : "dx.TypedBuffer"; - SmallVector<unsigned, 3> Ints = {/*IsWriteable*/ ResAttrs.ResourceClass == + llvm::StringRef TypeName = "dx.TypedBuffer"; + if (ResAttrs.RawBuffer) + TypeName = "dx.RawBuffer"; + else if (ResAttrs.ResourceDimension != + llvm::dxil::ResourceDimension::DimensionUnknown) + TypeName = "dx.Texture"; + + SmallVector<unsigned, 4> Ints = {/*IsWriteable*/ ResAttrs.ResourceClass == llvm::dxil::ResourceClass::UAV, /*IsROV*/ ResAttrs.IsROV}; - if (!ResAttrs.RawBuffer) { + if (TypeName != "dx.RawBuffer") { const clang::Type *ElemType = ContainedTy->getUnqualifiedDesugaredType(); if (ElemType->isVectorType()) ElemType = cast<clang::VectorType>(ElemType) @@ -80,6 +85,28 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( Ints.push_back(/*IsSigned*/ ElemType->isSignedIntegerType()); } + if (TypeName == "dx.Texture") { + // Map ResourceDimension to dxil::ResourceKind + llvm::dxil::ResourceKind RK = llvm::dxil::ResourceKind::Invalid; + switch (ResAttrs.ResourceDimension) { + case llvm::dxil::ResourceDimension::Dimension1D: + RK = llvm::dxil::ResourceKind::Texture1D; + break; + case llvm::dxil::ResourceDimension::Dimension2D: + RK = llvm::dxil::ResourceKind::Texture2D; + break; + case llvm::dxil::ResourceDimension::Dimension3D: + RK = llvm::dxil::ResourceKind::Texture3D; + break; + case llvm::dxil::ResourceDimension::DimensionCube: + RK = llvm::dxil::ResourceKind::TextureCube; + break; + default: + break; + } + Ints.push_back(static_cast<unsigned>(RK)); + } + return llvm::TargetExtType::get(Ctx, TypeName, {ElemType}, Ints); } case llvm::dxil::ResourceClass::CBuffer: { @@ -96,8 +123,7 @@ llvm::Type *DirectXTargetCodeGenInfo::getHLSLType( return llvm::TargetExtType::get(Ctx, "dx.CBuffer", {BufferLayoutTy}); } case llvm::dxil::ResourceClass::Sampler: - llvm_unreachable("dx.Sampler handles are not implemented yet"); - break; + return llvm::TargetExtType::get(Ctx, "dx.Sampler", {}, {0}); } llvm_unreachable("Unknown llvm::dxil::ResourceClass enum"); } diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index 61ea677292492..d3c1d322aea77 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -861,8 +861,23 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getSPIRVImageTypeFromHLSLResource( Ty->isSignedIntegerType() ? "spirv.SignedImage" : "spirv.Image"; // Dim - // For now we assume everything is a buffer. - IntParams[0] = 5; + switch (attributes.ResourceDimension) { + case llvm::dxil::ResourceDimension::Dimension1D: + IntParams[0] = 0; + break; + case llvm::dxil::ResourceDimension::Dimension2D: + IntParams[0] = 1; + break; + case llvm::dxil::ResourceDimension::Dimension3D: + IntParams[0] = 2; + break; + case llvm::dxil::ResourceDimension::DimensionCube: + IntParams[0] = 3; + break; + case llvm::dxil::ResourceDimension::DimensionUnknown: + IntParams[0] = 5; + break; + } // Depth // HLSL does not indicate if it is a depth texture or not, so we use unknown. diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index f74aabf08d2c7..a76a1db65ff53 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -206,6 +206,11 @@ struct BuiltinTypeMethodBuilder { template <typename TLHS, typename TRHS> BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS); template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr); + + template <typename T> + BuiltinTypeMethodBuilder &accessFieldOnResource(T ResourceRecord, + FieldDecl *Field); + template <typename T> BuiltinTypeMethodBuilder &accessHandleFieldOnResource(T ResourceRecord); template <typename ResourceT, typename ValueT> @@ -497,7 +502,7 @@ void BuiltinTypeMethodBuilder::createDecl() { else Method = CXXMethodDecl::Create( AST, DeclBuilder.Record, SourceLocation(), NameInfo, FuncTy, TSInfo, SC, - false, false, ConstexprSpecKind::Unspecified, SourceLocation()); + false, true, ConstexprSpecKind::Unspecified, SourceLocation()); // Create params & set them to the method/constructor and function prototype. SmallVector<ParmVarDecl *> ParmDecls; @@ -631,6 +636,23 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) { return *this; } +template <typename T> +BuiltinTypeMethodBuilder & +BuiltinTypeMethodBuilder::accessFieldOnResource(T ResourceRecord, + FieldDecl *Field) { + ensureCompleteDecl(); + + Expr *ResourceExpr = convertPlaceholder(ResourceRecord); + + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + + MemberExpr *FieldExpr = + MemberExpr::CreateImplicit(AST, ResourceExpr, false, Field, + Field->getType(), VK_LValue, OK_Ordinary); + StmtsList.push_back(FieldExpr); + return *this; +} + template <typename T> BuiltinTypeMethodBuilder & BuiltinTypeMethodBuilder::accessHandleFieldOnResource(T ResourceRecord) { @@ -850,27 +872,46 @@ BuiltinTypeDeclBuilder & BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV, bool RawBuffer, bool HasCounter, AccessSpecifier Access) { - addHandleMember(RC, IsROV, RawBuffer, Access); + addHandleMember(RC, ResourceDimension::DimensionUnknown, IsROV, RawBuffer, + Access); if (HasCounter) addCounterHandleMember(RC, IsROV, RawBuffer, Access); return *this; } -BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember( - ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { - return addResourceMember("__handle", RC, IsROV, RawBuffer, +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addTextureHandle(ResourceClass RC, bool IsROV, + ResourceDimension RD, + AccessSpecifier Access) { + addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, Access); + return *this; +} + +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSamplerHandle() { + addHandleMember(ResourceClass::Sampler, ResourceDimension::DimensionUnknown, + /*IsROV=*/false, /*RawBuffer=*/false); + return *this; +} + +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addHandleMember(ResourceClass RC, ResourceDimension RD, + bool IsROV, bool RawBuffer, + AccessSpecifier Access) { + return addResourceMember("__handle", RC, RD, IsROV, RawBuffer, /*IsCounter=*/false, Access); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember( ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) { - return addResourceMember("__counter_handle", RC, IsROV, RawBuffer, + return addResourceMember("__counter_handle", RC, + ResourceDimension::DimensionUnknown, IsROV, + RawBuffer, /*IsCounter=*/true, Access); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember( - StringRef MemberName, ResourceClass RC, bool IsROV, bool RawBuffer, - bool IsCounter, AccessSpecifier Access) { + StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV, + bool RawBuffer, bool IsCounter, AccessSpecifier Access) { assert(!Record->isCompleteDefinition() && "record is already complete"); ASTContext &Ctx = SemaRef.getASTContext(); @@ -883,7 +924,10 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember( HLSLResourceClassAttr::CreateImplicit(Ctx, RC), IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr, RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr, - ElementTypeInfo + RD != ResourceDimension::DimensionUnknown + ? HLSLResourceDimensionAttr::CreateImplicit(Ctx, RD) + : nullptr, + ElementTypeInfo && RC != ResourceClass::Sampler ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) : nullptr}; if (IsCounter) @@ -1165,6 +1209,72 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() { return *this; } +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSampleMethods() { + assert(!Record->isCompleteDefinition() && "record is already complete"); + + ASTContext &AST = Record->getASTContext(); + QualType ReturnType = getFirstTemplateTypeParam(); + + // Look up SamplerState + IdentifierInfo &SamplerStateII = AST.Idents.get("SamplerState"); + LookupResult Result(SemaRef, &SamplerStateII, SourceLocation(), + Sema::LookupTagName); + SemaRef.LookupQualifiedName(Result, Record->getDeclContext()); + assert(!Result.empty() && "SamplerState not found"); + QualType SamplerStateType = + AST.getTypeDeclType(Result.getAsSingle<TypeDecl>()); + SemaRef.RequireCompleteType(SourceLocation(), SamplerStateType, + diag::err_tentative_def_incomplete_type); + + // TODO: The location type depends on the texture dimension. + // For Texture2D it is float2. + QualType FloatTy = AST.FloatTy; + QualType Float2Ty = AST.getExtVectorType(FloatTy, 2); + + QualType IntTy = AST.IntTy; + QualType Int2Ty = AST.getExtVectorType(IntTy, 2); + + auto *RT = SamplerStateType->getAsCXXRecordDecl(); + assert(RT); + assert(!RT->field_empty()); + FieldDecl *SamplerHandleField = *RT->field_begin(); + + using PH = BuiltinTypeMethodBuilder::PlaceHolder; + + // T Sample(SamplerState s, float2 location) + BuiltinTypeMethodBuilder(*this, "Sample", ReturnType) + .addParam("Sampler", SamplerStateType) + .addParam("Location", Float2Ty) + .accessFieldOnResource(PH::_0, SamplerHandleField) + .callBuiltin("__builtin_hlsl_resource_sample", ReturnType, PH::Handle, + PH::LastStmt, PH::_1) + .returnValue(PH::LastStmt) + .finalize(); + + // T Sample(SamplerState s, float2 location, int2 offset) + BuiltinTypeMethodBuilder(*this, "Sample", ReturnType) + .addParam("Sampler", SamplerStateType) + .addParam("Location", Float2Ty) + .addParam("Offset", Int2Ty) + .accessFieldOnResource(PH::_0, SamplerHandleField) + .callBuiltin("__builtin_hlsl_resource_sample", ReturnType, PH::Handle, + PH::LastStmt, PH::_1, PH::_2) + .returnValue(PH::LastStmt) + .finalize(); + + // T Sample(SamplerState s, float2 location, int2 offset, float clamp) + return BuiltinTypeMethodBuilder(*this, "Sample", ReturnType) + .addParam("Sampler", SamplerStateType) + .addParam("Location", Float2Ty) + .addParam("Offset", Int2Ty) + .addParam("Clamp", FloatTy) + .accessFieldOnResource(PH::_0, SamplerHandleField) + .callBuiltin("__builtin_hlsl_resource_sample", ReturnType, PH::Handle, + PH::LastStmt, PH::_1, PH::_2, PH::_3) + .returnValue(PH::LastStmt) + .finalize(); +} + FieldDecl *BuiltinTypeDeclBuilder::getResourceHandleField() const { auto I = Fields.find("__handle"); assert(I != Fields.end() && diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 47c8b0e225612..86e50207541ec 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -19,6 +19,7 @@ #include "llvm/ADT/StringMap.h" using llvm::hlsl::ResourceClass; +using llvm::hlsl::ResourceDimension; namespace clang { @@ -75,6 +76,10 @@ class BuiltinTypeDeclBuilder { addBufferHandles(ResourceClass RC, bool IsROV, bool RawBuffer, bool HasCounter, AccessSpecifier Access = AccessSpecifier::AS_private); + BuiltinTypeDeclBuilder & + addTextureHandle(ResourceClass RC, bool IsROV, ResourceDimension RD, + AccessSpecifier Access = AccessSpecifier::AS_private); + BuiltinTypeDeclBuilder &addSamplerHandle(); BuiltinTypeDeclBuilder &addArraySubscriptOperators(); // Builtin types constructors @@ -87,6 +92,7 @@ class BuiltinTypeDeclBuilder { // Builtin types methods BuiltinTypeDeclBuilder &addLoadMethods(); + BuiltinTypeDeclBuilder &addSampleMethods(); BuiltinTypeDeclBuilder &addIncrementCounterMethod(); BuiltinTypeDeclBuilder &addDecrementCounterMethod(); BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name, @@ -104,11 +110,13 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder &addCreateFromBindingWithImplicitCounter(); BuiltinTypeDeclBuilder &addCreateFromImplicitBindingWithImplicitCounter(); BuiltinTypeDeclBuilder &addResourceMember(StringRef MemberName, - ResourceClass RC, bool IsROV, + ResourceClass RC, + ResourceDimension RD, bool IsROV, bool RawBuffer, bool IsCounter, AccessSpecifier Access); BuiltinTypeDeclBuilder & - addHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer, + addHandleMember(ResourceClass RC, ResourceDimension RD, bool IsROV, + bool RawBuffer, 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 6be84f19a8f08..db72c0b07aa6b 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -500,6 +500,46 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { .addGetDimensionsMethodForBuffer() .completeDefinition(); }); + + Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "SamplerState") + .finalizeForwardDeclaration(); + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + BuiltinTypeDeclBuilder(*SemaPtr, Decl) + .addSamplerHandle() + .addDefaultHandleConstructor() + .addCopyConstructor() + .addCopyAssignmentOperator() + .addStaticInitializationFunctions(false) + .completeDefinition(); + }); + + Decl = + BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "SamplerComparisonState") + .finalizeForwardDeclaration(); + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + BuiltinTypeDeclBuilder(*SemaPtr, Decl) + .addSamplerHandle() + .addDefaultHandleConstructor() + .addCopyConstructor() + .addCopyAssignmentOperator() + .addStaticInitializationFunctions(false) + .completeDefinition(); + }); + + Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Texture2D") + .addSimpleTemplateParams({"element_type"}, TypedBufferConcept) + .finalizeForwardDeclaration(); + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + BuiltinTypeDeclBuilder(*SemaPtr, Decl) + .addTextureHandle(ResourceClass::SRV, /*IsROV=*/false, + ResourceDimension::Dimension2D) + .addDefaultHandleConstructor() + .addCopyConstructor() + .addCopyAssignmentOperator() + .addStaticInitializationFunctions(false) + .addSampleMethods() + .completeDefinition(); + }); } void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record, diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 4d31e26d56e6b..52d6eab10fea5 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2005,6 +2005,7 @@ bool clang::CreateHLSLAttributedResourceType( HLSLAttributedResourceType::Attributes ResAttrs; bool HasResourceClass = false; + bool HasResourceDimension = false; for (const Attr *A : AttrList) { if (!A) continue; @@ -2023,6 +2024,20 @@ bool clang::CreateHLSLAttributedResourceType( HasResourceClass = true; break; } + case attr::HLSLResourceDimension: { + llvm::dxil::ResourceDimension RD = + cast<HLSLResourceDimensionAttr>(A)->getDimension(); + if (HasResourceDimension) { + S.Diag(A->getLocation(), ResAttrs.ResourceDimension == RD + ? diag::warn_duplicate_attribute_exact + : diag::warn_duplicate_attribute) + << A; + return false; + } + ResAttrs.ResourceDimension = RD; + HasResourceDimension = true; + break; + } case attr::HLSLROV: if (ResAttrs.IsROV) { S.Diag(A->getLocation(), diag::warn_duplicate_attribute_exact) << A; @@ -3264,6 +3279,23 @@ static bool CheckResourceHandle( return false; } +static bool CheckVectorElementCount(Sema *S, QualType PassedType, + QualType BaseType, unsigned ExpectedCount, + SourceLocation Loc) { + unsigned PassedCount = 1; + if (const auto *VecTy = PassedType->getAs<VectorType>()) + PassedCount = VecTy->getNumElements(); + + if (PassedCount != ExpectedCount) { + QualType ExpectedType = + S->Context.getExtVectorType(BaseType, ExpectedCount); + S->Diag(Loc, diag::err_typecheck_convert_incompatible) + << PassedType << ExpectedType << 1 << 0 << 0; + return true; + } + return false; +} + // Note: returning true in this case results in CheckBuiltinFunctionCall // returning an ExprError bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { @@ -3334,7 +3366,63 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } + case Builtin::BI__builtin_hlsl_resource_sample: { + if (SemaRef.checkArgCountRange(TheCall, 3, 5) || + CheckResourceHandle(&SemaRef, TheCall, 0) || + CheckResourceHandle(&SemaRef, TheCall, 1, + [](const HLSLAttributedResourceType *ResType) { + return ResType->getAttrs().ResourceClass != + llvm::hlsl::ResourceClass::Sampler; + })) + return true; + auto *ResourceTy = + TheCall->getArg(0)->getType()->castAs<HLSLAttributedResourceType>(); + + unsigned ExpectedDim = 0; + switch (ResourceTy->getAttrs().ResourceDimension) { + case llvm::dxil::ResourceDimension::Dimension1D: + ExpectedDim = 1; + break; + case llvm::dxil::ResourceDimension::Dimension2D: + ExpectedDim = 2; + break; + case llvm::dxil::ResourceDimension::Dimension3D: + case llvm::dxil::ResourceDimension::DimensionCube: + ExpectedDim = 3; + break; + case llvm::dxil::ResourceDimension::DimensionUnknown: + break; + } + + if (ExpectedDim != 0) { + if (CheckVectorElementCount(&SemaRef, TheCall->getArg(2)->getType(), + SemaRef.Context.FloatTy, ExpectedDim, + TheCall->getArg(2)->getBeginLoc())) + return true; + + if (TheCall->getNumArgs() > 3) { + if (CheckVectorElementCount(&SemaRef, TheCall->getArg(3)->getType(), + SemaRef.Context.IntTy, ExpectedDim, + TheCall->getArg(3)->getBeginLoc())) + return true; + } + } + if (TheCall->getNumArgs() > 4) { + QualType ClampTy = TheCall->getArg(4)->getType(); + if (!ClampTy->isFloatingType() || ClampTy->isVectorType()) { + SemaRef.Diag(TheCall->getArg(4)->getBeginLoc(), + diag::err_typecheck_convert_incompatible) + << ClampTy << SemaRef.Context.FloatTy << 1 << 0 << 0; + return true; + } + } + + QualType ReturnType = ResourceTy->getContainedType(); + TheCall->setType(ReturnType); + + break; + } case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: { assert(TheCall->getNumArgs() == 1 && "expected 1 arg"); // Update return type to be the attributed resource type from arg0. diff --git a/clang/test/AST/HLSL/Texture2D-AST.hlsl b/clang/test/AST/HLSL/Texture2D-AST.hlsl new file mode 100644 index 0000000000000..95343712b72fa --- /dev/null +++ b/clang/test/AST/HLSL/Texture2D-AST.hlsl @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -disable-llvm-passes -finclude-default-header -o - %s | FileCheck %s + +// CHECK: CXXRecordDecl {{.*}} SamplerState definition +// CHECK: FinalAttr {{.*}} Implicit final +// CHECK-NEXT: FieldDecl {{.*}} implicit {{.*}} __handle '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(Sampler)]] + +// CHECK: ClassTemplateDecl {{.*}} Texture2D +// CHECK: TemplateTypeParmDecl {{.*}} element_type +// CHECK: CXXRecordDecl {{.*}} Texture2D definition +// CHECK: FinalAttr {{.*}} Implicit final +// CHECK-NEXT: FieldDecl {{.*}} implicit __handle '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] + +// CHECK: CXXMethodDecl {{.*}} Sample 'element_type (hlsl::SamplerState, vector<float, 2>)' +// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState' +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 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_sample' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::Texture2D<element_type>' lvalue implicit this +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(Sampler)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl::SamplerState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerState' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>' +// CHECK-NEXT: AlwaysInlineAttr + +// CHECK: CXXMethodDecl {{.*}} Sample 'element_type (hlsl::SamplerState, vector<float, 2>, vector<int, 2>)' +// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState' +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>' +// 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_sample' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::Texture2D<element_type>' lvalue implicit this +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(Sampler)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl::SamplerState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerState' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>' +// CHECK-NEXT: AlwaysInlineAttr + +// CHECK: CXXMethodDecl {{.*}} Sample 'element_type (hlsl::SamplerState, vector<float, 2>, vector<int, 2>, float)' +// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState' +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>' +// CHECK-NEXT: ParmVarDecl {{.*}} Offset 'vector<int, 2>' +// CHECK-NEXT: ParmVarDecl {{.*}} Clamp 'float' +// 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_sample' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::Texture2D<element_type>' lvalue implicit this +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(Sampler)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl::SamplerState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerState' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>' +// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'Clamp' 'float' +// CHECK-NEXT: AlwaysInlineAttr + +Texture2D<float4> t; +SamplerState s; + +void main(float2 loc) { + t.Sample(s, loc); + t.Sample(s, loc, int2(1, 2)); + t.Sample(s, loc, int2(1, 2), 1.0); +} \ No newline at end of file diff --git a/clang/test/CodeGenHLSL/builtins/Texture2D.hlsl b/clang/test/CodeGenHLSL/builtins/Texture2D.hlsl new file mode 100644 index 0000000000000..88fa2b5f623f8 --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/Texture2D.hlsl @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | FileCheck %s --check-prefixes=CHECK,DXIL +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | FileCheck %s --check-prefixes=CHECK,SPIRV + +// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) } +// DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) } + +// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) } +// SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") } + +Texture2D<float4> t; +SamplerState s; + +// CHECK: define hidden {{.*}} <4 x float> @_Z4mainDv2_f(<2 x float> noundef nofpclass(nan inf) %[[LOC:.*]]) +// CHECK: %[[CALL:.*]] = call reassoc nnan ninf nsz arcp afn noundef nofpclass(nan inf) <4 x float> @_ZN4hlsl9Texture2DIDv4_fE6SampleENS_12SamplerStateEDv2_f(ptr {{.*}} @_ZL1t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}) +// CHECK: ret <4 x float> %[[CALL]] + +float4 main(float2 loc : LOC) : SV_Target { + return t.Sample(s, loc); +} + +// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x float> @_ZN4hlsl9Texture2DIDv4_fE6SampleENS_12SamplerStateEDv2_f(ptr {{.*}} %[[THIS:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER:[^,]+]], <2 x float> {{.*}} %[[COORD:[^)]+]]) +// CHECK: %[[THIS_ADDR:[^ ]+]] = alloca ptr +// CHECK: %[[COORD_ADDR:[^ ]+]] = alloca <2 x float> +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] +// CHECK: store <2 x float> %[[COORD]], ptr %[[COORD_ADDR]] +// CHECK: %[[THIS_VAL:[^ ]+]] = load ptr, ptr %[[THIS_ADDR]] +// CHECK: %[[HANDLE_GEP:[^ ]+]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL]], i32 0, i32 0 +// CHECK: %[[HANDLE:[^ ]+]] = load target{{.*}}, ptr %[[HANDLE_GEP]] +// CHECK: %[[SAMPLER_GEP:[^ ]+]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER]], i32 0, i32 0 +// CHECK: %[[SAMPLER_H:[^ ]+]] = load target{{.*}}, ptr %[[SAMPLER_GEP]] +// CHECK: %[[COORD_VAL:[^ ]+]] = load <2 x float>, ptr %[[COORD_ADDR]] +// DXIL: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.dx.resource.sample.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE]], target("dx.Sampler", 0) %[[SAMPLER_H]], <2 x float> %[[COORD_VAL]], <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE]], target("spirv.Sampler") %[[SAMPLER_H]], <2 x float> %[[COORD_VAL]], <2 x i32> zeroinitializer) +// CHECK: ret <4 x float> %[[RES]] + +// CHECK: define hidden {{.*}} <4 x float> @_Z11test_offsetDv2_f(<2 x float> noundef nofpclass(nan inf) %[[LOC:.*]]) +// CHECK: %[[CALL:.*]] = call reassoc nnan ninf nsz arcp afn noundef nofpclass(nan inf) <4 x float> @_ZN4hlsl9Texture2DIDv4_fE6SampleENS_12SamplerStateEDv2_fDv2_i(ptr {{.*}} @_ZL1t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x i32> {{.*}} <i32 1, i32 2>) +// CHECK: ret <4 x float> %[[CALL]] + +float4 test_offset(float2 loc : LOC) : SV_Target { + return t.Sample(s, loc, int2(1, 2)); +} + +// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x float> @_ZN4hlsl9Texture2DIDv4_fE6SampleENS_12SamplerStateEDv2_fDv2_i(ptr {{.*}} %[[THIS:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER:[^,]+]], <2 x float> {{.*}} %[[COORD:[^,]+]], <2 x i32> {{.*}} %[[OFFSET:[^)]+]]) +// CHECK: %[[THIS_ADDR:[^ ]+]] = alloca ptr +// CHECK: %[[COORD_ADDR:[^ ]+]] = alloca <2 x float> +// CHECK: %[[OFFSET_ADDR:[^ ]+]] = alloca <2 x i32> +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] +// CHECK: store <2 x float> %[[COORD]], ptr %[[COORD_ADDR]] +// CHECK: store <2 x i32> %[[OFFSET]], ptr %[[OFFSET_ADDR]] +// CHECK: %[[THIS_VAL:[^ ]+]] = load ptr, ptr %[[THIS_ADDR]] +// CHECK: %[[HANDLE_GEP:[^ ]+]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL]], i32 0, i32 0 +// CHECK: %[[HANDLE:[^ ]+]] = load target{{.*}}, ptr %[[HANDLE_GEP]] +// CHECK: %[[SAMPLER_GEP:[^ ]+]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER]], i32 0, i32 0 +// CHECK: %[[SAMPLER_H:[^ ]+]] = load target{{.*}}, ptr %[[SAMPLER_GEP]] +// CHECK: %[[COORD_VAL:[^ ]+]] = load <2 x float>, ptr %[[COORD_ADDR]] +// CHECK: %[[OFFSET_VAL:[^ ]+]] = load <2 x i32>, ptr %[[OFFSET_ADDR]] +// DXIL: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.dx.resource.sample.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE]], target("dx.Sampler", 0) %[[SAMPLER_H]], <2 x float> %[[COORD_VAL]], <2 x i32> %[[OFFSET_VAL]]) +// SPIRV: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE]], target("spirv.Sampler") %[[SAMPLER_H]], <2 x float> %[[COORD_VAL]], <2 x i32> %[[OFFSET_VAL]]) +// CHECK: ret <4 x float> %[[RES]] + +// CHECK: define hidden {{.*}} <4 x float> @_Z10test_clampDv2_f(<2 x float> noundef nofpclass(nan inf) %[[LOC:.*]]) +// CHECK: %[[CALL:.*]] = call reassoc nnan ninf nsz arcp afn noundef nofpclass(nan inf) <4 x float> @_ZN4hlsl9Texture2DIDv4_fE6SampleENS_12SamplerStateEDv2_fDv2_if(ptr {{.*}} @_ZL1t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}}, <2 x i32> {{.*}} <i32 1, i32 2>, float {{.*}} 1.000000e+00) +// CHECK: ret <4 x float> %[[CALL]] + +float4 test_clamp(float2 loc : LOC) : SV_Target { + return t.Sample(s, loc, int2(1, 2), 1.0f); +} + +// CHECK: define linkonce_odr hidden noundef nofpclass(nan inf) <4 x float> @_ZN4hlsl9Texture2DIDv4_fE6SampleENS_12SamplerStateEDv2_fDv2_if(ptr {{.*}} %[[THIS:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER:[^,]+]], <2 x float> {{.*}} %[[COORD:[^,]+]], <2 x i32> {{.*}} %[[OFFSET:[^,]+]], float {{.*}} %[[CLAMP:[^)]+]]) +// CHECK: %[[THIS_ADDR:[^ ]+]] = alloca ptr +// CHECK: %[[COORD_ADDR:[^ ]+]] = alloca <2 x float> +// CHECK: %[[OFFSET_ADDR:[^ ]+]] = alloca <2 x i32> +// CHECK: %[[CLAMP_ADDR:[^ ]+]] = alloca float +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] +// CHECK: store <2 x float> %[[COORD]], ptr %[[COORD_ADDR]] +// CHECK: store <2 x i32> %[[OFFSET]], ptr %[[OFFSET_ADDR]] +// CHECK: store float %[[CLAMP]], ptr %[[CLAMP_ADDR]] +// CHECK: %[[THIS_VAL:[^ ]+]] = load ptr, ptr %[[THIS_ADDR]] +// CHECK: %[[HANDLE_GEP:[^ ]+]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL]], i32 0, i32 0 +// CHECK: %[[HANDLE:[^ ]+]] = load target{{.*}}, ptr %[[HANDLE_GEP]] +// CHECK: %[[SAMPLER_GEP:[^ ]+]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER]], i32 0, i32 0 +// CHECK: %[[SAMPLER_H:[^ ]+]] = load target{{.*}}, ptr %[[SAMPLER_GEP]] +// CHECK: %[[COORD_VAL:[^ ]+]] = load <2 x float>, ptr %[[COORD_ADDR]] +// CHECK: %[[OFFSET_VAL:[^ ]+]] = load <2 x i32>, ptr %[[OFFSET_ADDR]] +// CHECK: %[[CLAMP_VAL:[^ ]+]] = load float, ptr %[[CLAMP_ADDR]] +// CHECK: %[[CLAMP_CAST:[^ ]+]] = fptrunc {{.*}} double {{.*}} to float +// DXIL: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.dx.resource.sample.clamp.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE]], target("dx.Sampler", 0) %[[SAMPLER_H]], <2 x float> %[[COORD_VAL]], <2 x i32> %[[OFFSET_VAL]], float %[[CLAMP_CAST]]) +// SPIRV: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE]], target("spirv.Sampler") %[[SAMPLER_H]], <2 x float> %[[COORD_VAL]], <2 x i32> %[[OFFSET_VAL]], float %[[CLAMP_CAST]]) +// CHECK: ret <4 x float> %[[RES]] \ No newline at end of file diff --git a/clang/test/CodeGenHLSL/resources/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/resources/ByteAddressBuffers-constructors.hlsl index 9354ee714f86e..4ac71d0093b8b 100644 --- a/clang/test/CodeGenHLSL/resources/ByteAddressBuffers-constructors.hlsl +++ b/clang/test/CodeGenHLSL/resources/ByteAddressBuffers-constructors.hlsl @@ -37,7 +37,7 @@ export void foo() { // CHECK-SAME: (ptr {{.*}} @Buf1, i32 noundef 1, i32 noundef 2, i32 noundef 1, i32 noundef 0, ptr noundef @[[Buf1Str]]) // Buf1 initialization part 2 - body of ByteAddressBuffer::__createFromBinding -// CHECK: define {{.*}} void @hlsl::ByteAddressBuffer::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*) +// CHECK: define linkonce_odr hidden void @hlsl::ByteAddressBuffer::__createFromBinding(unsigned int, unsigned int, int, unsigned int, char const*) // CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::ByteAddressBuffer") align 4 %[[RetValue1:.*]], i32 noundef %registerNo, // CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, ptr noundef %name) // CHECK: %[[Tmp1:.*]] = alloca %"class.hlsl::ByteAddressBuffer", align 4 @@ -54,7 +54,7 @@ export void foo() { // CHECK-SAME: (ptr {{.*}} @Buf2, i32 noundef 0, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @[[Buf2Str]]) // Buf2 initialization part 2 - body of RWByteAddressBuffer::__createFromImplicitBinding -// CHECK: define hidden void @hlsl::RWByteAddressBuffer::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*) +// CHECK: define linkonce_odr hidden void @hlsl::RWByteAddressBuffer::__createFromImplicitBinding(unsigned int, unsigned int, int, unsigned int, char const*) // CHECK-SAME: (ptr {{.*}} sret(%"class.hlsl::RWByteAddressBuffer") align 4 %[[RetValue2:.*]], i32 noundef %orderId, // CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, ptr noundef %name) // CHECK: %[[Tmp2:.*]] = alloca %"class.hlsl::RWByteAddressBuffer", align 4 diff --git a/clang/test/SemaHLSL/Texture2D-Sema.hlsl b/clang/test/SemaHLSL/Texture2D-Sema.hlsl new file mode 100644 index 0000000000000..9013148d901eb --- /dev/null +++ b/clang/test/SemaHLSL/Texture2D-Sema.hlsl @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -fsyntax-only -finclude-default-header -verify %s + +Texture2D<float4> t; +SamplerState s; + +void main(float2 loc) { + t.Sample(s, loc); + t.Sample(s, loc, int2(1, 2)); + + // expected-error@+4 {{no matching member function for call to 'Sample'}} + // 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}} + // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 1 was provided}} + t.Sample(loc); + + t.Sample(s, loc, int2(1, 2), 1.0); + + // expected-error@+4 {{no matching member function for call to 'Sample'}} + // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 5 were provided}} + // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 5 were provided}} + // expected-note@*:* {{candidate function not viable: requires 2 arguments, but 5 were provided}} + t.Sample(s, loc, int2(1, 2), 1.0, 1.0); + + // expected-error@+4 {{no matching member function for call to 'Sample'}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'SamplerState' to 'vector<int, 2>' (vector of 2 'int' values) for 3rd argument}} + // expected-note@*:* {{candidate function not viable: requires 2 arguments, but 3 were provided}} + // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 3 were provided}} + t.Sample(s, loc, s); + + // expected-error@+4 {{no matching member function for call to 'Sample'}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'SamplerState' to 'float' for 4th argument}} + // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 4 were provided}} + // expected-note@*:* {{candidate function not viable: requires 2 arguments, but 4 were provided}} + t.Sample(s, loc, int2(1, 2), s); + + // Test with wrong coordinate dimension. + // Note: float implicitly converts to float2 (splat), so no error here. + t.Sample(s, loc.x); + + // Test with wrong offset dimension. + // Note: int implicitly converts to int2 (splat), so no error here. + t.Sample(s, loc, 1); +} diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLResource.h b/llvm/include/llvm/Frontend/HLSL/HLSLResource.h index cfa0e957c9589..8849ba880ad11 100644 --- a/llvm/include/llvm/Frontend/HLSL/HLSLResource.h +++ b/llvm/include/llvm/Frontend/HLSL/HLSLResource.h @@ -20,6 +20,7 @@ namespace hlsl { // For now we use DXIL ABI enum values directly. This may change in the future. using dxil::ResourceClass; +using dxil::ResourceDimension; const unsigned CBufferRowSizeInBytes = 16U; diff --git a/llvm/include/llvm/Support/DXILABI.h b/llvm/include/llvm/Support/DXILABI.h index e6600c3406df5..285ac1b663685 100644 --- a/llvm/include/llvm/Support/DXILABI.h +++ b/llvm/include/llvm/Support/DXILABI.h @@ -31,6 +31,14 @@ enum class ResourceClass : uint8_t { LastEntry = Sampler, }; +enum class ResourceDimension : uint8_t { + DimensionUnknown = 0, + Dimension1D, + Dimension2D, + Dimension3D, + DimensionCube, +}; + /// The kind of resource for an SRV or UAV resource. Sometimes referred to as /// "Shape" in the DXIL docs. enum class ResourceKind : uint32_t { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
