[clang] [llvm] [HLSL] Implement Sample* methods for Texture2D (PR #179322)

Steven Perron via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 6 08:52:07 PST 2026


https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/179322

>From 2a0af61943a1ddd7920ce922e093792009f227a9 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 23 Jan 2026 13:34:05 -0500
Subject: [PATCH 1/8] spirv: implement lowering for HLSL Texture2D sampling
 intrinsics

This patch implements the SPIR-V lowering for the following HLSL intrinsics:
- SampleBias
- SampleGrad
- SampleLevel
- SampleCmp
- SampleCmpLevelZero

It defines the required LLVM intrinsics in 'IntrinsicsDirectX.td' and
'IntrinsicsSPIRV.td'.

It updates 'SPIRVInstructionSelector.cpp' to handle the new intrinsics and
generates the correct 'OpImageSample*' instructions with the required operands
(Bias, Grad, Lod, ConstOffset, MinLod, etc.).

CodeGen tests are added to verify the implementation for images with
dimension 1D, 2D, 3D, and Cube.

Assisted-by: Gemini
---
 llvm/include/llvm/IR/IntrinsicsDirectX.td     |  47 +++
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |  49 +++
 llvm/lib/Target/SPIRV/SPIRVInstrInfo.td       |   8 +-
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 279 +++++++++++++++---
 llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp |  27 +-
 .../CodeGen/SPIRV/hlsl-resources/Sample.ll    |   1 +
 .../CodeGen/SPIRV/hlsl-resources/Sample1D.ll  |  69 +++++
 .../CodeGen/SPIRV/hlsl-resources/Sample3D.ll  |  70 +++++
 .../SPIRV/hlsl-resources/SampleBias.ll        |  67 +++++
 .../CodeGen/SPIRV/hlsl-resources/SampleCmp.ll |  68 +++++
 .../hlsl-resources/SampleCmpLevelZero.ll      |  52 ++++
 .../SPIRV/hlsl-resources/SampleCube.ll        |  52 ++++
 .../SPIRV/hlsl-resources/SampleGrad.ll        |  68 +++++
 .../SPIRV/hlsl-resources/SampleLevel.ll       |  51 ++++
 14 files changed, 854 insertions(+), 54 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/Sample1D.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/Sample3D.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/SampleBias.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmp.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmpLevelZero.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCube.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/SampleGrad.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/SampleLevel.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index f3494450fa88f..3ede80e8f36af 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -90,6 +90,53 @@ def int_dx_resource_sample_clamp
                             [llvm_any_ty, llvm_any_ty, llvm_any_ty, llvm_any_ty,
                              llvm_float_ty],
                             [IntrReadMem]>;
+def int_dx_resource_samplebias
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_float_ty, llvm_any_ty],
+                            [IntrReadMem]>;
+
+def int_dx_resource_samplebias_clamp
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_float_ty, llvm_any_ty, llvm_float_ty],
+                            [IntrReadMem]>;
+
+def int_dx_resource_samplegrad
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_any_ty, llvm_any_ty],
+                            [IntrReadMem]>;
+
+def int_dx_resource_samplegrad_clamp
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_any_ty, llvm_any_ty, llvm_float_ty],
+                            [IntrReadMem]>;
+
+def int_dx_resource_samplelevel
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_float_ty, llvm_any_ty],
+                            [IntrReadMem]>;
+
+def int_dx_resource_samplecmp
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_float_ty, llvm_any_ty],
+                            [IntrReadMem]>;
+
+def int_dx_resource_samplecmp_clamp
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_float_ty, llvm_any_ty, llvm_float_ty],
+                            [IntrReadMem]>;
+
+def int_dx_resource_samplecmplevelzero
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                             llvm_float_ty, llvm_any_ty],
+                            [IntrReadMem]>;
 
 // Cast between target extension handle types and dxil-style opaque handles
 def int_dx_resource_casthandle : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index f26ddfffdfe3d..a8121b0220017 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -194,6 +194,55 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
                                llvm_any_ty, llvm_float_ty],
                               [IntrReadMem]>;
 
+  def int_spv_resource_samplebias
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_float_ty, llvm_any_ty],
+                              [IntrReadMem]>;
+
+  def int_spv_resource_samplebias_clamp
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_float_ty, llvm_any_ty, llvm_float_ty],
+                              [IntrReadMem]>;
+
+  def int_spv_resource_samplegrad
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_any_ty, llvm_any_ty, llvm_any_ty],
+                              [IntrReadMem]>;
+
+  def int_spv_resource_samplegrad_clamp
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_float_ty],
+                              [IntrReadMem]>;
+
+  def int_spv_resource_samplelevel
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_float_ty, llvm_any_ty],
+                              [IntrReadMem]>;
+
+  def int_spv_resource_samplecmp
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_float_ty, llvm_any_ty],
+                              [IntrReadMem]>;
+
+  def int_spv_resource_samplecmp_clamp
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_float_ty, llvm_any_ty, llvm_float_ty],
+                              [IntrReadMem]>;
+
+  def int_spv_resource_samplecmplevelzero
+      : DefaultAttrsIntrinsic<[llvm_any_ty],
+                              [llvm_any_ty, llvm_any_ty, llvm_any_ty,
+                               llvm_float_ty, llvm_any_ty],
+                              [IntrReadMem]>;
+
   def int_spv_resource_getpointer
       : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_i32_ty],
                               [IntrNoMem]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index 4473a6cf8b5a7..7b5b2707f6771 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -320,7 +320,7 @@ def OpImageSampleExplicitLod: Op<88, (outs ID:$res),
 
 def OpImageSampleDrefImplicitLod: Op<89, (outs ID:$res),
                   (ins TYPE:$type, ID:$sampledImage, ID:$coord, ID:$dref, variable_ops),
-                  "$res = OpImageSampleDrefImplicitLod $type $sampledImage $dref $coord">;
+                  "$res = OpImageSampleDrefImplicitLod $type $sampledImage $coord $dref">;
 def OpImageSampleDrefExplicitLod: Op<90, (outs ID:$res),
                   (ins TYPE:$ty, ID:$im, ID:$uv, ID:$d, ImageOperand:$op, ID:$i, variable_ops),
                   "$res = OpImageSampleDrefExplicitLod $ty $im $uv $d $op $i">;
@@ -334,7 +334,7 @@ def OpImageSampleProjExplicitLod: Op<92, (outs ID:$res),
 
 def OpImageSampleProjDrefImplicitLod: Op<93, (outs ID:$res),
                   (ins TYPE:$type, ID:$sampledImage, ID:$coord, ID:$dref, variable_ops),
-                  "$res = OpImageSampleProjDrefImplicitLod $type $sampledImage $dref $coord">;
+                  "$res = OpImageSampleProjDrefImplicitLod $type $sampledImage $coord $dref">;
 def OpImageSampleProjDrefExplicitLod: Op<94, (outs ID:$res),
                   (ins TYPE:$ty, ID:$im, ID:$uv, ID:$d, ImageOperand:$op, ID:$i, variable_ops),
                   "$res = OpImageSampleProjDrefExplicitLod $ty $im $uv $d $op $i">;
@@ -373,7 +373,7 @@ def OpImageSparseSampleExplicitLod: Op<306, (outs ID:$res),
 
 def OpImageSparseSampleDrefImplicitLod: Op<307, (outs ID:$res),
                   (ins TYPE:$type, ID:$sampledImg, ID:$coord, ID:$dref, variable_ops),
-                  "$res = OpImageSparseSampleDrefImplicitLod $type $sampledImg $dref $coord">;
+                  "$res = OpImageSparseSampleDrefImplicitLod $type $sampledImg $coord $dref">;
 def OpImageSparseSampleDrefExplicitLod: Op<308, (outs ID:$res),
                   (ins TYPE:$ty, ID:$im, ID:$uv, ID:$d, ImageOperand:$op, ID:$i, variable_ops),
                   "$res = OpImageSparseSampleDrefExplicitLod $ty $im $uv $d $op $i">;
@@ -387,7 +387,7 @@ def OpImageSparseSampleProjExplicitLod: Op<310, (outs ID:$res),
 
 def OpImageSparseSampleProjDrefImplicitLod: Op<311, (outs ID:$res),
                   (ins TYPE:$type, ID:$sImage, ID:$coord, ID:$dref, variable_ops),
-                  "$res = OpImageSparseSampleProjDrefImplicitLod $type $sImage $dref $coord">;
+                  "$res = OpImageSparseSampleProjDrefImplicitLod $type $sImage $coord $dref">;
 def OpImageSparseSampleProjDrefExplicitLod: Op<312, (outs ID:$res),
                   (ins TYPE:$ty, ID:$im, ID:$uv, ID:$d, ImageOperand:$op, ID:$i, variable_ops),
                   "$res = OpImageSparseSampleProjDrefExplicitLod $ty $im $uv $d $op $i">;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index ecbe24cb1c3b6..21c02352c0102 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -44,6 +44,16 @@ using ExtInstList =
 
 namespace {
 
+struct ImageOperands {
+  std::optional<Register> Bias;
+  std::optional<Register> Offset;
+  std::optional<Register> MinLod;
+  std::optional<Register> GradX;
+  std::optional<Register> GradY;
+  std::optional<Register> Lod;
+  std::optional<Register> Compare;
+};
+
 llvm::SPIRV::SelectionControl::SelectionControl
 getSelectionOperandForImm(int Imm) {
   if (Imm == 2)
@@ -329,8 +339,19 @@ class SPIRVInstructionSelector : public InstructionSelector {
 
   bool selectReadImageIntrinsic(Register &ResVReg, const SPIRVType *ResType,
                                 MachineInstr &I) const;
-  bool selectSampleIntrinsic(Register &ResVReg, const SPIRVType *ResType,
-                             MachineInstr &I) const;
+  bool selectSampleBasicIntrinsic(Register &ResVReg, const SPIRVType *ResType,
+                                  MachineInstr &I) const;
+  bool selectSampleBiasIntrinsic(Register &ResVReg, const SPIRVType *ResType,
+                                 MachineInstr &I) const;
+  bool selectSampleGradIntrinsic(Register &ResVReg, const SPIRVType *ResType,
+                                 MachineInstr &I) const;
+  bool selectSampleLevelIntrinsic(Register &ResVReg, const SPIRVType *ResType,
+                                  MachineInstr &I) const;
+  bool selectSampleCmpIntrinsic(Register &ResVReg, const SPIRVType *ResType,
+                                MachineInstr &I) const;
+  bool selectSampleCmpLevelZeroIntrinsic(Register &ResVReg,
+                                         const SPIRVType *ResType,
+                                         MachineInstr &I) const;
   bool selectImageWriteIntrinsic(MachineInstr &I) const;
   bool selectResourceGetPointer(Register &ResVReg, const SPIRVType *ResType,
                                 MachineInstr &I) const;
@@ -354,6 +375,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
 
   Register buildZerosVal(const SPIRVType *ResType, MachineInstr &I) const;
   bool isScalarOrVectorIntConstantZero(Register Reg) const;
+  bool isScalarOrVectorConstant(Register Reg) const;
   Register buildZerosValF(const SPIRVType *ResType, MachineInstr &I) const;
   Register buildOnesVal(bool AllOnes, const SPIRVType *ResType,
                         MachineInstr &I) const;
@@ -381,6 +403,10 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool generateImageReadOrFetch(Register &ResVReg, const SPIRVType *ResType,
                                 Register ImageReg, Register IdxReg,
                                 DebugLoc Loc, MachineInstr &Pos) const;
+  bool generateSampleImage(Register ResVReg, const SPIRVType *ResType,
+                           Register ImageReg, Register SamplerReg,
+                           Register CoordinateReg, const ImageOperands &ImOps,
+                           DebugLoc Loc, MachineInstr &I) const;
   bool BuildCOPY(Register DestReg, Register SrcReg, MachineInstr &I) const;
   bool loadVec3BuiltinInputID(SPIRV::BuiltIn::BuiltIn BuiltInValue,
                               Register ResVReg, const SPIRVType *ResType,
@@ -3122,6 +3148,47 @@ bool SPIRVInstructionSelector::isScalarOrVectorIntConstantZero(
   return false;
 }
 
+bool SPIRVInstructionSelector::isScalarOrVectorConstant(Register Reg) const {
+  auto IsConstant = [this](Register Reg) {
+    MachineInstr *Def = getDefInstrMaybeConstant(Reg, MRI);
+    if (!Def)
+      return false;
+
+    if (TII.isConstantInstr(*Def))
+      return true;
+
+    if (Def->getOpcode() == TargetOpcode::G_CONSTANT ||
+        Def->getOpcode() == TargetOpcode::G_FCONSTANT)
+      return true;
+
+    if (Def->getOpcode() == SPIRV::OpConstantNull)
+      return true;
+
+    return false;
+  };
+
+  if (IsConstant(Reg))
+    return true;
+
+  MachineInstr *Def = MRI->getVRegDef(Reg);
+  if (!Def)
+    return false;
+
+  if (Def->getOpcode() == TargetOpcode::G_BUILD_VECTOR ||
+      (Def->getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS &&
+       cast<GIntrinsic>(Def)->getIntrinsicID() ==
+           Intrinsic::spv_const_composite)) {
+    unsigned StartOp = Def->getOpcode() == TargetOpcode::G_BUILD_VECTOR ? 1 : 2;
+    for (unsigned i = StartOp; i < Def->getNumOperands(); ++i) {
+      if (!isScalarOrVectorConstant(Def->getOperand(i).getReg()))
+        return false;
+    }
+    return true;
+  }
+
+  return false;
+}
+
 Register SPIRVInstructionSelector::buildZerosValF(const SPIRVType *ResType,
                                                   MachineInstr &I) const {
   // OpenCL uses nulls for Zero. In HLSL we don't use null constants.
@@ -3973,9 +4040,21 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     return selectReadImageIntrinsic(ResVReg, ResType, I);
   }
   case Intrinsic::spv_resource_sample:
-  case Intrinsic::spv_resource_sample_clamp: {
-    return selectSampleIntrinsic(ResVReg, ResType, I);
-  }
+  case Intrinsic::spv_resource_sample_clamp:
+    return selectSampleBasicIntrinsic(ResVReg, ResType, I);
+  case Intrinsic::spv_resource_samplebias:
+  case Intrinsic::spv_resource_samplebias_clamp:
+    return selectSampleBiasIntrinsic(ResVReg, ResType, I);
+  case Intrinsic::spv_resource_samplegrad:
+  case Intrinsic::spv_resource_samplegrad_clamp:
+    return selectSampleGradIntrinsic(ResVReg, ResType, I);
+  case Intrinsic::spv_resource_samplelevel:
+    return selectSampleLevelIntrinsic(ResVReg, ResType, I);
+  case Intrinsic::spv_resource_samplecmp:
+  case Intrinsic::spv_resource_samplecmp_clamp:
+    return selectSampleCmpIntrinsic(ResVReg, ResType, I);
+  case Intrinsic::spv_resource_samplecmplevelzero:
+    return selectSampleCmpLevelZeroIntrinsic(ResVReg, ResType, I);
   case Intrinsic::spv_resource_getpointer: {
     return selectResourceGetPointer(ResVReg, ResType, I);
   }
@@ -4179,79 +4258,181 @@ bool SPIRVInstructionSelector::selectReadImageIntrinsic(
                                   Pos);
 }
 
-bool SPIRVInstructionSelector::selectSampleIntrinsic(Register &ResVReg,
-                                                     const SPIRVType *ResType,
-                                                     MachineInstr &I) const {
-  Register ImageReg = I.getOperand(2).getReg();
-  Register SamplerReg = I.getOperand(3).getReg();
-  Register CoordinateReg = I.getOperand(4).getReg();
-  std::optional<Register> OffsetReg;
-  std::optional<Register> ClampReg;
-
-  if (I.getNumOperands() > 5)
-    OffsetReg = I.getOperand(5).getReg();
-  if (I.getNumOperands() > 6)
-    ClampReg = I.getOperand(6).getReg();
-
-  DebugLoc Loc = I.getDebugLoc();
-
+bool SPIRVInstructionSelector::generateSampleImage(
+    Register ResVReg, const SPIRVType *ResType, Register ImageReg,
+    Register SamplerReg, Register CoordinateReg, const ImageOperands &ImOps,
+    DebugLoc Loc, MachineInstr &Pos) const {
   auto *ImageDef = cast<GIntrinsic>(getVRegDef(*MRI, ImageReg));
   Register NewImageReg = MRI->createVirtualRegister(MRI->getRegClass(ImageReg));
   if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg),
-                                *ImageDef, I)) {
+                                *ImageDef, Pos)) {
     return false;
   }
 
   auto *SamplerDef = cast<GIntrinsic>(getVRegDef(*MRI, SamplerReg));
   Register NewSamplerReg =
       MRI->createVirtualRegister(MRI->getRegClass(SamplerReg));
-  if (!loadHandleBeforePosition(
-          NewSamplerReg, GR.getSPIRVTypeForVReg(SamplerReg), *SamplerDef, I)) {
+  if (!loadHandleBeforePosition(NewSamplerReg,
+                                GR.getSPIRVTypeForVReg(SamplerReg), *SamplerDef,
+                                Pos)) {
     return false;
   }
 
-  MachineIRBuilder MIRBuilder(I);
+  MachineIRBuilder MIRBuilder(Pos);
   SPIRVType *SampledImageType = GR.getOrCreateOpTypeSampledImage(
       GR.getSPIRVTypeForVReg(ImageReg), MIRBuilder);
-
   Register SampledImageReg =
       MRI->createVirtualRegister(GR.getRegClass(SampledImageType));
-  bool Succeed = BuildMI(*I.getParent(), I, Loc, TII.get(SPIRV::OpSampledImage))
-                     .addDef(SampledImageReg)
-                     .addUse(GR.getSPIRVTypeID(SampledImageType))
-                     .addUse(NewImageReg)
-                     .addUse(NewSamplerReg)
-                     .constrainAllUses(TII, TRI, RBI);
+
+  bool Succeed =
+      BuildMI(*Pos.getParent(), Pos, Loc, TII.get(SPIRV::OpSampledImage))
+          .addDef(SampledImageReg)
+          .addUse(GR.getSPIRVTypeID(SampledImageType))
+          .addUse(NewImageReg)
+          .addUse(NewSamplerReg)
+          .constrainAllUses(TII, TRI, RBI);
   if (!Succeed)
     return false;
 
-  auto MIB =
-      BuildMI(*I.getParent(), I, Loc, TII.get(SPIRV::OpImageSampleImplicitLod))
-          .addDef(ResVReg)
-          .addUse(GR.getSPIRVTypeID(ResType))
-          .addUse(SampledImageReg)
-          .addUse(CoordinateReg);
+  bool IsExplicitLod = ImOps.GradX.has_value() || ImOps.GradY.has_value() ||
+                       ImOps.Lod.has_value();
+  unsigned Opcode = IsExplicitLod ? SPIRV::OpImageSampleExplicitLod
+                                  : SPIRV::OpImageSampleImplicitLod;
+  if (ImOps.Compare)
+    Opcode = IsExplicitLod ? SPIRV::OpImageSampleDrefExplicitLod
+                           : SPIRV::OpImageSampleDrefImplicitLod;
 
-  uint32_t ImageOperands = 0;
-  if (OffsetReg && !isScalarOrVectorIntConstantZero(*OffsetReg)) {
-    ImageOperands |= 0x8; // ConstOffset
-  }
+  auto MIB = BuildMI(*Pos.getParent(), Pos, Loc, TII.get(Opcode))
+                 .addDef(ResVReg)
+                 .addUse(GR.getSPIRVTypeID(ResType))
+                 .addUse(SampledImageReg)
+                 .addUse(CoordinateReg);
 
-  if (ClampReg) {
-    ImageOperands |= 0x80; // MinLod
-  }
+  if (ImOps.Compare)
+    MIB.addUse(*ImOps.Compare);
+
+  uint32_t ImageOperands = 0;
+  if (ImOps.Bias)
+    ImageOperands |= SPIRV::ImageOperand::Bias;
+  if (ImOps.Lod)
+    ImageOperands |= SPIRV::ImageOperand::Lod;
+  if (ImOps.GradX && ImOps.GradY)
+    ImageOperands |= SPIRV::ImageOperand::Grad;
+  if (ImOps.Offset && !isScalarOrVectorIntConstantZero(*ImOps.Offset)) {
+    if (!isScalarOrVectorConstant(*ImOps.Offset))
+      report_fatal_error("The Offset image operand must be a constant.");
+    ImageOperands |= SPIRV::ImageOperand::ConstOffset;
+  }
+  if (ImOps.MinLod)
+    ImageOperands |= SPIRV::ImageOperand::MinLod;
 
   if (ImageOperands != 0) {
     MIB.addImm(ImageOperands);
-    if (ImageOperands & 0x8)
-      MIB.addUse(*OffsetReg);
-    if (ImageOperands & 0x80)
-      MIB.addUse(*ClampReg);
+    if (ImageOperands & SPIRV::ImageOperand::Bias)
+      MIB.addUse(*ImOps.Bias);
+    if (ImageOperands & SPIRV::ImageOperand::Lod)
+      MIB.addUse(*ImOps.Lod);
+    if (ImageOperands & SPIRV::ImageOperand::Grad) {
+      MIB.addUse(*ImOps.GradX);
+      MIB.addUse(*ImOps.GradY);
+    }
+    if (ImageOperands & SPIRV::ImageOperand::ConstOffset)
+      MIB.addUse(*ImOps.Offset);
+    if (ImageOperands & SPIRV::ImageOperand::MinLod)
+      MIB.addUse(*ImOps.MinLod);
   }
 
   return MIB.constrainAllUses(TII, TRI, RBI);
 }
 
+bool SPIRVInstructionSelector::selectSampleBasicIntrinsic(
+    Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
+  Register ImageReg = I.getOperand(2).getReg();
+  Register SamplerReg = I.getOperand(3).getReg();
+  Register CoordinateReg = I.getOperand(4).getReg();
+  ImageOperands ImOps;
+  if (I.getNumOperands() > 5)
+    ImOps.Offset = I.getOperand(5).getReg();
+  if (I.getNumOperands() > 6)
+    ImOps.MinLod = I.getOperand(6).getReg();
+  return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
+                             CoordinateReg, ImOps, I.getDebugLoc(), I);
+}
+
+bool SPIRVInstructionSelector::selectSampleBiasIntrinsic(
+    Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
+  Register ImageReg = I.getOperand(2).getReg();
+  Register SamplerReg = I.getOperand(3).getReg();
+  Register CoordinateReg = I.getOperand(4).getReg();
+  ImageOperands ImOps;
+  ImOps.Bias = I.getOperand(5).getReg();
+  if (I.getNumOperands() > 6)
+    ImOps.Offset = I.getOperand(6).getReg();
+  if (I.getNumOperands() > 7)
+    ImOps.MinLod = I.getOperand(7).getReg();
+  return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
+                             CoordinateReg, ImOps, I.getDebugLoc(), I);
+}
+
+bool SPIRVInstructionSelector::selectSampleGradIntrinsic(
+    Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
+  Register ImageReg = I.getOperand(2).getReg();
+  Register SamplerReg = I.getOperand(3).getReg();
+  Register CoordinateReg = I.getOperand(4).getReg();
+  ImageOperands ImOps;
+  ImOps.GradX = I.getOperand(5).getReg();
+  ImOps.GradY = I.getOperand(6).getReg();
+  if (I.getNumOperands() > 7)
+    ImOps.Offset = I.getOperand(7).getReg();
+  if (I.getNumOperands() > 8)
+    ImOps.MinLod = I.getOperand(8).getReg();
+  return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
+                             CoordinateReg, ImOps, I.getDebugLoc(), I);
+}
+
+bool SPIRVInstructionSelector::selectSampleLevelIntrinsic(
+    Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
+  Register ImageReg = I.getOperand(2).getReg();
+  Register SamplerReg = I.getOperand(3).getReg();
+  Register CoordinateReg = I.getOperand(4).getReg();
+  ImageOperands ImOps;
+  ImOps.Lod = I.getOperand(5).getReg();
+  if (I.getNumOperands() > 6)
+    ImOps.Offset = I.getOperand(6).getReg();
+  return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
+                             CoordinateReg, ImOps, I.getDebugLoc(), I);
+}
+
+bool SPIRVInstructionSelector::selectSampleCmpIntrinsic(
+    Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
+  Register ImageReg = I.getOperand(2).getReg();
+  Register SamplerReg = I.getOperand(3).getReg();
+  Register CoordinateReg = I.getOperand(4).getReg();
+  ImageOperands ImOps;
+  ImOps.Compare = I.getOperand(5).getReg();
+  if (I.getNumOperands() > 6)
+    ImOps.Offset = I.getOperand(6).getReg();
+  if (I.getNumOperands() > 7)
+    ImOps.MinLod = I.getOperand(7).getReg();
+  return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
+                             CoordinateReg, ImOps, I.getDebugLoc(), I);
+}
+
+bool SPIRVInstructionSelector::selectSampleCmpLevelZeroIntrinsic(
+    Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const {
+  Register ImageReg = I.getOperand(2).getReg();
+  Register SamplerReg = I.getOperand(3).getReg();
+  Register CoordinateReg = I.getOperand(4).getReg();
+  ImageOperands ImOps;
+  ImOps.Compare = I.getOperand(5).getReg();
+  if (I.getNumOperands() > 6)
+    ImOps.Offset = I.getOperand(6).getReg();
+  SPIRVType *FloatTy = GR.getOrCreateSPIRVFloatType(32, I, TII);
+  ImOps.Lod = GR.getOrCreateConstFP(APFloat(0.0f), I, FloatTy, TII);
+  return generateSampleImage(ResVReg, ResType, ImageReg, SamplerReg,
+                             CoordinateReg, ImOps, I.getDebugLoc(), I);
+}
+
 bool SPIRVInstructionSelector::generateImageReadOrFetch(
     Register &ResVReg, const SPIRVType *ResType, Register ImageReg,
     Register IdxReg, DebugLoc Loc, MachineInstr &Pos) const {
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index ecdf1c47d6926..64ce7b1c1c043 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -939,7 +939,7 @@ void RequirementHandler::initAvailableCapabilitiesForVulkan(
                     Capability::SampledImageArrayDynamicIndexing,
                     Capability::StorageBufferArrayDynamicIndexing,
                     Capability::StorageImageArrayDynamicIndexing,
-                    Capability::DerivativeControl});
+                    Capability::DerivativeControl, Capability::MinLod});
 
   // Became core in Vulkan 1.2
   if (ST.isAtLeastSPIRVVer(VersionTuple(1, 5))) {
@@ -1398,6 +1398,18 @@ void addPrintfRequirements(const MachineInstr &MI,
   }
 }
 
+static void addImageOperandReqs(const MachineInstr &MI,
+                                SPIRV::RequirementHandler &Reqs,
+                                const SPIRVSubtarget &ST, unsigned OpIdx) {
+  if (MI.getNumOperands() <= OpIdx)
+    return;
+  uint32_t Mask = MI.getOperand(OpIdx).getImm();
+  for (uint32_t I = 0; I < 32; ++I)
+    if (Mask & (1U << I))
+      Reqs.getAndAddRequirements(SPIRV::OperandCategory::ImageOperandOperand,
+                                 1U << I, ST);
+}
+
 void addInstrRequirements(const MachineInstr &MI,
                           SPIRV::ModuleAnalysisInfo &MAI,
                           const SPIRVSubtarget &ST) {
@@ -2136,6 +2148,19 @@ void addInstrRequirements(const MachineInstr &MI,
     break;
   case SPIRV::OpImageSampleImplicitLod:
     Reqs.addCapability(SPIRV::Capability::Shader);
+    addImageOperandReqs(MI, Reqs, ST, 4);
+    break;
+  case SPIRV::OpImageSampleExplicitLod:
+    Reqs.addCapability(SPIRV::Capability::Shader);
+    addImageOperandReqs(MI, Reqs, ST, 4);
+    break;
+  case SPIRV::OpImageSampleDrefImplicitLod:
+    Reqs.addCapability(SPIRV::Capability::Shader);
+    addImageOperandReqs(MI, Reqs, ST, 5);
+    break;
+  case SPIRV::OpImageSampleDrefExplicitLod:
+    Reqs.addCapability(SPIRV::Capability::Shader);
+    addImageOperandReqs(MI, Reqs, ST, 5);
     break;
   case SPIRV::OpImageRead: {
     Register ImageReg = MI.getOperand(2).getReg();
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample.ll
index f4410fc5d841b..68ae4a8e7779b 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample.ll
@@ -1,4 +1,5 @@
 ; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
 
 ; CHECK-DAG: OpCapability Shader
 
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample1D.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample1D.ll
new file mode 100644
index 0000000000000..a35b0a89ada4b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample1D.ll
@@ -0,0 +1,69 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+; CHECK-DAG: OpCapability Sampled1D
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[int:[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 1D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstant %[[float]] 0{{$}}
+; CHECK-DAG: %[[coord1:[0-9]+]] = OpConstant %[[float]] 0.5
+; CHECK-DAG: %[[offset1:[0-9]+]] = OpConstant %[[int]] 1
+; CHECK-DAG: %[[minlod_val:[0-9]+]] = OpConstant %[[float]] 1
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 0, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_0_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si]] %[[coord0]]{{[ ]*$}}
+  %res0 = call <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_0_2_0_0_1_0t.tspirv.Samplert.f32.i32(target("spirv.Image", float, 0, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, float 0.0, i32 0)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res1:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si2]] %[[coord1]] ConstOffset %[[offset1]]{{[ ]*$}}
+  %res1 = call <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_0_2_0_0_1_0t.tspirv.Samplert.f32.i32(target("spirv.Image", float, 0, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, float 0.5, i32 1)
+
+; CHECK: %[[img_val3:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val3:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si3:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val3]] %[[sampler_val3]]
+; CHECK: %[[res2:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si3]] %[[coord0]] MinLod %[[minlod_val]]{{[ ]*$}}
+  %res2 = call <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_0_2_0_0_1_0t.tspirv.Samplert.f32.i32(target("spirv.Image", float, 0, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, float 0.0, i32 0, float 1.0)
+
+; CHECK: %[[img_val4:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val4:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si4:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val4]] %[[sampler_val4]]
+; CHECK: %[[res3:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si4]] %[[coord1]] ConstOffset|MinLod %[[offset1]] %[[minlod_val]]{{[ ]*$}}
+  %res3 = call <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_0_2_0_0_1_0t.tspirv.Samplert.f32.i32(target("spirv.Image", float, 0, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, float 0.5, i32 1, float 1.0)
+
+  %tmp0 = fadd <4 x float> %res0, %res1
+  %tmp1 = fadd <4 x float> %res2, %res3
+  %res = fadd <4 x float> %tmp0, %tmp1
+
+; CHECK: %[[out_handle:[0-9]+]] = OpLoad {{.*}}
+; CHECK: OpImageWrite %[[out_handle]] {{.*}} %[[final_res:[0-9]+]]
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store <4 x float> %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 0, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_0_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_0_2_0_0_1_0t.tspirv.Samplert.f32.i32(target("spirv.Image", float, 0, 2, 0, 0, 1, 0), target("spirv.Sampler"), float, i32)
+declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_0_2_0_0_1_0t.tspirv.Samplert.f32.i32(target("spirv.Image", float, 0, 2, 0, 0, 1, 0), target("spirv.Sampler"), float, i32, float)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample3D.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample3D.ll
new file mode 100644
index 0000000000000..f04717365af8e
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/Sample3D.ll
@@ -0,0 +1,70 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 3D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v3float:[0-9]+]] = OpTypeVector %[[float]] 3
+; CHECK-DAG: %[[v3int:[0-9]+]] = OpTypeVector %[[int:[0-9]+]] 3
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v3float]]
+; CHECK-DAG: %[[coord1_val:[0-9]+]] = OpConstant %[[float]] 0.5
+; CHECK-DAG: %[[coord1:[0-9]+]] = OpConstantComposite %[[v3float]] %[[coord1_val]] %[[coord1_val]] %[[coord1_val]]
+; CHECK-DAG: %[[offset1_val:[0-9]+]] = OpConstant %[[int]] 1
+; CHECK-DAG: %[[offset1:[0-9]+]] = OpConstantComposite %[[v3int]] %[[offset1_val]] %[[offset1_val]] %[[offset1_val]]
+; CHECK-DAG: %[[minlod_val:[0-9]+]] = OpConstant %[[float]] 1
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 2, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_2_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si]] %[[coord0]]{{[ ]*$}}
+  %res0 = call <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_2_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 2, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <3 x float> <float 0.0, float 0.0, float 0.0>, <3 x i32> zeroinitializer)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res1:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si2]] %[[coord1]] ConstOffset %[[offset1]]{{[ ]*$}}
+  %res1 = call <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_2_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 2, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <3 x float> <float 0.5, float 0.5, float 0.5>, <3 x i32> <i32 1, i32 1, i32 1>)
+
+; CHECK: %[[img_val3:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val3:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si3:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val3]] %[[sampler_val3]]
+; CHECK: %[[res2:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si3]] %[[coord0]] MinLod %[[minlod_val]]{{[ ]*$}}
+  %res2 = call <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_2_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 2, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <3 x float> <float 0.0, float 0.0, float 0.0>, <3 x i32> zeroinitializer, float 1.0)
+
+; CHECK: %[[img_val4:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val4:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si4:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val4]] %[[sampler_val4]]
+; CHECK: %[[res3:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si4]] %[[coord1]] ConstOffset|MinLod %[[offset1]] %[[minlod_val]]{{[ ]*$}}
+  %res3 = call <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_2_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 2, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <3 x float> <float 0.5, float 0.5, float 0.5>, <3 x i32> <i32 1, i32 1, i32 1>, float 1.0)
+
+  %tmp0 = fadd <4 x float> %res0, %res1
+  %tmp1 = fadd <4 x float> %res2, %res3
+  %res = fadd <4 x float> %tmp0, %tmp1
+
+; CHECK: %[[out_handle:[0-9]+]] = OpLoad {{.*}}
+; CHECK: OpImageWrite %[[out_handle]] {{.*}} %[[final_res:[0-9]+]]
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store <4 x float> %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 2, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_2_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_2_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 2, 2, 0, 0, 1, 0), target("spirv.Sampler"), <3 x float>, <3 x i32>)
+declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_2_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 2, 2, 0, 0, 1, 0), target("spirv.Sampler"), <3 x float>, <3 x i32>, float)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleBias.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleBias.ll
new file mode 100644
index 0000000000000..a6c078528f44b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleBias.ll
@@ -0,0 +1,67 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 2D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v2float:[0-9]+]] = OpTypeVector %[[float]] 2
+; CHECK-DAG: %[[v2int:[0-9]+]] = OpTypeVector %[[int:[0-9]+]] 2
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v2float]]
+; CHECK-DAG: %[[bias:[0-9]+]] = OpConstant %[[float]] 2
+; CHECK-DAG: %[[offset1_val:[0-9]+]] = OpConstant %[[int]] 1
+; CHECK-DAG: %[[offset1:[0-9]+]] = OpConstantComposite %[[v2int]] %[[offset1_val]] %[[offset1_val]]
+; CHECK-DAG: %[[minlod_val:[0-9]+]] = OpConstant %[[float]] 1
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si]] %[[coord0]] Bias %[[bias]]{{[ ]*$}}
+  %res0 = call <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 2.0, <2 x i32> zeroinitializer)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res1:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si2]] %[[coord0]] Bias|ConstOffset %[[bias]] %[[offset1]]{{[ ]*$}}
+  %res1 = call <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 2.0, <2 x i32> <i32 1, i32 1>)
+
+; CHECK: %[[img_val3:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val3:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si3:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val3]] %[[sampler_val3]]
+; CHECK: %[[res2:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si3]] %[[coord0]] Bias|MinLod %[[bias]] %[[minlod_val]]{{[ ]*$}}
+  %res2 = call <4 x float> @llvm.spv.resource.samplebias.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) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 2.0, <2 x i32> zeroinitializer, float 1.0)
+
+; CHECK: %[[img_val4:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val4:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si4:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val4]] %[[sampler_val4]]
+; CHECK: %[[res3:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si4]] %[[coord0]] Bias|ConstOffset|MinLod %[[bias]] %[[offset1]] %[[minlod_val]]{{[ ]*$}}
+  %res3 = call <4 x float> @llvm.spv.resource.samplebias.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) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 2.0, <2 x i32> <i32 1, i32 1>, float 1.0)
+
+  %tmp0 = fadd <4 x float> %res0, %res1
+  %tmp1 = fadd <4 x float> %res2, %res3
+  %res = fadd <4 x float> %tmp0, %tmp1
+
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store <4 x float> %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>)
+declare <4 x float> @llvm.spv.resource.samplebias.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), target("spirv.Sampler"), <2 x float>, float, <2 x i32>, float)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmp.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmp.ll
new file mode 100644
index 0000000000000..9151bc67f5bf0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmp.ll
@@ -0,0 +1,68 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 2D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v2float:[0-9]+]] = OpTypeVector %[[float]] 2
+; CHECK-DAG: %[[v2int:[0-9]+]] = OpTypeVector %[[int:[0-9]+]] 2
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v2float]]
+; CHECK-DAG: %[[coord1_val:[0-9]+]] = OpConstant %[[float]] 0.5
+; CHECK-DAG: %[[coord1:[0-9]+]] = OpConstantComposite %[[v2float]] %[[coord1_val]] %[[coord1_val]]
+; CHECK-DAG: %[[dref0:[0-9]+]] = OpConstant %[[float]] 0
+; CHECK-DAG: %[[offset1_val:[0-9]+]] = OpConstant %[[int]] 1
+; CHECK-DAG: %[[offset1:[0-9]+]] = OpConstantComposite %[[v2int]] %[[offset1_val]] %[[offset1_val]]
+; CHECK-DAG: %[[minlod_val:[0-9]+]] = OpConstant %[[float]] 1
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleDrefImplicitLod %[[float]] %[[si]] %[[coord0]] %[[dref0]]{{[ ]*$}}
+  %res0 = call float @llvm.spv.resource.samplecmp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 0.0, <2 x i32> zeroinitializer)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res1:[0-9]+]] = OpImageSampleDrefImplicitLod %[[float]] %[[si2]] %[[coord1]] %[[dref0]] ConstOffset %[[offset1]]{{[ ]*$}}
+  %res1 = call float @llvm.spv.resource.samplecmp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.5, float 0.5>, float 0.0, <2 x i32> <i32 1, i32 1>)
+
+; CHECK: %[[img_val3:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val3:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si3:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val3]] %[[sampler_val3]]
+; CHECK: %[[res2:[0-9]+]] = OpImageSampleDrefImplicitLod %[[float]] %[[si3]] %[[coord0]] %[[dref0]] MinLod %[[minlod_val]]{{[ ]*$}}
+  %res2 = call float @llvm.spv.resource.samplecmp.clamp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 0.0, <2 x i32> zeroinitializer, float 1.0)
+
+; CHECK: %[[img_val4:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val4:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si4:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val4]] %[[sampler_val4]]
+; CHECK: %[[res3:[0-9]+]] = OpImageSampleDrefImplicitLod %[[float]] %[[si4]] %[[coord1]] %[[dref0]] ConstOffset|MinLod %[[offset1]] %[[minlod_val]]{{[ ]*$}}
+  %res3 = call float @llvm.spv.resource.samplecmp.clamp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.5, float 0.5>, float 0.0, <2 x i32> <i32 1, i32 1>, float 1.0)
+
+  %tmp0 = fadd float %res0, %res1
+  %tmp1 = fadd float %res2, %res3
+  %res = fadd float %tmp0, %tmp1
+
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store float %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare float @llvm.spv.resource.samplecmp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>)
+declare float @llvm.spv.resource.samplecmp.clamp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>, float)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmpLevelZero.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmpLevelZero.ll
new file mode 100644
index 0000000000000..49adf7d32cb86
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCmpLevelZero.ll
@@ -0,0 +1,52 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 2D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v2float:[0-9]+]] = OpTypeVector %[[float]] 2
+; CHECK-DAG: %[[v2int:[0-9]+]] = OpTypeVector %[[int:[0-9]+]] 2
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v2float]]
+; CHECK-DAG: %[[coord1_val:[0-9]+]] = OpConstant %[[float]] 0.5
+; CHECK-DAG: %[[coord1:[0-9]+]] = OpConstantComposite %[[v2float]] %[[coord1_val]] %[[coord1_val]]
+; CHECK-DAG: %[[zero:[0-9]+]] = OpConstantNull %[[float]]
+; CHECK-DAG: %[[offset1_val:[0-9]+]] = OpConstant %[[int]] 1
+; CHECK-DAG: %[[offset1:[0-9]+]] = OpConstantComposite %[[v2int]] %[[offset1_val]] %[[offset1_val]]
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleDrefExplicitLod %[[float]] %[[si]] %[[coord0]] %[[zero]] Lod %[[zero]]{{[ ]*$}}
+  %res0 = call float @llvm.spv.resource.samplecmplevelzero.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 0.0, <2 x i32> zeroinitializer)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res1:[0-9]+]] = OpImageSampleDrefExplicitLod %[[float]] %[[si2]] %[[coord1]] %[[zero]] Lod|ConstOffset %[[zero]] %[[offset1]]{{[ ]*$}}
+  %res1 = call float @llvm.spv.resource.samplecmplevelzero.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.5, float 0.5>, float 0.0, <2 x i32> <i32 1, i32 1>)
+
+  %res = fadd float %res0, %res1
+
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store float %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare float @llvm.spv.resource.samplecmplevelzero.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCube.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCube.ll
new file mode 100644
index 0000000000000..7d079f3935327
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleCube.ll
@@ -0,0 +1,52 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] Cube 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v3float:[0-9]+]] = OpTypeVector %[[float]] 3
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v3float]]
+; CHECK-DAG: %[[minlod_val:[0-9]+]] = OpConstant %[[float]] 1
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 3, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_3_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si]] %[[coord0]]{{[ ]*$}}
+  %res0 = call <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_3_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 3, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <3 x float> <float 0.0, float 0.0, float 0.0>, <3 x i32> zeroinitializer)
+
+; CHECK: %[[img_val3:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val3:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si3:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val3]] %[[sampler_val3]]
+; CHECK: %[[res2:[0-9]+]] = OpImageSampleImplicitLod %[[v4float]] %[[si3]] %[[coord0]] MinLod %[[minlod_val]]{{[ ]*$}}
+  %res2 = call <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_3_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 3, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <3 x float> <float 0.0, float 0.0, float 0.0>, <3 x i32> zeroinitializer, float 1.0)
+
+  %tmp0 = fadd <4 x float> %res0, %res2
+  %res = fadd <4 x float> %tmp0, %tmp0
+
+; CHECK: %[[out_handle:[0-9]+]] = OpLoad {{.*}}
+; CHECK: OpImageWrite %[[out_handle]] {{.*}} %[[final_res:[0-9]+]]
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store <4 x float> %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 3, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_3_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare <4 x float> @llvm.spv.resource.sample.v4f32.tspirv.Image_f32_3_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 3, 2, 0, 0, 1, 0), target("spirv.Sampler"), <3 x float>, <3 x i32>)
+declare <4 x float> @llvm.spv.resource.sample.clamp.v4f32.tspirv.Image_f32_3_2_0_0_1_0t.tspirv.Samplert.v3f32.v3i32(target("spirv.Image", float, 3, 2, 0, 0, 1, 0), target("spirv.Sampler"), <3 x float>, <3 x i32>, float)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleGrad.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleGrad.ll
new file mode 100644
index 0000000000000..17d38eca921df
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleGrad.ll
@@ -0,0 +1,68 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 2D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v2float:[0-9]+]] = OpTypeVector %[[float]] 2
+; CHECK-DAG: %[[v2int:[0-9]+]] = OpTypeVector %[[int:[0-9]+]] 2
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v2float]]
+; CHECK-DAG: %[[ddx:[0-9]+]] = OpConstantComposite %[[v2float]] %[[float_val:[0-9]+]] %[[float_val]]
+; CHECK-DAG: %[[ddy:[0-9]+]] = OpConstantComposite %[[v2float]] %[[float_val2:[0-9]+]] %[[float_val2]]
+; CHECK-DAG: %[[offset1_val:[0-9]+]] = OpConstant %[[int]] 1
+; CHECK-DAG: %[[offset1:[0-9]+]] = OpConstantComposite %[[v2int]] %[[offset1_val]] %[[offset1_val]]
+; CHECK-DAG: %[[minlod_val:[0-9]+]] = OpConstant %[[float]] 1
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleExplicitLod %[[v4float]] %[[si]] %[[coord0]] Grad %[[ddx]] %[[ddy]]{{[ ]*$}}
+  %res0 = call <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, <2 x float> <float 0.5, float 0.5>, <2 x float> <float 0.25, float 0.25>, <2 x i32> zeroinitializer)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res1:[0-9]+]] = OpImageSampleExplicitLod %[[v4float]] %[[si2]] %[[coord0]] Grad|ConstOffset %[[ddx]] %[[ddy]] %[[offset1]]{{[ ]*$}}
+  %res1 = call <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, <2 x float> <float 0.5, float 0.5>, <2 x float> <float 0.25, float 0.25>, <2 x i32> <i32 1, i32 1>)
+
+; CHECK: %[[img_val3:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val3:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si3:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val3]] %[[sampler_val3]]
+; CHECK: %[[res2:[0-9]+]] = OpImageSampleExplicitLod %[[v4float]] %[[si3]] %[[coord0]] Grad|MinLod %[[ddx]] %[[ddy]] %[[minlod_val]]{{[ ]*$}}
+  %res2 = call <4 x float> @llvm.spv.resource.samplegrad.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, <2 x float> <float 0.5, float 0.5>, <2 x float> <float 0.25, float 0.25>, <2 x i32> zeroinitializer, float 1.0)
+
+; CHECK: %[[img_val4:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val4:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si4:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val4]] %[[sampler_val4]]
+; CHECK: %[[res3:[0-9]+]] = OpImageSampleExplicitLod %[[v4float]] %[[si4]] %[[coord0]] Grad|ConstOffset|MinLod %[[ddx]] %[[ddy]] %[[offset1]] %[[minlod_val]]{{[ ]*$}}
+  %res3 = call <4 x float> @llvm.spv.resource.samplegrad.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, <2 x float> <float 0.5, float 0.5>, <2 x float> <float 0.25, float 0.25>, <2 x i32> <i32 1, i32 1>, float 1.0)
+
+  %tmp0 = fadd <4 x float> %res0, %res1
+  %tmp1 = fadd <4 x float> %res2, %res3
+  %res = fadd <4 x float> %tmp0, %tmp1
+
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store <4 x float> %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x float>, <2 x float>, <2 x i32>)
+declare <4 x float> @llvm.spv.resource.samplegrad.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, <2 x float>, <2 x float>, <2 x i32>, float)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32)
\ No newline at end of file
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleLevel.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleLevel.ll
new file mode 100644
index 0000000000000..f021664e6367f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/SampleLevel.ll
@@ -0,0 +1,51 @@
+; RUN: llc -O0 -mtriple=spirv-vulkan-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-compute %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpCapability Shader
+
+; CHECK-DAG: %[[float:[0-9]+]] = OpTypeFloat 32
+; CHECK-DAG: %[[v4float:[0-9]+]] = OpTypeVector %[[float]] 4
+; CHECK-DAG: %[[image:[0-9]+]] = OpTypeImage %[[float]] 2D 2 0 0 1 Unknown
+; CHECK-DAG: %[[sampled_image:[0-9]+]] = OpTypeSampledImage %[[image]]
+; CHECK-DAG: %[[sampler:[0-9]+]] = OpTypeSampler
+; CHECK-DAG: %[[v2float:[0-9]+]] = OpTypeVector %[[float]] 2
+; CHECK-DAG: %[[v2int:[0-9]+]] = OpTypeVector %[[int:[0-9]+]] 2
+; CHECK-DAG: %[[coord0:[0-9]+]] = OpConstantNull %[[v2float]]
+; CHECK-DAG: %[[lod:[0-9]+]] = OpConstant %[[float]] 2
+; CHECK-DAG: %[[offset1_val:[0-9]+]] = OpConstant %[[int]] 1
+; CHECK-DAG: %[[offset1:[0-9]+]] = OpConstantComposite %[[v2int]] %[[offset1_val]] %[[offset1_val]]
+
+ at .str = private unnamed_addr constant [4 x i8] c"img\00", align 1
+ at .str.1 = private unnamed_addr constant [5 x i8] c"samp\00", align 1
+ at .str.2 = private unnamed_addr constant [4 x i8] c"out\00", align 1
+
+define void @main() {
+entry:
+  %img = tail call target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32 0, i32 0, i32 1, i32 0, ptr @.str)
+  %sampler = tail call target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32 0, i32 1, i32 1, i32 0, ptr @.str.1)
+  
+; CHECK: %[[img_val:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val]] %[[sampler_val]]
+; CHECK: %[[res0:[0-9]+]] = OpImageSampleExplicitLod %[[v4float]] %[[si]] %[[coord0]] Lod %[[lod]]{{[ ]*$}}
+  %res0 = call <4 x float> @llvm.spv.resource.samplelevel.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 2.0, <2 x i32> zeroinitializer)
+
+; CHECK: %[[img_val2:[0-9]+]] = OpLoad %[[image]]
+; CHECK: %[[sampler_val2:[0-9]+]] = OpLoad %[[sampler]]
+; CHECK: %[[si2:[0-9]+]] = OpSampledImage %[[sampled_image]] %[[img_val2]] %[[sampler_val2]]
+; CHECK: %[[res1:[0-9]+]] = OpImageSampleExplicitLod %[[v4float]] %[[si2]] %[[coord0]] Lod|ConstOffset %[[lod]] %[[offset1]]{{[ ]*$}}
+  %res1 = call <4 x float> @llvm.spv.resource.samplelevel.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %img, target("spirv.Sampler") %sampler, <2 x float> <float 0.0, float 0.0>, float 2.0, <2 x i32> <i32 1, i32 1>)
+
+  %res = fadd <4 x float> %res0, %res1
+
+  %out = tail call target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32 0, i32 2, i32 1, i32 0, ptr @.str.2)
+  %out_ptr = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1) %out, i32 0)
+  store <4 x float> %res, ptr addrspace(11) %out_ptr
+  ret void
+}
+
+declare target("spirv.Image", float, 1, 2, 0, 0, 1, 0) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_1_2_0_0_1_0t(i32, i32, i32, i32, ptr)
+declare target("spirv.Sampler") @llvm.spv.resource.handlefrombinding.tspirv.Samplert(i32, i32, i32, i32, ptr)
+declare <4 x float> @llvm.spv.resource.samplelevel.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0), target("spirv.Sampler"), <2 x float>, float, <2 x i32>)
+declare target("spirv.Image", float, 5, 2, 0, 0, 2, 1) @llvm.spv.resource.handlefrombinding.tspirv.Image_f32_5_2_0_0_2_1t(i32, i32, i32, i32, ptr)
+declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_f32_5_2_0_0_2_1t(target("spirv.Image", float, 5, 2, 0, 0, 2, 1), i32)

>From 34a838eab10635b345bf1d3ef55706d8f5c6f72c Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Tue, 3 Feb 2026 10:43:45 -0500
Subject: [PATCH 2/8] Don't require Shader for OpImageSampleExplicitLod.

---
 llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 64ce7b1c1c043..40cddbf7897b4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -2151,7 +2151,6 @@ void addInstrRequirements(const MachineInstr &MI,
     addImageOperandReqs(MI, Reqs, ST, 4);
     break;
   case SPIRV::OpImageSampleExplicitLod:
-    Reqs.addCapability(SPIRV::Capability::Shader);
     addImageOperandReqs(MI, Reqs, ST, 4);
     break;
   case SPIRV::OpImageSampleDrefImplicitLod:

>From fa810541a6027c11f76870fc07695abcf5019996 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 23 Jan 2026 13:34:05 -0500
Subject: [PATCH 3/8] [HLSL] Implement Sample* methods for Texture2D

This commit implement the methods:

- SampleBias
- SampleCmp
- SampleCmpLevelZero
- SampleGrad
- SampleLevel

They are added to the Texture2D resource type. All overloads except for
those with the `status` argument.

Part of https://github.com/llvm/llvm-project/issues/175630

Assisted-by: Gemini
---
 clang/include/clang/Basic/Builtins.td         |  30 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/lib/CodeGen/CGHLSLBuiltins.cpp          | 220 +++++++++++
 clang/lib/CodeGen/CGHLSLRuntime.h             |   9 +
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 270 ++++++++++++++
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h   |   5 +
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |   7 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 182 ++++++---
 clang/test/AST/HLSL/Texture2D-AST.hlsl        | 350 +++++++++++++++++-
 ...re2D.sample.hlsl => Texture2D-Sample.hlsl} |   0
 .../resources/Texture2D-SampleBias.hlsl       |  66 ++++
 .../resources/Texture2D-SampleCmp.hlsl        |  78 ++++
 .../Texture2D-SampleCmpLevelZero.hlsl         |  48 +++
 .../resources/Texture2D-SampleGrad.hlsl       | 108 ++++++
 .../resources/Texture2D-SampleLevel.hlsl      |  68 ++++
 clang/test/SemaHLSL/Texture2D-SampleBias.hlsl |  33 ++
 clang/test/SemaHLSL/Texture2D-SampleCmp.hlsl  |  47 +++
 .../Texture2D-SampleCmpLevelZero.hlsl         |  36 ++
 clang/test/SemaHLSL/Texture2D-SampleGrad.hlsl |  34 ++
 .../test/SemaHLSL/Texture2D-SampleLevel.hlsl  |  28 ++
 20 files changed, 1566 insertions(+), 56 deletions(-)
 rename clang/test/CodeGenHLSL/resources/{Texture2D.sample.hlsl => Texture2D-Sample.hlsl} (100%)
 create mode 100644 clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
 create mode 100644 clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
 create mode 100644 clang/test/CodeGenHLSL/resources/Texture2D-SampleCmpLevelZero.hlsl
 create mode 100644 clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
 create mode 100644 clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
 create mode 100644 clang/test/SemaHLSL/Texture2D-SampleBias.hlsl
 create mode 100644 clang/test/SemaHLSL/Texture2D-SampleCmp.hlsl
 create mode 100644 clang/test/SemaHLSL/Texture2D-SampleCmpLevelZero.hlsl
 create mode 100644 clang/test/SemaHLSL/Texture2D-SampleGrad.hlsl
 create mode 100644 clang/test/SemaHLSL/Texture2D-SampleLevel.hlsl

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index a410a138836eb..5c5f92480da04 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5007,6 +5007,36 @@ def HLSLResourceSample : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void(...)";
 }
 
+def HLSLResourceSampleBias : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_resource_sample_bias"];
+  let Attributes = [NoThrow];
+  let Prototype = "void(...)";
+}
+
+def HLSLResourceSampleGrad : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_resource_sample_grad"];
+  let Attributes = [NoThrow];
+  let Prototype = "void(...)";
+}
+
+def HLSLResourceSampleLevel : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_resource_sample_level"];
+  let Attributes = [NoThrow];
+  let Prototype = "void(...)";
+}
+
+def HLSLResourceSampleCmp : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_resource_sample_cmp"];
+  let Attributes = [NoThrow];
+  let Prototype = "void(...)";
+}
+
+def HLSLResourceSampleCmpLevelZero : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_resource_sample_cmp_level_zero"];
+  let Attributes = [NoThrow];
+  let Prototype = "void(...)";
+}
+
 def HLSLResourceUninitializedHandle : LangBuiltin<"HLSL_LANG"> {
   let Spellings = ["__builtin_hlsl_resource_uninitializedhandle"];
   let Attributes = [NoThrow];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 807440c107897..d091792117857 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13470,6 +13470,9 @@ def err_hlsl_assign_to_global_resource: Error<
 
 def err_hlsl_push_constant_unique
     : Error<"cannot have more than one push constant block">;
+def err_hlsl_samplecmp_requires_float
+    : Error<"'SampleCmp' and 'SampleCmpLevelZero' require resource to contain "
+            "a floating point type">;
 
 // Layout randomization diagnostics.
 def err_non_designated_init_used : Error<
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index b3c0c90faeb02..581f4318915e7 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -545,6 +545,226 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     return Builder.CreateIntrinsic(
         RetTy, CGM.getHLSLRuntime().getSampleClampIntrinsic(), Args);
   }
+  case Builtin::BI__builtin_hlsl_resource_sample_bias: {
+    Value *HandleOp = EmitScalarExpr(E->getArg(0));
+    Value *SamplerOp = EmitScalarExpr(E->getArg(1));
+    Value *CoordOp = EmitScalarExpr(E->getArg(2));
+    Value *BiasOp = EmitScalarExpr(E->getArg(3));
+    if (BiasOp->getType() != Builder.getFloatTy())
+      BiasOp = Builder.CreateFPCast(BiasOp, Builder.getFloatTy());
+
+    SmallVector<Value *, 6> Args; // Max 6 arguments for SampleBias
+    Args.push_back(HandleOp);
+    Args.push_back(SamplerOp);
+    Args.push_back(CoordOp);
+    Args.push_back(BiasOp); // Bias is always the 4th argument (index 3)
+
+    // Handle optional Offset (E->getArg(4))
+    Value *OffsetOp;
+    if (E->getNumArgs() > 4) { // if E has at least 5 arguments (Handle,
+                               // Sampler, Coord, Bias, Offset)
+      OffsetOp = EmitScalarExpr(E->getArg(4));
+    } else {
+      // Default offset is 0.
+      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());
+      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
+    }
+    Args.push_back(OffsetOp); // Offset is always the 5th argument (index 4)
+
+    llvm::Type *RetTy = ConvertType(E->getType());
+
+    // Determine which intrinsic to call based on total number of arguments in E
+    if (E->getNumArgs() <=
+        5) { // No clamp parameter (Handle, Sampler, Coord, Bias, Offset)
+      return Builder.CreateIntrinsic(
+          RetTy, CGM.getHLSLRuntime().getSampleBiasIntrinsic(), Args);
+    } else { // Has clamp parameter (Handle, Sampler, Coord, Bias, Offset,
+             // Clamp)
+      llvm::Value *Clamp =
+          EmitScalarExpr(E->getArg(5)); // Clamp is E->getArg(5)
+      // 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); // Clamp is the 6th argument (index 5)
+      return Builder.CreateIntrinsic(
+          RetTy, CGM.getHLSLRuntime().getSampleBiasClampIntrinsic(), Args);
+    }
+  }
+  case Builtin::BI__builtin_hlsl_resource_sample_grad: {
+    Value *HandleOp = EmitScalarExpr(E->getArg(0));
+    Value *SamplerOp = EmitScalarExpr(E->getArg(1));
+    Value *CoordOp = EmitScalarExpr(E->getArg(2));
+    Value *DDXOp = EmitScalarExpr(E->getArg(3));
+    Value *DDYOp = EmitScalarExpr(E->getArg(4));
+
+    SmallVector<Value *, 7> Args; // Max 7 arguments for SampleGrad
+    Args.push_back(HandleOp);
+    Args.push_back(SamplerOp);
+    Args.push_back(CoordOp);
+    Args.push_back(DDXOp);
+    Args.push_back(DDYOp);
+
+    // Handle optional Offset (E->getArg(5))
+    Value *OffsetOp;
+    if (E->getNumArgs() > 5) { // if E has at least 6 arguments (Handle,
+                               // Sampler, Coord, DDX, DDY, Offset)
+      OffsetOp = EmitScalarExpr(E->getArg(5));
+    } else {
+      // Default offset is 0.
+      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());
+      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
+    }
+    Args.push_back(OffsetOp); // Offset is always the 6th argument (index 5)
+
+    llvm::Type *RetTy = ConvertType(E->getType());
+
+    // Determine which intrinsic to call based on total number of arguments in E
+    if (E->getNumArgs() <=
+        6) { // No clamp parameter (Handle, Sampler, Coord, DDX, DDY, Offset)
+      return Builder.CreateIntrinsic(
+          RetTy, CGM.getHLSLRuntime().getSampleGradIntrinsic(), Args);
+    } else { // Has clamp parameter (Handle, Sampler, Coord, DDX, DDY, Offset,
+             // Clamp)
+      llvm::Value *Clamp =
+          EmitScalarExpr(E->getArg(6)); // Clamp is E->getArg(6)
+      // 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); // Clamp is the 7th argument (index 6)
+      return Builder.CreateIntrinsic(
+          RetTy, CGM.getHLSLRuntime().getSampleGradClampIntrinsic(), Args);
+    }
+  }
+  case Builtin::BI__builtin_hlsl_resource_sample_level: {
+    Value *HandleOp = EmitScalarExpr(E->getArg(0));
+    Value *SamplerOp = EmitScalarExpr(E->getArg(1));
+    Value *CoordOp = EmitScalarExpr(E->getArg(2));
+    Value *LODOp = EmitScalarExpr(E->getArg(3));
+    if (LODOp->getType() != Builder.getFloatTy())
+      LODOp = Builder.CreateFPCast(LODOp, Builder.getFloatTy());
+
+    SmallVector<Value *, 5> Args; // Max 5 arguments for SampleLevel
+    Args.push_back(HandleOp);
+    Args.push_back(SamplerOp);
+    Args.push_back(CoordOp);
+    Args.push_back(LODOp);
+
+    // Handle optional Offset (E->getArg(4))
+    Value *OffsetOp;
+    if (E->getNumArgs() > 4) { // if E has 5 arguments (Handle, Sampler, Coord,
+                               // LOD, Offset)
+      OffsetOp = EmitScalarExpr(E->getArg(4));
+    } else {
+      // Default offset is 0.
+      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());
+      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
+    }
+    Args.push_back(OffsetOp);
+
+    llvm::Type *RetTy = ConvertType(E->getType());
+    return Builder.CreateIntrinsic(
+        RetTy, CGM.getHLSLRuntime().getSampleLevelIntrinsic(), Args);
+  }
+  case Builtin::BI__builtin_hlsl_resource_sample_cmp: {
+    Value *HandleOp = EmitScalarExpr(E->getArg(0));
+    Value *SamplerOp = EmitScalarExpr(E->getArg(1));
+    Value *CoordOp = EmitScalarExpr(E->getArg(2));
+    Value *CmpOp = EmitScalarExpr(E->getArg(3));
+    if (CmpOp->getType() != Builder.getFloatTy())
+      CmpOp = Builder.CreateFPCast(CmpOp, Builder.getFloatTy());
+
+    SmallVector<Value *, 6> Args; // Max 6 arguments for SampleCmp
+    Args.push_back(HandleOp);
+    Args.push_back(SamplerOp);
+    Args.push_back(CoordOp);
+    Args.push_back(CmpOp);
+
+    // Handle optional Offset (E->getArg(4))
+    Value *OffsetOp;
+    if (E->getNumArgs() > 4) { // if E has at least 5 arguments (Handle,
+                               // Sampler, Coord, CompareValue, Offset)
+      OffsetOp = EmitScalarExpr(E->getArg(4));
+    } else {
+      // Default offset is 0.
+      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());
+      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
+    }
+    Args.push_back(OffsetOp);
+
+    llvm::Type *RetTy = ConvertType(E->getType());
+
+    // Determine which intrinsic to call based on total number of arguments in E
+    if (E->getNumArgs() <= 5) { // No clamp parameter (Handle, Sampler, Coord,
+                                // CompareValue, Offset)
+      return Builder.CreateIntrinsic(
+          RetTy, CGM.getHLSLRuntime().getSampleCmpIntrinsic(), Args);
+    } else { // Has clamp parameter (Handle, Sampler, Coord, CompareValue,
+             // Offset, Clamp)
+      llvm::Value *Clamp =
+          EmitScalarExpr(E->getArg(5)); // Clamp is E->getArg(5)
+      // 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().getSampleCmpClampIntrinsic(), Args);
+    }
+  }
+  case Builtin::BI__builtin_hlsl_resource_sample_cmp_level_zero: {
+    Value *HandleOp = EmitScalarExpr(E->getArg(0));
+    Value *SamplerOp = EmitScalarExpr(E->getArg(1));
+    Value *CoordOp = EmitScalarExpr(E->getArg(2));
+    Value *CmpOp = EmitScalarExpr(E->getArg(3));
+    if (CmpOp->getType() != Builder.getFloatTy())
+      CmpOp = Builder.CreateFPCast(CmpOp, Builder.getFloatTy());
+
+    SmallVector<Value *, 5> Args;
+    Args.push_back(HandleOp);
+    Args.push_back(SamplerOp);
+    Args.push_back(CoordOp);
+    Args.push_back(CmpOp);
+
+    // Handle optional Offset (E->getArg(4))
+    Value *OffsetOp;
+    if (E->getNumArgs() > 4) {
+      OffsetOp = EmitScalarExpr(E->getArg(4));
+    } else {
+      // Default offset is 0.
+      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());
+      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
+    }
+    Args.push_back(OffsetOp);
+
+    llvm::Type *RetTy = ConvertType(E->getType());
+    return Builder.CreateIntrinsic(
+        RetTy, CGM.getHLSLRuntime().getSampleCmpLevelZeroIntrinsic(), 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 03a64ed0fa6bb..6afea68c68ec7 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -161,6 +161,15 @@ class CGHLSLRuntime {
                                    resource_getpointer)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Sample, resource_sample)
   GENERATE_HLSL_INTRINSIC_FUNCTION(SampleClamp, resource_sample_clamp)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleBias, resource_samplebias)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleBiasClamp, resource_samplebias_clamp)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleGrad, resource_samplegrad)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleGradClamp, resource_samplegrad_clamp)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleLevel, resource_samplelevel)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleCmp, resource_samplecmp)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleCmpClamp, resource_samplecmp_clamp)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(SampleCmpLevelZero,
+                                   resource_samplecmplevelzero)
   GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding,
                                    resource_handlefrombinding)
   GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromImplicitBinding,
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index e799594023dbc..f0e54eb092011 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1265,6 +1265,276 @@ BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim) {
       .finalize();
 }
 
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+
+  ASTContext &AST = Record->getASTContext();
+  QualType ReturnType = getFirstTemplateTypeParam();
+
+  QualType SamplerStateType =
+      lookupBuiltinType(SemaRef, "SamplerState", Record->getDeclContext());
+
+  uint32_t VecSize = getResourceDimensions(Dim);
+
+  QualType FloatTy = AST.FloatTy;
+  QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
+
+  QualType IntTy = AST.IntTy;
+  QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
+
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // T SampleBias(SamplerState s, float2 location, float bias)
+  BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("Bias", FloatTy)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_bias", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleBias(SamplerState s, float2 location, float bias, int2 offset)
+  BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("Bias", FloatTy)
+      .addParam("Offset", Int2Ty)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_bias", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleBias(SamplerState s, float2 location, float bias, int2 offset,
+  // float clamp)
+  return BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("Bias", FloatTy)
+      .addParam("Offset", Int2Ty)
+      .addParam("Clamp", FloatTy)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_bias", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4)
+      .returnValue(PH::LastStmt)
+      .finalize();
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addSampleGradMethods(ResourceDimension Dim) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+
+  ASTContext &AST = Record->getASTContext();
+  QualType ReturnType = getFirstTemplateTypeParam();
+
+  QualType SamplerStateType =
+      lookupBuiltinType(SemaRef, "SamplerState", Record->getDeclContext());
+
+  uint32_t VecSize = getResourceDimensions(Dim);
+
+  QualType FloatTy = AST.FloatTy;
+  QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
+
+  QualType IntTy = AST.IntTy;
+  QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
+
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // T SampleGrad(SamplerState s, float2 location, float2 ddx, float2 ddy)
+  BuiltinTypeMethodBuilder(*this, "SampleGrad", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("DDX", Float2Ty)
+      .addParam("DDY", Float2Ty)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_grad", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleGrad(SamplerState s, float2 location, float2 ddx, float2 ddy,
+  // int2 offset)
+  BuiltinTypeMethodBuilder(*this, "SampleGrad", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("DDX", Float2Ty)
+      .addParam("DDY", Float2Ty)
+      .addParam("Offset", Int2Ty)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_grad", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleGrad(SamplerState s, float2 location, float2 ddx, float2 ddy,
+  // int2 offset, float clamp)
+  return BuiltinTypeMethodBuilder(*this, "SampleGrad", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("DDX", Float2Ty)
+      .addParam("DDY", Float2Ty)
+      .addParam("Offset", Int2Ty)
+      .addParam("Clamp", FloatTy)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_grad", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4,
+                   PH::_5)
+      .returnValue(PH::LastStmt)
+      .finalize();
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addSampleLevelMethods(ResourceDimension Dim) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+
+  ASTContext &AST = Record->getASTContext();
+  QualType ReturnType = getFirstTemplateTypeParam();
+
+  QualType SamplerStateType =
+      lookupBuiltinType(SemaRef, "SamplerState", Record->getDeclContext());
+
+  uint32_t VecSize = getResourceDimensions(Dim);
+
+  QualType FloatTy = AST.FloatTy;
+  QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
+
+  QualType IntTy = AST.IntTy;
+  QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
+
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // T SampleLevel(SamplerState s, float2 location, float lod)
+  BuiltinTypeMethodBuilder(*this, "SampleLevel", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("LOD", FloatTy)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_level", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleLevel(SamplerState s, float2 location, float lod, int2 offset)
+  return BuiltinTypeMethodBuilder(*this, "SampleLevel", ReturnType)
+      .addParam("Sampler", SamplerStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("LOD", FloatTy)
+      .addParam("Offset", Int2Ty)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_level", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3)
+      .returnValue(PH::LastStmt)
+      .finalize();
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+
+  ASTContext &AST = Record->getASTContext();
+  QualType ReturnType = AST.FloatTy;
+
+  QualType SamplerComparisonStateType = lookupBuiltinType(
+      SemaRef, "SamplerComparisonState", Record->getDeclContext());
+
+  uint32_t VecSize = getResourceDimensions(Dim);
+
+  QualType FloatTy = AST.FloatTy;
+  QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
+
+  QualType IntTy = AST.IntTy;
+  QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
+
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // T SampleCmp(SamplerComparisonState s, float2 location, float compare_value)
+  BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
+      .addParam("Sampler", SamplerComparisonStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("CompareValue", FloatTy)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_cmp", ReturnType, PH::Handle,
+                   PH::LastStmt, PH::_1, PH::_2)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleCmp(SamplerComparisonState s, float2 location, float compare_value,
+  // int2 offset)
+  BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
+      .addParam("Sampler", SamplerComparisonStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("CompareValue", FloatTy)
+      .addParam("Offset", Int2Ty)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_cmp", ReturnType, PH::Handle,
+                   PH::LastStmt, PH::_1, PH::_2, PH::_3)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleCmp(SamplerComparisonState s, float2 location, float compare_value,
+  // int2 offset, float clamp)
+  return BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType)
+      .addParam("Sampler", SamplerComparisonStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("CompareValue", FloatTy)
+      .addParam("Offset", Int2Ty)
+      .addParam("Clamp", FloatTy)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_cmp", ReturnType, PH::Handle,
+                   PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4)
+      .returnValue(PH::LastStmt)
+      .finalize();
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addSampleCmpLevelZeroMethods(ResourceDimension Dim) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+
+  ASTContext &AST = Record->getASTContext();
+  QualType ReturnType = AST.FloatTy;
+
+  QualType SamplerComparisonStateType = lookupBuiltinType(
+      SemaRef, "SamplerComparisonState", Record->getDeclContext());
+
+  uint32_t VecSize = getResourceDimensions(Dim);
+
+  QualType FloatTy = AST.FloatTy;
+  QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
+
+  QualType IntTy = AST.IntTy;
+  QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
+
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // T SampleCmpLevelZero(SamplerComparisonState s, float2 location, float
+  // compare_value)
+  BuiltinTypeMethodBuilder(*this, "SampleCmpLevelZero", ReturnType)
+      .addParam("Sampler", SamplerComparisonStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("CompareValue", FloatTy)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_cmp_level_zero", ReturnType,
+                   PH::Handle, PH::LastStmt, PH::_1, PH::_2)
+      .returnValue(PH::LastStmt)
+      .finalize();
+
+  // T SampleCmpLevelZero(SamplerComparisonState s, float2 location, float
+  // compare_value, int2 offset)
+  return BuiltinTypeMethodBuilder(*this, "SampleCmpLevelZero", ReturnType)
+      .addParam("Sampler", SamplerComparisonStateType)
+      .addParam("Location", Float2Ty)
+      .addParam("CompareValue", FloatTy)
+      .addParam("Offset", Int2Ty)
+      .accessHandleFieldOnResource(PH::_0)
+      .callBuiltin("__builtin_hlsl_resource_sample_cmp_level_zero", 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 a505e2fb466dd..ffe55e9ebf8b7 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -93,6 +93,11 @@ class BuiltinTypeDeclBuilder {
   // Builtin types methods
   BuiltinTypeDeclBuilder &addLoadMethods();
   BuiltinTypeDeclBuilder &addSampleMethods(ResourceDimension Dim);
+  BuiltinTypeDeclBuilder &addSampleBiasMethods(ResourceDimension Dim);
+  BuiltinTypeDeclBuilder &addSampleGradMethods(ResourceDimension Dim);
+  BuiltinTypeDeclBuilder &addSampleLevelMethods(ResourceDimension Dim);
+  BuiltinTypeDeclBuilder &addSampleCmpMethods(ResourceDimension Dim);
+  BuiltinTypeDeclBuilder &addSampleCmpLevelZeroMethods(ResourceDimension Dim);
   BuiltinTypeDeclBuilder &addIncrementCounterMethod();
   BuiltinTypeDeclBuilder &addDecrementCounterMethod();
   BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name,
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 92a4247e8796a..d9d869da8b9a8 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -260,7 +260,12 @@ static BuiltinTypeDeclBuilder setupTextureType(CXXRecordDecl *Decl, Sema &S,
       .addCopyConstructor()
       .addCopyAssignmentOperator()
       .addStaticInitializationFunctions(false)
-      .addSampleMethods(Dim);
+      .addSampleMethods(Dim)
+      .addSampleBiasMethods(Dim)
+      .addSampleGradMethods(Dim)
+      .addSampleLevelMethods(Dim)
+      .addSampleCmpMethods(Dim)
+      .addSampleCmpLevelZeroMethods(Dim);
 }
 
 // This function is responsible for constructing the constraint expression for
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a880cb2271270..b7d137ff4eb8e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3310,6 +3310,123 @@ static bool CheckVectorElementCount(Sema *S, QualType PassedType,
   return false;
 }
 
+enum class SampleKind { Sample, Bias, Grad, Level, Cmp, CmpLevelZero };
+
+static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
+  unsigned MinArgs, MaxArgs;
+  if (Kind == SampleKind::Sample) {
+    MinArgs = 3;
+    MaxArgs = 5;
+  } else if (Kind == SampleKind::Bias) {
+    MinArgs = 4;
+    MaxArgs = 6;
+  } else if (Kind == SampleKind::Grad) {
+    MinArgs = 5;
+    MaxArgs = 7;
+  } else if (Kind == SampleKind::Level) {
+    MinArgs = 4;
+    MaxArgs = 5;
+  } else if (Kind == SampleKind::Cmp) {
+    MinArgs = 4;
+    MaxArgs = 6;
+  } else {
+    assert(Kind == SampleKind::CmpLevelZero);
+    MinArgs = 4;
+    MaxArgs = 5;
+  }
+
+  if (S.checkArgCountRange(TheCall, MinArgs, MaxArgs))
+    return true;
+
+  if (CheckResourceHandle(&S, TheCall, 0,
+                          [](const HLSLAttributedResourceType *ResType) {
+                            return ResType->getAttrs().ResourceDimension ==
+                                   llvm::dxil::ResourceDimension::Unknown;
+                          }))
+    return true;
+
+  if (CheckResourceHandle(&S, 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 =
+      getResourceDimensions(ResourceTy->getAttrs().ResourceDimension);
+  if (CheckVectorElementCount(&S, TheCall->getArg(2)->getType(),
+                              S.Context.FloatTy, ExpectedDim,
+                              TheCall->getBeginLoc()))
+    return true;
+
+  unsigned NextIdx = 3;
+  if (Kind == SampleKind::Bias || Kind == SampleKind::Level ||
+      Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero) {
+    QualType BiasOrLODOrCmpTy = TheCall->getArg(NextIdx)->getType();
+    if (!BiasOrLODOrCmpTy->isFloatingType() ||
+        BiasOrLODOrCmpTy->isVectorType()) {
+      S.Diag(TheCall->getArg(NextIdx)->getBeginLoc(),
+             diag::err_typecheck_convert_incompatible)
+          << BiasOrLODOrCmpTy << S.Context.FloatTy << 1 << 0 << 0;
+      return true;
+    }
+    NextIdx++;
+  } else if (Kind == SampleKind::Grad) {
+    if (ExpectedDim != 0) {
+      if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
+                                  S.Context.FloatTy, ExpectedDim,
+                                  TheCall->getArg(NextIdx)->getBeginLoc()))
+        return true;
+      if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx + 1)->getType(),
+                                  S.Context.FloatTy, ExpectedDim,
+                                  TheCall->getArg(NextIdx + 1)->getBeginLoc()))
+        return true;
+    }
+    NextIdx += 2;
+  }
+
+  // Offset
+  if (TheCall->getNumArgs() > NextIdx) {
+    if (ExpectedDim != 0) {
+      if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
+                                  S.Context.IntTy, ExpectedDim,
+                                  TheCall->getArg(NextIdx)->getBeginLoc()))
+        return true;
+    }
+    NextIdx++;
+  }
+
+  // Clamp
+  if (Kind != SampleKind::Level && Kind != SampleKind::CmpLevelZero &&
+      TheCall->getNumArgs() > NextIdx) {
+    QualType ClampTy = TheCall->getArg(NextIdx)->getType();
+    if (!ClampTy->isFloatingType() || ClampTy->isVectorType()) {
+      S.Diag(TheCall->getArg(NextIdx)->getBeginLoc(),
+             diag::err_typecheck_convert_incompatible)
+          << ClampTy << S.Context.FloatTy << 1 << 0 << 0;
+      return true;
+    }
+  }
+
+  assert(ResourceTy->hasContainedType() &&
+         "Expecting a contained type for resource with a dimension "
+         "attribute.");
+  QualType ReturnType = ResourceTy->getContainedType();
+  if (Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero) {
+    if (!ReturnType->hasFloatingRepresentation()) {
+      S.Diag(TheCall->getBeginLoc(), diag::err_hlsl_samplecmp_requires_float);
+      return true;
+    }
+    ReturnType = S.Context.FloatTy;
+  }
+  TheCall->setType(ReturnType);
+
+  return false;
+}
+
 // Note: returning true in this case results in CheckBuiltinFunctionCall
 // returning an ExprError
 bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
@@ -3380,59 +3497,18 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
 
     break;
   }
-  case Builtin::BI__builtin_hlsl_resource_sample: {
-    if (SemaRef.checkArgCountRange(TheCall, 3, 5))
-      return true;
-
-    if (CheckResourceHandle(&SemaRef, TheCall, 0,
-                            [](const HLSLAttributedResourceType *ResType) {
-                              return ResType->getAttrs().ResourceDimension ==
-                                     llvm::dxil::ResourceDimension::Unknown;
-                            }))
-      return true;
-
-    if (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 =
-        getResourceDimensions(ResourceTy->getAttrs().ResourceDimension);
-    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;
-      }
-    }
-
-    assert(ResourceTy->hasContainedType() &&
-           "Expecting a contained type for resource with a dimension "
-           "attribute.");
-    QualType ReturnType = ResourceTy->getContainedType();
-    TheCall->setType(ReturnType);
-
-    break;
-  }
+  case Builtin::BI__builtin_hlsl_resource_sample:
+    return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::Sample);
+  case Builtin::BI__builtin_hlsl_resource_sample_bias:
+    return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::Bias);
+  case Builtin::BI__builtin_hlsl_resource_sample_grad:
+    return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::Grad);
+  case Builtin::BI__builtin_hlsl_resource_sample_level:
+    return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::Level);
+  case Builtin::BI__builtin_hlsl_resource_sample_cmp:
+    return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::Cmp);
+  case Builtin::BI__builtin_hlsl_resource_sample_cmp_level_zero:
+    return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::CmpLevelZero);
   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
index 95343712b72fa..abdf0a8b35ab7 100644
--- a/clang/test/AST/HLSL/Texture2D-AST.hlsl
+++ b/clang/test/AST/HLSL/Texture2D-AST.hlsl
@@ -5,6 +5,11 @@
 // CHECK-NEXT: FieldDecl {{.*}} implicit {{.*}} __handle '__hlsl_resource_t
 // CHECK-SAME{LITERAL}: [[hlsl::resource_class(Sampler)]]
 
+// CHECK: CXXRecordDecl {{.*}} SamplerComparisonState 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
@@ -83,11 +88,352 @@
 // CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'Clamp' 'float'
 // CHECK-NEXT: AlwaysInlineAttr
 
+// CHECK: CXXMethodDecl {{.*}} SampleBias 'element_type (hlsl::SamplerState, vector<float, 2>, float)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} Bias '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_bias' '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 {{.*}} 'float' lvalue ParmVar {{.*}} 'Bias' 'float'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleBias 'element_type (hlsl::SamplerState, vector<float, 2>, float, vector<int, 2>)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} Bias 'float'
+// 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_bias' '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 {{.*}} 'float' lvalue ParmVar {{.*}} 'Bias' 'float'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleBias 'element_type (hlsl::SamplerState, vector<float, 2>, float, vector<int, 2>, float)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} Bias 'float'
+// 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_bias' '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 {{.*}} 'float' lvalue ParmVar {{.*}} 'Bias' 'float'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'Clamp' 'float'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleGrad 'element_type (hlsl::SamplerState, vector<float, 2>, vector<float, 2>, vector<float, 2>)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} DDX 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} DDY '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_grad' '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<float, 2>' lvalue ParmVar {{.*}} 'DDX' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'DDY' 'vector<float, 2>'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleGrad 'element_type (hlsl::SamplerState, vector<float, 2>, vector<float, 2>, vector<float, 2>, vector<int, 2>)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} DDX 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} DDY '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_grad' '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<float, 2>' lvalue ParmVar {{.*}} 'DDX' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'DDY' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleGrad 'element_type (hlsl::SamplerState, vector<float, 2>, vector<float, 2>, vector<float, 2>, vector<int, 2>, float)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} DDX 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} DDY '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_grad' '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<float, 2>' lvalue ParmVar {{.*}} 'DDX' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'DDY' '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
+
+// CHECK: CXXMethodDecl {{.*}} SampleLevel 'element_type (hlsl::SamplerState, vector<float, 2>, float)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} LOD '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_level' '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 {{.*}} 'float' lvalue ParmVar {{.*}} 'LOD' 'float'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleLevel 'element_type (hlsl::SamplerState, vector<float, 2>, float, vector<int, 2>)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} LOD 'float'
+// 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_level' '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 {{.*}} 'float' lvalue ParmVar {{.*}} 'LOD' 'float'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleCmp 'float (hlsl::SamplerComparisonState, vector<float, 2>, float)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} CompareValue 'float'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: ReturnStmt
+// CHECK-NEXT: CStyleCastExpr {{.*}} 'float' <Dependent>
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_sample_cmp' '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::SamplerComparisonState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'CompareValue' 'float'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleCmp 'float (hlsl::SamplerComparisonState, vector<float, 2>, float, vector<int, 2>)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} CompareValue 'float'
+// CHECK-NEXT: ParmVarDecl {{.*}} Offset 'vector<int, 2>'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: ReturnStmt
+// CHECK-NEXT: CStyleCastExpr {{.*}} 'float' <Dependent>
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_sample_cmp' '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::SamplerComparisonState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'CompareValue' 'float'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleCmp 'float (hlsl::SamplerComparisonState, vector<float, 2>, float, vector<int, 2>, float)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} CompareValue 'float'
+// CHECK-NEXT: ParmVarDecl {{.*}} Offset 'vector<int, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} Clamp 'float'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: ReturnStmt
+// CHECK-NEXT: CStyleCastExpr {{.*}} 'float' <Dependent>
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_sample_cmp' '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::SamplerComparisonState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'CompareValue' 'float'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'Clamp' 'float'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleCmpLevelZero 'float (hlsl::SamplerComparisonState, vector<float, 2>, float)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} CompareValue 'float'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: ReturnStmt
+// CHECK-NEXT: CStyleCastExpr {{.*}} 'float' <Dependent>
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_sample_cmp_level_zero' '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::SamplerComparisonState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'CompareValue' 'float'
+// CHECK-NEXT: AlwaysInlineAttr
+
+// CHECK: CXXMethodDecl {{.*}} SampleCmpLevelZero 'float (hlsl::SamplerComparisonState, vector<float, 2>, float, vector<int, 2>)'
+// CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>'
+// CHECK-NEXT: ParmVarDecl {{.*}} CompareValue 'float'
+// CHECK-NEXT: ParmVarDecl {{.*}} Offset 'vector<int, 2>'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: ReturnStmt
+// CHECK-NEXT: CStyleCastExpr {{.*}} 'float' <Dependent>
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_sample_cmp_level_zero' '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::SamplerComparisonState' lvalue ParmVar {{.*}} 'Sampler' 'hlsl::SamplerComparisonState'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<float, 2>' lvalue ParmVar {{.*}} 'Location' 'vector<float, 2>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'float' lvalue ParmVar {{.*}} 'CompareValue' 'float'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>'
+// CHECK-NEXT: AlwaysInlineAttr
+
 Texture2D<float4> t;
 SamplerState s;
+SamplerComparisonState scs;
 
-void main(float2 loc) {
+void main(float2 loc, float cmp) {
   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
+  t.SampleBias(s, loc, 0.0);
+  t.SampleBias(s, loc, 0.0, int2(1, 2));
+  t.SampleBias(s, loc, 0.0, int2(1, 2), 1.0);
+  t.SampleGrad(s, loc, float2(0,0), float2(0,0));
+  t.SampleGrad(s, loc, float2(0,0), float2(0,0), int2(1, 2));
+  t.SampleGrad(s, loc, float2(0,0), float2(0,0), int2(1, 2), 1.0);
+  t.SampleLevel(s, loc, 0.0);
+  t.SampleLevel(s, loc, 0.0, int2(1, 2));
+  t.SampleCmp(scs, loc, cmp);
+  t.SampleCmp(scs, loc, cmp, int2(1, 2));
+  t.SampleCmp(scs, loc, cmp, int2(1, 2), 1.0f);
+  t.SampleCmpLevelZero(scs, loc, cmp);
+  t.SampleCmpLevelZero(scs, loc, cmp, int2(1, 2));
+}
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D.sample.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
similarity index 100%
rename from clang/test/CodeGenHLSL/resources/Texture2D.sample.hlsl
rename to clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
new file mode 100644
index 0000000000000..c138e7f0a6c8b
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 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 | llvm-cxxfilt | 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-LABEL: @test_bias(float vector[2])
+// CHECK: %[[CALL:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleBias(hlsl::SamplerState, float vector[2], float)(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00)
+// CHECK: ret <4 x float> %[[CALL]]
+
+float4 test_bias(float2 loc : LOC) : SV_Target {
+  return t.SampleBias(s, loc, 0.0f);
+}
+
+// CHECK-LABEL: define linkonce_odr {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleBias(hlsl::SamplerState, float vector[2], float)(
+// CHECK: %[[THIS_VAL1:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL1]], i32 0, i32 0
+// CHECK: %[[HANDLE1:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP1]]
+// CHECK: %[[SAMPLER_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %{{.*}}, i32 0, i32 0
+// CHECK: %[[SAMPLER_H1:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP1]]
+// CHECK: %[[BIAS_CAST1:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: %{{.*}} = call {{.*}} <4 x float> @llvm.dx.resource.samplebias.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE1]], target("dx.Sampler", 0) %[[SAMPLER_H1]], <2 x float> %{{.*}}, float %[[BIAS_CAST1]], <2 x i32> zeroinitializer)
+// SPIRV: %{{.*}} = call {{.*}} <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE1]], target("spirv.Sampler") %[[SAMPLER_H1]], <2 x float> %{{.*}}, float %[[BIAS_CAST1]], <2 x i32> zeroinitializer)
+
+// CHECK-LABEL: @test_offset(float vector[2])
+// CHECK: %[[CALL_OFFSET:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleBias(hlsl::SamplerState, float vector[2], float, int vector[2])(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00, <2 x i32> noundef <i32 1, i32 2>)
+// CHECK: ret <4 x float> %[[CALL_OFFSET]]
+
+float4 test_offset(float2 loc : LOC) : SV_Target {
+  return t.SampleBias(s, loc, 0.0f, int2(1, 2));
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleBias(hlsl::SamplerState, float vector[2], float, int vector[2])(
+// CHECK: %[[THIS_VAL2:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL2]], i32 0, i32 0
+// CHECK: %[[HANDLE2:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP2]]
+// CHECK: %[[SAMPLER_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %{{.*}}, i32 0, i32 0
+// CHECK: %[[SAMPLER_H2:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP2]]
+// CHECK: %[[BIAS_CAST2:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: %{{.*}} = call {{.*}} <4 x float> @llvm.dx.resource.samplebias.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE2]], target("dx.Sampler", 0) %[[SAMPLER_H2]], <2 x float> %{{.*}}, float %[[BIAS_CAST2]], <2 x i32> %{{.*}})
+// SPIRV: %{{.*}} = call {{.*}} <4 x float> @llvm.spv.resource.samplebias.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE2]], target("spirv.Sampler") %[[SAMPLER_H2]], <2 x float> %{{.*}}, float %[[BIAS_CAST2]], <2 x i32> %{{.*}})
+
+// CHECK-LABEL: @test_clamp(float vector[2])
+// CHECK: %[[CALL_CLAMP:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleBias(hlsl::SamplerState, float vector[2], float, int vector[2], float)(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00, <2 x i32> noundef <i32 1, i32 2>, float {{.*}} 1.000000e+00)
+// CHECK: ret <4 x float> %[[CALL_CLAMP]]
+
+float4 test_clamp(float2 loc : LOC) : SV_Target {
+  return t.SampleBias(s, loc, 0.0f, int2(1, 2), 1.0f);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleBias(hlsl::SamplerState, float vector[2], float, int vector[2], float)(
+// CHECK: %[[THIS_VAL3:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP3:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL3]], i32 0, i32 0
+// CHECK: %[[HANDLE3:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP3]]
+// CHECK: %[[SAMPLER_GEP3:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %{{.*}}, i32 0, i32 0
+// CHECK: %[[SAMPLER_H3:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP3]]
+// CHECK: %[[BIAS_CAST3:.*]] = fptrunc {{.*}} double {{.*}} to float
+// CHECK: %[[CLAMP_CAST3:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: %{{.*}} = call {{.*}} <4 x float> @llvm.dx.resource.samplebias.clamp.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE3]], target("dx.Sampler", 0) %[[SAMPLER_H3]], <2 x float> %{{.*}}, float %[[BIAS_CAST3]], <2 x i32> %{{.*}}, float %[[CLAMP_CAST3]])
+// SPIRV: %{{.*}} = call {{.*}} <4 x float> @llvm.spv.resource.samplebias.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) %[[HANDLE3]], target("spirv.Sampler") %[[SAMPLER_H3]], <2 x float> %{{.*}}, float %[[BIAS_CAST3]], <2 x i32> %{{.*}}, float %[[CLAMP_CAST3]])
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
new file mode 100644
index 0000000000000..b76b02177abbc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
@@ -0,0 +1,78 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 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 | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,SPIRV
+
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// DXIL: %"class.hlsl::SamplerComparisonState" = type { target("dx.Sampler", 0) }
+
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::SamplerComparisonState" = type { target("spirv.Sampler") }
+
+Texture2D<float4> t;
+SamplerComparisonState s;
+
+// CHECK-LABEL: @test_cmp(float vector[2], float)
+// CHECK: %[[CALL:.*]] = call {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmp(hlsl::SamplerComparisonState, float vector[2], float)(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerComparisonState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00)
+// CHECK: ret float %[[CALL]]
+
+float test_cmp(float2 loc : LOC, float cmp : CMP) : SV_Target {
+  return t.SampleCmp(s, loc, 0.0f);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmp(hlsl::SamplerComparisonState, float vector[2], float)(
+// CHECK-SAME: ptr noundef nonnull {{.*}} %[[THIS1:[^,]+]], ptr noundef byval(%"class.hlsl::SamplerComparisonState") {{.*}} %[[SAMPLER1:[^,]+]], <2 x float> noundef nofpclass(nan inf) %[[COORD1:[^,]+]], float noundef nofpclass(nan inf) %[[CMP1:[^)]+]])
+// CHECK: %[[THIS_VAL1:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL1]], i32 0, i32 0
+// CHECK: %[[HANDLE1:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP1]]
+// CHECK: %[[SAMPLER_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerComparisonState", ptr %[[SAMPLER1]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H1:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP1]]
+// CHECK: %[[COORD_VAL1:.*]] = load <2 x float>, ptr %{{.*}}
+// CHECK: %[[CMP_VAL1:.*]] = load float, ptr %{{.*}}
+// CHECK: %[[CMP_CAST1:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: call {{.*}} float @llvm.dx.resource.samplecmp.f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE1]], target("dx.Sampler", 0) %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL1]], float %[[CMP_CAST1]], <2 x i32> zeroinitializer)
+// SPIRV: call {{.*}} float @llvm.spv.resource.samplecmp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE1]], target("spirv.Sampler") %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL1]], float %[[CMP_CAST1]], <2 x i32> zeroinitializer)
+
+// CHECK-LABEL: @test_offset(float vector[2], float)
+// CHECK: %[[CALL_OFFSET:.*]] = call {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmp(hlsl::SamplerComparisonState, float vector[2], float, int vector[2])(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerComparisonState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00, <2 x i32> noundef <i32 1, i32 2>)
+// CHECK: ret float %[[CALL_OFFSET]]
+
+float test_offset(float2 loc : LOC, float cmp : CMP) : SV_Target {
+  return t.SampleCmp(s, loc, 0.0f, int2(1, 2));
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmp(hlsl::SamplerComparisonState, float vector[2], float, int vector[2])(
+// CHECK-SAME: ptr noundef nonnull {{.*}} %[[THIS2:[^,]+]], ptr noundef byval(%"class.hlsl::SamplerComparisonState") {{.*}} %[[SAMPLER2:[^,]+]], <2 x float> noundef nofpclass(nan inf) %[[COORD2:[^,]+]], float noundef nofpclass(nan inf) %[[CMP2:[^,]+]], <2 x i32> noundef %[[OFFSET2:[^)]+]])
+// CHECK: %[[THIS_VAL2:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL2]], i32 0, i32 0
+// CHECK: %[[HANDLE2:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP2]]
+// CHECK: %[[SAMPLER_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerComparisonState", ptr %[[SAMPLER2]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H2:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP2]]
+// CHECK: %[[COORD_VAL2:.*]] = load <2 x float>, ptr %{{.*}}
+// CHECK: %[[CMP_VAL2:.*]] = load float, ptr %{{.*}}
+// CHECK: %[[CMP_CAST2:.*]] = fptrunc {{.*}} double {{.*}} to float
+// CHECK: %[[OFFSET_VAL2:.*]] = load <2 x i32>, ptr %{{.*}}
+// DXIL: call {{.*}} float @llvm.dx.resource.samplecmp.f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE2]], target("dx.Sampler", 0) %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL2]], float %[[CMP_CAST2]], <2 x i32> %[[OFFSET_VAL2]])
+// SPIRV: call {{.*}} float @llvm.spv.resource.samplecmp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE2]], target("spirv.Sampler") %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL2]], float %[[CMP_CAST2]], <2 x i32> %[[OFFSET_VAL2]])
+
+// CHECK-LABEL: @test_clamp(float vector[2], float)
+// CHECK: %[[CALL_CLAMP:.*]] = call {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmp(hlsl::SamplerComparisonState, float vector[2], float, int vector[2], float)(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerComparisonState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00, <2 x i32> noundef <i32 1, i32 2>, float {{.*}} 1.000000e+00)
+// CHECK: ret float %[[CALL_CLAMP]]
+
+float test_clamp(float2 loc : LOC, float cmp : CMP) : SV_Target {
+  return t.SampleCmp(s, loc, 0.0f, int2(1, 2), 1.0f);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmp(hlsl::SamplerComparisonState, float vector[2], float, int vector[2], float)(
+// CHECK-SAME: ptr noundef nonnull {{.*}} %[[THIS3:[^,]+]], ptr noundef byval(%"class.hlsl::SamplerComparisonState") {{.*}} %[[SAMPLER3:[^,]+]], <2 x float> noundef nofpclass(nan inf) %[[COORD3:[^,]+]], float noundef nofpclass(nan inf) %[[CMP3:[^,]+]], <2 x i32> noundef %[[OFFSET3:[^,]+]], float noundef nofpclass(nan inf) %[[CLAMP3:[^)]+]])
+// CHECK: %[[THIS_VAL3:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP3:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL3]], i32 0, i32 0
+// CHECK: %[[HANDLE3:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP3]]
+// CHECK: %[[SAMPLER_GEP3:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerComparisonState", ptr %[[SAMPLER3]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H3:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP3]]
+// CHECK: %[[COORD_VAL3:.*]] = load <2 x float>, ptr %{{.*}}
+// CHECK: %[[CMP_VAL3:.*]] = load float, ptr %{{.*}}
+// CHECK: %[[CMP_CAST3:.*]] = fptrunc {{.*}} double {{.*}} to float
+// CHECK: %[[OFFSET_VAL3:.*]] = load <2 x i32>, ptr %{{.*}}
+// CHECK: %[[CLAMP_VAL3:.*]] = load float, ptr %{{.*}}
+// CHECK: %[[CLAMP_CAST3:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: call {{.*}} float @llvm.dx.resource.samplecmp.clamp.f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE3]], target("dx.Sampler", 0) %[[SAMPLER_H3]], <2 x float> %[[COORD_VAL3]], float %[[CMP_CAST3]], <2 x i32> %[[OFFSET_VAL3]], float %[[CLAMP_CAST3]])
+// SPIRV: call {{.*}} float @llvm.spv.resource.samplecmp.clamp.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE3]], target("spirv.Sampler") %[[SAMPLER_H3]], <2 x float> %[[COORD_VAL3]], float %[[CMP_CAST3]], <2 x i32> %[[OFFSET_VAL3]], float %[[CLAMP_CAST3]])
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmpLevelZero.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmpLevelZero.hlsl
new file mode 100644
index 0000000000000..224e9d3837374
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmpLevelZero.hlsl
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 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 | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,SPIRV
+
+Texture2D<float4> t;
+SamplerComparisonState s;
+
+// CHECK-LABEL: @test_cmp_level_zero(float vector[2], float)
+// CHECK: %[[CALL:.*]] = call {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmpLevelZero(hlsl::SamplerComparisonState, float vector[2], float)(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerComparisonState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00)
+// CHECK: ret float %[[CALL]]
+
+float test_cmp_level_zero(float2 loc : LOC, float cmp : CMP) : SV_Target {
+  return t.SampleCmpLevelZero(s, loc, 0.0f);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmpLevelZero(hlsl::SamplerComparisonState, float vector[2], float)(
+// CHECK-SAME: ptr noundef nonnull {{.*}} %[[THIS1:[^,]+]], ptr noundef byval(%"class.hlsl::SamplerComparisonState") {{.*}} %[[SAMPLER1:[^,]+]], <2 x float> noundef nofpclass(nan inf) %[[COORD1:[^,]+]], float noundef nofpclass(nan inf) %[[CMP1:[^)]+]])
+// CHECK: %[[THIS_VAL1:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL1]], i32 0, i32 0
+// CHECK: %[[HANDLE1:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP1]]
+// CHECK: %[[SAMPLER_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerComparisonState", ptr %[[SAMPLER1]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H1:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP1]]
+// CHECK: %[[COORD_VAL1:.*]] = load <2 x float>, ptr %{{.*}}
+// CHECK: %[[CMP_VAL1:.*]] = load float, ptr %{{.*}}
+// CHECK: %[[CMP_CAST1:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: call {{.*}} float @llvm.dx.resource.samplecmplevelzero.f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE1]], target("dx.Sampler", 0) %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL1]], float %[[CMP_CAST1]], <2 x i32> zeroinitializer)
+// SPIRV: call {{.*}} float @llvm.spv.resource.samplecmplevelzero.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE1]], target("spirv.Sampler") %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL1]], float %[[CMP_CAST1]], <2 x i32> zeroinitializer)
+
+// CHECK-LABEL: @test_cmp_level_zero_offset(float vector[2], float)
+// CHECK: %[[CALL_OFFSET:.*]] = call {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmpLevelZero(hlsl::SamplerComparisonState, float vector[2], float, int vector[2])(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerComparisonState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00, <2 x i32> noundef <i32 1, i32 2>)
+// CHECK: ret float %[[CALL_OFFSET]]
+
+float test_cmp_level_zero_offset(float2 loc : LOC, float cmp : CMP) : SV_Target {
+  return t.SampleCmpLevelZero(s, loc, 0.0f, int2(1, 2));
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} float @hlsl::Texture2D<float vector[4]>::SampleCmpLevelZero(hlsl::SamplerComparisonState, float vector[2], float, int vector[2])(
+// CHECK-SAME: ptr noundef nonnull {{.*}} %[[THIS2:[^,]+]], ptr noundef byval(%"class.hlsl::SamplerComparisonState") {{.*}} %[[SAMPLER2:[^,]+]], <2 x float> noundef nofpclass(nan inf) %[[COORD2:[^,]+]], float noundef nofpclass(nan inf) %[[CMP2:[^,]+]], <2 x i32> noundef %[[OFFSET2:[^)]+]])
+// CHECK: %[[THIS_VAL2:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[HANDLE_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL2]], i32 0, i32 0
+// CHECK: %[[HANDLE2:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP2]]
+// CHECK: %[[SAMPLER_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerComparisonState", ptr %[[SAMPLER2]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H2:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP2]]
+// CHECK: %[[COORD_VAL2:.*]] = load <2 x float>, ptr %{{.*}}
+// CHECK: %[[CMP_VAL2:.*]] = load float, ptr %{{.*}}
+// CHECK: %[[CMP_CAST2:.*]] = fptrunc {{.*}} double {{.*}} to float
+// CHECK: %[[OFFSET_VAL2:.*]] = load <2 x i32>, ptr %{{.*}}
+// DXIL: call {{.*}} float @llvm.dx.resource.samplecmplevelzero.f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE2]], target("dx.Sampler", 0) %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL2]], float %[[CMP_CAST2]], <2 x i32> %[[OFFSET_VAL2]])
+// SPIRV: call {{.*}} float @llvm.spv.resource.samplecmplevelzero.f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE2]], target("spirv.Sampler") %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL2]], float %[[CMP_CAST2]], <2 x i32> %[[OFFSET_VAL2]])
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
new file mode 100644
index 0000000000000..279521c0bb988
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 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 | llvm-cxxfilt | 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-LABEL: @test_grad(float vector[2], float vector[2], float vector[2])
+// CHECK: %[[CALL:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleGrad(hlsl::SamplerState, float vector[2], float vector[2], float vector[2])(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x float> {{.*}} %{{.*}})
+// CHECK: ret <4 x float> %[[CALL]]
+
+float4 test_grad(float2 loc : LOC, float2 ddx : DDX, float2 ddy : DDY) : SV_Target {
+  return t.SampleGrad(s, loc, ddx, ddy);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleGrad(hlsl::SamplerState, float vector[2], float vector[2], float vector[2])(
+// CHECK-SAME: ptr {{.*}} %[[THIS:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER:[^,]+]], <2 x float> {{.*}} %[[COORD:[^,]+]], <2 x float> {{.*}} %[[DDX:[^,]+]], <2 x float> {{.*}} %[[DDY:[^)]+]])
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr
+// CHECK: %[[COORD_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[DDX_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[DDY_ADDR:.*]] = alloca <2 x float>
+// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]]
+// CHECK: store <2 x float> %[[COORD]], ptr %[[COORD_ADDR]]
+// CHECK: store <2 x float> %[[DDX]], ptr %[[DDX_ADDR]]
+// CHECK: store <2 x float> %[[DDY]], ptr %[[DDY_ADDR]]
+// CHECK: %[[THIS_VAL1:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// CHECK: %[[HANDLE_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL1]], i32 0, i32 0
+// CHECK: %[[HANDLE1:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP1]]
+// CHECK: %[[SAMPLER_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H1:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP1]]
+// CHECK: %[[COORD_VAL:.*]] = load <2 x float>, ptr %[[COORD_ADDR]]
+// CHECK: %[[DDX_VAL:.*]] = load <2 x float>, ptr %[[DDX_ADDR]]
+// CHECK: %[[DDY_VAL:.*]] = load <2 x float>, ptr %[[DDY_ADDR]]
+// DXIL: call {{.*}} <4 x float> @llvm.dx.resource.samplegrad.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2f32.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE1]], target("dx.Sampler", 0) %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL]], <2 x float> %[[DDX_VAL]], <2 x float> %[[DDY_VAL]], <2 x i32> zeroinitializer)
+// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE1]], target("spirv.Sampler") %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL]], <2 x float> %[[DDX_VAL]], <2 x float> %[[DDY_VAL]], <2 x i32> zeroinitializer)
+
+// CHECK-LABEL: @test_offset(float vector[2], float vector[2], float vector[2])
+// CHECK: %[[CALL_OFFSET:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleGrad(hlsl::SamplerState, float vector[2], float vector[2], float vector[2], int vector[2])(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x i32> noundef <i32 1, i32 2>)
+// CHECK: ret <4 x float> %[[CALL_OFFSET]]
+
+float4 test_offset(float2 loc : LOC, float2 ddx : DDX, float2 ddy : DDY) : SV_Target {
+  return t.SampleGrad(s, loc, ddx, ddy, int2(1, 2));
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleGrad(hlsl::SamplerState, float vector[2], float vector[2], float vector[2], int vector[2])(
+// CHECK-SAME: ptr {{.*}} %[[THIS:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER:[^,]+]], <2 x float> {{.*}} %[[COORD:[^,]+]], <2 x float> {{.*}} %[[DDX:[^,]+]], <2 x float> {{.*}} %[[DDY:[^,]+]], <2 x i32> {{.*}} %[[OFFSET:[^)]+]])
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr
+// CHECK: %[[COORD_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[DDX_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[DDY_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 float> %[[DDX]], ptr %[[DDX_ADDR]]
+// CHECK: store <2 x float> %[[DDY]], ptr %[[DDY_ADDR]]
+// CHECK: store <2 x i32> %[[OFFSET]], ptr %[[OFFSET_ADDR]]
+// CHECK: %[[THIS_VAL2:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// CHECK: %[[HANDLE_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL2]], i32 0, i32 0
+// CHECK: %[[HANDLE2:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP2]]
+// CHECK: %[[SAMPLER_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H2:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP2]]
+// CHECK: %[[COORD_VAL:.*]] = load <2 x float>, ptr %[[COORD_ADDR]]
+// CHECK: %[[DDX_VAL:.*]] = load <2 x float>, ptr %[[DDX_ADDR]]
+// CHECK: %[[DDY_VAL:.*]] = load <2 x float>, ptr %[[DDY_ADDR]]
+// CHECK: %[[OFFSET_VAL:.*]] = load <2 x i32>, ptr %[[OFFSET_ADDR]]
+// DXIL: call {{.*}} <4 x float> @llvm.dx.resource.samplegrad.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2f32.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE2]], target("dx.Sampler", 0) %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL]], <2 x float> %[[DDX_VAL]], <2 x float> %[[DDY_VAL]], <2 x i32> %[[OFFSET_VAL]])
+// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.samplegrad.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE2]], target("spirv.Sampler") %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL]], <2 x float> %[[DDX_VAL]], <2 x float> %[[DDY_VAL]], <2 x i32> %[[OFFSET_VAL]])
+
+// CHECK-LABEL: @test_clamp(float vector[2], float vector[2], float vector[2])
+// CHECK: %[[CALL_CLAMP:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleGrad(hlsl::SamplerState, float vector[2], float vector[2], float vector[2], int vector[2], float)(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x float> {{.*}} %{{.*}}, <2 x i32> noundef <i32 1, i32 2>, float {{.*}} 1.000000e+00)
+// CHECK: ret <4 x float> %[[CALL_CLAMP]]
+
+float4 test_clamp(float2 loc : LOC, float2 ddx : DDX, float2 ddy : DDY) : SV_Target {
+  return t.SampleGrad(s, loc, ddx, ddy, int2(1, 2), 1.0f);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleGrad(hlsl::SamplerState, float vector[2], float vector[2], float vector[2], int vector[2], float)(
+// CHECK-SAME: ptr {{.*}} %[[THIS:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER:[^,]+]], <2 x float> {{.*}} %[[COORD:[^,]+]], <2 x float> {{.*}} %[[DDX:[^,]+]], <2 x float> {{.*}} %[[DDY:[^,]+]], <2 x i32> {{.*}} %[[OFFSET:[^,]+]], float {{.*}} %[[CLAMP:[^)]+]])
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr
+// CHECK: %[[COORD_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[DDX_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[DDY_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 float> %[[DDX]], ptr %[[DDX_ADDR]]
+// CHECK: store <2 x float> %[[DDY]], ptr %[[DDY_ADDR]]
+// CHECK: store <2 x i32> %[[OFFSET]], ptr %[[OFFSET_ADDR]]
+// CHECK: store float %[[CLAMP]], ptr %[[CLAMP_ADDR]]
+// CHECK: %[[THIS_VAL3:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// CHECK: %[[HANDLE_GEP3:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL3]], i32 0, i32 0
+// CHECK: %[[HANDLE3:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP3]]
+// CHECK: %[[SAMPLER_GEP3:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H3:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP3]]
+// CHECK: %[[COORD_VAL:.*]] = load <2 x float>, ptr %[[COORD_ADDR]]
+// CHECK: %[[DDX_VAL:.*]] = load <2 x float>, ptr %[[DDX_ADDR]]
+// CHECK: %[[DDY_VAL:.*]] = load <2 x float>, ptr %[[DDY_ADDR]]
+// CHECK: %[[OFFSET_VAL:.*]] = load <2 x i32>, ptr %[[OFFSET_ADDR]]
+// CHECK: %[[CLAMP_VAL:.*]] = load float, ptr %[[CLAMP_ADDR]]
+// CHECK: %[[CLAMP_CAST3:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: call {{.*}} <4 x float> @llvm.dx.resource.samplegrad.clamp.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2f32.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE3]], target("dx.Sampler", 0) %[[SAMPLER_H3]], <2 x float> %[[COORD_VAL]], <2 x float> %[[DDX_VAL]], <2 x float> %[[DDY_VAL]], <2 x i32> %[[OFFSET_VAL]], float %[[CLAMP_CAST3]])
+// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.samplegrad.clamp.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2f32.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE3]], target("spirv.Sampler") %[[SAMPLER_H3]], <2 x float> %[[COORD_VAL]], <2 x float> %[[DDX_VAL]], <2 x float> %[[DDY_VAL]], <2 x i32> %[[OFFSET_VAL]], float %[[CLAMP_CAST3]])
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
new file mode 100644
index 0000000000000..bcd025c164ac0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 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 | llvm-cxxfilt | 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-LABEL: @test_level(float vector[2], float)
+// CHECK: %[[CALL:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleLevel(hlsl::SamplerState, float vector[2], float)(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00)
+// CHECK: ret <4 x float> %[[CALL]]
+
+float4 test_level(float2 loc : LOC, float lod : LOD) : SV_Target {
+  return t.SampleLevel(s, loc, 0.0f);
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleLevel(hlsl::SamplerState, float vector[2], float)(
+// CHECK-SAME: ptr {{.*}} %[[THIS1:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER1:[^,]+]], <2 x float> noundef nofpclass(nan inf) %[[COORD1:[^,]+]], float noundef nofpclass(nan inf) %[[LOD1:[^)]+]])
+// CHECK: %[[THIS_ADDR1:.*]] = alloca ptr
+// CHECK: %[[COORD_ADDR1:.*]] = alloca <2 x float>
+// CHECK: %[[LOD_ADDR1:.*]] = alloca float
+// CHECK: store ptr %[[THIS1]], ptr %[[THIS_ADDR1]]
+// CHECK: store <2 x float> %[[COORD1]], ptr %[[COORD_ADDR1]]
+// CHECK: store float %[[LOD1]], ptr %[[LOD_ADDR1]]
+// CHECK: %[[THIS_VAL1:.*]] = load ptr, ptr %[[THIS_ADDR1]]
+// CHECK: %[[HANDLE_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL1]], i32 0, i32 0
+// CHECK: %[[HANDLE1:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP1]]
+// CHECK: %[[SAMPLER_GEP1:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER1]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H1:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP1]]
+// CHECK: %[[COORD_VAL1:.*]] = load <2 x float>, ptr %[[COORD_ADDR1]]
+// CHECK: %[[LOD_VAL1:.*]] = load float, ptr %[[LOD_ADDR1]]
+// CHECK: %[[LOD_CAST1:.*]] = fptrunc {{.*}} double {{.*}} to float
+// DXIL: call {{.*}}                         <4 x float> @llvm.dx.resource.samplelevel.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE1]], target("dx.Sampler", 0) %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL1]], float %[[LOD_CAST1]], <2 x i32> zeroinitializer)
+// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.samplelevel.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE1]], target("spirv.Sampler") %[[SAMPLER_H1]], <2 x float> %[[COORD_VAL1]], float %[[LOD_CAST1]], <2 x i32> zeroinitializer)
+
+// CHECK-LABEL: @test_offset(float vector[2], float)
+// CHECK: %[[CALL_OFFSET:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleLevel(hlsl::SamplerState, float vector[2], float, int vector[2])(ptr {{.*}} @t, ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}}, <2 x float> {{.*}} %{{.*}}, float {{.*}} 0.000000e+00, <2 x i32> noundef <i32 1, i32 2>)
+// CHECK: ret <4 x float> %[[CALL_OFFSET]]
+
+float4 test_offset(float2 loc : LOC, float lod : LOD) : SV_Target {
+  return t.SampleLevel(s, loc, 0.0f, int2(1, 2));
+}
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::SampleLevel(hlsl::SamplerState, float vector[2], float, int vector[2])(
+// CHECK-SAME: ptr {{.*}} %[[THIS2:[^,]+]], ptr {{.*}} byval(%"class.hlsl::SamplerState") {{.*}} %[[SAMPLER2:[^,]+]], <2 x float> noundef nofpclass(nan inf) %[[COORD2:[^,]+]], float noundef nofpclass(nan inf) %[[LOD2:[^,]+]], <2 x i32> noundef %[[OFFSET2:[^)]+]])
+// CHECK: %[[THIS_ADDR2:.*]] = alloca ptr
+// CHECK: %[[COORD_ADDR2:.*]] = alloca <2 x float>
+// CHECK: %[[LOD_ADDR2:.*]] = alloca float
+// CHECK: %[[OFFSET_ADDR2:.*]] = alloca <2 x i32>
+// CHECK: store ptr %[[THIS2]], ptr %[[THIS_ADDR2]]
+// CHECK: store <2 x float> %[[COORD2]], ptr %[[COORD_ADDR2]]
+// CHECK: store float %[[LOD2]], ptr %[[LOD_ADDR2]]
+// CHECK: store <2 x i32> %[[OFFSET2]], ptr %[[OFFSET_ADDR2]]
+// CHECK: %[[THIS_VAL2:.*]] = load ptr, ptr %[[THIS_ADDR2]]
+// CHECK: %[[HANDLE_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL2]], i32 0, i32 0
+// CHECK: %[[HANDLE2:.*]] = load target{{.*}}, ptr %[[HANDLE_GEP2]]
+// CHECK: %[[SAMPLER_GEP2:.*]] = getelementptr inbounds nuw %"class.hlsl::SamplerState", ptr %[[SAMPLER2]], i32 0, i32 0
+// CHECK: %[[SAMPLER_H2:.*]] = load target{{.*}}, ptr %[[SAMPLER_GEP2]]
+// CHECK: %[[COORD_VAL2:.*]] = load <2 x float>, ptr %[[COORD_ADDR2]]
+// CHECK: %[[LOD_VAL2:.*]] = load float, ptr %[[LOD_ADDR2]]
+// CHECK: %[[LOD_CAST2:.*]] = fptrunc {{.*}} double {{.*}} to float
+// CHECK: %[[OFFSET_VAL2:.*]] = load <2 x i32>, ptr %[[OFFSET_ADDR2]]
+// DXIL: call {{.*}} <4 x float> @llvm.dx.resource.samplelevel.v4f32.tdx.Texture_v4f32_0_0_0_2t.tdx.Sampler_0t.v2f32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE2]], target("dx.Sampler", 0) %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL2]], float %[[LOD_CAST2]], <2 x i32> %[[OFFSET_VAL2]])
+// SPIRV: call {{.*}} <4 x float> @llvm.spv.resource.samplelevel.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.tspirv.Samplert.v2f32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE2]], target("spirv.Sampler") %[[SAMPLER_H2]], <2 x float> %[[COORD_VAL2]], float %[[LOD_CAST2]], <2 x i32> %[[OFFSET_VAL2]])
diff --git a/clang/test/SemaHLSL/Texture2D-SampleBias.hlsl b/clang/test/SemaHLSL/Texture2D-SampleBias.hlsl
new file mode 100644
index 0000000000000..e49575c0dfc7d
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-SampleBias.hlsl
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -finclude-default-header -fsyntax-only -verify %s
+
+Texture2D<float4> tex;
+SamplerState samp;
+
+void main() {
+  float2 loc = float2(0, 0);
+  float bias = 0;
+  int2 offset = int2(0, 0);
+  float clamp = 0;
+
+  tex.SampleBias(samp, loc, bias);
+  tex.SampleBias(samp, loc, bias, offset);
+  tex.SampleBias(samp, loc, bias, offset, clamp);
+
+  // Too few arguments.
+  tex.SampleBias(samp, loc); // expected-error {{no matching member function for call to 'SampleBias'}}
+  // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 2 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 2 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 2 were provided}}
+
+  // Too many arguments.
+  tex.SampleBias(samp, loc, bias, offset, clamp, 0); // expected-error {{no matching member function for call to 'SampleBias'}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 6 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 6 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 6 were provided}}
+
+  // Invalid argument types.
+  tex.SampleBias(samp, loc, bias, offset, "invalid"); // expected-error {{no matching member function for call to 'SampleBias'}}
+  // expected-note@*:* {{no known conversion from 'const char[8]' to 'float' for 5th argument}}
+  // 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}}
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Texture2D-SampleCmp.hlsl b/clang/test/SemaHLSL/Texture2D-SampleCmp.hlsl
new file mode 100644
index 0000000000000..b05fa2d59e29f
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-SampleCmp.hlsl
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -fsyntax-only -finclude-default-header -verify %s
+
+
+Texture2D<float4> t;
+Texture2D<int4> t_int;
+SamplerComparisonState s;
+SamplerState s2;
+
+void main(float2 loc, float cmp) {
+  t.SampleCmp(s, loc, cmp);
+  t.SampleCmp(s, loc, cmp, int2(1, 2));
+  t.SampleCmp(s, loc, cmp, int2(1, 2), 1.0f);
+
+  // expected-error@* {{'SampleCmp' and 'SampleCmpLevelZero' require resource to contain a floating point type}}
+  // expected-note@*:* {{in instantiation of member function 'hlsl::Texture2D<vector<int, 4>>::SampleCmp' requested here}}
+  t_int.SampleCmp(s, loc, cmp);
+
+  // expected-error at +4 {{no matching member function for call to 'SampleCmp'}}
+  // 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}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 1 was provided}}
+  t.SampleCmp(loc);
+
+  // expected-error at +4 {{no matching member function for call to 'SampleCmp'}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 6 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 6 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 6 were provided}}
+  t.SampleCmp(s, loc, cmp, int2(1, 2), 1.0f, 1.0f);
+
+  // expected-error at +4 {{no matching member function for call to 'SampleCmp'}}
+  // expected-note@*:* {{candidate function not viable: no known conversion from 'SamplerState' to 'hlsl::SamplerComparisonState' for 1st argument}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 3 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 3 were provided}}
+  t.SampleCmp(s2, loc, cmp);
+
+  // expected-error at +4 {{no matching member function for call to 'SampleCmp'}}
+  // expected-note@*:* {{candidate function not viable: no known conversion from 'SamplerComparisonState' to 'vector<int, 2>' (vector of 2 'int' values) for 4th argument}}
+  // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 4 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 4 were provided}}
+  t.SampleCmp(s, loc, cmp, s);
+
+  // expected-error at +4 {{no matching member function for call to 'SampleCmp'}}
+  // expected-note@*:* {{candidate function not viable: no known conversion from 'SamplerComparisonState' to 'float' for 5th argument}}
+  // 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}}
+  t.SampleCmp(s, loc, cmp, int2(1, 2), s);
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Texture2D-SampleCmpLevelZero.hlsl b/clang/test/SemaHLSL/Texture2D-SampleCmpLevelZero.hlsl
new file mode 100644
index 0000000000000..a0879e7bcae4a
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-SampleCmpLevelZero.hlsl
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -fsyntax-only -finclude-default-header -verify %s
+
+// expected-error@* {{'SampleCmp' and 'SampleCmpLevelZero' require resource to contain a floating point type}}
+
+Texture2D<float4> t;
+Texture2D<int4> t_int;
+SamplerComparisonState s;
+SamplerState s2;
+
+void main(float2 loc, float cmp) {
+  t.SampleCmpLevelZero(s, loc, cmp);
+  t.SampleCmpLevelZero(s, loc, cmp, int2(1, 2));
+
+  // expected-note@*:* {{in instantiation of member function 'hlsl::Texture2D<vector<int, 4>>::SampleCmpLevelZero' requested here}}
+  t_int.SampleCmpLevelZero(s, loc, cmp);
+
+  // expected-error at +3 {{no matching member function for call to 'SampleCmpLevelZero'}}
+  // 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.SampleCmpLevelZero(loc);
+
+  // expected-error at +3 {{no matching member function for call to 'SampleCmpLevelZero'}}
+  // 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}}
+  t.SampleCmpLevelZero(s, loc, cmp, int2(1, 2), 1.0f);
+
+  // expected-error at +3 {{no matching member function for call to 'SampleCmpLevelZero'}}
+  // expected-note@*:* {{candidate function not viable: no known conversion from 'SamplerState' to 'hlsl::SamplerComparisonState' for 1st argument}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 3 were provided}}
+  t.SampleCmpLevelZero(s2, loc, cmp);
+
+  // expected-error at +3 {{no matching member function for call to 'SampleCmpLevelZero'}}
+  // expected-note@*:* {{candidate function not viable: no known conversion from 'SamplerComparisonState' to 'vector<int, 2>' (vector of 2 'int' values) for 4th argument}}
+  // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 4 were provided}}
+  t.SampleCmpLevelZero(s, loc, cmp, s);
+}
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/Texture2D-SampleGrad.hlsl b/clang/test/SemaHLSL/Texture2D-SampleGrad.hlsl
new file mode 100644
index 0000000000000..9705dc9e37d49
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-SampleGrad.hlsl
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -finclude-default-header -fsyntax-only -verify %s
+
+Texture2D<float4> tex;
+SamplerState samp;
+
+void main() {
+  float2 loc = float2(0, 0);
+  float2 ddx = float2(0, 0);
+  float2 ddy = float2(0, 0);
+  int2 offset = int2(0, 0);
+  float clamp = 0;
+
+  tex.SampleGrad(samp, loc, ddx, ddy);
+  tex.SampleGrad(samp, loc, ddx, ddy, offset);
+  tex.SampleGrad(samp, loc, ddx, ddy, offset, clamp);
+
+  // Too few arguments.
+  tex.SampleGrad(samp, loc, ddx); // expected-error {{no matching member function for call to 'SampleGrad'}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 3 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 3 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 6 arguments, but 3 were provided}}
+
+  // Too many arguments.
+  tex.SampleGrad(samp, loc, ddx, ddy, offset, clamp, 0); // expected-error {{no matching member function for call to 'SampleGrad'}}
+  // expected-note@*:* {{candidate function not viable: requires 6 arguments, but 7 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 7 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 7 were provided}}
+
+  // Invalid argument types.
+  tex.SampleGrad(samp, loc, ddx, ddy, offset, "invalid"); // expected-error {{no matching member function for call to 'SampleGrad'}}
+  // expected-note@*:* {{no known conversion from 'const char[8]' to 'float' for 6th argument}}
+  // expected-note@*:* {{candidate function not viable: requires 5 arguments, but 6 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 6 were provided}}
+}
diff --git a/clang/test/SemaHLSL/Texture2D-SampleLevel.hlsl b/clang/test/SemaHLSL/Texture2D-SampleLevel.hlsl
new file mode 100644
index 0000000000000..43f3815201b10
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-SampleLevel.hlsl
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -finclude-default-header -fsyntax-only -verify %s
+
+Texture2D<float4> tex;
+SamplerState samp;
+
+void main() {
+  float2 loc = float2(0, 0);
+  float lod = 0;
+  int2 offset = int2(0, 0);
+
+  tex.SampleLevel(samp, loc, lod);
+  tex.SampleLevel(samp, loc, lod, offset);
+
+  // Too few arguments.
+  tex.SampleLevel(samp, loc); // expected-error {{no matching member function for call to 'SampleLevel'}}
+  // expected-note@*:* {{candidate function not viable: requires 3 arguments, but 2 were provided}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 2 were provided}}
+
+  // Too many arguments.
+  tex.SampleLevel(samp, loc, lod, offset, 0); // expected-error {{no matching member function for call to 'SampleLevel'}}
+  // 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}}
+
+  // Invalid argument types.
+  tex.SampleLevel(samp, loc, "invalid"); // expected-error {{no matching member function for call to 'SampleLevel'}}
+  // expected-note@*:* {{no known conversion from 'const char[8]' to 'float' for 3rd argument}}
+  // expected-note@*:* {{candidate function not viable: requires 4 arguments, but 3 were provided}}
+}

>From 4ddd3450407eabddac04aa221874962cff8d86a8 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Thu, 5 Feb 2026 12:27:29 -0500
Subject: [PATCH 4/8] Remove new lines.

---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 36 -------------------
 1 file changed, 36 deletions(-)

diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index f0e54eb092011..93ef7a87a7bd5 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1214,21 +1214,15 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
-
   ASTContext &AST = Record->getASTContext();
   QualType ReturnType = getFirstTemplateTypeParam();
-
   QualType SamplerStateType =
       lookupBuiltinType(SemaRef, "SamplerState", Record->getDeclContext());
-
   uint32_t VecSize = getResourceDimensions(Dim);
-
   QualType FloatTy = AST.FloatTy;
   QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
-
   QualType IntTy = AST.IntTy;
   QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
-
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
 
   // T Sample(SamplerState s, float2 location)
@@ -1268,21 +1262,15 @@ BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim) {
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
-
   ASTContext &AST = Record->getASTContext();
   QualType ReturnType = getFirstTemplateTypeParam();
-
   QualType SamplerStateType =
       lookupBuiltinType(SemaRef, "SamplerState", Record->getDeclContext());
-
   uint32_t VecSize = getResourceDimensions(Dim);
-
   QualType FloatTy = AST.FloatTy;
   QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
-
   QualType IntTy = AST.IntTy;
   QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
-
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
 
   // T SampleBias(SamplerState s, float2 location, float bias)
@@ -1326,21 +1314,15 @@ BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim) {
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleGradMethods(ResourceDimension Dim) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
-
   ASTContext &AST = Record->getASTContext();
   QualType ReturnType = getFirstTemplateTypeParam();
-
   QualType SamplerStateType =
       lookupBuiltinType(SemaRef, "SamplerState", Record->getDeclContext());
-
   uint32_t VecSize = getResourceDimensions(Dim);
-
   QualType FloatTy = AST.FloatTy;
   QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
-
   QualType IntTy = AST.IntTy;
   QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
-
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
 
   // T SampleGrad(SamplerState s, float2 location, float2 ddx, float2 ddy)
@@ -1389,21 +1371,15 @@ BuiltinTypeDeclBuilder::addSampleGradMethods(ResourceDimension Dim) {
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleLevelMethods(ResourceDimension Dim) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
-
   ASTContext &AST = Record->getASTContext();
   QualType ReturnType = getFirstTemplateTypeParam();
-
   QualType SamplerStateType =
       lookupBuiltinType(SemaRef, "SamplerState", Record->getDeclContext());
-
   uint32_t VecSize = getResourceDimensions(Dim);
-
   QualType FloatTy = AST.FloatTy;
   QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
-
   QualType IntTy = AST.IntTy;
   QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
-
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
 
   // T SampleLevel(SamplerState s, float2 location, float lod)
@@ -1433,21 +1409,15 @@ BuiltinTypeDeclBuilder::addSampleLevelMethods(ResourceDimension Dim) {
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
-
   ASTContext &AST = Record->getASTContext();
   QualType ReturnType = AST.FloatTy;
-
   QualType SamplerComparisonStateType = lookupBuiltinType(
       SemaRef, "SamplerComparisonState", Record->getDeclContext());
-
   uint32_t VecSize = getResourceDimensions(Dim);
-
   QualType FloatTy = AST.FloatTy;
   QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
-
   QualType IntTy = AST.IntTy;
   QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
-
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
 
   // T SampleCmp(SamplerComparisonState s, float2 location, float compare_value)
@@ -1492,21 +1462,15 @@ BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim) {
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleCmpLevelZeroMethods(ResourceDimension Dim) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
-
   ASTContext &AST = Record->getASTContext();
   QualType ReturnType = AST.FloatTy;
-
   QualType SamplerComparisonStateType = lookupBuiltinType(
       SemaRef, "SamplerComparisonState", Record->getDeclContext());
-
   uint32_t VecSize = getResourceDimensions(Dim);
-
   QualType FloatTy = AST.FloatTy;
   QualType Float2Ty = AST.getExtVectorType(FloatTy, VecSize);
-
   QualType IntTy = AST.IntTy;
   QualType Int2Ty = AST.getExtVectorType(IntTy, VecSize);
-
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
 
   // T SampleCmpLevelZero(SamplerComparisonState s, float2 location, float

>From 50abc3943ba86d17d9a7a6d1ba74ce5c731efa67 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Thu, 5 Feb 2026 14:12:36 -0500
Subject: [PATCH 5/8] Remove checks for ExpectedDim != 0

---
 clang/lib/Sema/SemaHLSL.cpp | 28 ++++++++++++----------------
 1 file changed, 12 insertions(+), 16 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index b7d137ff4eb8e..23f58f4d941fa 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3375,27 +3375,23 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
     }
     NextIdx++;
   } else if (Kind == SampleKind::Grad) {
-    if (ExpectedDim != 0) {
-      if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
-                                  S.Context.FloatTy, ExpectedDim,
-                                  TheCall->getArg(NextIdx)->getBeginLoc()))
-        return true;
-      if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx + 1)->getType(),
-                                  S.Context.FloatTy, ExpectedDim,
-                                  TheCall->getArg(NextIdx + 1)->getBeginLoc()))
-        return true;
-    }
+    if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
+                                S.Context.FloatTy, ExpectedDim,
+                                TheCall->getArg(NextIdx)->getBeginLoc()))
+      return true;
+    if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx + 1)->getType(),
+                                S.Context.FloatTy, ExpectedDim,
+                                TheCall->getArg(NextIdx + 1)->getBeginLoc()))
+      return true;
     NextIdx += 2;
   }
 
   // Offset
   if (TheCall->getNumArgs() > NextIdx) {
-    if (ExpectedDim != 0) {
-      if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
-                                  S.Context.IntTy, ExpectedDim,
-                                  TheCall->getArg(NextIdx)->getBeginLoc()))
-        return true;
-    }
+    if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
+                                S.Context.IntTy, ExpectedDim,
+                                TheCall->getArg(NextIdx)->getBeginLoc()))
+      return true;
     NextIdx++;
   }
 

>From 079213300c67c664946e6aa87a85fff5489bfa54 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Thu, 5 Feb 2026 14:23:52 -0500
Subject: [PATCH 6/8] Add comments to say which operand is being checked.

---
 clang/lib/Sema/SemaHLSL.cpp | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 23f58f4d941fa..fea0796685b81 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3338,6 +3338,7 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
   if (S.checkArgCountRange(TheCall, MinArgs, MaxArgs))
     return true;
 
+  // Check the texture handle.
   if (CheckResourceHandle(&S, TheCall, 0,
                           [](const HLSLAttributedResourceType *ResType) {
                             return ResType->getAttrs().ResourceDimension ==
@@ -3345,6 +3346,7 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
                           }))
     return true;
 
+  // Check the sampler handle.
   if (CheckResourceHandle(&S, TheCall, 1,
                           [](const HLSLAttributedResourceType *ResType) {
                             return ResType->getAttrs().ResourceClass !=
@@ -3355,6 +3357,7 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
   auto *ResourceTy =
       TheCall->getArg(0)->getType()->castAs<HLSLAttributedResourceType>();
 
+  // Check the location.
   unsigned ExpectedDim =
       getResourceDimensions(ResourceTy->getAttrs().ResourceDimension);
   if (CheckVectorElementCount(&S, TheCall->getArg(2)->getType(),
@@ -3365,6 +3368,8 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
   unsigned NextIdx = 3;
   if (Kind == SampleKind::Bias || Kind == SampleKind::Level ||
       Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero) {
+    // Check the bias, lod level, or compare value, depending on the kind.
+    // All of them must be a scalar float value.
     QualType BiasOrLODOrCmpTy = TheCall->getArg(NextIdx)->getType();
     if (!BiasOrLODOrCmpTy->isFloatingType() ||
         BiasOrLODOrCmpTy->isVectorType()) {
@@ -3375,10 +3380,13 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
     }
     NextIdx++;
   } else if (Kind == SampleKind::Grad) {
+    // Check the DDX operand.
     if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
                                 S.Context.FloatTy, ExpectedDim,
                                 TheCall->getArg(NextIdx)->getBeginLoc()))
       return true;
+
+    // Check the DDY operand.
     if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx + 1)->getType(),
                                 S.Context.FloatTy, ExpectedDim,
                                 TheCall->getArg(NextIdx + 1)->getBeginLoc()))
@@ -3386,7 +3394,7 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
     NextIdx += 2;
   }
 
-  // Offset
+  // Check the offset operand.
   if (TheCall->getNumArgs() > NextIdx) {
     if (CheckVectorElementCount(&S, TheCall->getArg(NextIdx)->getType(),
                                 S.Context.IntTy, ExpectedDim,
@@ -3395,7 +3403,7 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
     NextIdx++;
   }
 
-  // Clamp
+  // Check the clamp operand.
   if (Kind != SampleKind::Level && Kind != SampleKind::CmpLevelZero &&
       TheCall->getNumArgs() > NextIdx) {
     QualType ClampTy = TheCall->getArg(NextIdx)->getType();

>From 07ed304f7d597ceb7f0c5aab037b4b0899daed33 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Thu, 5 Feb 2026 14:48:52 -0500
Subject: [PATCH 7/8] Refactor expansion of the sample builtin function.

---
 clang/lib/CodeGen/CGHLSLBuiltins.cpp | 195 +++++++--------------------
 1 file changed, 48 insertions(+), 147 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 581f4318915e7..139455794003c 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -435,6 +435,30 @@ static std::string getSpecConstantFunctionName(clang::QualType SpecConstantType,
   return Name;
 }
 
+static Value *emitHlslOffset(CodeGenFunction &CGF, const CallExpr *E,
+                             unsigned OffsetArgIndex) {
+  if (E->getNumArgs() > OffsetArgIndex)
+    return CGF.EmitScalarExpr(E->getArg(OffsetArgIndex));
+
+  llvm::Type *CoordTy = CGF.ConvertType(E->getArg(2)->getType());
+  llvm::Type *Int32Ty = CGF.Int32Ty;
+  llvm::Type *OffsetTy = Int32Ty;
+  if (auto *VT = dyn_cast<llvm::FixedVectorType>(CoordTy))
+    OffsetTy = llvm::FixedVectorType::get(Int32Ty, VT->getNumElements());
+  return llvm::Constant::getNullValue(OffsetTy);
+}
+
+static Value *emitHlslClamp(CodeGenFunction &CGF, const CallExpr *E,
+                            unsigned ClampArgIndex) {
+  Value *Clamp = CGF.EmitScalarExpr(E->getArg(ClampArgIndex));
+  // 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() != CGF.Builder.getFloatTy())
+    Clamp = CGF.Builder.CreateFPCast(Clamp, CGF.Builder.getFloatTy());
+  return Clamp;
+}
+
 Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
                                             const CallExpr *E,
                                             ReturnValueSlot ReturnValue) {
@@ -515,19 +539,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     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));
-    }
+    Args.push_back(emitHlslOffset(*this, E, 3));
 
     llvm::Type *RetTy = ConvertType(E->getType());
     if (E->getNumArgs() <= 4) {
@@ -535,13 +547,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
           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);
+    Args.push_back(emitHlslClamp(*this, E, 4));
     return Builder.CreateIntrinsic(
         RetTy, CGM.getHLSLRuntime().getSampleClampIntrinsic(), Args);
   }
@@ -557,44 +563,18 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     Args.push_back(HandleOp);
     Args.push_back(SamplerOp);
     Args.push_back(CoordOp);
-    Args.push_back(BiasOp); // Bias is always the 4th argument (index 3)
-
-    // Handle optional Offset (E->getArg(4))
-    Value *OffsetOp;
-    if (E->getNumArgs() > 4) { // if E has at least 5 arguments (Handle,
-                               // Sampler, Coord, Bias, Offset)
-      OffsetOp = EmitScalarExpr(E->getArg(4));
-    } else {
-      // Default offset is 0.
-      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());
-      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
-    }
-    Args.push_back(OffsetOp); // Offset is always the 5th argument (index 4)
+    Args.push_back(BiasOp);
+    Args.push_back(emitHlslOffset(*this, E, 4));
 
     llvm::Type *RetTy = ConvertType(E->getType());
-
-    // Determine which intrinsic to call based on total number of arguments in E
-    if (E->getNumArgs() <=
-        5) { // No clamp parameter (Handle, Sampler, Coord, Bias, Offset)
+    if (E->getNumArgs() <= 5) {
       return Builder.CreateIntrinsic(
           RetTy, CGM.getHLSLRuntime().getSampleBiasIntrinsic(), Args);
-    } else { // Has clamp parameter (Handle, Sampler, Coord, Bias, Offset,
-             // Clamp)
-      llvm::Value *Clamp =
-          EmitScalarExpr(E->getArg(5)); // Clamp is E->getArg(5)
-      // 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); // Clamp is the 6th argument (index 5)
-      return Builder.CreateIntrinsic(
-          RetTy, CGM.getHLSLRuntime().getSampleBiasClampIntrinsic(), Args);
     }
+
+    Args.push_back(emitHlslClamp(*this, E, 5));
+    return Builder.CreateIntrinsic(
+        RetTy, CGM.getHLSLRuntime().getSampleBiasClampIntrinsic(), Args);
   }
   case Builtin::BI__builtin_hlsl_resource_sample_grad: {
     Value *HandleOp = EmitScalarExpr(E->getArg(0));
@@ -603,49 +583,24 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     Value *DDXOp = EmitScalarExpr(E->getArg(3));
     Value *DDYOp = EmitScalarExpr(E->getArg(4));
 
-    SmallVector<Value *, 7> Args; // Max 7 arguments for SampleGrad
+    SmallVector<Value *, 7> Args;
     Args.push_back(HandleOp);
     Args.push_back(SamplerOp);
     Args.push_back(CoordOp);
     Args.push_back(DDXOp);
     Args.push_back(DDYOp);
-
-    // Handle optional Offset (E->getArg(5))
-    Value *OffsetOp;
-    if (E->getNumArgs() > 5) { // if E has at least 6 arguments (Handle,
-                               // Sampler, Coord, DDX, DDY, Offset)
-      OffsetOp = EmitScalarExpr(E->getArg(5));
-    } else {
-      // Default offset is 0.
-      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());
-      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
-    }
-    Args.push_back(OffsetOp); // Offset is always the 6th argument (index 5)
+    Args.push_back(emitHlslOffset(*this, E, 5));
 
     llvm::Type *RetTy = ConvertType(E->getType());
 
-    // Determine which intrinsic to call based on total number of arguments in E
-    if (E->getNumArgs() <=
-        6) { // No clamp parameter (Handle, Sampler, Coord, DDX, DDY, Offset)
+    if (E->getNumArgs() <= 6) {
       return Builder.CreateIntrinsic(
           RetTy, CGM.getHLSLRuntime().getSampleGradIntrinsic(), Args);
-    } else { // Has clamp parameter (Handle, Sampler, Coord, DDX, DDY, Offset,
-             // Clamp)
-      llvm::Value *Clamp =
-          EmitScalarExpr(E->getArg(6)); // Clamp is E->getArg(6)
-      // 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); // Clamp is the 7th argument (index 6)
-      return Builder.CreateIntrinsic(
-          RetTy, CGM.getHLSLRuntime().getSampleGradClampIntrinsic(), Args);
     }
+
+    Args.push_back(emitHlslClamp(*this, E, 6));
+    return Builder.CreateIntrinsic(
+        RetTy, CGM.getHLSLRuntime().getSampleGradClampIntrinsic(), Args);
   }
   case Builtin::BI__builtin_hlsl_resource_sample_level: {
     Value *HandleOp = EmitScalarExpr(E->getArg(0));
@@ -660,22 +615,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     Args.push_back(SamplerOp);
     Args.push_back(CoordOp);
     Args.push_back(LODOp);
-
-    // Handle optional Offset (E->getArg(4))
-    Value *OffsetOp;
-    if (E->getNumArgs() > 4) { // if E has 5 arguments (Handle, Sampler, Coord,
-                               // LOD, Offset)
-      OffsetOp = EmitScalarExpr(E->getArg(4));
-    } else {
-      // Default offset is 0.
-      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());
-      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
-    }
-    Args.push_back(OffsetOp);
+    Args.push_back(emitHlslOffset(*this, E, 4));
 
     llvm::Type *RetTy = ConvertType(E->getType());
     return Builder.CreateIntrinsic(
@@ -694,43 +634,17 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     Args.push_back(SamplerOp);
     Args.push_back(CoordOp);
     Args.push_back(CmpOp);
-
-    // Handle optional Offset (E->getArg(4))
-    Value *OffsetOp;
-    if (E->getNumArgs() > 4) { // if E has at least 5 arguments (Handle,
-                               // Sampler, Coord, CompareValue, Offset)
-      OffsetOp = EmitScalarExpr(E->getArg(4));
-    } else {
-      // Default offset is 0.
-      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());
-      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
-    }
-    Args.push_back(OffsetOp);
+    Args.push_back(emitHlslOffset(*this, E, 4));
 
     llvm::Type *RetTy = ConvertType(E->getType());
-
-    // Determine which intrinsic to call based on total number of arguments in E
-    if (E->getNumArgs() <= 5) { // No clamp parameter (Handle, Sampler, Coord,
-                                // CompareValue, Offset)
+    if (E->getNumArgs() <= 5) {
       return Builder.CreateIntrinsic(
           RetTy, CGM.getHLSLRuntime().getSampleCmpIntrinsic(), Args);
-    } else { // Has clamp parameter (Handle, Sampler, Coord, CompareValue,
-             // Offset, Clamp)
-      llvm::Value *Clamp =
-          EmitScalarExpr(E->getArg(5)); // Clamp is E->getArg(5)
-      // 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().getSampleCmpClampIntrinsic(), Args);
     }
+
+    Args.push_back(emitHlslClamp(*this, E, 5));
+    return Builder.CreateIntrinsic(
+        RetTy, CGM.getHLSLRuntime().getSampleCmpClampIntrinsic(), Args);
   }
   case Builtin::BI__builtin_hlsl_resource_sample_cmp_level_zero: {
     Value *HandleOp = EmitScalarExpr(E->getArg(0));
@@ -746,20 +660,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     Args.push_back(CoordOp);
     Args.push_back(CmpOp);
 
-    // Handle optional Offset (E->getArg(4))
-    Value *OffsetOp;
-    if (E->getNumArgs() > 4) {
-      OffsetOp = EmitScalarExpr(E->getArg(4));
-    } else {
-      // Default offset is 0.
-      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());
-      OffsetOp = llvm::Constant::getNullValue(OffsetTy);
-    }
-    Args.push_back(OffsetOp);
+    Args.push_back(emitHlslOffset(*this, E, 4));
 
     llvm::Type *RetTy = ConvertType(E->getType());
     return Builder.CreateIntrinsic(

>From 4a4ceed710431dd3272b02ce742b63d03ac94e2d Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 6 Feb 2026 11:51:55 -0500
Subject: [PATCH 8/8] Update clang/lib/CodeGen/CGHLSLBuiltins.cpp

Co-authored-by: Helena Kotas <hekotas at microsoft.com>
---
 clang/lib/CodeGen/CGHLSLBuiltins.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 139455794003c..830172c1629e6 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -567,10 +567,9 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     Args.push_back(emitHlslOffset(*this, E, 4));
 
     llvm::Type *RetTy = ConvertType(E->getType());
-    if (E->getNumArgs() <= 5) {
+    if (E->getNumArgs() <= 5)
       return Builder.CreateIntrinsic(
           RetTy, CGM.getHLSLRuntime().getSampleBiasIntrinsic(), Args);
-    }
 
     Args.push_back(emitHlslClamp(*this, E, 5));
     return Builder.CreateIntrinsic(



More information about the cfe-commits mailing list