From bb349466784ef8612d5aae6f4e3a7fe7fee17d74 Mon Sep 17 00:00:00 2001 From: Mark Heffernan Date: Thu, 24 Jul 2014 18:09:38 +0000 Subject: [PATCH] Add support for #pragma nounroll. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@213885 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ReleaseNotes.rst | 13 +++++++------ include/clang/Basic/Attr.td | 20 +++++++++++++------- include/clang/Basic/AttrDocs.td | 23 +++++++++++++++++------ include/clang/Parse/Parser.h | 1 + include/clang/Sema/LoopHint.h | 3 ++- lib/Parse/ParsePragma.cpp | 31 ++++++++++++++++++++++--------- lib/Sema/SemaStmtAttr.cpp | 14 +++++++++++--- test/CodeGen/pragma-unroll.cpp | 6 +++--- test/PCH/pragma-loop.cpp | 12 +++++++++++- test/Parser/pragma-unroll.cpp | 24 ++++++++++++++++++++++++ 10 files changed, 111 insertions(+), 36 deletions(-) diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index f17a1a4b16..6544a3da81 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -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 extensions for details. -Clang now supports the `#pragma unroll` directive to specify loop unrolling -optimization hints. Placed just prior to the desired loop, `#pragma unroll` -directs the loop unroller to attempt to fully unroll the loop. The pragma may -also be specified with a positive integer parameter indicating the desired -unroll count: `#pragma unroll _value_`. The unroll count parameter can be -optionally enclosed in parentheses. +Clang now supports the `#pragma unroll` and `#pragma nounroll` directives to +specify loop unrolling optimization hints. Placed just prior to the desired +loop, `#pragma unroll` directs the loop unroller to attempt to fully unroll the +loop. The pragma may also be specified with a positive integer parameter +indicating the desired unroll count: `#pragma unroll _value_`. The unroll count +parameter can be optionally enclosed in parentheses. The directive `#pragma +nounroll` indicates that the loop should not be unrolled. C Language Changes in Clang --------------------------- diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 708f4f49bd..0af6f97504 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1791,7 +1791,8 @@ def LoopHint : Attr { /// unroll: fully unroll loop if 'value != 0'. /// 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. let Args = [EnumArgument<"Option", "OptionType", @@ -1822,9 +1823,13 @@ def LoopHint : Attr { void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const { 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) { - // String "unroll" of "#pragma unroll" is already emitted as the - // pragma name. if (option == UnrollCount) printArgument(OS); OS << "\n"; @@ -1858,11 +1863,12 @@ def LoopHint : Attr { std::string DiagnosticName; llvm::raw_string_ostream OS(DiagnosticName); unsigned SpellingIndex = getSpellingListIndex(); - if (SpellingIndex == Pragma_unroll && option == Unroll) - OS << "#pragma unroll"; - else if (SpellingIndex == Pragma_unroll && option == UnrollCount) { + if (SpellingIndex == Pragma_nounroll) + OS << "#pragma nounroll"; + else if (SpellingIndex == Pragma_unroll) { OS << "#pragma unroll"; - printArgument(OS); + if (option == UnrollCount) + printArgument(OS); } else { assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling"); OS << getOptionName(option); diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 34dd1b0c45..b04afa794d 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1065,11 +1065,11 @@ for details. def UnrollHintDocs : Documentation { let Category = DocCatStmt; - let Heading = "#pragma unroll"; + let Heading = "#pragma unroll, #pragma nounroll"; let Content = [{ -Loop unrolling optimization hints can be specified with ``#pragma unroll``. The -pragma is placed immediately before a for, while, do-while, or c++11 range-based -for loop. +Loop unrolling optimization hints can be specified with ``#pragma unroll`` and +``#pragma nounroll``. The pragma is placed immediately before a for, while, +do-while, or c++11 range-based for loop. 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: @@ -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 clang loop unroll(full)`` and ``#pragma clang loop -unroll_count(_value_)`` respectively. See `language extensions +``#pragma clang loop unroll(full)`` and +``#pragma clang loop unroll_count(_value_)`` respectively. ``#pragma nounroll`` +is equivalent to ``#pragma clang loop unroll(disable)``. See +`language extensions `_ for further details including limitations of the unroll hints. }]; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 5b5f8eb967..3dccee7660 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -164,6 +164,7 @@ class Parser : public CodeCompletionHandler { std::unique_ptr OptimizeHandler; std::unique_ptr LoopHintHandler; std::unique_ptr UnrollHintHandler; + std::unique_ptr NoUnrollHintHandler; std::unique_ptr CommentSemaHandler; diff --git a/include/clang/Sema/LoopHint.h b/include/clang/Sema/LoopHint.h index d4b985df54..a0bc45572f 100644 --- a/include/clang/Sema/LoopHint.h +++ b/include/clang/Sema/LoopHint.h @@ -26,7 +26,8 @@ struct LoopHint { // hints. IdentifierLoc *PragmaNameLoc; // 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; // Identifier for the hint argument. If null, then the hint has no argument // such as for "#pragma unroll". diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index 5fb999d749..20b87d92b7 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -227,6 +227,9 @@ void Parser::initializePragmaHandlers() { UnrollHintHandler.reset(new PragmaUnrollHintHandler("unroll")); PP.AddPragmaHandler(UnrollHintHandler.get()); + + NoUnrollHintHandler.reset(new PragmaUnrollHintHandler("nounroll")); + PP.AddPragmaHandler(NoUnrollHintHandler.get()); } void Parser::resetPragmaHandlers() { @@ -290,6 +293,9 @@ void Parser::resetPragmaHandlers() { PP.RemovePragmaHandler(UnrollHintHandler.get()); UnrollHintHandler.reset(); + + PP.RemovePragmaHandler(NoUnrollHintHandler.get()); + NoUnrollHintHandler.reset(); } /// \brief Handle the annotation token produced for #pragma unused(...) @@ -1908,29 +1914,36 @@ void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP, /// #pragma unroll /// #pragma unroll unroll-hint-value /// #pragma unroll '(' unroll-hint-value ')' +/// #pragma nounroll /// /// unroll-hint-value: /// constant-expression /// -/// Loop unrolling hints are specified with '#pragma unroll'. '#pragma unroll' -/// can take a numeric argument optionally contained in parentheses. With no -/// argument the directive instructs llvm to try to unroll the loop -/// completely. A positive integer argument can be specified to indicate the -/// number of times the loop should be unrolled. To maximize compatibility with -/// other compilers the unroll count argument can be specified with or without -/// parentheses. +/// Loop unrolling hints can be specified with '#pragma unroll' or +/// '#pragma nounroll'. '#pragma unroll' can take a numeric argument optionally +/// contained in parentheses. With no argument the directive instructs llvm to +/// try to unroll the loop completely. A positive integer argument can be +/// specified to indicate the number of times the loop should be unrolled. To +/// maximize compatibility with other compilers the unroll count argument can be +/// specified with or without parentheses. Specifying, '#pragma nounroll' +/// disables unrolling of the loop. void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, Token &Tok) { - // Incoming token is "unroll" of "#pragma unroll". + // Incoming token is "unroll" for "#pragma unroll", or "nounroll" for + // "#pragma nounroll". Token PragmaName = Tok; PP.Lex(Tok); auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo; if (Tok.is(tok::eod)) { - // Unroll pragma without an argument. + // nounroll or unroll pragma without an argument. Info->PragmaName = PragmaName; Info->Option = PragmaName; Info->HasValue = false; + } else if (PragmaName.getIdentifierInfo()->getName() == "nounroll") { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "nounroll"; + return; } else { // Unroll pragma with an argument: "#pragma unroll N" or // "#pragma unroll(N)". diff --git a/lib/Sema/SemaStmtAttr.cpp b/lib/Sema/SemaStmtAttr.cpp index 603581d117..86c487b4cd 100644 --- a/lib/Sema/SemaStmtAttr.cpp +++ b/lib/Sema/SemaStmtAttr.cpp @@ -58,9 +58,11 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A, St->getStmtClass() != Stmt::ForStmtClass && St->getStmtClass() != Stmt::CXXForRangeStmtClass && St->getStmtClass() != Stmt::WhileStmtClass) { - const char *Pragma = PragmaNameLoc->Ident->getName() == "unroll" - ? "#pragma unroll" - : "#pragma clang loop"; + const char *Pragma = + llvm::StringSwitch(PragmaNameLoc->Ident->getName()) + .Case("unroll", "#pragma unroll") + .Case("nounroll", "#pragma nounroll") + .Default("#pragma clang loop"); S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop) << Pragma; return nullptr; } @@ -70,6 +72,9 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A, if (PragmaNameLoc->Ident->getName() == "unroll") { Option = ValueLoc ? LoopHintAttr::UnrollCount : LoopHintAttr::Unroll; Spelling = LoopHintAttr::Pragma_unroll; + } else if (PragmaNameLoc->Ident->getName() == "nounroll") { + Option = LoopHintAttr::Unroll; + Spelling = LoopHintAttr::Pragma_nounroll; } else { Option = llvm::StringSwitch(OptionInfo->getName()) .Case("vectorize", LoopHintAttr::Vectorize) @@ -86,6 +91,9 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A, if (Option == LoopHintAttr::Unroll && Spelling == LoopHintAttr::Pragma_unroll) { ValueInt = 1; + } else if (Option == LoopHintAttr::Unroll && + Spelling == LoopHintAttr::Pragma_nounroll) { + ValueInt = 0; } else if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) { diff --git a/test/CodeGen/pragma-unroll.cpp b/test/CodeGen/pragma-unroll.cpp index 9c3617e564..05d17331c5 100644 --- a/test/CodeGen/pragma-unroll.cpp +++ b/test/CodeGen/pragma-unroll.cpp @@ -17,7 +17,7 @@ void while_test(int *List, int Length) { void do_test(int *List, int Length) { int i = 0; -#pragma unroll 16 +#pragma nounroll do { // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_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: ![[UNROLL_FULL]] = metadata !{metadata !"llvm.loop.unroll.full"} -// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[UNROLL_16:.*]]} -// CHECK: ![[UNROLL_16]] = metadata !{metadata !"llvm.loop.unroll.count", i32 16} +// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[UNROLL_DISABLE:.*]]} +// CHECK: ![[UNROLL_DISABLE]] = metadata !{metadata !"llvm.loop.unroll.disable"} // CHECK: ![[LOOP_3]] = metadata !{metadata ![[LOOP_3]], metadata ![[UNROLL_8:.*]]} // CHECK: ![[UNROLL_8]] = metadata !{metadata !"llvm.loop.unroll.count", i32 8} // CHECK: ![[LOOP_4]] = metadata !{metadata ![[LOOP_4]], metadata ![[UNROLL_4:.*]]} diff --git a/test/PCH/pragma-loop.cpp b/test/PCH/pragma-loop.cpp index 670830d912..3eb1f60d96 100644 --- a/test/PCH/pragma-loop.cpp +++ b/test/PCH/pragma-loop.cpp @@ -15,6 +15,7 @@ // CHECK: #pragma clang loop vectorize(disable) // CHECK: #pragma unroll // CHECK: #pragma unroll (32) +// CHECK: #pragma nounroll #ifndef HEADER #define HEADER @@ -71,8 +72,16 @@ public: i++; } } -}; + inline void run6(int *List, int Length) { + int i = 0; +#pragma nounroll + while (i - 3 < Length) { + List[i] = i; + i++; + } + } +}; #else void test() { @@ -85,6 +94,7 @@ void test() { pt.run3(List, 100); pt.run4(List, 100); pt.run5(List, 100); + pt.run6(List, 100); } #endif diff --git a/test/Parser/pragma-unroll.cpp b/test/Parser/pragma-unroll.cpp index 1fa23ef467..5d28dae2d8 100644 --- a/test/Parser/pragma-unroll.cpp +++ b/test/Parser/pragma-unroll.cpp @@ -11,6 +11,11 @@ void test(int *List, int Length) { List[i] = i; } +#pragma nounroll + while (i < Length) { + List[i] = i; + } + #pragma unroll 4 while (i - 1 < Length) { List[i] = i; @@ -28,6 +33,11 @@ void test(int *List, int Length) { 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(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; #pragma unroll 4 /* 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 #pragma clang loop unroll(disable) @@ -61,6 +73,18 @@ void test(int *List, int Length) { 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 #pragma unroll while (i-14 < Length) { -- 2.40.0