[HLSL] Add sema for use of samplers and gathers on textures of doubles and ints (#212613)

Fixes https://github.com/llvm/llvm-project/issues/198882 and
https://github.com/llvm/llvm-project/issues/198883

This PR:
- Implements sema checks to reject the use of samplers and gathers on
textures of doubles.
- Implements sema checks to reject use of samplers on textures of
integers before shader model 6.7

Assisted by: Claude Opus 5
This commit is contained in:
Deric C. 2026-07-29 16:09:27 -07:00 committed by GitHub
parent c4dbdfc12e
commit 2815f298d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 237 additions and 0 deletions

View file

@ -13852,6 +13852,11 @@ def warn_hlsl_assigning_local_resource_is_not_unique
def err_hlsl_push_constant_unique
: Error<"cannot have more than one push constant block">;
def err_hlsl_sample_double_element_type
: Error<"'%0' is not supported for resources containing %1">;
def err_hlsl_sample_integer_element_type
: Error<"'%0' on resources containing %1 requires shader model 6.7 or "
"newer; the target shader model is %2">;
def err_hlsl_samplecmp_requires_float
: Error<"'SampleCmp' and 'SampleCmpLevelZero' require resource to contain "
"a floating point type">;

View file

@ -3770,6 +3770,96 @@ static bool CheckVectorElementCount(Sema *S, QualType PassedType,
enum class SampleKind { Sample, Bias, Grad, Level, Cmp, CmpLevelZero };
static StringRef getSampleMethodName(SampleKind Kind) {
switch (Kind) {
case SampleKind::Sample:
return "Sample";
case SampleKind::Bias:
return "SampleBias";
case SampleKind::Grad:
return "SampleGrad";
case SampleKind::Level:
return "SampleLevel";
case SampleKind::Cmp:
return "SampleCmp";
case SampleKind::CmpLevelZero:
return "SampleCmpLevelZero";
}
llvm_unreachable("Invalid SampleKind");
}
// Returns the name of the resource method whose body the sampling or gather
// builtin is being emitted into, which is the name the user called. This
// matters for methods that share a builtin, like 'Gather' and 'GatherRed'.
// Falls back to DefaultName if the builtin is used outside of a resource
// method.
static StringRef getCurrentResourceMethodName(Sema &S, StringRef DefaultName) {
const auto *MD = dyn_cast_if_present<CXXMethodDecl>(S.getCurFunctionDecl());
if (!MD || !MD->getDeclName().isIdentifier())
return DefaultName;
QualType RecordTy = S.Context.getCanonicalTagType(MD->getParent());
if (!RecordTy->isHLSLResourceRecord())
return DefaultName;
return MD->getName();
}
// Returns the element type of a typed resource's contained type. Typed resource
// element types are scalars or vectors of scalars, so anything that is not a
// vector is already the element type.
static QualType getTypedResourceElementType(QualType ContainedType) {
if (const auto *VecTy = ContainedType->getAs<VectorType>())
return VecTy->getElementType();
return ContainedType;
}
// Sampling from and gathering on resources with a 'double' element type is not
// supported. Such resources are still valid declarations whose contents can be
// accessed by other means, like Load or the subscript operator.
static bool CheckNoDoubleElementType(Sema &S, CallExpr *TheCall,
QualType ContainedType,
StringRef DefaultName) {
QualType EltTy = getTypedResourceElementType(ContainedType);
if (!EltTy->isSpecificBuiltinType(BuiltinType::Double))
return false;
S.Diag(TheCall->getBeginLoc(), diag::err_hlsl_sample_double_element_type)
<< getCurrentResourceMethodName(S, DefaultName) << ContainedType;
return true;
}
// Sampling textures with an integer element type was introduced in SM 6.7 as
// part of Advanced Texture Operations. The shader model only applies to DirectX
// targets; Vulkan has no such restriction.
static bool CheckIntegerElementTypeShaderModel(Sema &S, CallExpr *TheCall,
QualType ContainedType,
SampleKind Kind) {
// Comparison sampling requires a floating point element type at every shader
// model, which the caller diagnoses.
if (Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero)
return false;
// 'bool' is an integer type in HLSL, but sampling bool resources is never
// allowed, so it must not be reported as requiring shader model 6.7.
QualType EltTy = getTypedResourceElementType(ContainedType);
if (!EltTy->isIntegerType() || EltTy->isBooleanType())
return false;
const TargetInfo &TI = S.Context.getTargetInfo();
if (!TI.getTriple().isDXIL())
return false;
VersionTuple SMVersion = TI.getPlatformMinVersion();
if (SMVersion >= VersionTuple(6, 7))
return false;
S.Diag(TheCall->getBeginLoc(), diag::err_hlsl_sample_integer_element_type)
<< getCurrentResourceMethodName(S, getSampleMethodName(Kind))
<< ContainedType << SMVersion.getAsString();
return true;
}
static bool CheckTextureSamplerAndLocation(Sema &S, CallExpr *TheCall,
bool IncludeArraySlice = true) {
// Check the texture handle.
@ -3885,6 +3975,10 @@ static bool CheckGatherBuiltin(Sema &S, CallExpr *TheCall, bool IsCmp) {
"attribute.");
QualType ReturnType = ResourceTy->getContainedType();
if (CheckNoDoubleElementType(S, TheCall, ReturnType,
IsCmp ? "GatherCmp" : "Gather"))
return true;
if (IsCmp) {
if (!ReturnType->hasFloatingRepresentation()) {
S.Diag(TheCall->getBeginLoc(), diag::err_hlsl_samplecmp_requires_float);
@ -4033,6 +4127,14 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
"Expecting a contained type for resource with a dimension "
"attribute.");
QualType ReturnType = ResourceTy->getContainedType();
if (CheckNoDoubleElementType(S, TheCall, ReturnType,
getSampleMethodName(Kind)))
return true;
if (CheckIntegerElementTypeShaderModel(S, TheCall, ReturnType, Kind))
return true;
if (Kind == SampleKind::Cmp || Kind == SampleKind::CmpLevelZero) {
if (!ReturnType->hasFloatingRepresentation()) {
S.Diag(TheCall->getBeginLoc(), diag::err_hlsl_samplecmp_requires_float);

View file

@ -0,0 +1,69 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -DTEXTURE=Texture2D -DCOORD_TYPE=float2 -DLOCATION=int3 -DINDEX=uint2 -verify %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -DTEXTURE=Texture2DArray -DCOORD_TYPE=float3 -DLOCATION=int4 -DINDEX=uint3 -verify %s
// Textures with a 'double' element type are valid declarations, but sampling
// from them and gathering on them is not supported.
TEXTURE<double2> TexVec;
TEXTURE<double> Tex;
SamplerState Samp;
SamplerComparisonState SampCmp;
void main(COORD_TYPE uv) {
float compare = 0.5f;
// expected-error@* {{'Sample' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::Sample' requested here}}
Tex.Sample(Samp, uv);
// expected-error@* {{'SampleBias' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::SampleBias' requested here}}
Tex.SampleBias(Samp, uv, 0.5f);
// expected-error@* {{'SampleGrad' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::SampleGrad' requested here}}
Tex.SampleGrad(Samp, uv, float2(0, 0), float2(0, 0));
// expected-error@* {{'SampleLevel' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::SampleLevel' requested here}}
Tex.SampleLevel(Samp, uv, 0.0f);
// expected-error@* {{'SampleCmp' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::SampleCmp' requested here}}
Tex.SampleCmp(SampCmp, uv, compare);
// expected-error@* {{'SampleCmpLevelZero' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::SampleCmpLevelZero' requested here}}
Tex.SampleCmpLevelZero(SampCmp, uv, compare);
// expected-error@* {{'Gather' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::Gather' requested here}}
Tex.Gather(Samp, uv);
// expected-error@* {{'GatherRed' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::GatherRed' requested here}}
Tex.GatherRed(Samp, uv);
// expected-error@* {{'GatherCmp' is not supported for resources containing 'double'}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<double>::GatherCmp' requested here}}
Tex.GatherCmp(SampCmp, uv, compare);
// Textures containing vectors of doubles are rejected as well.
// expected-error@* {{'Sample' is not supported for resources containing 'vector<double, 2>' (vector of 2 'double' values)}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<vector<double, 2>>::Sample' requested here}}
TexVec.Sample(Samp, uv);
// expected-error@* {{'SampleLevel' is not supported for resources containing 'vector<double, 2>' (vector of 2 'double' values)}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<vector<double, 2>>::SampleLevel' requested here}}
TexVec.SampleLevel(Samp, uv, 0.0f);
// expected-error@* {{'Gather' is not supported for resources containing 'vector<double, 2>' (vector of 2 'double' values)}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<vector<double, 2>>::Gather' requested here}}
TexVec.Gather(Samp, uv);
// Loading from and subscripting textures containing doubles is allowed.
double a = Tex.Load((LOCATION)0);
double b = Tex[(INDEX)0];
double2 c = TexVec.Load((LOCATION)0);
double2 d = TexVec[(INDEX)0];
}

View file

@ -0,0 +1,61 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -x hlsl -fsyntax-only -finclude-default-header -fnative-half-type -fnative-int16-type -DTEXTURE=Texture2D -DCOORD_TYPE=float2 -verify=sm66,expected %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -x hlsl -fsyntax-only -finclude-default-header -fnative-half-type -fnative-int16-type -DTEXTURE=Texture2DArray -DCOORD_TYPE=float3 -verify=sm66,expected %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.7-library -x hlsl -fsyntax-only -finclude-default-header -fnative-half-type -fnative-int16-type -DTEXTURE=Texture2D -DCOORD_TYPE=float2 -verify=expected %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.7-library -x hlsl -fsyntax-only -finclude-default-header -fnative-half-type -fnative-int16-type -DTEXTURE=Texture2DArray -DCOORD_TYPE=float3 -verify=expected %s
// RUN: %clang_cc1 -triple spirv-unknown-vulkan-library -x hlsl -fsyntax-only -finclude-default-header -fnative-half-type -fnative-int16-type -DTEXTURE=Texture2D -DCOORD_TYPE=float2 -verify=expected %s
// Sampling textures with an integer element type was introduced in shader model
// 6.7, so the `sm66` diagnostics are only expected in the shader model 6.6 runs.
// The Vulkan target has no shader model and does not restrict integer sampling.
// Comparison sampling requires a floating point element type at every shader
// model, so those diagnostics use the `expected` prefix.
TEXTURE<int4> TexVec;
TEXTURE<uint> Tex;
TEXTURE<int16_t> TexShort;
SamplerState Samp;
SamplerComparisonState SampCmp;
void main(COORD_TYPE uv) {
float compare = 0.5f;
// sm66-error@* {{'Sample' on resources containing 'unsigned int' requires shader model 6.7 or newer; the target shader model is 6.6}}
// sm66-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<unsigned int>::Sample' requested here}}
Tex.Sample(Samp, uv);
// sm66-error@* {{'SampleBias' on resources containing 'unsigned int' requires shader model 6.7 or newer; the target shader model is 6.6}}
// sm66-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<unsigned int>::SampleBias' requested here}}
Tex.SampleBias(Samp, uv, 0.5f);
// sm66-error@* {{'SampleGrad' on resources containing 'unsigned int' requires shader model 6.7 or newer; the target shader model is 6.6}}
// sm66-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<unsigned int>::SampleGrad' requested here}}
Tex.SampleGrad(Samp, uv, float2(0, 0), float2(0, 0));
// sm66-error@* {{'SampleLevel' on resources containing 'unsigned int' requires shader model 6.7 or newer; the target shader model is 6.6}}
// sm66-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<unsigned int>::SampleLevel' requested here}}
Tex.SampleLevel(Samp, uv, 0.0f);
// Textures containing vectors of integers and 16-bit integers are gated the
// same way.
// sm66-error@* {{'Sample' on resources containing 'vector<int, 4>' (vector of 4 'int' values) requires shader model 6.7 or newer; the target shader model is 6.6}}
// sm66-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<vector<int, 4>>::Sample' requested here}}
TexVec.Sample(Samp, uv);
// sm66-error@* {{'SampleLevel' on resources containing 'short' requires shader model 6.7 or newer; the target shader model is 6.6}}
// sm66-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<short>::SampleLevel' requested here}}
TexShort.SampleLevel(Samp, uv, 0.0f);
// Comparison sampling rejects integer element types at every shader model.
// expected-error@* {{'SampleCmp' and 'SampleCmpLevelZero' require resource to contain a floating point type}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<unsigned int>::SampleCmp' requested here}}
Tex.SampleCmp(SampCmp, uv, compare);
// expected-error@* {{'SampleCmp' and 'SampleCmpLevelZero' require resource to contain a floating point type}}
// expected-note-re@*:* {{in instantiation of member function 'hlsl::Texture{{.+}}<unsigned int>::SampleCmpLevelZero' requested here}}
Tex.SampleCmpLevelZero(SampCmp, uv, compare);
// Gathering on textures with an integer element type is allowed at every
// shader model.
Tex.Gather(Samp, uv);
TexVec.Gather(Samp, uv);
}