aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/altera
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-03-13 13:58:24 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-03-13 14:11:53 +0300
commit11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch)
treefabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/altera
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/altera')
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/AlteraTidyModule.cpp48
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.cpp260
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.h79
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.cpp103
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.h31
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.cpp80
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.h36
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.cpp144
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.h37
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.cpp268
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.h74
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/altera/ya.make41
12 files changed, 1201 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/AlteraTidyModule.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/altera/AlteraTidyModule.cpp
new file mode 100644
index 0000000000..21610c7d1e
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/AlteraTidyModule.cpp
@@ -0,0 +1,48 @@
+//===--- AlteraTidyModule.cpp - clang-tidy --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "IdDependentBackwardBranchCheck.h"
+#include "KernelNameRestrictionCheck.h"
+#include "SingleWorkItemBarrierCheck.h"
+#include "StructPackAlignCheck.h"
+#include "UnrollLoopsCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy {
+namespace altera {
+
+class AlteraModule : public ClangTidyModule {
+public:
+ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<IdDependentBackwardBranchCheck>(
+ "altera-id-dependent-backward-branch");
+ CheckFactories.registerCheck<KernelNameRestrictionCheck>(
+ "altera-kernel-name-restriction");
+ CheckFactories.registerCheck<SingleWorkItemBarrierCheck>(
+ "altera-single-work-item-barrier");
+ CheckFactories.registerCheck<StructPackAlignCheck>(
+ "altera-struct-pack-align");
+ CheckFactories.registerCheck<UnrollLoopsCheck>("altera-unroll-loops");
+ }
+};
+
+} // namespace altera
+
+// Register the AlteraTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<altera::AlteraModule>
+ X("altera-module", "Adds Altera FPGA OpenCL lint checks.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the AlteraModule.
+volatile int AlteraModuleAnchorSource = 0;
+
+} // namespace clang::tidy
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.cpp
new file mode 100644
index 0000000000..66e6d97f50
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.cpp
@@ -0,0 +1,260 @@
+//===--- IdDependentBackwardBranchCheck.cpp - clang-tidy ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IdDependentBackwardBranchCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::altera {
+
+void IdDependentBackwardBranchCheck::registerMatchers(MatchFinder *Finder) {
+ // Prototype to identify all variables which hold a thread-variant ID.
+ // First Matcher just finds all the direct assignments of either ID call.
+ const auto ThreadID = expr(hasDescendant(callExpr(callee(functionDecl(
+ anyOf(hasName("get_global_id"), hasName("get_local_id")))))));
+
+ const auto RefVarOrField = forEachDescendant(
+ stmt(anyOf(declRefExpr(to(varDecl())).bind("assign_ref_var"),
+ memberExpr(member(fieldDecl())).bind("assign_ref_field"))));
+
+ Finder->addMatcher(
+ compoundStmt(
+ // Bind on actual get_local/global_id calls.
+ forEachDescendant(
+ stmt(
+ anyOf(declStmt(hasDescendant(varDecl(hasInitializer(ThreadID))
+ .bind("tid_dep_var"))),
+ binaryOperator(allOf(
+ isAssignmentOperator(), hasRHS(ThreadID),
+ hasLHS(anyOf(
+ declRefExpr(to(varDecl().bind("tid_dep_var"))),
+ memberExpr(member(
+ fieldDecl().bind("tid_dep_field")))))))))
+ .bind("straight_assignment"))),
+ this);
+
+ // Bind all VarDecls that include an initializer with a variable DeclRefExpr
+ // (in case it is ID-dependent).
+ Finder->addMatcher(
+ stmt(forEachDescendant(
+ varDecl(hasInitializer(RefVarOrField)).bind("pot_tid_var"))),
+ this);
+
+ // Bind all VarDecls that are assigned a value with a variable DeclRefExpr (in
+ // case it is ID-dependent).
+ Finder->addMatcher(
+ stmt(forEachDescendant(binaryOperator(
+ allOf(isAssignmentOperator(), hasRHS(RefVarOrField),
+ hasLHS(anyOf(
+ declRefExpr(to(varDecl().bind("pot_tid_var"))),
+ memberExpr(member(fieldDecl().bind("pot_tid_field"))))))))),
+ this);
+
+ // Second Matcher looks for branch statements inside of loops and bind on the
+ // condition expression IF it either calls an ID function or has a variable
+ // DeclRefExpr. DeclRefExprs are checked later to confirm whether the variable
+ // is ID-dependent.
+ const auto CondExpr =
+ expr(anyOf(hasDescendant(callExpr(callee(functionDecl(
+ anyOf(hasName("get_global_id"),
+ hasName("get_local_id")))))
+ .bind("id_call")),
+ hasDescendant(stmt(anyOf(declRefExpr(to(varDecl())),
+ memberExpr(member(fieldDecl())))))))
+ .bind("cond_expr");
+ Finder->addMatcher(stmt(anyOf(forStmt(hasCondition(CondExpr)),
+ doStmt(hasCondition(CondExpr)),
+ whileStmt(hasCondition(CondExpr))))
+ .bind("backward_branch"),
+ this);
+}
+
+IdDependentBackwardBranchCheck::IdDependencyRecord *
+IdDependentBackwardBranchCheck::hasIdDepVar(const Expr *Expression) {
+ if (const auto *Declaration = dyn_cast<DeclRefExpr>(Expression)) {
+ // It is a DeclRefExpr, so check if it's an ID-dependent variable.
+ const auto *CheckVariable = dyn_cast<VarDecl>(Declaration->getDecl());
+ auto FoundVariable = IdDepVarsMap.find(CheckVariable);
+ if (FoundVariable == IdDepVarsMap.end())
+ return nullptr;
+ return &(FoundVariable->second);
+ }
+ for (const auto *Child : Expression->children())
+ if (const auto *ChildExpression = dyn_cast<Expr>(Child))
+ if (IdDependencyRecord *Result = hasIdDepVar(ChildExpression))
+ return Result;
+ return nullptr;
+}
+
+IdDependentBackwardBranchCheck::IdDependencyRecord *
+IdDependentBackwardBranchCheck::hasIdDepField(const Expr *Expression) {
+ if (const auto *MemberExpression = dyn_cast<MemberExpr>(Expression)) {
+ const auto *CheckField =
+ dyn_cast<FieldDecl>(MemberExpression->getMemberDecl());
+ auto FoundField = IdDepFieldsMap.find(CheckField);
+ if (FoundField == IdDepFieldsMap.end())
+ return nullptr;
+ return &(FoundField->second);
+ }
+ for (const auto *Child : Expression->children())
+ if (const auto *ChildExpression = dyn_cast<Expr>(Child))
+ if (IdDependencyRecord *Result = hasIdDepField(ChildExpression))
+ return Result;
+ return nullptr;
+}
+
+void IdDependentBackwardBranchCheck::saveIdDepVar(const Stmt *Statement,
+ const VarDecl *Variable) {
+ // Record that this variable is thread-dependent.
+ IdDepVarsMap[Variable] =
+ IdDependencyRecord(Variable, Variable->getBeginLoc(),
+ Twine("assignment of ID-dependent variable ") +
+ Variable->getNameAsString());
+}
+
+void IdDependentBackwardBranchCheck::saveIdDepField(const Stmt *Statement,
+ const FieldDecl *Field) {
+ // Record that this field is thread-dependent.
+ IdDepFieldsMap[Field] = IdDependencyRecord(
+ Field, Statement->getBeginLoc(),
+ Twine("assignment of ID-dependent field ") + Field->getNameAsString());
+}
+
+void IdDependentBackwardBranchCheck::saveIdDepVarFromReference(
+ const DeclRefExpr *RefExpr, const MemberExpr *MemExpr,
+ const VarDecl *PotentialVar) {
+ // If the variable is already in IdDepVarsMap, ignore it.
+ if (IdDepVarsMap.find(PotentialVar) != IdDepVarsMap.end())
+ return;
+ std::string Message;
+ llvm::raw_string_ostream StringStream(Message);
+ StringStream << "inferred assignment of ID-dependent value from "
+ "ID-dependent ";
+ if (RefExpr) {
+ const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
+ // If variable isn't ID-dependent, but RefVar is.
+ if (IdDepVarsMap.find(RefVar) != IdDepVarsMap.end())
+ StringStream << "variable " << RefVar->getNameAsString();
+ }
+ if (MemExpr) {
+ const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
+ // If variable isn't ID-dependent, but RefField is.
+ if (IdDepFieldsMap.find(RefField) != IdDepFieldsMap.end())
+ StringStream << "member " << RefField->getNameAsString();
+ }
+ IdDepVarsMap[PotentialVar] =
+ IdDependencyRecord(PotentialVar, PotentialVar->getBeginLoc(), Message);
+}
+
+void IdDependentBackwardBranchCheck::saveIdDepFieldFromReference(
+ const DeclRefExpr *RefExpr, const MemberExpr *MemExpr,
+ const FieldDecl *PotentialField) {
+ // If the field is already in IdDepFieldsMap, ignore it.
+ if (IdDepFieldsMap.find(PotentialField) != IdDepFieldsMap.end())
+ return;
+ std::string Message;
+ llvm::raw_string_ostream StringStream(Message);
+ StringStream << "inferred assignment of ID-dependent member from "
+ "ID-dependent ";
+ if (RefExpr) {
+ const auto *RefVar = dyn_cast<VarDecl>(RefExpr->getDecl());
+ // If field isn't ID-dependent, but RefVar is.
+ if (IdDepVarsMap.find(RefVar) != IdDepVarsMap.end())
+ StringStream << "variable " << RefVar->getNameAsString();
+ }
+ if (MemExpr) {
+ const auto *RefField = dyn_cast<FieldDecl>(MemExpr->getMemberDecl());
+ if (IdDepFieldsMap.find(RefField) != IdDepFieldsMap.end())
+ StringStream << "member " << RefField->getNameAsString();
+ }
+ IdDepFieldsMap[PotentialField] = IdDependencyRecord(
+ PotentialField, PotentialField->getBeginLoc(), Message);
+}
+
+IdDependentBackwardBranchCheck::LoopType
+IdDependentBackwardBranchCheck::getLoopType(const Stmt *Loop) {
+ switch (Loop->getStmtClass()) {
+ case Stmt::DoStmtClass:
+ return DoLoop;
+ case Stmt::WhileStmtClass:
+ return WhileLoop;
+ case Stmt::ForStmtClass:
+ return ForLoop;
+ default:
+ return UnknownLoop;
+ }
+}
+
+void IdDependentBackwardBranchCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ // The first half of the callback only deals with identifying and storing
+ // ID-dependency information into the IdDepVars and IdDepFields maps.
+ const auto *Variable = Result.Nodes.getNodeAs<VarDecl>("tid_dep_var");
+ const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("tid_dep_field");
+ const auto *Statement = Result.Nodes.getNodeAs<Stmt>("straight_assignment");
+ const auto *RefExpr = Result.Nodes.getNodeAs<DeclRefExpr>("assign_ref_var");
+ const auto *MemExpr = Result.Nodes.getNodeAs<MemberExpr>("assign_ref_field");
+ const auto *PotentialVar = Result.Nodes.getNodeAs<VarDecl>("pot_tid_var");
+ const auto *PotentialField =
+ Result.Nodes.getNodeAs<FieldDecl>("pot_tid_field");
+
+ // Save variables and fields assigned directly through ID function calls.
+ if (Statement && (Variable || Field)) {
+ if (Variable)
+ saveIdDepVar(Statement, Variable);
+ else if (Field)
+ saveIdDepField(Statement, Field);
+ }
+
+ // Save variables assigned to values of Id-dependent variables and fields.
+ if ((RefExpr || MemExpr) && PotentialVar)
+ saveIdDepVarFromReference(RefExpr, MemExpr, PotentialVar);
+
+ // Save fields assigned to values of ID-dependent variables and fields.
+ if ((RefExpr || MemExpr) && PotentialField)
+ saveIdDepFieldFromReference(RefExpr, MemExpr, PotentialField);
+
+ // The second part of the callback deals with checking if a branch inside a
+ // loop is thread dependent.
+ const auto *CondExpr = Result.Nodes.getNodeAs<Expr>("cond_expr");
+ const auto *IDCall = Result.Nodes.getNodeAs<CallExpr>("id_call");
+ const auto *Loop = Result.Nodes.getNodeAs<Stmt>("backward_branch");
+ if (!Loop)
+ return;
+ LoopType Type = getLoopType(Loop);
+ if (CondExpr) {
+ if (IDCall) { // Conditional expression calls an ID function directly.
+ diag(CondExpr->getBeginLoc(),
+ "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
+ "to ID function call and may cause performance degradation")
+ << Type;
+ return;
+ }
+ // Conditional expression has DeclRefExpr(s), check ID-dependency.
+ IdDependencyRecord *IdDepVar = hasIdDepVar(CondExpr);
+ IdDependencyRecord *IdDepField = hasIdDepField(CondExpr);
+ if (IdDepVar) {
+ // Change one of these to a Note
+ diag(IdDepVar->Location, IdDepVar->Message, DiagnosticIDs::Note);
+ diag(CondExpr->getBeginLoc(),
+ "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
+ "to variable reference to %1 and may cause performance degradation")
+ << Type << IdDepVar->VariableDeclaration;
+ } else if (IdDepField) {
+ diag(IdDepField->Location, IdDepField->Message, DiagnosticIDs::Note);
+ diag(CondExpr->getBeginLoc(),
+ "backward branch (%select{do|while|for}0 loop) is ID-dependent due "
+ "to member reference to %1 and may cause performance degradation")
+ << Type << IdDepField->FieldDeclaration;
+ }
+ }
+}
+
+} // namespace clang::tidy::altera
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.h
new file mode 100644
index 0000000000..cf964a2d5d
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/IdDependentBackwardBranchCheck.h
@@ -0,0 +1,79 @@
+//===--- IdDependentBackwardBranchCheck.h - clang-tidy ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_IDDEPENDENTBACKWARDBRANCHCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_IDDEPENDENTBACKWARDBRANCHCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::altera {
+
+/// Finds ID-dependent variables and fields used within loops, and warns of
+/// their usage. Using these variables in loops can lead to performance
+/// degradation.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/altera/id-dependent-backward-branch.html
+class IdDependentBackwardBranchCheck : public ClangTidyCheck {
+private:
+ enum LoopType { UnknownLoop = -1, DoLoop = 0, WhileLoop = 1, ForLoop = 2 };
+ // Stores information necessary for printing out source of error.
+ struct IdDependencyRecord {
+ IdDependencyRecord(const VarDecl *Declaration, SourceLocation Location,
+ const llvm::Twine &Message)
+ : VariableDeclaration(Declaration), Location(Location),
+ Message(Message.str()) {}
+ IdDependencyRecord(const FieldDecl *Declaration, SourceLocation Location,
+ const llvm::Twine &Message)
+ : FieldDeclaration(Declaration), Location(Location),
+ Message(Message.str()) {}
+ IdDependencyRecord() = default;
+ const VarDecl *VariableDeclaration = nullptr;
+ const FieldDecl *FieldDeclaration = nullptr;
+ SourceLocation Location;
+ std::string Message;
+ };
+ // Stores the locations where ID-dependent variables are created.
+ std::map<const VarDecl *, IdDependencyRecord> IdDepVarsMap;
+ // Stores the locations where ID-dependent fields are created.
+ std::map<const FieldDecl *, IdDependencyRecord> IdDepFieldsMap;
+ /// Returns an IdDependencyRecord if the Expression contains an ID-dependent
+ /// variable, returns a nullptr otherwise.
+ IdDependencyRecord *hasIdDepVar(const Expr *Expression);
+ /// Returns an IdDependencyRecord if the Expression contains an ID-dependent
+ /// field, returns a nullptr otherwise.
+ IdDependencyRecord *hasIdDepField(const Expr *Expression);
+ /// Stores the location an ID-dependent variable is created from a call to
+ /// an ID function in IdDepVarsMap.
+ void saveIdDepVar(const Stmt *Statement, const VarDecl *Variable);
+ /// Stores the location an ID-dependent field is created from a call to an ID
+ /// function in IdDepFieldsMap.
+ void saveIdDepField(const Stmt *Statement, const FieldDecl *Field);
+ /// Stores the location an ID-dependent variable is created from a reference
+ /// to another ID-dependent variable or field in IdDepVarsMap.
+ void saveIdDepVarFromReference(const DeclRefExpr *RefExpr,
+ const MemberExpr *MemExpr,
+ const VarDecl *PotentialVar);
+ /// Stores the location an ID-dependent field is created from a reference to
+ /// another ID-dependent variable or field in IdDepFieldsMap.
+ void saveIdDepFieldFromReference(const DeclRefExpr *RefExpr,
+ const MemberExpr *MemExpr,
+ const FieldDecl *PotentialField);
+ /// Returns the loop type.
+ LoopType getLoopType(const Stmt *Loop);
+
+public:
+ IdDependentBackwardBranchCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::altera
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_IDDEPENDENTBACKWARDBRANCHCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.cpp
new file mode 100644
index 0000000000..f88b70b8d0
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.cpp
@@ -0,0 +1,103 @@
+//===--- KernelNameRestrictionCheck.cpp - clang-tidy ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "KernelNameRestrictionCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::altera {
+
+namespace {
+
+class KernelNameRestrictionPPCallbacks : public PPCallbacks {
+public:
+ explicit KernelNameRestrictionPPCallbacks(ClangTidyCheck &Check,
+ const SourceManager &SM)
+ : Check(Check), SM(SM) {}
+
+ void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+ StringRef FileName, bool IsAngled,
+ CharSourceRange FileNameRange,
+ OptionalFileEntryRef File, StringRef SearchPath,
+ StringRef RelativePath, const Module *Imported,
+ SrcMgr::CharacteristicKind FileType) override;
+
+ void EndOfMainFile() override;
+
+private:
+ /// Returns true if the name of the file with path FilePath is 'kernel.cl',
+ /// 'verilog.cl', or 'vhdl.cl'. The file name check is case insensitive.
+ bool fileNameIsRestricted(StringRef FilePath);
+
+ struct IncludeDirective {
+ SourceLocation Loc; // Location in the include directive.
+ StringRef FileName; // Filename as a string.
+ };
+
+ std::vector<IncludeDirective> IncludeDirectives;
+ ClangTidyCheck &Check;
+ const SourceManager &SM;
+};
+
+} // namespace
+
+void KernelNameRestrictionCheck::registerPPCallbacks(const SourceManager &SM,
+ Preprocessor *PP,
+ Preprocessor *) {
+ PP->addPPCallbacks(
+ std::make_unique<KernelNameRestrictionPPCallbacks>(*this, SM));
+}
+
+void KernelNameRestrictionPPCallbacks::InclusionDirective(
+ SourceLocation HashLoc, const Token &, StringRef FileName, bool,
+ CharSourceRange, OptionalFileEntryRef, StringRef, StringRef, const Module *,
+ SrcMgr::CharacteristicKind) {
+ IncludeDirective ID = {HashLoc, FileName};
+ IncludeDirectives.push_back(std::move(ID));
+}
+
+bool KernelNameRestrictionPPCallbacks::fileNameIsRestricted(
+ StringRef FileName) {
+ return FileName.equals_insensitive("kernel.cl") ||
+ FileName.equals_insensitive("verilog.cl") ||
+ FileName.equals_insensitive("vhdl.cl");
+}
+
+void KernelNameRestrictionPPCallbacks::EndOfMainFile() {
+
+ // Check main file for restricted names.
+ const FileEntry *Entry = SM.getFileEntryForID(SM.getMainFileID());
+ StringRef FileName = llvm::sys::path::filename(Entry->getName());
+ if (fileNameIsRestricted(FileName))
+ Check.diag(SM.getLocForStartOfFile(SM.getMainFileID()),
+ "compiling '%0' may cause additional compilation errors due "
+ "to the name of the kernel source file; consider renaming the "
+ "included kernel source file")
+ << FileName;
+
+ if (IncludeDirectives.empty())
+ return;
+
+ // Check included files for restricted names.
+ for (const IncludeDirective &ID : IncludeDirectives) {
+ StringRef FileName = llvm::sys::path::filename(ID.FileName);
+ if (fileNameIsRestricted(FileName))
+ Check.diag(ID.Loc,
+ "including '%0' may cause additional compilation errors due "
+ "to the name of the kernel source file; consider renaming the "
+ "included kernel source file")
+ << FileName;
+ }
+}
+
+} // namespace clang::tidy::altera
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.h
new file mode 100644
index 0000000000..02d82f4d68
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/KernelNameRestrictionCheck.h
@@ -0,0 +1,31 @@
+//===--- KernelNameRestrictionCheck.h - clang-tidy --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_KERNEL_NAME_RESTRICTION_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_KERNEL_NAME_RESTRICTION_CHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::altera {
+
+/// Finds kernel files and include directives whose filename is `kernel.cl`,
+/// `Verilog.cl`, or `VHDL.cl`.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/altera/kernel-name-restriction.html
+class KernelNameRestrictionCheck : public ClangTidyCheck {
+public:
+ KernelNameRestrictionCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+ Preprocessor *) override;
+};
+
+} // namespace clang::tidy::altera
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_KERNEL_NAME_RESTRICTION_CHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.cpp
new file mode 100644
index 0000000000..a4e7cf4835
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.cpp
@@ -0,0 +1,80 @@
+//===--- SingleWorkItemBarrierCheck.cpp - clang-tidy-----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SingleWorkItemBarrierCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::altera {
+
+void SingleWorkItemBarrierCheck::registerMatchers(MatchFinder *Finder) {
+ // Find any function that calls barrier but does not call an ID function.
+ // hasAttr(attr::Kind::OpenCLKernel) restricts it to only kernel functions.
+ // FIXME: Have it accept all functions but check for a parameter that gets an
+ // ID from one of the four ID functions.
+ Finder->addMatcher(
+ // Find function declarations...
+ functionDecl(
+ allOf(
+ // That are OpenCL kernels...
+ hasAttr(attr::Kind::OpenCLKernel),
+ // And call a barrier function (either 1.x or 2.x version)...
+ forEachDescendant(callExpr(callee(functionDecl(hasAnyName(
+ "barrier", "work_group_barrier"))))
+ .bind("barrier")),
+ // But do not call an ID function.
+ unless(hasDescendant(callExpr(callee(functionDecl(
+ hasAnyName("get_global_id", "get_local_id", "get_group_id",
+ "get_local_linear_id"))))))))
+ .bind("function"),
+ this);
+}
+
+void SingleWorkItemBarrierCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("function");
+ const auto *MatchedBarrier = Result.Nodes.getNodeAs<CallExpr>("barrier");
+ if (AOCVersion < 1701) {
+ // get_group_id and get_local_linear_id were added at/after v17.01
+ diag(MatchedDecl->getLocation(),
+ "kernel function %0 does not call 'get_global_id' or 'get_local_id' "
+ "and will be treated as a single work-item")
+ << MatchedDecl;
+ diag(MatchedBarrier->getBeginLoc(),
+ "barrier call is in a single work-item and may error out",
+ DiagnosticIDs::Note);
+ } else {
+ // If reqd_work_group_size is anything other than (1,1,1), it will be
+ // interpreted as an NDRange in AOC version >= 17.1.
+ bool IsNDRange = false;
+ if (MatchedDecl->hasAttr<ReqdWorkGroupSizeAttr>()) {
+ const auto *Attribute = MatchedDecl->getAttr<ReqdWorkGroupSizeAttr>();
+ if (Attribute->getXDim() > 1 || Attribute->getYDim() > 1 ||
+ Attribute->getZDim() > 1)
+ IsNDRange = true;
+ }
+ if (IsNDRange) // No warning if kernel is treated as an NDRange.
+ return;
+ diag(MatchedDecl->getLocation(),
+ "kernel function %0 does not call an ID function and may be a viable "
+ "single work-item, but will be forced to execute as an NDRange")
+ << MatchedDecl;
+ diag(MatchedBarrier->getBeginLoc(),
+ "barrier call will force NDRange execution; if single work-item "
+ "semantics are desired a mem_fence may be more efficient",
+ DiagnosticIDs::Note);
+ }
+}
+
+void SingleWorkItemBarrierCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AOCVersion", AOCVersion);
+}
+
+} // namespace clang::tidy::altera
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.h
new file mode 100644
index 0000000000..62a055b094
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/SingleWorkItemBarrierCheck.h
@@ -0,0 +1,36 @@
+//===--- SingleWorkItemBarrierCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_SINGLE_WORK_ITEM_BARRIER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_SINGLE_WORK_ITEM_BARRIER_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::altera {
+
+/// Detects OpenCL kernel functions that call a barrier but do not call an
+/// ID-function function. These functions will be treated as single work-item
+/// kernels, which may be inefficient or cause an error.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/altera/single-work-item-barrier.html
+class SingleWorkItemBarrierCheck : public ClangTidyCheck {
+ const unsigned AOCVersion;
+
+public:
+ SingleWorkItemBarrierCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AOCVersion(Options.get("AOCVersion", 1600U)) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+};
+
+} // namespace clang::tidy::altera
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_SINGLE_WORK_ITEM_BARRIER_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.cpp
new file mode 100644
index 0000000000..70a6ab3cc8
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.cpp
@@ -0,0 +1,144 @@
+//===--- StructPackAlignCheck.cpp - clang-tidy ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "StructPackAlignCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <math.h>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::altera {
+
+void StructPackAlignCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(recordDecl(isStruct(), isDefinition(),
+ unless(isExpansionInSystemHeader()))
+ .bind("struct"),
+ this);
+}
+
+CharUnits
+StructPackAlignCheck::computeRecommendedAlignment(CharUnits MinByteSize) {
+ CharUnits NewAlign = CharUnits::fromQuantity(1);
+ if (!MinByteSize.isPowerOfTwo()) {
+ int MSB = (int)MinByteSize.getQuantity();
+ for (; MSB > 0; MSB /= 2) {
+ NewAlign = NewAlign.alignTo(
+ CharUnits::fromQuantity(((int)NewAlign.getQuantity()) * 2));
+ // Abort if the computed alignment meets the maximum configured alignment.
+ if (NewAlign.getQuantity() >= MaxConfiguredAlignment)
+ break;
+ }
+ } else {
+ NewAlign = MinByteSize;
+ }
+ return NewAlign;
+}
+
+void StructPackAlignCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Struct = Result.Nodes.getNodeAs<RecordDecl>("struct");
+
+ // Do not trigger on templated struct declarations because the packing and
+ // alignment requirements are unknown.
+ if (Struct->isTemplated())
+ return;
+
+ // Packing and alignment requirements for invalid decls are meaningless.
+ if (Struct->isInvalidDecl())
+ return;
+
+ // Get sizing info for the struct.
+ llvm::SmallVector<std::pair<unsigned int, unsigned int>, 10> FieldSizes;
+ unsigned int TotalBitSize = 0;
+ for (const FieldDecl *StructField : Struct->fields()) {
+ // For each StructField, record how big it is (in bits).
+ // Would be good to use a pair of <offset, size> to advise a better
+ // packing order.
+ QualType StructFieldTy = StructField->getType();
+ if (StructFieldTy->isIncompleteType())
+ return;
+ unsigned int StructFieldWidth =
+ (unsigned int)Result.Context->getTypeInfo(StructFieldTy.getTypePtr())
+ .Width;
+ FieldSizes.emplace_back(StructFieldWidth, StructField->getFieldIndex());
+ // FIXME: Recommend a reorganization of the struct (sort by StructField
+ // size, largest to smallest).
+ TotalBitSize += StructFieldWidth;
+ }
+
+ uint64_t CharSize = Result.Context->getCharWidth();
+ CharUnits CurrSize = Result.Context->getASTRecordLayout(Struct).getSize();
+ CharUnits MinByteSize =
+ CharUnits::fromQuantity(std::max<clang::CharUnits::QuantityType>(
+ ceil(static_cast<float>(TotalBitSize) / CharSize), 1));
+ CharUnits MaxAlign = CharUnits::fromQuantity(
+ ceil((float)Struct->getMaxAlignment() / CharSize));
+ CharUnits CurrAlign =
+ Result.Context->getASTRecordLayout(Struct).getAlignment();
+ CharUnits NewAlign = computeRecommendedAlignment(MinByteSize);
+
+ bool IsPacked = Struct->hasAttr<PackedAttr>();
+ bool NeedsPacking = (MinByteSize < CurrSize) && (MaxAlign != NewAlign) &&
+ (CurrSize != NewAlign);
+ bool NeedsAlignment = CurrAlign.getQuantity() != NewAlign.getQuantity();
+
+ if (!NeedsAlignment && !NeedsPacking)
+ return;
+
+ // If it's using much more space than it needs, suggest packing.
+ // (Do not suggest packing if it is currently explicitly aligned to what the
+ // minimum byte size would suggest as the new alignment.)
+ if (NeedsPacking && !IsPacked) {
+ diag(Struct->getLocation(),
+ "accessing fields in struct %0 is inefficient due to padding; only "
+ "needs %1 bytes but is using %2 bytes")
+ << Struct << (int)MinByteSize.getQuantity()
+ << (int)CurrSize.getQuantity()
+ << FixItHint::CreateInsertion(Struct->getEndLoc().getLocWithOffset(1),
+ " __attribute__((packed))");
+ diag(Struct->getLocation(),
+ "use \"__attribute__((packed))\" to reduce the amount of padding "
+ "applied to struct %0",
+ DiagnosticIDs::Note)
+ << Struct;
+ }
+
+ FixItHint FixIt;
+ AlignedAttr *Attribute = Struct->getAttr<AlignedAttr>();
+ std::string NewAlignQuantity = std::to_string((int)NewAlign.getQuantity());
+ if (Attribute) {
+ FixIt = FixItHint::CreateReplacement(
+ Attribute->getRange(),
+ (Twine("aligned(") + NewAlignQuantity + ")").str());
+ } else {
+ FixIt = FixItHint::CreateInsertion(
+ Struct->getEndLoc().getLocWithOffset(1),
+ (Twine(" __attribute__((aligned(") + NewAlignQuantity + ")))").str());
+ }
+
+ // And suggest the minimum power-of-two alignment for the struct as a whole
+ // (with and without packing).
+ if (NeedsAlignment) {
+ diag(Struct->getLocation(),
+ "accessing fields in struct %0 is inefficient due to poor alignment; "
+ "currently aligned to %1 bytes, but recommended alignment is %2 bytes")
+ << Struct << (int)CurrAlign.getQuantity() << NewAlignQuantity << FixIt;
+
+ diag(Struct->getLocation(),
+ "use \"__attribute__((aligned(%0)))\" to align struct %1 to %0 bytes",
+ DiagnosticIDs::Note)
+ << NewAlignQuantity << Struct;
+ }
+}
+
+void StructPackAlignCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "MaxConfiguredAlignment", MaxConfiguredAlignment);
+}
+
+} // namespace clang::tidy::altera
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.h
new file mode 100644
index 0000000000..f46d8e3b87
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/StructPackAlignCheck.h
@@ -0,0 +1,37 @@
+//===--- StructPackAlignCheck.h - clang-tidy --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_STRUCTPACKALIGNCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_STRUCTPACKALIGNCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::altera {
+
+/// Finds structs that are inefficiently packed or aligned, and recommends
+/// packing and/or aligning of said structs as needed.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/altera/struct-pack-align.html
+class StructPackAlignCheck : public ClangTidyCheck {
+public:
+ StructPackAlignCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ MaxConfiguredAlignment(Options.get("MaxConfiguredAlignment", 128)) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ const unsigned MaxConfiguredAlignment;
+ CharUnits computeRecommendedAlignment(CharUnits MinByteSize);
+};
+
+} // namespace clang::tidy::altera
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_STRUCTPACKALIGNCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.cpp
new file mode 100644
index 0000000000..667c1b9239
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.cpp
@@ -0,0 +1,268 @@
+//===--- UnrollLoopsCheck.cpp - clang-tidy --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnrollLoopsCheck.h"
+#include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <math.h>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::altera {
+
+UnrollLoopsCheck::UnrollLoopsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ MaxLoopIterations(Options.get("MaxLoopIterations", 100U)) {}
+
+void UnrollLoopsCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasLoopBound = hasDescendant(
+ varDecl(allOf(matchesName("__end*"),
+ hasDescendant(integerLiteral().bind("cxx_loop_bound")))));
+ const auto CXXForRangeLoop =
+ cxxForRangeStmt(anyOf(HasLoopBound, unless(HasLoopBound)));
+ const auto AnyLoop = anyOf(forStmt(), whileStmt(), doStmt(), CXXForRangeLoop);
+ Finder->addMatcher(
+ stmt(allOf(AnyLoop, unless(hasDescendant(stmt(AnyLoop))))).bind("loop"),
+ this);
+}
+
+void UnrollLoopsCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Loop = Result.Nodes.getNodeAs<Stmt>("loop");
+ const auto *CXXLoopBound =
+ Result.Nodes.getNodeAs<IntegerLiteral>("cxx_loop_bound");
+ const ASTContext *Context = Result.Context;
+ switch (unrollType(Loop, Result.Context)) {
+ case NotUnrolled:
+ diag(Loop->getBeginLoc(),
+ "kernel performance could be improved by unrolling this loop with a "
+ "'#pragma unroll' directive");
+ break;
+ case PartiallyUnrolled:
+ // Loop already partially unrolled, do nothing.
+ break;
+ case FullyUnrolled:
+ if (hasKnownBounds(Loop, CXXLoopBound, Context)) {
+ if (hasLargeNumIterations(Loop, CXXLoopBound, Context)) {
+ diag(Loop->getBeginLoc(),
+ "loop likely has a large number of iterations and thus "
+ "cannot be fully unrolled; to partially unroll this loop, use "
+ "the '#pragma unroll <num>' directive");
+ return;
+ }
+ return;
+ }
+ if (isa<WhileStmt, DoStmt>(Loop)) {
+ diag(Loop->getBeginLoc(),
+ "full unrolling requested, but loop bounds may not be known; to "
+ "partially unroll this loop, use the '#pragma unroll <num>' "
+ "directive",
+ DiagnosticIDs::Note);
+ break;
+ }
+ diag(Loop->getBeginLoc(),
+ "full unrolling requested, but loop bounds are not known; to "
+ "partially unroll this loop, use the '#pragma unroll <num>' "
+ "directive");
+ break;
+ }
+}
+
+enum UnrollLoopsCheck::UnrollType
+UnrollLoopsCheck::unrollType(const Stmt *Statement, ASTContext *Context) {
+ const DynTypedNodeList Parents = Context->getParents<Stmt>(*Statement);
+ for (const DynTypedNode &Parent : Parents) {
+ const auto *ParentStmt = Parent.get<AttributedStmt>();
+ if (!ParentStmt)
+ continue;
+ for (const Attr *Attribute : ParentStmt->getAttrs()) {
+ const auto *LoopHint = dyn_cast<LoopHintAttr>(Attribute);
+ if (!LoopHint)
+ continue;
+ switch (LoopHint->getState()) {
+ case LoopHintAttr::Numeric:
+ return PartiallyUnrolled;
+ case LoopHintAttr::Disable:
+ return NotUnrolled;
+ case LoopHintAttr::Full:
+ return FullyUnrolled;
+ case LoopHintAttr::Enable:
+ return FullyUnrolled;
+ case LoopHintAttr::AssumeSafety:
+ return NotUnrolled;
+ case LoopHintAttr::FixedWidth:
+ return NotUnrolled;
+ case LoopHintAttr::ScalableWidth:
+ return NotUnrolled;
+ }
+ }
+ }
+ return NotUnrolled;
+}
+
+bool UnrollLoopsCheck::hasKnownBounds(const Stmt *Statement,
+ const IntegerLiteral *CXXLoopBound,
+ const ASTContext *Context) {
+ if (isa<CXXForRangeStmt>(Statement))
+ return CXXLoopBound != nullptr;
+ // Too many possibilities in a while statement, so always recommend partial
+ // unrolling for these.
+ if (isa<WhileStmt, DoStmt>(Statement))
+ return false;
+ // The last loop type is a for loop.
+ const auto *ForLoop = cast<ForStmt>(Statement);
+ const Stmt *Initializer = ForLoop->getInit();
+ const Expr *Conditional = ForLoop->getCond();
+ const Expr *Increment = ForLoop->getInc();
+ if (!Initializer || !Conditional || !Increment)
+ return false;
+ // If the loop variable value isn't known, loop bounds are unknown.
+ if (const auto *InitDeclStatement = dyn_cast<DeclStmt>(Initializer)) {
+ if (const auto *VariableDecl =
+ dyn_cast<VarDecl>(InitDeclStatement->getSingleDecl())) {
+ APValue *Evaluation = VariableDecl->evaluateValue();
+ if (!Evaluation || !Evaluation->hasValue())
+ return false;
+ }
+ }
+ // If increment is unary and not one of ++ and --, loop bounds are unknown.
+ if (const auto *Op = dyn_cast<UnaryOperator>(Increment))
+ if (!Op->isIncrementDecrementOp())
+ return false;
+
+ if (const auto *BinaryOp = dyn_cast<BinaryOperator>(Conditional)) {
+ const Expr *LHS = BinaryOp->getLHS();
+ const Expr *RHS = BinaryOp->getRHS();
+ // If both sides are value dependent or constant, loop bounds are unknown.
+ return LHS->isEvaluatable(*Context) != RHS->isEvaluatable(*Context);
+ }
+ return false; // If it's not a binary operator, loop bounds are unknown.
+}
+
+const Expr *UnrollLoopsCheck::getCondExpr(const Stmt *Statement) {
+ if (const auto *ForLoop = dyn_cast<ForStmt>(Statement))
+ return ForLoop->getCond();
+ if (const auto *WhileLoop = dyn_cast<WhileStmt>(Statement))
+ return WhileLoop->getCond();
+ if (const auto *DoWhileLoop = dyn_cast<DoStmt>(Statement))
+ return DoWhileLoop->getCond();
+ if (const auto *CXXRangeLoop = dyn_cast<CXXForRangeStmt>(Statement))
+ return CXXRangeLoop->getCond();
+ llvm_unreachable("Unknown loop");
+}
+
+bool UnrollLoopsCheck::hasLargeNumIterations(const Stmt *Statement,
+ const IntegerLiteral *CXXLoopBound,
+ const ASTContext *Context) {
+ // Because hasKnownBounds is called before this, if this is true, then
+ // CXXLoopBound is also matched.
+ if (isa<CXXForRangeStmt>(Statement)) {
+ assert(CXXLoopBound && "CXX ranged for loop has no loop bound");
+ return exprHasLargeNumIterations(CXXLoopBound, Context);
+ }
+ const auto *ForLoop = cast<ForStmt>(Statement);
+ const Stmt *Initializer = ForLoop->getInit();
+ const Expr *Conditional = ForLoop->getCond();
+ const Expr *Increment = ForLoop->getInc();
+ int InitValue;
+ // If the loop variable value isn't known, we can't know the loop bounds.
+ if (const auto *InitDeclStatement = dyn_cast<DeclStmt>(Initializer)) {
+ if (const auto *VariableDecl =
+ dyn_cast<VarDecl>(InitDeclStatement->getSingleDecl())) {
+ APValue *Evaluation = VariableDecl->evaluateValue();
+ if (!Evaluation || !Evaluation->isInt())
+ return true;
+ InitValue = Evaluation->getInt().getExtValue();
+ }
+ }
+
+ int EndValue;
+ const auto *BinaryOp = cast<BinaryOperator>(Conditional);
+ if (!extractValue(EndValue, BinaryOp, Context))
+ return true;
+
+ double Iterations;
+
+ // If increment is unary and not one of ++, --, we can't know the loop bounds.
+ if (const auto *Op = dyn_cast<UnaryOperator>(Increment)) {
+ if (Op->isIncrementOp())
+ Iterations = EndValue - InitValue;
+ else if (Op->isDecrementOp())
+ Iterations = InitValue - EndValue;
+ else
+ llvm_unreachable("Unary operator neither increment nor decrement");
+ }
+
+ // If increment is binary and not one of +, -, *, /, we can't know the loop
+ // bounds.
+ if (const auto *Op = dyn_cast<BinaryOperator>(Increment)) {
+ int ConstantValue;
+ if (!extractValue(ConstantValue, Op, Context))
+ return true;
+ switch (Op->getOpcode()) {
+ case (BO_AddAssign):
+ Iterations = ceil(float(EndValue - InitValue) / ConstantValue);
+ break;
+ case (BO_SubAssign):
+ Iterations = ceil(float(InitValue - EndValue) / ConstantValue);
+ break;
+ case (BO_MulAssign):
+ Iterations = 1 + (log(EndValue) - log(InitValue)) / log(ConstantValue);
+ break;
+ case (BO_DivAssign):
+ Iterations = 1 + (log(InitValue) - log(EndValue)) / log(ConstantValue);
+ break;
+ default:
+ // All other operators are not handled; assume large bounds.
+ return true;
+ }
+ }
+ return Iterations > MaxLoopIterations;
+}
+
+bool UnrollLoopsCheck::extractValue(int &Value, const BinaryOperator *Op,
+ const ASTContext *Context) {
+ const Expr *LHS = Op->getLHS();
+ const Expr *RHS = Op->getRHS();
+ Expr::EvalResult Result;
+ if (LHS->isEvaluatable(*Context))
+ LHS->EvaluateAsRValue(Result, *Context);
+ else if (RHS->isEvaluatable(*Context))
+ RHS->EvaluateAsRValue(Result, *Context);
+ else
+ return false; // Cannot evaluate either side.
+ if (!Result.Val.isInt())
+ return false; // Cannot check number of iterations, return false to be
+ // safe.
+ Value = Result.Val.getInt().getExtValue();
+ return true;
+}
+
+bool UnrollLoopsCheck::exprHasLargeNumIterations(const Expr *Expression,
+ const ASTContext *Context) {
+ Expr::EvalResult Result;
+ if (Expression->EvaluateAsRValue(Result, *Context)) {
+ if (!Result.Val.isInt())
+ return false; // Cannot check number of iterations, return false to be
+ // safe.
+ // The following assumes values go from 0 to Val in increments of 1.
+ return Result.Val.getInt() > MaxLoopIterations;
+ }
+ // Cannot evaluate Expression as an r-value, so cannot check number of
+ // iterations.
+ return false;
+}
+
+void UnrollLoopsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "MaxLoopIterations", MaxLoopIterations);
+}
+
+} // namespace clang::tidy::altera
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.h b/contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.h
new file mode 100644
index 0000000000..e4bf7464c6
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/UnrollLoopsCheck.h
@@ -0,0 +1,74 @@
+//===--- UnrollLoopsCheck.h - clang-tidy ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_UNROLLLOOPSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_UNROLLLOOPSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::altera {
+
+/// Finds inner loops that have not been unrolled, as well as fully unrolled
+/// loops with unknown loop bounds or a large number of iterations.
+///
+/// Unrolling inner loops could improve the performance of OpenCL kernels.
+/// However, if they have unknown loop bounds or a large number of iterations,
+/// they cannot be fully unrolled, and should be partially unrolled.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/altera/unroll-loops.html
+class UnrollLoopsCheck : public ClangTidyCheck {
+public:
+ UnrollLoopsCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ /// Recommend partial unrolling if number of loop iterations is greater than
+ /// MaxLoopIterations.
+ const unsigned MaxLoopIterations;
+ /// The kind of unrolling, if any, applied to a given loop.
+ enum UnrollType {
+ // This loop has no #pragma unroll directive associated with it.
+ NotUnrolled,
+ // This loop has a #pragma unroll directive associated with it.
+ FullyUnrolled,
+ // This loop has a #pragma unroll <num> directive associated with it.
+ PartiallyUnrolled
+ };
+ /// Attempts to extract an integer value from either side of the
+ /// BinaryOperator. Returns true and saves the result to &value if successful,
+ /// returns false otherwise.
+ bool extractValue(int &Value, const BinaryOperator *Op,
+ const ASTContext *Context);
+ /// Returns true if the given loop statement has a large number of iterations,
+ /// as determined by the integer value in the loop's condition expression,
+ /// if one exists.
+ bool hasLargeNumIterations(const Stmt *Statement,
+ const IntegerLiteral *CXXLoopBound,
+ const ASTContext *Context);
+ /// Checks one hand side of the binary operator to ascertain if the upper
+ /// bound on the number of loops is greater than max_loop_iterations or not.
+ /// If the expression is not evaluatable or not an integer, returns false.
+ bool exprHasLargeNumIterations(const Expr *Expression,
+ const ASTContext *Context);
+ /// Returns the type of unrolling, if any, associated with the given
+ /// statement.
+ enum UnrollType unrollType(const Stmt *Statement, ASTContext *Context);
+ /// Returns the condition expression within a given for statement. If there is
+ /// none, or if the Statement is not a loop, then returns a NULL pointer.
+ const Expr *getCondExpr(const Stmt *Statement);
+ /// Returns True if the loop statement has known bounds.
+ bool hasKnownBounds(const Stmt *Statement, const IntegerLiteral *CXXLoopBound,
+ const ASTContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+};
+
+} // namespace clang::tidy::altera
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_UNROLLLOOPSCHECK_H
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/altera/ya.make b/contrib/libs/clang16/tools/extra/clang-tidy/altera/ya.make
new file mode 100644
index 0000000000..35b9a6907a
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/altera/ya.make
@@ -0,0 +1,41 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(Apache-2.0 WITH LLVM-exception)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/clang16
+ contrib/libs/clang16/include
+ contrib/libs/clang16/lib
+ contrib/libs/clang16/lib/AST
+ contrib/libs/clang16/lib/ASTMatchers
+ contrib/libs/clang16/lib/Analysis
+ contrib/libs/clang16/lib/Basic
+ contrib/libs/clang16/lib/Lex
+ contrib/libs/clang16/tools/extra/clang-tidy/utils
+ contrib/libs/llvm16
+ contrib/libs/llvm16/lib/Frontend/OpenMP
+ contrib/libs/llvm16/lib/Support
+)
+
+ADDINCL(
+ contrib/libs/clang16/tools/extra/clang-tidy/altera
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+SRCS(
+ AlteraTidyModule.cpp
+ IdDependentBackwardBranchCheck.cpp
+ KernelNameRestrictionCheck.cpp
+ SingleWorkItemBarrierCheck.cpp
+ StructPackAlignCheck.cpp
+ UnrollLoopsCheck.cpp
+)
+
+END()