[clang] [HLSL] Implement Sample* methods for Texture2D (PR #179322)
Steven Perron via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 10 06:25:10 PST 2026
https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/179322
>From b8004158aad02b91d5b45be2623b78682ab9f2be 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/7] [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 | 129 +++++++
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(+), 3 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 05e3af4a0e96f..ed4ec10375e48 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5024,6 +5024,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 f12677ac11600..887d1b5f2bbfd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13509,6 +13509,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 c72eef1982e9e..09e30326712f6 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -525,6 +525,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:
case Builtin::BI__builtin_hlsl_resource_load_with_status_typed: {
Value *HandleOp = EmitScalarExpr(E->getArg(0));
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c13f91afa6a39..62349c9dea7eb 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -165,6 +165,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 1dd7fd6fac455..d8028f9d67413 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1354,6 +1354,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 aa6967e1eb725..fcb61731c5416 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -95,6 +95,11 @@ class BuiltinTypeDeclBuilder {
BuiltinTypeDeclBuilder &addByteAddressBufferLoadMethods();
BuiltinTypeDeclBuilder &addByteAddressBufferStoreMethods();
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 f7862b3a3f594..662627901539a 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 813ab16fece73..49ba31ac1321e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3324,6 +3324,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) {
@@ -3497,6 +3614,18 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
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 283d800f1ea56fb05aa64070b6cc6d306bf0b3c2 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 2/7] 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 d8028f9d67413..4ef54cf49412f 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1303,21 +1303,15 @@ BuiltinTypeDeclBuilder::addByteAddressBufferStoreMethods() {
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)
@@ -1357,21 +1351,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)
@@ -1415,21 +1403,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)
@@ -1478,21 +1460,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)
@@ -1522,21 +1498,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)
@@ -1581,21 +1551,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 3b7e894dac1d03d39a33f62e53c79343ae6ad0e9 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 3/7] 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 49ba31ac1321e..0e8a4d7c72360 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3389,27 +3389,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 08d560051f16a448a6e67b4fa9223a4d4b532a3c 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 4/7] 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 0e8a4d7c72360..d610e3288b00e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3352,6 +3352,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 ==
@@ -3359,6 +3360,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 !=
@@ -3369,6 +3371,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(),
@@ -3379,6 +3382,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()) {
@@ -3389,10 +3394,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()))
@@ -3400,7 +3408,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,
@@ -3409,7 +3417,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 f6216297d298c5c7941cab7eedace0cd72d27959 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 5/7] 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 09e30326712f6..20e4af1889115 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -414,6 +414,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) {
@@ -495,19 +519,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) {
@@ -515,13 +527,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);
}
@@ -537,44 +543,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));
@@ -583,49 +563,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));
@@ -640,22 +595,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(
@@ -674,43 +614,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));
@@ -726,20 +640,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 38d6ab80960b158a3acbf7d06c84ac9041f556da 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 6/7] 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 20e4af1889115..5c060598e4b39 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -547,10 +547,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(
>From d03e5c26a339ce7742a6471d7d3c9a55d25039d6 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Tue, 10 Feb 2026 09:24:43 -0500
Subject: [PATCH 7/7] Remove error introduced when merging with main.
---
clang/lib/Sema/SemaHLSL.cpp | 53 -------------------------------------
1 file changed, 53 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d610e3288b00e..4271056ad43cc 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3565,59 +3565,6 @@ 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:
More information about the cfe-commits
mailing list