[clang] [HLSL] Default and Relaxed Availability Diagnostics (PR #92704)
Chris B via cfe-commits
cfe-commits at lists.llvm.org
Thu May 23 09:52:33 PDT 2024
================
@@ -290,3 +294,295 @@ void SemaHLSL::DiagnoseAttrStageMismatch(
<< A << HLSLShaderAttr::ConvertShaderTypeToStr(Stage)
<< (AllowedStages.size() != 1) << join(StageStrings, ", ");
}
+
+namespace {
+
+/// This class implements HLSL availability diagnostics for default
+/// and relaxed mode
+///
+/// The goal of this diagnostic is to emit an error or warning when an
+/// unavailable API is found in a code that is reachable from the shader
+/// entry function or from an exported function (when compiling shader
+/// 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
+/// from this AST. In other words, any function that are reachable from
+/// the entry points.
+class DiagnoseHLSLAvailability
+ : public RecursiveASTVisitor<DiagnoseHLSLAvailability> {
+ // HEKOTAS this is probably not needed
+ // typedef RecursiveASTVisitor<DiagnoseHLSLAvailability> Base;
+
+ Sema &SemaRef;
+
+ // Stack of functions to be scaned
+ llvm::SmallVector<const FunctionDecl *, 8> DeclsToScan;
+
+ // List of functions that were already scanned and in which environment.
+ //
+ // Maps FunctionDecl to a unsigned number that represents a set of shader
+ // environments the function has been scanned for.
+ // Since HLSLShaderAttr::ShaderType enum is generated from Attr.td and is
+ // defined without any assigned values, it is guaranteed to be numbered
+ // sequentially from 0 up and we can use it to 'index' individual bits
+ // in the set.
+ // The N'th bit in the set will be set if the function has been scanned
+ // in shader environment whose ShaderType integer value equals N.
+ // For example, if a function has been scanned in compute and pixel stage
+ // environment, the value will be 0x21 (100001 binary) because
+ // (int)HLSLShaderAttr::ShaderType::Pixel == 1 and
+ // (int)HLSLShaderAttr::ShaderType::Compute == 5.
+ llvm::DenseMap<const FunctionDecl *, unsigned> ScannedDecls;
+
+ // Do not access these directly, use the get/set methods below to make
+ // sure the values are in sync
+ llvm::Triple::EnvironmentType CurrentShaderEnvironment;
+ unsigned CurrentShaderStageBit;
+
+ // True if scanning a function that was already scanned in a different
+ // shader stage context, and therefore we should not report issues that
+ // depend only on shader model version because they would be duplicate.
+ bool ReportOnlyShaderStageIssues;
+
+ void SetShaderStageContext(HLSLShaderAttr::ShaderType ShaderType) {
+ assert((((unsigned)1) << (unsigned)ShaderType) != 0 &&
+ "ShaderType is too big for this bitmap");
+ CurrentShaderEnvironment = HLSLShaderAttr::getTypeAsEnvironment(ShaderType);
+ CurrentShaderStageBit = (1 << ShaderType);
+ }
+ void SetUnknownShaderStageContext() {
+ CurrentShaderEnvironment =
+ llvm::Triple::EnvironmentType::UnknownEnvironment;
+ CurrentShaderStageBit = (1 << 31);
+ }
+ llvm::Triple::EnvironmentType GetCurrentShaderEnvironment() {
+ return CurrentShaderEnvironment;
+ }
+ bool InUnknownShaderStageContext() {
+ return CurrentShaderEnvironment ==
+ llvm::Triple::EnvironmentType::UnknownEnvironment;
+ }
+
+ // Scanning methods
+ void HandleFunctionOrMethodRef(FunctionDecl *FD, Expr *RefExpr);
+ void CheckDeclAvailability(NamedDecl *D, const AvailabilityAttr *AA,
+ SourceRange Range);
+ const AvailabilityAttr *FindAvailabilityAttr(const Decl *D);
+ bool HasMatchingEnvironmentOrNone(const AvailabilityAttr *AA);
+ bool WasAlreadyScannedInCurrentShaderStage(const FunctionDecl *FD,
+ bool *WasNeverScanned = nullptr);
+ void AddToScannedFunctions(const FunctionDecl *FD);
+
+public:
+ DiagnoseHLSLAvailability(Sema &SemaRef) : SemaRef(SemaRef) {}
+
+ // AST traversal methods
+ void RunOnTranslationUnit(const TranslationUnitDecl *TU);
+ void RunOnFunction(const FunctionDecl *FD);
+
+ bool VisitDeclRefExpr(DeclRefExpr *DRE) {
+ FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(DRE->getDecl());
+ if (FD)
+ HandleFunctionOrMethodRef(FD, DRE);
+ return true;
+ }
+
+ bool VisitMemberExpr(MemberExpr *ME) {
+ FunctionDecl *FD = llvm::dyn_cast<FunctionDecl>(ME->getMemberDecl());
+ if (FD)
+ HandleFunctionOrMethodRef(FD, ME);
+ return true;
+ }
+};
+
+// Returns true if the function has already been scanned in the current
+// shader environment. WasNeverScanned will be set to true if the function
+// has never been scanned before for any shader environment.
+bool DiagnoseHLSLAvailability::WasAlreadyScannedInCurrentShaderStage(
+ const FunctionDecl *FD, bool *WasNeverScanned) {
----------------
llvm-beanz wrote:
Super nitpick: I have a bit of an aversion to this multi-return pattern because I think they get a bit confusing. It seems like this function gets used two ways:
1) Has this already been scanned for the current stage?
2) What stage(s) has this function been scanned for?
Maybe we can make two functions, one that returns the unsigned integer mask, and another that returns bool if the current stage bit is set.
https://github.com/llvm/llvm-project/pull/92704
More information about the cfe-commits
mailing list