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

Reply via email to