new altera unroll loops check

This lint check is a part of the FLOCL (FPGA Linters for OpenCL)
project out of the Synergy Lab at Virginia Tech.

FLOCL is a set of lint checks aimed at FPGA developers who write code
in OpenCL.

The altera unroll loops check finds inner loops that have not been
unrolled, as well as fully-unrolled loops that should be partially
unrolled due to unknown loop bounds or a large number of loop
iterations.

Based on the Altera SDK for OpenCL: Best Practices Guide.
This commit is contained in:
Frank Derry Wanye 2021-03-22 13:08:14 -04:00 committed by Aaron Ballman
parent e421a74108
commit 5a87f81fe9
8 changed files with 986 additions and 0 deletions

View File

@ -12,6 +12,7 @@
#include "KernelNameRestrictionCheck.h"
#include "SingleWorkItemBarrierCheck.h"
#include "StructPackAlignCheck.h"
#include "UnrollLoopsCheck.h"
using namespace clang::ast_matchers;
@ -28,6 +29,7 @@ public:
"altera-single-work-item-barrier");
CheckFactories.registerCheck<StructPackAlignCheck>(
"altera-struct-pack-align");
CheckFactories.registerCheck<UnrollLoopsCheck>("altera-unroll-loops");
}
};

View File

@ -8,6 +8,7 @@ add_clang_library(clangTidyAlteraModule
KernelNameRestrictionCheck.cpp
SingleWorkItemBarrierCheck.cpp
StructPackAlignCheck.cpp
UnrollLoopsCheck.cpp
LINK_LIBS
clangTidy

View File

@ -0,0 +1,277 @@
//===--- 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 {
namespace tidy {
namespace 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 = dyn_cast<ForStmt>(Statement);
if (!ForLoop)
llvm_unreachable("Unknown loop");
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 (isa<BinaryOperator>(Conditional)) {
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 = dyn_cast<ForStmt>(Statement);
assert(ForLoop && "Unknown loop");
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();
}
}
assert(isa<BinaryOperator>(Conditional) &&
"Conditional is not a binary operator");
int EndValue;
const auto *BinaryOp = dyn_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 evalue 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 altera
} // namespace tidy
} // namespace clang

View File

@ -0,0 +1,78 @@
//===--- 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 {
namespace tidy {
namespace 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 altera
} // namespace tidy
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALTERA_UNROLLLOOPSCHECK_H

View File

@ -83,6 +83,12 @@ New checks
Finds ``pthread_setcanceltype`` function calls where a thread's cancellation
type is set to asynchronous.
- New :doc:`altera-unroll-loops
<clang-tidy/checks/altera-unroll-loops>` check.
Finds inner loops that have not been unrolled, as well as fully unrolled
loops with unknown loops bounds or a large number of iterations.
- New :doc:`cppcoreguidelines-prefer-member-initializer
<clang-tidy/checks/cppcoreguidelines-prefer-member-initializer>` check.

View File

@ -0,0 +1,105 @@
.. title:: clang-tidy - altera-unroll-loops
altera-unroll-loops
===================
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.
Notes:
- This check is unable to determine the number of iterations in a ``while`` or
``do..while`` loop; hence if such a loop is fully unrolled, a note is emitted
advising the user to partially unroll instead.
- In ``for`` loops, our check only works with simple arithmetic increments (
``+``, ``-``, ``*``, ``/``). For all other increments, partial unrolling is
advised.
- Depending on the exit condition, the calculations for determining if the
number of iterations is large may be off by 1. This should not be an issue
since the cut-off is generally arbitrary.
Based on the `Altera SDK for OpenCL: Best Practices Guide
<https://www.altera.com/en_US/pdfs/literature/hb/opencl-sdk/aocl_optimization_guide.pdf>`_.
.. code-block:: c++
for (int i = 0; i < 10; i++) { // ok: outer loops should not be unrolled
int j = 0;
do { // warning: this inner do..while loop should be unrolled
j++;
} while (j < 15);
int k = 0;
#pragma unroll
while (k < 20) { // ok: this inner loop is already unrolled
k++;
}
}
int A[1000];
#pragma unroll
// warning: this loop is large and should be partially unrolled
for (int a : A) {
printf("%d", a);
}
#pragma unroll 5
// ok: this loop is large, but is partially unrolled
for (int a : A) {
printf("%d", a);
}
#pragma unroll
// warning: this loop is large and should be partially unrolled
for (int i = 0; i < 1000; ++i) {
printf("%d", i);
}
#pragma unroll 5
// ok: this loop is large, but is partially unrolled
for (int i = 0; i < 1000; ++i) {
printf("%d", i);
}
#pragma unroll
// warning: << operator not supported, recommend partial unrolling
for (int i = 0; i < 1000; i<<1) {
printf("%d", i);
}
std::vector<int> someVector (100, 0);
int i = 0;
#pragma unroll
// note: loop may be large, recommend partial unrolling
while (i < someVector.size()) {
someVector[i]++;
}
#pragma unroll
// note: loop may be large, recommend partial unrolling
while (true) {
printf("In loop");
}
#pragma unroll 5
// ok: loop may be large, but is partially unrolled
while (i < someVector.size()) {
someVector[i]++;
}
Options
-------
.. option:: MaxLoopIterations
Defines the maximum number of loop iterations that a fully unrolled loop
can have. By default, it is set to `100`.
In practice, this refers to the integer value of the upper bound
within the loop statement's condition expression.

View File

@ -33,6 +33,7 @@ Clang-Tidy Checks
`altera-kernel-name-restriction <altera-kernel-name-restriction.html>`_,
`altera-single-work-item-barrier <altera-single-work-item-barrier.html>`_,
`altera-struct-pack-align <altera-struct-pack-align.html>`_, "Yes"
`altera-unroll-loops <altera-unroll-loops.html>`_,
`android-cloexec-accept <android-cloexec-accept.html>`_, "Yes"
`android-cloexec-accept4 <android-cloexec-accept4.html>`_,
`android-cloexec-creat <android-cloexec-creat.html>`_, "Yes"

View File

@ -0,0 +1,516 @@
// RUN: %check_clang_tidy %s altera-unroll-loops %t -- -config="{CheckOptions: [{key: "altera-unroll-loops.MaxLoopIterations", value: 50}]}" -header-filter=.*
// RUN: %check_clang_tidy -check-suffix=MULT %s altera-unroll-loops %t -- -config="{CheckOptions: [{key: "altera-unroll-loops.MaxLoopIterations", value: 5}]}" -header-filter=.* "--" -DMULT
#ifdef MULT
// For loops with *= and /= increments.
void for_loop_mult_div_increments(int *A) {
// *=
#pragma unroll
for (int i = 2; i <= 32; i *= 2)
A[i]++; // OK
#pragma unroll
for (int i = 2; i <= 64; i *= 2)
// CHECK-MESSAGES-MULT: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[i]++; // Not OK
// /=
#pragma unroll
for (int i = 32; i >= 2; i /= 2)
A[i]++; // OK
#pragma unroll
for (int i = 64; i >= 2; i /= 2)
// CHECK-MESSAGES-MULT: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[i]++; // Not OK
}
#else
// Cannot determine loop bounds for while loops.
void while_loops(int *A) {
// Recommend unrolling loops that aren't already unrolled.
int j = 0;
while (j < 2000) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[1] += j;
j++;
}
do {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[2] += j;
j++;
} while (j < 2000);
// If a while loop is fully unrolled, add a note recommending partial
// unrolling.
#pragma unroll
while (j < 2000) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: full unrolling requested, but loop bounds may not be known; to partially unroll this loop, use the '#pragma unroll <num>' directive
A[j]++;
}
#pragma unroll
do {
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: full unrolling requested, but loop bounds may not be known; to partially unroll this loop, use the '#pragma unroll <num>' directive
A[j]++;
} while (j < 2000);
// While loop is partially unrolled, no action needed.
#pragma unroll 4
while (j < 2000) {
A[j]++;
}
#pragma unroll 4
do {
A[j]++;
} while (j < 2000);
}
// Range-based for loops.
void cxx_for_loops(int *A, int vectorSize) {
// Loop with known array size should be unrolled.
int a[] = {0, 1, 2, 3, 4, 5};
for (int k : a) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[k]++;
}
// Loop with known size correctly unrolled.
#pragma unroll
for (int k : a) {
A[k]++;
}
// Loop with unknown size should be partially unrolled.
int b[vectorSize];
#pragma unroll
for (int k : b) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
k++;
}
// Loop with unknown size correctly unrolled.
#pragma unroll 5
for (int k : b) {
k++;
}
// Loop with large size should be partially unrolled.
int c[51];
#pragma unroll
for (int k : c) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[k]++;
}
// Loop with large size correctly unrolled.
#pragma unroll 5
for (int k : c) {
A[k]++;
}
}
// Simple for loops.
void for_loops(int *A, int size) {
// Recommend unrolling loops that aren't already unrolled.
for (int i = 0; i < 2000; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[0] += i;
}
// Loop with known size correctly unrolled.
#pragma unroll
for (int i = 0; i < 50; ++i) {
A[i]++;
}
// Loop with unknown size should be partially unrolled.
#pragma unroll
for (int i = 0; i < size; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[i]++;
}
#pragma unroll
for (;;) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[0]++;
}
int i = 0;
#pragma unroll
for (; i < size; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[i]++;
}
#pragma unroll
for (int i = 0;; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[i]++;
}
#pragma unroll
for (int i = 0; i < size;) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[i]++;
}
#pragma unroll
for (int i = size; i < 50; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[i]++;
}
#pragma unroll
for (int i = 0; true; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[i]++;
}
#pragma unroll
for (int i = 0; i == i; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
A[i]++;
}
// Loop with unknown size correctly unrolled.
#pragma unroll 5
for (int i = 0; i < size; ++i) {
A[i]++;
}
// Loop with large size should be partially unrolled.
#pragma unroll
for (int i = 0; i < 51; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[i]++;
}
// Loop with large size correctly unrolled.
#pragma unroll 5
for (int i = 0; i < 51; ++i) {
A[i]++;
}
}
// For loops with different increments.
void for_loop_increments(int *A) {
// ++
#pragma unroll
for (int i = 0; i < 50; ++i)
A[i]++; // OK
#pragma unroll
for (int i = 0; i < 51; ++i)
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[i]++; // Not OK
// --
#pragma unroll
for (int i = 50; i > 0; --i)
A[i]++; // OK
#pragma unroll
for (int i = 51; i > 0; --i)
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[i]++; // Not OK
// +=
#pragma unroll
for (int i = 0; i < 100; i += 2)
A[i]++; // OK
#pragma unroll
for (int i = 0; i < 101; i += 2)
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[i]++; // Not OK
// -=
#pragma unroll
for (int i = 100; i > 0; i -= 2)
A[i]++; // OK
#pragma unroll
for (int i = 101; i > 0; i -= 2)
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 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 [altera-unroll-loops]
A[i]++; // Not OK
}
// Inner loops should be unrolled.
void nested_simple_loops(int *A) {
for (int i = 0; i < 1000; ++i) {
for (int j = 0; j < 2000; ++j) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[0] += i + j;
}
}
for (int i = 0; i < 1000; ++i) {
int j = 0;
while (j < 2000) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[1] += i + j;
j++;
}
}
for (int i = 0; i < 1000; ++i) {
int j = 0;
do {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[2] += i + j;
j++;
} while (j < 2000);
}
int i = 0;
while (i < 1000) {
for (int j = 0; j < 2000; ++j) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[3] += i + j;
}
i++;
}
i = 0;
while (i < 1000) {
int j = 0;
while (j < 2000) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[4] += i + j;
j++;
}
i++;
}
i = 0;
while (i < 1000) {
int j = 0;
do {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[5] += i + j;
j++;
} while (j < 2000);
i++;
}
i = 0;
do {
for (int j = 0; j < 2000; ++j) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[6] += i + j;
}
i++;
} while (i < 1000);
i = 0;
do {
int j = 0;
while (j < 2000) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[7] += i + j;
j++;
}
i++;
} while (i < 1000);
i = 0;
do {
int j = 0;
do {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[8] += i + j;
j++;
} while (j < 2000);
i++;
} while (i < 1000);
for (int i = 0; i < 100; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
A[i]++;
}
i = 0;
while (i < 100) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
i++;
}
i = 0;
do {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: kernel performance could be improved by unrolling this loop with a '#pragma unroll' directive [altera-unroll-loops]
i++;
} while (i < 100);
}
// These loops are all correctly unrolled.
void unrolled_nested_simple_loops(int *A) {
for (int i = 0; i < 1000; ++i) {
#pragma unroll
for (int j = 0; j < 50; ++j) {
A[0] += i + j;
}
}
for (int i = 0; i < 1000; ++i) {
int j = 0;
#pragma unroll 5
while (j < 50) {
A[1] += i + j;
j++;
}
}
for (int i = 0; i < 1000; ++i) {
int j = 0;
#pragma unroll 5
do {
A[2] += i + j;
j++;
} while (j < 50);
}
int i = 0;
while (i < 1000) {
#pragma unroll
for (int j = 0; j < 50; ++j) {
A[3] += i + j;
}
i++;
}
i = 0;
while (i < 1000) {
int j = 0;
#pragma unroll 5
while (50 > j) {
A[4] += i + j;
j++;
}
i++;
}
i = 0;
while (1000 > i) {
int j = 0;
#pragma unroll 5
do {
A[5] += i + j;
j++;
} while (j < 50);
i++;
}
i = 0;
do {
#pragma unroll
for (int j = 0; j < 50; ++j) {
A[6] += i + j;
}
i++;
} while (i < 1000);
i = 0;
do {
int j = 0;
#pragma unroll 5
while (j < 50) {
A[7] += i + j;
j++;
}
i++;
} while (i < 1000);
i = 0;
do {
int j = 0;
#pragma unroll 5
do {
A[8] += i + j;
j++;
} while (j < 50);
i++;
} while (i < 1000);
}
// These inner loops are large and should be partially unrolled.
void unrolled_nested_simple_loops_large_num_iterations(int *A) {
for (int i = 0; i < 1000; ++i) {
#pragma unroll
for (int j = 0; j < 51; ++j) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 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 [altera-unroll-loops]
A[0] += i + j;
}
}
int i = 0;
while (i < 1000) {
#pragma unroll
for (int j = 0; j < 51; ++j) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 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 [altera-unroll-loops]
A[3] += i + j;
}
i++;
}
i = 0;
do {
#pragma unroll
for (int j = 0; j < 51; ++j) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 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 [altera-unroll-loops]
A[6] += i + j;
}
i++;
} while (i < 1000);
i = 0;
do {
int j = 0;
#pragma unroll
do {
// CHECK-MESSAGES: :[[@LINE-1]]:5: note: full unrolling requested, but loop bounds may not be known; to partially unroll this loop, use the '#pragma unroll <num>' directive
A[8] += i + j;
j++;
} while (j < 51);
i++;
} while (i < 1000);
i = 0;
int a[51];
do {
#pragma unroll
for (int k : a) {
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 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 [altera-unroll-loops]
A[k]++;
}
} while (i < 1000);
}
// These loops have unknown bounds and should be partially unrolled.
void fully_unrolled_unknown_bounds(int vectorSize) {
int someVector[101];
// There is no loop condition
#pragma unroll
for (;;) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
someVector[0]++;
}
#pragma unroll
for (int i = 0; 1 < 5; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
someVector[i]++;
}
// Both sides are value-dependent
#pragma unroll
for (int i = 0; i < vectorSize; ++i) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: full unrolling requested, but loop bounds are not known; to partially unroll this loop, use the '#pragma unroll <num>' directive [altera-unroll-loops]
someVector[i]++;
}
}
#endif
// There are no fix-its for this check