Skip to content

[HLSL] Strict Availability Diagnostics #93860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1060,11 +1060,6 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
.Case("ShaderModel", "shadermodel")
.Default(Platform);
}
static llvm::StringRef getPrettyEnviromentName(llvm::Triple::EnvironmentType EnvironmentType) {
if (EnvironmentType >= llvm::Triple::Pixel && EnvironmentType <= llvm::Triple::Amplification)
return llvm::Triple::getEnvironmentTypeName(EnvironmentType);
return "";
}
static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environment) {
return llvm::StringSwitch<llvm::Triple::EnvironmentType>(Environment)
.Case("pixel", llvm::Triple::Pixel)
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ LANGOPT(RenderScript , 1, 0, "RenderScript")

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

LANGOPT(CUDAIsDevice , 1, 0, "compiling for CUDA device")
LANGOPT(CUDAAllowVariadicFunctions, 1, 0, "allowing variadic functions in CUDA device code")
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ def m_Group : OptionGroup<"<m group>">, Group<CompileOnly_Group>,
DocName<"Target-dependent compilation options">,
Visibility<[ClangOption, CLOption]>;

def hlsl_Group : OptionGroup<"<HLSL group>">, Group<f_Group>,
DocName<"HLSL options">,
Visibility<[ClangOption]>;

// Feature groups - these take command line options that correspond directly to
// target specific features and can be translated directly from command line
// options.
Expand Down Expand Up @@ -7867,6 +7871,11 @@ def finclude_default_header : Flag<["-"], "finclude-default-header">,
def fdeclare_opencl_builtins : Flag<["-"], "fdeclare-opencl-builtins">,
HelpText<"Add OpenCL builtin function declarations (experimental)">;

def fhlsl_strict_availability : Flag<["-"], "fhlsl-strict-availability">,
HelpText<"Enables strict availability diagnostic mode for HLSL built-in functions.">,
Group<hlsl_Group>,
MarshallingInfoFlag<LangOpts<"HLSLStrictAvailability">>;

def fpreserve_vec3_type : Flag<["-"], "fpreserve-vec3-type">,
HelpText<"Preserve 3-component vector type">,
MarshallingInfoFlag<CodeGenOpts<"PreserveVec3Type">>,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
IdentifierInfo *IIEnv = A->getEnvironment();
StringRef TargetEnv =
Context.getTargetInfo().getTriple().getEnvironmentName();
StringRef EnvName = AvailabilityAttr::getPrettyEnviromentName(
StringRef EnvName = llvm::Triple::getEnvironmentTypeName(
Context.getTargetInfo().getTriple().getEnvironment());
// Matching environment or no environment on attribute
if (!IIEnv || (!TargetEnv.empty() && IIEnv->getName() == TargetEnv)) {
Expand Down
96 changes: 49 additions & 47 deletions clang/lib/Sema/SemaAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ static bool ShouldDiagnoseAvailabilityInContext(
}
}

// In HLSL, skip emitting diagnostic if the diagnostic mode is not set to
// strict (-fhlsl-strict-availability), or if the target is library and the
// availability is restricted to a specific environment/shader stage.
// For libraries the availability will be checked later in
// DiagnoseHLSLAvailability class once where the specific environment/shader
// stage of the caller is known.
if (S.getLangOpts().HLSL) {
if (!S.getLangOpts().HLSLStrictAvailability ||
(DeclEnv != nullptr &&
S.getASTContext().getTargetInfo().getTriple().getEnvironment() ==
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here (and a bit further down in the PR as well) there are places that assume that "not library" implies "known target stage". I wonder if we're opening ourselves up to subtle bugs issues in the future if we end up adding another library-like target?

Maybe this is unlikely enough that we can deal with that later?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is the current assumption everywhere - it is either library or a shader with a known target stage. Unless you see any potential library-like target on the horizon I think we can leave it like that.

Copy link
Contributor

@damyanp damyanp Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llvm-beanz mentioned something this morning (non-DXIL outputs ready to drive LTO) that made me think there may be a possibility of another environment that isn't a library shader, but also isn't tied to a specific shader stage. I may have misunderstood though.

If we've already got a load of code making this same assumption then maybe now isn't the right time to tackle it anyway. Happy for no change here.

llvm::Triple::EnvironmentType::Library))
return false;
}

// Checks if we should emit the availability diagnostic in the context of C.
auto CheckContext = [&](const Decl *C) {
if (K == AR_NotYetIntroduced) {
Expand Down Expand Up @@ -210,13 +224,16 @@ static bool ShouldDiagnoseAvailabilityInContext(
return true;
}

static bool
shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
const VersionTuple &DeploymentVersion,
const VersionTuple &DeclVersion) {
static unsigned getAvailabilityDiagnosticKind(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is unsigned really the best type we have for diagnostic kinds?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if it is the best, but that's what SemaBase::Diag method takes :)

const ASTContext &Context, const VersionTuple &DeploymentVersion,
const VersionTuple &DeclVersion, bool HasMatchingEnv) {
const auto &Triple = Context.getTargetInfo().getTriple();
VersionTuple ForceAvailabilityFromVersion;
switch (Triple.getOS()) {
// For iOS, emit the diagnostic even if -Wunguarded-availability is
// not specified for deployment targets >= to iOS 11 or equivalent or
// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
// later.
case llvm::Triple::IOS:
case llvm::Triple::TvOS:
ForceAvailabilityFromVersion = VersionTuple(/*Major=*/11);
Expand All @@ -228,16 +245,26 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context,
case llvm::Triple::MacOSX:
ForceAvailabilityFromVersion = VersionTuple(/*Major=*/10, /*Minor=*/13);
break;
// For HLSL, use diagnostic from HLSLAvailability group which
// are reported as errors by default and in strict diagnostic mode
// (-fhlsl-strict-availability) and as warnings in relaxed diagnostic
// mode (-Wno-error=hlsl-availability)
case llvm::Triple::ShaderModel:
// FIXME: This will be updated when HLSL strict diagnostic mode
// is implemented (issue #90096)
return false;
return HasMatchingEnv ? diag::warn_hlsl_availability
: diag::warn_hlsl_availability_unavailable;
default:
// New targets should always warn about availability.
return Triple.getVendor() == llvm::Triple::Apple;
// New Apple targets should always warn about availability.
ForceAvailabilityFromVersion =
(Triple.getVendor() == llvm::Triple::Apple)
? VersionTuple(/*Major=*/0, 0)
: VersionTuple(/*Major=*/(unsigned)-1, (unsigned)-1);
}
return DeploymentVersion >= ForceAvailabilityFromVersion ||
DeclVersion >= ForceAvailabilityFromVersion;
if (DeploymentVersion >= ForceAvailabilityFromVersion ||
DeclVersion >= ForceAvailabilityFromVersion)
return HasMatchingEnv ? diag::warn_unguarded_availability_new
: diag::warn_unguarded_availability_unavailable_new;
return HasMatchingEnv ? diag::warn_unguarded_availability
: diag::warn_unguarded_availability_unavailable;
}

static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) {
Expand Down Expand Up @@ -410,26 +437,16 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
const TargetInfo &TI = S.getASTContext().getTargetInfo();
std::string PlatformName(
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
TI.getTriple().getEnvironment()));
llvm::StringRef TargetEnvironment(
llvm::Triple::getEnvironmentTypeName(TI.getTriple().getEnvironment()));
llvm::StringRef AttrEnvironment =
AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
AvailabilityAttr::getEnvironmentType(
AA->getEnvironment()->getName()))
: "";
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
bool UseEnvironment =
(!AttrEnvironment.empty() && !TargetEnvironment.empty());

bool UseNewWarning = shouldDiagnoseAvailabilityByDefault(
unsigned DiagKind = getAvailabilityDiagnosticKind(
S.Context, S.Context.getTargetInfo().getPlatformMinVersion(),
Introduced);

unsigned DiagKind =
EnvironmentMatchesOrNone
? (UseNewWarning ? diag::warn_unguarded_availability_new
: diag::warn_unguarded_availability)
: (UseNewWarning ? diag::warn_unguarded_availability_unavailable_new
: diag::warn_unguarded_availability_unavailable);
Introduced, EnvironmentMatchesOrNone);

S.Diag(Loc, DiagKind) << OffendingDecl << PlatformName
<< Introduced.getAsString() << UseEnvironment
Expand Down Expand Up @@ -834,34 +851,19 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
OffendingDecl))
return;

// We would like to emit the diagnostic even if -Wunguarded-availability is
// not specified for deployment targets >= to iOS 11 or equivalent or
// for declarations that were introduced in iOS 11 (macOS 10.13, ...) or
// later.
bool UseNewDiagKind = shouldDiagnoseAvailabilityByDefault(
SemaRef.Context,
SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced);

const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
std::string PlatformName(
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
llvm::StringRef TargetEnvironment(AvailabilityAttr::getPrettyEnviromentName(
TI.getTriple().getEnvironment()));
llvm::StringRef TargetEnvironment(TI.getTriple().getEnvironmentName());
llvm::StringRef AttrEnvironment =
AA->getEnvironment() ? AvailabilityAttr::getPrettyEnviromentName(
AvailabilityAttr::getEnvironmentType(
AA->getEnvironment()->getName()))
: "";
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
bool UseEnvironment =
(!AttrEnvironment.empty() && !TargetEnvironment.empty());

unsigned DiagKind =
EnvironmentMatchesOrNone
? (UseNewDiagKind ? diag::warn_unguarded_availability_new
: diag::warn_unguarded_availability)
: (UseNewDiagKind
? diag::warn_unguarded_availability_unavailable_new
: diag::warn_unguarded_availability_unavailable);
unsigned DiagKind = getAvailabilityDiagnosticKind(
SemaRef.Context,
SemaRef.Context.getTargetInfo().getPlatformMinVersion(), Introduced,
EnvironmentMatchesOrNone);

SemaRef.Diag(Range.getBegin(), DiagKind)
<< Range << D << PlatformName << Introduced.getAsString()
Expand Down
54 changes: 40 additions & 14 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ namespace {
/// library).
///
/// This is done by traversing the AST of all shader entry point functions
/// and of all exported functions, and any functions that are refrenced
/// and of all exported functions, and any functions that are referenced
/// from this AST. In other words, any functions that are reachable from
/// the entry points.
class DiagnoseHLSLAvailability
Expand Down Expand Up @@ -536,9 +536,34 @@ DiagnoseHLSLAvailability::FindAvailabilityAttr(const Decl *D) {
void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
const AvailabilityAttr *AA,
SourceRange Range) {
if (ReportOnlyShaderStageIssues && !AA->getEnvironment())
return;

IdentifierInfo *IIEnv = AA->getEnvironment();

if (!IIEnv) {
// The availability attribute does not have environment -> it depends only
// on shader model version and not on specific the shader stage.

// Skip emitting the diagnostics if the diagnostic mode is set to
// strict (-fhlsl-strict-availability) because all relevant diagnostics
// were already emitted in the DiagnoseUnguardedAvailability scan
// (SemaAvailability.cpp).
if (SemaRef.getLangOpts().HLSLStrictAvailability)
return;

// Do not report shader-stage-independent issues if scanning a function
// that was already scanned in a different shader stage context (they would
// be duplicate)
if (ReportOnlyShaderStageIssues)
return;

} else {
// The availability attribute has environment -> we need to know
// the current stage context to property diagnose it.
if (InUnknownShaderStageContext())
return;
}

// Check introduced version and if environment matches
bool EnvironmentMatches = HasMatchingEnvironmentOrNone(AA);
VersionTuple Introduced = AA->getIntroduced();
VersionTuple TargetVersion =
Expand All @@ -547,24 +572,16 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
if (TargetVersion >= Introduced && EnvironmentMatches)
return;

// Do not diagnose shade-stage-specific availability when the shader stage
// context is unknown
if (InUnknownShaderStageContext() && AA->getEnvironment() != nullptr)
return;

// Emit diagnostic message
const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
llvm::StringRef PlatformName(
AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));

llvm::StringRef CurrentEnvStr =
AvailabilityAttr::getPrettyEnviromentName(GetCurrentShaderEnvironment());
llvm::Triple::getEnvironmentTypeName(GetCurrentShaderEnvironment());

llvm::StringRef AttrEnvStr = AA->getEnvironment()
? AvailabilityAttr::getPrettyEnviromentName(
AvailabilityAttr::getEnvironmentType(
AA->getEnvironment()->getName()))
: "";
llvm::StringRef AttrEnvStr =
AA->getEnvironment() ? AA->getEnvironment()->getName() : "";
bool UseEnvironment = !AttrEnvStr.empty();

if (EnvironmentMatches) {
Expand All @@ -585,5 +602,14 @@ void DiagnoseHLSLAvailability::CheckDeclAvailability(NamedDecl *D,
} // namespace

void SemaHLSL::DiagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
// Skip running the diagnostics scan if the diagnostic mode is
// strict (-fhlsl-strict-availability) and the target shader stage is known
// because all relevant diagnostics were already emitted in the
// DiagnoseUnguardedAvailability scan (SemaAvailability.cpp).
const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
if (SemaRef.getLangOpts().HLSLStrictAvailability &&
TI.getTriple().getEnvironment() != llvm::Triple::EnvironmentType::Library)
return;

DiagnoseHLSLAvailability(SemaRef).RunOnTranslationUnit(TU);
}
Loading
Loading