diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/altera | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/altera')
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() |