Skip to content

Commit 30efdce

Browse files
authored
[HLSL] Strict Availability Diagnostics (#93860)
Implements HLSL availability diagnostics' strict mode. HLSL availability diagnostics emits errors or warning when unavailable shader APIs are used. Unavailable shader APIs are APIs that are exposed in HLSL code but are not available in the target shader stage or shader model version. In the strict mode the compiler emits an error when an unavailable API is found in any function regardless of whether it is reachable from the shader entry point or not. This mode is enabled by ``-fhlsl-strict-availability``. See HLSL Availability Diagnostics design doc [here](https://github.com/llvm/llvm-project/blob/main/clang/docs/HLSL/AvailabilityDiagnostics.rst) for more details. Fixes #90096
1 parent 6f13f0b commit 30efdce

File tree

8 files changed

+372
-67
lines changed

8 files changed

+372
-67
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,11 +1060,6 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
10601060
.Case("ShaderModel", "shadermodel")
10611061
.Default(Platform);
10621062
}
1063-
static llvm::StringRef getPrettyEnviromentName(llvm::Triple::EnvironmentType EnvironmentType) {
1064-
if (EnvironmentType >= llvm::Triple::Pixel && EnvironmentType <= llvm::Triple::Amplification)
1065-
return llvm::Triple::getEnvironmentTypeName(EnvironmentType);
1066-
return "";
1067-
}
10681063
static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {
10691064
return llvm::StringSwitch<llvm::Triple::EnvironmentType>(Environment)
10701065
.Case("pixel", llvm::Triple::Pixel)

clang/include/clang/Basic/LangOptions.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ LANGOPT(RenderScript , 1, 0, "RenderScript")
276276

277277
LANGOPT(HLSL, 1, 0, "HLSL")
278278
ENUM_LANGOPT(HLSLVersion, HLSLLangStd, 16, HLSL_Unset, "HLSL Version")
279+
LANGOPT(HLSLStrictAvailability, 1, 0,
280+
"Strict availability diagnostic mode for HLSL built-in functions.")
279281

280282
LANGOPT(CUDAIsDevice , 1, 0, "compiling for CUDA device")
281283
LANGOPT(CUDAAllowVariadicFunctions, 1, 0, "allowing variadic functions in CUDA device code")

clang/include/clang/Driver/Options.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ def m_Group : OptionGroup<"<m group>">, Group<CompileOnly_Group>,
196196
DocName<"Target-dependent compilation options">,
197197
Visibility<[ClangOption, CLOption]>;
198198

199+
def hlsl_Group : OptionGroup<"<HLSL group>">, Group<f_Group>,
200+
DocName<"HLSL options">,
201+
Visibility<[ClangOption]>;
202+
199203
// Feature groups - these take command line options that correspond directly to
200204
// target specific features and can be translated directly from command line
201205
// options.
@@ -7896,6 +7900,11 @@ def finclude_default_header : Flag<["-"], "finclude-default-header">,
78967900
def fdeclare_opencl_builtins : Flag<["-"], "fdeclare-opencl-builtins">,
78977901
HelpText<"Add OpenCL builtin function declarations (experimental)">;
78987902

7903+
def fhlsl_strict_availability : Flag<["-"], "fhlsl-strict-availability">,
7904+
HelpText<"Enables strict availability diagnostic mode for HLSL built-in functions.">,
7905+
Group<hlsl_Group>,
7906+
MarshallingInfoFlag<LangOpts<"HLSLStrictAvailability">>;
7907+
78997908
def fpreserve_vec3_type : Flag<["-"], "fpreserve-vec3-type">,
79007909
HelpText<"Preserve 3-component vector type">,
79017910
MarshallingInfoFlag<CodeGenOpts<"PreserveVec3Type">>,

clang/lib/AST/DeclBase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
691691
IdentifierInfo *IIEnv = A->getEnvironment();
692692
StringRef TargetEnv =
693693
Context.getTargetInfo().getTriple().getEnvironmentName();
694-
StringRef EnvName = AvailabilityAttr::getPrettyEnviromentName(
694+
StringRef EnvName = llvm::Triple::getEnvironmentTypeName(
695695
Context.getTargetInfo().getTriple().getEnvironment());
696696
// Matching environment or no environment on attribute
697697
if (!IIEnv || (!TargetEnv.empty() && IIEnv->getName() == TargetEnv)) {

clang/lib/Sema/SemaAvailability.cpp

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,20 @@ static bool ShouldDiagnoseAvailabilityInContext(
162162
}
163163
}
164164

165+
// In HLSL, skip emitting diagnostic if the diagnostic mode is not set to
166+
// strict (-fhlsl-strict-availability), or if the target is library and the
167+
// availability is restricted to a specific environment/shader stage.
168+
// For libraries the availability will be checked later in
169+
// DiagnoseHLSLAvailability class once where the specific environment/shader
170+
// stage of the caller is known.
171+
if (S.getLangOpts().HLSL) {
172+
if (!S.getLangOpts().HLSLStrictAvailability ||
173+
(DeclEnv != nullptr &&
174+
S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
175+
llvm::Triple::EnvironmentType::Library))
176+
return false;
177+
}
178+
165179
// Checks if we should emit the availability diagnostic in the context of C.
166180
auto CheckContext = [&](const Decl *C) {
167181
if (K == AR_NotYetIntroduced) {
@@ -215,13 +229,16 @@ static bool ShouldDiagnoseAvailabilityInContext(
215229
return true;
216230
}
217231

218-
static bool
219-
shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
220-
const VersionTuple &DeploymentVersion,
221-
const VersionTuple &DeclVersion) {
232+
static unsigned getAvailabilityDiagnosticKind(
233+
const ASTContext &Context, const VersionTuple &DeploymentVersion,
234+
const VersionTuple &DeclVersion, bool HasMatchingEnv) {
222235
const auto &Triple = Context.getTargetInfo().getTriple();
223236
VersionTuple ForceAvailabilityFromVersion;
224237
switch (Triple.getOS()) {
238+
// For iOS, emit the diagnostic even if -Wunguarded-availability is
239+
// not specified for deployment targets >= to iOS 11 or equivalent or
240+
// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
241+
// later.
225242
case llvm::Triple::IOS:
226243
case llvm::Triple::TvOS:
227244
ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
@@ -233,16 +250,26 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
233250
case llvm::Triple::MacOSX:
234251
ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
235252
break;
253+
// For HLSL, use diagnostic from HLSLAvailability group which
254+
// are reported as errors by default and in strict diagnostic mode
255+
// (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
256+
// mode (-Wno-error=hlsl-availability)
236257
case llvm::Triple::ShaderModel:
237-
// FIXME: This will be updated when HLSL strict diagnostic mode
238-
// is implemented (issue #90096)
239-
return false;
258+
return HasMatchingEnv ? diag::warn_hlsl_availability
259+
: diag::warn_hlsl_availability_unavailable;
240260
default:
241-
// New targets should always warn about availability.
242-
return Triple.getVendor() == llvm::Triple::Apple;
261+
// New Apple targets should always warn about availability.
262+
ForceAvailabilityFromVersion =
263+
(Triple.getVendor() == llvm::Triple::Apple)
264+
? VersionTuple(/*Major=*/0, 0)
265+
: VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
243266
}
244-
return DeploymentVersion >= ForceAvailabilityFromVersion ||
245-
DeclVersion >= ForceAvailabilityFromVersion;
267+
if (DeploymentVersion >= ForceAvailabilityFromVersion ||
268+
DeclVersion >= ForceAvailabilityFromVersion)
269+
return HasMatchingEnv ? diag::warn_unguarded_availability_new
270+
: diag::warn_unguarded_availability_unavailable_new;
271+
return HasMatchingEnv ? diag::warn_unguarded_availability
272+
: diag::warn_unguarded_availability_unavailable;
246273
}
247274

248275
static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
@@ -415,26 +442,16 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
415442
const TargetInfo &TI = S.getASTContext().getTargetInfo();
416443
std::string PlatformName(
417444
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
418-
llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
419-
TI.getTriple().getEnvironment()));
445+
llvm::StringRef TargetEnvironment(
446+
llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment()));
420447
llvm::StringRef AttrEnvironment =
421-
AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
422-
AvailabilityAttr::getEnvironmentType(
423-
AA->getEnvironment()->getName()))
424-
: "";
448+
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
425449
bool UseEnvironment =
426450
(!AttrEnvironment.empty() && !TargetEnvironment.empty());
427451

428-
bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
452+
unsigned DiagKind = getAvailabilityDiagnosticKind(
429453
S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
430-
Introduced);
431-
432-
unsigned DiagKind =
433-
EnvironmentMatchesOrNone
434-
? (UseNewWarning ? diag::warn_unguarded_availability_new
435-
: diag::warn_unguarded_availability)
436-
: (UseNewWarning ? diag::warn_unguarded_availability_unavailable_new
437-
: diag::warn_unguarded_availability_unavailable);
454+
Introduced, EnvironmentMatchesOrNone);
438455

439456
S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
440457
<< Introduced.getAsString() << UseEnvironment
@@ -839,34 +856,19 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
839856
OffendingDecl))
840857
return;
841858

842-
// We would like to emit the diagnostic even if -Wunguarded-availability is
843-
// not specified for deployment targets >= to iOS 11 or equivalent or
844-
// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
845-
// later.
846-
bool UseNewDiagKind = shouldDiagnoseAvailabilityByDefault(
847-
SemaRef.Context,
848-
SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced);
849-
850859
const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
851860
std::string PlatformName(
852861
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
853-
llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
854-
TI.getTriple().getEnvironment()));
862+
llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());
855863
llvm::StringRef AttrEnvironment =
856-
AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
857-
AvailabilityAttr::getEnvironmentType(
858-
AA->getEnvironment()->getName()))
859-
: "";
864+
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
860865
bool UseEnvironment =
861866
(!AttrEnvironment.empty() && !TargetEnvironment.empty());
862867

863-
unsigned DiagKind =
864-
EnvironmentMatchesOrNone
865-
? (UseNewDiagKind ? diag::warn_unguarded_availability_new
866-
: diag::warn_unguarded_availability)
867-
: (UseNewDiagKind
868-
? diag::warn_unguarded_availability_unavailable_new
869-
: diag::warn_unguarded_availability_unavailable);
868+
unsigned DiagKind = getAvailabilityDiagnosticKind(
869+
SemaRef.Context,
870+
SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,
871+
EnvironmentMatchesOrNone);
870872

871873
SemaRef.Diag(Range.getBegin(), DiagKind)
872874
<< Range << D << PlatformName << Introduced.getAsString()

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ namespace {
535535
/// library).
536536
///
537537
/// This is done by traversing the AST of all shader entry point functions
538-
/// and of all exported functions, and any functions that are refrenced
538+
/// and of all exported functions, and any functions that are referenced
539539
/// from this AST. In other words, any functions that are reachable from
540540
/// the entry points.
541541
class DiagnoseHLSLAvailability
@@ -775,9 +775,34 @@ DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
775775
void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
776776
const AvailabilityAttr *AA,
777777
SourceRange Range) {
778-
if (ReportOnlyShaderStageIssues && !AA->getEnvironment())
779-
return;
780778

779+
IdentifierInfo *IIEnv = AA->getEnvironment();
780+
781+
if (!IIEnv) {
782+
// The availability attribute does not have environment -> it depends only
783+
// on shader model version and not on specific the shader stage.
784+
785+
// Skip emitting the diagnostics if the diagnostic mode is set to
786+
// strict (-fhlsl-strict-availability) because all relevant diagnostics
787+
// were already emitted in the DiagnoseUnguardedAvailability scan
788+
// (SemaAvailability.cpp).
789+
if (SemaRef.getLangOpts().HLSLStrictAvailability)
790+
return;
791+
792+
// Do not report shader-stage-independent issues if scanning a function
793+
// that was already scanned in a different shader stage context (they would
794+
// be duplicate)
795+
if (ReportOnlyShaderStageIssues)
796+
return;
797+
798+
} else {
799+
// The availability attribute has environment -> we need to know
800+
// the current stage context to property diagnose it.
801+
if (InUnknownShaderStageContext())
802+
return;
803+
}
804+
805+
// Check introduced version and if environment matches
781806
bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA);
782807
VersionTuple Introduced = AA->getIntroduced();
783808
VersionTuple TargetVersion =
@@ -786,24 +811,16 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
786811
if (TargetVersion >= Introduced && EnvironmentMatches)
787812
return;
788813

789-
// Do not diagnose shade-stage-specific availability when the shader stage
790-
// context is unknown
791-
if (InUnknownShaderStageContext() && AA->getEnvironment() != nullptr)
792-
return;
793-
794814
// Emit diagnostic message
795815
const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
796816
llvm::StringRef PlatformName(
797817
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
798818

799819
llvm::StringRef CurrentEnvStr =
800-
AvailabilityAttr::getPrettyEnviromentName(GetCurrentShaderEnvironment());
820+
llvm::Triple::getEnvironmentTypeName(GetCurrentShaderEnvironment());
801821

802-
llvm::StringRef AttrEnvStr = AA->getEnvironment()
803-
? AvailabilityAttr::getPrettyEnviromentName(
804-
AvailabilityAttr::getEnvironmentType(
805-
AA->getEnvironment()->getName()))
806-
: "";
822+
llvm::StringRef AttrEnvStr =
823+
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
807824
bool UseEnvironment = !AttrEnvStr.empty();
808825

809826
if (EnvironmentMatches) {
@@ -824,5 +841,14 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
824841
} // namespace
825842

826843
void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
844+
// Skip running the diagnostics scan if the diagnostic mode is
845+
// strict (-fhlsl-strict-availability) and the target shader stage is known
846+
// because all relevant diagnostics were already emitted in the
847+
// DiagnoseUnguardedAvailability scan (SemaAvailability.cpp).
848+
const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
849+
if (SemaRef.getLangOpts().HLSLStrictAvailability &&
850+
TI.getTriple().getEnvironment() != llvm::Triple::EnvironmentType::Library)
851+
return;
852+
827853
DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU);
828854
}

0 commit comments

Comments
 (0)