Add support for #pragma nounroll.

llvm-svn: 213885
This commit is contained in:
Mark Heffernan 2014-07-24 18:09:38 +00:00
parent 29a2005596
commit c888e41c0c
10 changed files with 111 additions and 36 deletions

View File

@ -111,12 +111,13 @@ interleaving, and unrolling to be enabled or disabled. Vector width as well
as interleave and unrolling count can be manually specified. See language as interleave and unrolling count can be manually specified. See language
extensions for details. extensions for details.
Clang now supports the `#pragma unroll` directive to specify loop unrolling Clang now supports the `#pragma unroll` and `#pragma nounroll` directives to
optimization hints. Placed just prior to the desired loop, `#pragma unroll` specify loop unrolling optimization hints. Placed just prior to the desired
directs the loop unroller to attempt to fully unroll the loop. The pragma may loop, `#pragma unroll` directs the loop unroller to attempt to fully unroll the
also be specified with a positive integer parameter indicating the desired loop. The pragma may also be specified with a positive integer parameter
unroll count: `#pragma unroll _value_`. The unroll count parameter can be indicating the desired unroll count: `#pragma unroll _value_`. The unroll count
optionally enclosed in parentheses. parameter can be optionally enclosed in parentheses. The directive `#pragma
nounroll` indicates that the loop should not be unrolled.
C Language Changes in Clang C Language Changes in Clang
--------------------------- ---------------------------

View File

@ -1791,7 +1791,8 @@ def LoopHint : Attr {
/// unroll: fully unroll loop if 'value != 0'. /// unroll: fully unroll loop if 'value != 0'.
/// unroll_count: unrolls loop 'value' times. /// unroll_count: unrolls loop 'value' times.
let Spellings = [Pragma<"clang", "loop">, Pragma<"", "unroll">]; let Spellings = [Pragma<"clang", "loop">, Pragma<"", "unroll">,
Pragma<"", "nounroll">];
/// State of the loop optimization specified by the spelling. /// State of the loop optimization specified by the spelling.
let Args = [EnumArgument<"Option", "OptionType", let Args = [EnumArgument<"Option", "OptionType",
@ -1822,9 +1823,13 @@ def LoopHint : Attr {
void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const { void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
unsigned SpellingIndex = getSpellingListIndex(); unsigned SpellingIndex = getSpellingListIndex();
// For "#pragma unroll" and "#pragma nounroll" the string "unroll" or
// "nounroll" is already emitted as the pragma name.
if (SpellingIndex == Pragma_nounroll) {
OS << "\n";
return;
}
if (SpellingIndex == Pragma_unroll) { if (SpellingIndex == Pragma_unroll) {
// String "unroll" of "#pragma unroll" is already emitted as the
// pragma name.
if (option == UnrollCount) if (option == UnrollCount)
printArgument(OS); printArgument(OS);
OS << "\n"; OS << "\n";
@ -1858,11 +1863,12 @@ def LoopHint : Attr {
std::string DiagnosticName; std::string DiagnosticName;
llvm::raw_string_ostream OS(DiagnosticName); llvm::raw_string_ostream OS(DiagnosticName);
unsigned SpellingIndex = getSpellingListIndex(); unsigned SpellingIndex = getSpellingListIndex();
if (SpellingIndex == Pragma_unroll && option == Unroll) if (SpellingIndex == Pragma_nounroll)
OS << "#pragma nounroll";
else if (SpellingIndex == Pragma_unroll) {
OS << "#pragma unroll"; OS << "#pragma unroll";
else if (SpellingIndex == Pragma_unroll && option == UnrollCount) { if (option == UnrollCount)
OS << "#pragma unroll"; printArgument(OS);
printArgument(OS);
} else { } else {
assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling"); assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling");
OS << getOptionName(option); OS << getOptionName(option);

View File

@ -1065,11 +1065,11 @@ for details.
def UnrollHintDocs : Documentation { def UnrollHintDocs : Documentation {
let Category = DocCatStmt; let Category = DocCatStmt;
let Heading = "#pragma unroll"; let Heading = "#pragma unroll, #pragma nounroll";
let Content = [{ let Content = [{
Loop unrolling optimization hints can be specified with ``#pragma unroll``. The Loop unrolling optimization hints can be specified with ``#pragma unroll`` and
pragma is placed immediately before a for, while, do-while, or c++11 range-based ``#pragma nounroll``. The pragma is placed immediately before a for, while,
for loop. do-while, or c++11 range-based for loop.
Specifying ``#pragma unroll`` without a parameter directs the loop unroller to Specifying ``#pragma unroll`` without a parameter directs the loop unroller to
attempt to fully unroll the loop if the trip count is known at compile time: attempt to fully unroll the loop if the trip count is known at compile time:
@ -1097,9 +1097,20 @@ enclosed in parentheses:
... ...
} }
Specifying ``#pragma nounroll`` indicates that the loop should not be unrolled:
.. code-block:: c++
#pragma nounroll
for (...) {
...
}
``#pragma unroll`` and ``#pragma unroll _value_`` have identical semantics to ``#pragma unroll`` and ``#pragma unroll _value_`` have identical semantics to
``#pragma clang loop unroll(full)`` and ``#pragma clang loop ``#pragma clang loop unroll(full)`` and
unroll_count(_value_)`` respectively. See `language extensions ``#pragma clang loop unroll_count(_value_)`` respectively. ``#pragma nounroll``
is equivalent to ``#pragma clang loop unroll(disable)``. See
`language extensions
<http://clang.llvm.org/docs/LanguageExtensions.html#extensions-for-loop-hint-optimizations>`_ <http://clang.llvm.org/docs/LanguageExtensions.html#extensions-for-loop-hint-optimizations>`_
for further details including limitations of the unroll hints. for further details including limitations of the unroll hints.
}]; }];

View File

@ -164,6 +164,7 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr<PragmaHandler> OptimizeHandler; std::unique_ptr<PragmaHandler> OptimizeHandler;
std::unique_ptr<PragmaHandler> LoopHintHandler; std::unique_ptr<PragmaHandler> LoopHintHandler;
std::unique_ptr<PragmaHandler> UnrollHintHandler; std::unique_ptr<PragmaHandler> UnrollHintHandler;
std::unique_ptr<PragmaHandler> NoUnrollHintHandler;
std::unique_ptr<CommentHandler> CommentSemaHandler; std::unique_ptr<CommentHandler> CommentSemaHandler;

View File

@ -26,7 +26,8 @@ struct LoopHint {
// hints. // hints.
IdentifierLoc *PragmaNameLoc; IdentifierLoc *PragmaNameLoc;
// Name of the loop hint. Examples: "unroll", "vectorize". In the // Name of the loop hint. Examples: "unroll", "vectorize". In the
// "#pragma unroll" case, this is identical to PragmaNameLoc. // "#pragma unroll" and "#pragma nounroll" cases, this is identical to
// PragmaNameLoc.
IdentifierLoc *OptionLoc; IdentifierLoc *OptionLoc;
// Identifier for the hint argument. If null, then the hint has no argument // Identifier for the hint argument. If null, then the hint has no argument
// such as for "#pragma unroll". // such as for "#pragma unroll".

View File

@ -227,6 +227,9 @@ void Parser::initializePragmaHandlers() {
UnrollHintHandler.reset(new PragmaUnrollHintHandler("unroll")); UnrollHintHandler.reset(new PragmaUnrollHintHandler("unroll"));
PP.AddPragmaHandler(UnrollHintHandler.get()); PP.AddPragmaHandler(UnrollHintHandler.get());
NoUnrollHintHandler.reset(new PragmaUnrollHintHandler("nounroll"));
PP.AddPragmaHandler(NoUnrollHintHandler.get());
} }
void Parser::resetPragmaHandlers() { void Parser::resetPragmaHandlers() {
@ -290,6 +293,9 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler(UnrollHintHandler.get()); PP.RemovePragmaHandler(UnrollHintHandler.get());
UnrollHintHandler.reset(); UnrollHintHandler.reset();
PP.RemovePragmaHandler(NoUnrollHintHandler.get());
NoUnrollHintHandler.reset();
} }
/// \brief Handle the annotation token produced for #pragma unused(...) /// \brief Handle the annotation token produced for #pragma unused(...)
@ -1908,29 +1914,36 @@ void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP,
/// #pragma unroll /// #pragma unroll
/// #pragma unroll unroll-hint-value /// #pragma unroll unroll-hint-value
/// #pragma unroll '(' unroll-hint-value ')' /// #pragma unroll '(' unroll-hint-value ')'
/// #pragma nounroll
/// ///
/// unroll-hint-value: /// unroll-hint-value:
/// constant-expression /// constant-expression
/// ///
/// Loop unrolling hints are specified with '#pragma unroll'. '#pragma unroll' /// Loop unrolling hints can be specified with '#pragma unroll' or
/// can take a numeric argument optionally contained in parentheses. With no /// '#pragma nounroll'. '#pragma unroll' can take a numeric argument optionally
/// argument the directive instructs llvm to try to unroll the loop /// contained in parentheses. With no argument the directive instructs llvm to
/// completely. A positive integer argument can be specified to indicate the /// try to unroll the loop completely. A positive integer argument can be
/// number of times the loop should be unrolled. To maximize compatibility with /// specified to indicate the number of times the loop should be unrolled. To
/// other compilers the unroll count argument can be specified with or without /// maximize compatibility with other compilers the unroll count argument can be
/// parentheses. /// specified with or without parentheses. Specifying, '#pragma nounroll'
/// disables unrolling of the loop.
void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP, void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP,
PragmaIntroducerKind Introducer, PragmaIntroducerKind Introducer,
Token &Tok) { Token &Tok) {
// Incoming token is "unroll" of "#pragma unroll". // Incoming token is "unroll" for "#pragma unroll", or "nounroll" for
// "#pragma nounroll".
Token PragmaName = Tok; Token PragmaName = Tok;
PP.Lex(Tok); PP.Lex(Tok);
auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo; auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo;
if (Tok.is(tok::eod)) { if (Tok.is(tok::eod)) {
// Unroll pragma without an argument. // nounroll or unroll pragma without an argument.
Info->PragmaName = PragmaName; Info->PragmaName = PragmaName;
Info->Option = PragmaName; Info->Option = PragmaName;
Info->HasValue = false; Info->HasValue = false;
} else if (PragmaName.getIdentifierInfo()->getName() == "nounroll") {
PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
<< "nounroll";
return;
} else { } else {
// Unroll pragma with an argument: "#pragma unroll N" or // Unroll pragma with an argument: "#pragma unroll N" or
// "#pragma unroll(N)". // "#pragma unroll(N)".

View File

@ -58,9 +58,11 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
St->getStmtClass() != Stmt::ForStmtClass && St->getStmtClass() != Stmt::ForStmtClass &&
St->getStmtClass() != Stmt::CXXForRangeStmtClass && St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
St->getStmtClass() != Stmt::WhileStmtClass) { St->getStmtClass() != Stmt::WhileStmtClass) {
const char *Pragma = PragmaNameLoc->Ident->getName() == "unroll" const char *Pragma =
? "#pragma unroll" llvm::StringSwitch<const char *>(PragmaNameLoc->Ident->getName())
: "#pragma clang loop"; .Case("unroll", "#pragma unroll")
.Case("nounroll", "#pragma nounroll")
.Default("#pragma clang loop");
S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop) << Pragma; S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop) << Pragma;
return nullptr; return nullptr;
} }
@ -70,6 +72,9 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
if (PragmaNameLoc->Ident->getName() == "unroll") { if (PragmaNameLoc->Ident->getName() == "unroll") {
Option = ValueLoc ? LoopHintAttr::UnrollCount : LoopHintAttr::Unroll; Option = ValueLoc ? LoopHintAttr::UnrollCount : LoopHintAttr::Unroll;
Spelling = LoopHintAttr::Pragma_unroll; Spelling = LoopHintAttr::Pragma_unroll;
} else if (PragmaNameLoc->Ident->getName() == "nounroll") {
Option = LoopHintAttr::Unroll;
Spelling = LoopHintAttr::Pragma_nounroll;
} else { } else {
Option = llvm::StringSwitch<LoopHintAttr::OptionType>(OptionInfo->getName()) Option = llvm::StringSwitch<LoopHintAttr::OptionType>(OptionInfo->getName())
.Case("vectorize", LoopHintAttr::Vectorize) .Case("vectorize", LoopHintAttr::Vectorize)
@ -86,6 +91,9 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
if (Option == LoopHintAttr::Unroll && if (Option == LoopHintAttr::Unroll &&
Spelling == LoopHintAttr::Pragma_unroll) { Spelling == LoopHintAttr::Pragma_unroll) {
ValueInt = 1; ValueInt = 1;
} else if (Option == LoopHintAttr::Unroll &&
Spelling == LoopHintAttr::Pragma_nounroll) {
ValueInt = 0;
} else if (Option == LoopHintAttr::Vectorize || } else if (Option == LoopHintAttr::Vectorize ||
Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Interleave ||
Option == LoopHintAttr::Unroll) { Option == LoopHintAttr::Unroll) {

View File

@ -17,7 +17,7 @@ void while_test(int *List, int Length) {
void do_test(int *List, int Length) { void do_test(int *List, int Length) {
int i = 0; int i = 0;
#pragma unroll 16 #pragma nounroll
do { do {
// CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_2:.*]] // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_2:.*]]
List[i] = i * 2; List[i] = i * 2;
@ -88,8 +88,8 @@ void template_test(double *List, int Length) {
// CHECK: ![[LOOP_1]] = metadata !{metadata ![[LOOP_1]], metadata ![[UNROLL_FULL:.*]]} // CHECK: ![[LOOP_1]] = metadata !{metadata ![[LOOP_1]], metadata ![[UNROLL_FULL:.*]]}
// CHECK: ![[UNROLL_FULL]] = metadata !{metadata !"llvm.loop.unroll.full"} // CHECK: ![[UNROLL_FULL]] = metadata !{metadata !"llvm.loop.unroll.full"}
// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[UNROLL_16:.*]]} // CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[UNROLL_DISABLE:.*]]}
// CHECK: ![[UNROLL_16]] = metadata !{metadata !"llvm.loop.unroll.count", i32 16} // CHECK: ![[UNROLL_DISABLE]] = metadata !{metadata !"llvm.loop.unroll.disable"}
// CHECK: ![[LOOP_3]] = metadata !{metadata ![[LOOP_3]], metadata ![[UNROLL_8:.*]]} // CHECK: ![[LOOP_3]] = metadata !{metadata ![[LOOP_3]], metadata ![[UNROLL_8:.*]]}
// CHECK: ![[UNROLL_8]] = metadata !{metadata !"llvm.loop.unroll.count", i32 8} // CHECK: ![[UNROLL_8]] = metadata !{metadata !"llvm.loop.unroll.count", i32 8}
// CHECK: ![[LOOP_4]] = metadata !{metadata ![[LOOP_4]], metadata ![[UNROLL_4:.*]]} // CHECK: ![[LOOP_4]] = metadata !{metadata ![[LOOP_4]], metadata ![[UNROLL_4:.*]]}

View File

@ -15,6 +15,7 @@
// CHECK: #pragma clang loop vectorize(disable) // CHECK: #pragma clang loop vectorize(disable)
// CHECK: #pragma unroll // CHECK: #pragma unroll
// CHECK: #pragma unroll (32) // CHECK: #pragma unroll (32)
// CHECK: #pragma nounroll
#ifndef HEADER #ifndef HEADER
#define HEADER #define HEADER
@ -71,8 +72,16 @@ public:
i++; i++;
} }
} }
};
inline void run6(int *List, int Length) {
int i = 0;
#pragma nounroll
while (i - 3 < Length) {
List[i] = i;
i++;
}
}
};
#else #else
void test() { void test() {
@ -85,6 +94,7 @@ void test() {
pt.run3(List, 100); pt.run3(List, 100);
pt.run4(List, 100); pt.run4(List, 100);
pt.run5(List, 100); pt.run5(List, 100);
pt.run6(List, 100);
} }
#endif #endif

View File

@ -11,6 +11,11 @@ void test(int *List, int Length) {
List[i] = i; List[i] = i;
} }
#pragma nounroll
while (i < Length) {
List[i] = i;
}
#pragma unroll 4 #pragma unroll 4
while (i - 1 < Length) { while (i - 1 < Length) {
List[i] = i; List[i] = i;
@ -28,6 +33,11 @@ void test(int *List, int Length) {
List[i] = i; List[i] = i;
} }
/* expected-warning {{extra tokens at end of '#pragma nounroll'}} */ #pragma nounroll 1
while (i-7 < Length) {
List[i] = i;
}
/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(() /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(()
/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll - /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll -
/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(0) /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(0)
@ -42,6 +52,8 @@ void test(int *List, int Length) {
/* expected-error {{expected a for, while, or do-while loop to follow '#pragma unroll'}} */ int j = Length; /* expected-error {{expected a for, while, or do-while loop to follow '#pragma unroll'}} */ int j = Length;
#pragma unroll 4 #pragma unroll 4
/* expected-error {{expected a for, while, or do-while loop to follow '#pragma unroll'}} */ int k = Length; /* expected-error {{expected a for, while, or do-while loop to follow '#pragma unroll'}} */ int k = Length;
#pragma nounroll
/* expected-error {{expected a for, while, or do-while loop to follow '#pragma nounroll'}} */ int l = Length;
/* expected-error {{incompatible directives 'unroll(disable)' and '#pragma unroll(4)'}} */ #pragma unroll 4 /* expected-error {{incompatible directives 'unroll(disable)' and '#pragma unroll(4)'}} */ #pragma unroll 4
#pragma clang loop unroll(disable) #pragma clang loop unroll(disable)
@ -61,6 +73,18 @@ void test(int *List, int Length) {
List[i] = i; List[i] = i;
} }
/* expected-error {{incompatible directives '#pragma nounroll' and 'unroll_count(4)'}} */ #pragma clang loop unroll_count(4)
#pragma nounroll
while (i-12 < Length) {
List[i] = i;
}
/* expected-error {{duplicate directives '#pragma nounroll' and '#pragma nounroll'}} */ #pragma nounroll
#pragma nounroll
while (i-13 < Length) {
List[i] = i;
}
/* expected-error {{duplicate directives '#pragma unroll' and '#pragma unroll'}} */ #pragma unroll /* expected-error {{duplicate directives '#pragma unroll' and '#pragma unroll'}} */ #pragma unroll
#pragma unroll #pragma unroll
while (i-14 < Length) { while (i-14 < Length) {