https://github.com/Icohedron created https://github.com/llvm/llvm-project/pull/212846
This PR adds availability attributes to texture sample methods that require implicit derivatives (fixes https://github.com/llvm/llvm-project/issues/198885) To make these availability attributes actually get checked, `DiagnoseHLSLAvailability::HandleFunctionOrMethodRef` in `SemaHLSL.cpp` has been changed to check availability attributes regardless of whether or not a function has a body/definition (fixes https://github.com/llvm/llvm-project/issues/212842). Assisted by: Claude Opus 5 >From 64503dc8e74a91f94e1a6adde4bb4a9e793009db Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Wed, 29 Jul 2026 11:32:09 -0700 Subject: [PATCH 1/2] Check availability attributes regardless of if the function has a definition --- clang/lib/Sema/SemaHLSL.cpp | 17 ++++++----------- .../avail-diag-default-compute.hlsl | 8 ++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index e26b18552d362..0af1cce24fbde 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2987,19 +2987,14 @@ void DiagnoseHLSLAvailability::HandleFunctionOrMethodRef(FunctionDecl *FD, assert((isa<DeclRefExpr>(RefExpr) || isa<MemberExpr>(RefExpr)) && "expected DeclRefExpr or MemberExpr"); - // has a definition -> add to stack to be scanned - const FunctionDecl *FDWithBody = nullptr; - if (FD->hasBody(FDWithBody)) { - if (!WasAlreadyScannedInCurrentStage(FDWithBody)) - DeclsToScan.push_back(FDWithBody); - return; - } - - // no body -> diagnose availability - const AvailabilityAttr *AA = FindAvailabilityAttr(FD); - if (AA) + if (const AvailabilityAttr *AA = FindAvailabilityAttr(FD)) CheckDeclAvailability( FD, AA, SourceRange(RefExpr->getBeginLoc(), RefExpr->getEndLoc())); + + // has a definition -> add to stack to be scanned + const FunctionDecl *FDWithBody = nullptr; + if (FD->hasBody(FDWithBody) && !WasAlreadyScannedInCurrentStage(FDWithBody)) + DeclsToScan.push_back(FDWithBody); } void DiagnoseHLSLAvailability::RunOnTranslationUnit( diff --git a/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl b/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl index 1424fe63242ae..75b5f160a3cb1 100644 --- a/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl +++ b/clang/test/SemaHLSL/Availability/avail-diag-default-compute.hlsl @@ -15,6 +15,11 @@ __attribute__((availability(shadermodel, introduced = 5.0, environment = pixel)) __attribute__((availability(shadermodel, introduced = 6.5, environment = mesh))) float fz(float); // #fz +// A function that has a definition is diagnosed based on its availability +// attribute as well; having a body does not make it available everywhere. +__attribute__((availability(shadermodel, introduced = 6.5))) +float fdef(float f) { return f; } // #fdef + float also_alive(float f) { // expected-error@#also_alive_fx_call {{'fx' is only available on Shader Model 6.5 or newer}} // expected-note@#fx {{'fx' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}} @@ -115,4 +120,7 @@ void main() { float c = C.makeF(); float d = test((float)1.0); float e = test((half)1.0); + // expected-error@#main_fdef_call {{'fdef' is only available on Shader Model 6.5 or newer}} + // expected-note@#fdef {{'fdef' has been marked as being introduced in Shader Model 6.5 here, but the deployment target is Shader Model 6.0}} + float g = fdef(f); // #main_fdef_call } >From e1470028ad9c4040c3d5472629a49a0b99e37001 Mon Sep 17 00:00:00 2001 From: Deric Cheung <[email protected]> Date: Wed, 29 Jul 2026 11:53:26 -0700 Subject: [PATCH 2/2] Add availability attributes to samplers that require implicit derivatives --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 61 +++++++++++- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 1 + .../Textures-derivative-availability.hlsl | 98 +++++++++++++++++++ 3 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 65b2da923d461..337e8795528c8 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -96,6 +96,32 @@ QualType getInoutParameterType(ASTContext &AST, QualType Ty) { return Ty; } +// Attaches availability attributes to a method that requires implicit +// derivatives. Implicit derivatives are always available in pixel +// shaders. Shader Model 6.6 made derivatives available in compute, mesh and +// amplification shaders as well. All other shader stages do not support +// derivatives. +void addDerivativeAvailabilityAttrs(ASTContext &AST, FunctionDecl *FD) { + struct DerivativeShaderStage { + StringRef Environment; + VersionTuple Introduced; + }; + const DerivativeShaderStage Stages[] = { + {"pixel", VersionTuple(6, 0)}, + {"compute", VersionTuple(6, 6)}, + {"mesh", VersionTuple(6, 6)}, + {"amplification", VersionTuple(6, 6)}, + }; + + const IdentifierInfo *Platform = &AST.Idents.get("shadermodel"); + for (const DerivativeShaderStage &Stage : Stages) + FD->addAttr(AvailabilityAttr::CreateImplicit( + AST, Platform, Stage.Introduced, /*Deprecated=*/VersionTuple(), + /*Obsoleted=*/VersionTuple(), /*Unavailable=*/false, /*Message=*/"", + /*Strict=*/false, /*Replacement=*/"", Sema::AP_Explicit, + &AST.Idents.get(Stage.Environment), /*InferredAttr=*/nullptr)); +} + } // namespace // Builder for template arguments of builtin types. Used internally @@ -1664,6 +1690,19 @@ BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() { return *this; } +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addDerivativeAvailability(StringRef MethodName) { + ASTContext &AST = Record->getASTContext(); + DeclarationName Name(&AST.Idents.get(MethodName, tok::TokenKind::identifier)); + for (NamedDecl *D : Record->lookup(Name)) { + if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) + D = FTD->getTemplatedDecl(); + if (auto *MD = dyn_cast<CXXMethodDecl>(D)) + addDerivativeAvailabilityAttrs(AST, MD); + } + return *this; +} + BuiltinTypeDeclBuilder & BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) { assert(!Record->isCompleteDefinition() && "record is already complete"); @@ -1701,7 +1740,7 @@ BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) { .finalize(); // T Sample(SamplerState s, float2 location, int2 offset, float clamp) - return BuiltinTypeMethodBuilder(*this, "Sample", ReturnType) + BuiltinTypeMethodBuilder(*this, "Sample", ReturnType) .addParam("Sampler", SamplerStateType) .addParam("Location", CoordTy) .addParam("Offset", OffsetTy) @@ -1711,6 +1750,9 @@ BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) { PH::LastStmt, PH::_1, PH::_2, PH::_3) .returnValue(PH::LastStmt) .finalize(); + + // Sample uses implicit derivatives to calculate the mip level. + return addDerivativeAvailability("Sample"); } BuiltinTypeDeclBuilder & @@ -1754,7 +1796,7 @@ BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim, // T SampleBias(SamplerState s, float2 location, float bias, int2 offset, // float clamp) - return BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType) + BuiltinTypeMethodBuilder(*this, "SampleBias", ReturnType) .addParam("Sampler", SamplerStateType) .addParam("Location", CoordTy) .addParam("Bias", FloatTy) @@ -1765,6 +1807,9 @@ BuiltinTypeDeclBuilder::addSampleBiasMethods(ResourceDimension Dim, PH::Handle, PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4) .returnValue(PH::LastStmt) .finalize(); + + // SampleBias uses implicit derivatives to calculate the mip level. + return addDerivativeAvailability("SampleBias"); } BuiltinTypeDeclBuilder & @@ -1909,7 +1954,7 @@ BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim, // T SampleCmp(SamplerComparisonState s, float2 location, float compare_value, // int2 offset, float clamp) - return BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType) + BuiltinTypeMethodBuilder(*this, "SampleCmp", ReturnType) .addParam("Sampler", SamplerComparisonStateType) .addParam("Location", CoordTy) .addParam("CompareValue", FloatTy) @@ -1920,6 +1965,9 @@ BuiltinTypeDeclBuilder::addSampleCmpMethods(ResourceDimension Dim, PH::LastStmt, PH::_1, PH::_2, PH::_3, PH::_4) .returnValue(PH::LastStmt) .finalize(); + + // SampleCmp uses implicit derivatives to calculate the mip level. + return addDerivativeAvailability("SampleCmp"); } BuiltinTypeDeclBuilder & @@ -2033,14 +2081,17 @@ BuiltinTypeDeclBuilder::addCalculateLodMethods(ResourceDimension Dim) { .finalize(); // float CalculateLevelOfDetailUnclamped(SamplerState s, float2 location) - return BuiltinTypeMethodBuilder(*this, "CalculateLevelOfDetailUnclamped", - ReturnType) + BuiltinTypeMethodBuilder(*this, "CalculateLevelOfDetailUnclamped", ReturnType) .addParam("Sampler", SamplerStateType) .addParam("Location", LocationTy) .accessHandleFieldOnResource(PH::_0) .callBuiltin("__builtin_hlsl_resource_calculate_lod_unclamped", ReturnType, PH::Handle, PH::LastStmt, PH::_1) .finalize(); + + // Both methods use implicit derivatives to calculate the level of detail. + addDerivativeAvailability("CalculateLevelOfDetail"); + return addDerivativeAvailability("CalculateLevelOfDetailUnclamped"); } QualType BuiltinTypeDeclBuilder::getGatherReturnType() { diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index e809ef264198c..afb336045b1f7 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -168,6 +168,7 @@ class BuiltinTypeDeclBuilder { QualType ElementTy, AccessSpecifier Access = AccessSpecifier::AS_private); QualType getGatherReturnType(); + BuiltinTypeDeclBuilder &addDerivativeAvailability(StringRef MethodName); FieldDecl *getResourceHandleField() const; FieldDecl *getResourceCounterHandleField() const; QualType getFirstTemplateTypeParam(); diff --git a/clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl b/clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl new file mode 100644 index 0000000000000..9d6ce30309de9 --- /dev/null +++ b/clang/test/SemaHLSL/Resources/Textures-derivative-availability.hlsl @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header \ +// RUN: -fsyntax-only -verify=expected,sm66 -DTEXTURE=Texture2D -DCOORD_TYPE=float2 %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.5-library -finclude-default-header \ +// RUN: -fsyntax-only -verify=expected,sm65 -DTEXTURE=Texture2D -DCOORD_TYPE=float2 %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header \ +// RUN: -fsyntax-only -verify=expected,sm66 -DTEXTURE=Texture2DArray -DCOORD_TYPE=float3 %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.5-library -finclude-default-header \ +// RUN: -fsyntax-only -verify=expected,sm65 -DTEXTURE=Texture2DArray -DCOORD_TYPE=float3 %s + +// Texture methods that rely on implicit derivatives are available in pixel +// shaders since Shader Model 6.0 and in compute, mesh and amplification +// shaders since Shader Model 6.6. They are not available in any other shader +// stage. + +TEXTURE<float4> tex; +SamplerState samp; +SamplerComparisonState cmpSamp; + +// Derivatives are always available in pixel shaders; no diagnostics expected. +[shader("pixel")] +void PixelEntry() { + COORD_TYPE loc = (COORD_TYPE)0; + float2 lodLoc = float2(0, 0); + + tex.Sample(samp, loc); + tex.SampleBias(samp, loc, 0.5); + tex.SampleCmp(cmpSamp, loc, 0.5); + tex.CalculateLevelOfDetail(samp, lodLoc); + tex.CalculateLevelOfDetailUnclamped(samp, lodLoc); +} + +// Derivatives are available in compute shaders only since Shader Model 6.6. +[shader("compute")] +[numthreads(1, 1, 1)] +void ComputeEntry() { + COORD_TYPE loc = (COORD_TYPE)0; + float2 lodLoc = float2(0, 0); + + // sm65-error@+2 {{'Sample' is only available in compute environment on Shader Model 6.6 or newer}} + // sm65-note@* {{'Sample' has been marked as being introduced in Shader Model 6.6 in compute environment here, but the deployment target is Shader Model 6.5 compute environment}} + tex.Sample(samp, loc); + + // sm65-error@+2 {{'SampleBias' is only available in compute environment on Shader Model 6.6 or newer}} + // sm65-note@* {{'SampleBias' has been marked as being introduced in Shader Model 6.6 in compute environment here}} + tex.SampleBias(samp, loc, 0.5); + + // sm65-error@+2 {{'SampleCmp' is only available in compute environment on Shader Model 6.6 or newer}} + // sm65-note@* {{'SampleCmp' has been marked as being introduced in Shader Model 6.6 in compute environment here}} + tex.SampleCmp(cmpSamp, loc, 0.5); + + // sm65-error@+2 {{'CalculateLevelOfDetail' is only available in compute environment on Shader Model 6.6 or newer}} + // sm65-note@* {{'CalculateLevelOfDetail' has been marked as being introduced in Shader Model 6.6 in compute environment here}} + tex.CalculateLevelOfDetail(samp, lodLoc); + + // sm65-error@+2 {{'CalculateLevelOfDetailUnclamped' is only available in compute environment on Shader Model 6.6 or newer}} + // sm65-note@* {{'CalculateLevelOfDetailUnclamped' has been marked as being introduced in Shader Model 6.6 in compute environment here}} + tex.CalculateLevelOfDetailUnclamped(samp, lodLoc); +} + +// Vertex shaders do not support derivatives in any shader model. +[shader("vertex")] +void VertexEntry() { + COORD_TYPE loc = (COORD_TYPE)0; + float2 lodLoc = float2(0, 0); + + // expected-error@+2 {{'Sample' is unavailable}} + // expected-note@* {{'Sample' has been marked as being introduced in Shader Model}} + tex.Sample(samp, loc); + + // expected-error@+2 {{'SampleBias' is unavailable}} + // expected-note@* {{'SampleBias' has been marked as being introduced in Shader Model}} + tex.SampleBias(samp, loc, 0.5); + + // expected-error@+2 {{'SampleCmp' is unavailable}} + // expected-note@* {{'SampleCmp' has been marked as being introduced in Shader Model}} + tex.SampleCmp(cmpSamp, loc, 0.5); + + // expected-error@+2 {{'CalculateLevelOfDetail' is unavailable}} + // expected-note@* {{'CalculateLevelOfDetail' has been marked as being introduced in Shader Model}} + tex.CalculateLevelOfDetail(samp, lodLoc); + + // expected-error@+2 {{'CalculateLevelOfDetailUnclamped' is unavailable}} + // expected-note@* {{'CalculateLevelOfDetailUnclamped' has been marked as being introduced in Shader Model}} + tex.CalculateLevelOfDetailUnclamped(samp, lodLoc); +} + +// Methods that take an explicit LOD or explicit gradients do not require +// derivatives and are available in all shader stages; no diagnostics expected. +[shader("vertex")] +void ExplicitLodVertexEntry() { + COORD_TYPE loc = (COORD_TYPE)0; + float2 grad = float2(0, 0); + + tex.SampleLevel(samp, loc, 0); + tex.SampleGrad(samp, loc, grad, grad); + tex.SampleCmpLevelZero(cmpSamp, loc, 0.5); + tex.Gather(samp, loc); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
