Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -111,12 +111,13 @@ 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 --------------------------- Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1791,7 +1791,8 @@ /// 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 @@ 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 @@ 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); Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1051,7 +1051,7 @@ def LoopHintDocs : Documentation { let Category = DocCatStmt; - let Heading = "#pragma clang loop, #pragma unroll"; + let Heading = "#pragma clang loop, #pragma unroll, #pragma nounroll"; let Content = [{ The ``#pragma clang loop`` directive allows loop optimization hints to be specified for the subsequent loop. The directive allows vectorization, @@ -1066,9 +1066,9 @@ def UnrollHintDocs : Documentation { let Category = DocCatStmt; 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: @@ -1096,9 +1096,20 @@ ... } +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. }]; Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -164,6 +164,7 @@ std::unique_ptr OptimizeHandler; std::unique_ptr LoopHintHandler; std::unique_ptr UnrollHintHandler; + std::unique_ptr NoUnrollHintHandler; std::unique_ptr CommentSemaHandler; Index: include/clang/Sema/LoopHint.h =================================================================== --- include/clang/Sema/LoopHint.h +++ include/clang/Sema/LoopHint.h @@ -26,7 +26,8 @@ // 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". Index: lib/Parse/ParsePragma.cpp =================================================================== --- lib/Parse/ParsePragma.cpp +++ lib/Parse/ParsePragma.cpp @@ -227,6 +227,9 @@ 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 @@ 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 @@ /// #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)". Index: lib/Sema/SemaStmtAttr.cpp =================================================================== --- lib/Sema/SemaStmtAttr.cpp +++ lib/Sema/SemaStmtAttr.cpp @@ -58,9 +58,11 @@ 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 @@ 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 @@ 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) { Index: test/CodeGen/pragma-unroll.cpp =================================================================== --- test/CodeGen/pragma-unroll.cpp +++ test/CodeGen/pragma-unroll.cpp @@ -17,7 +17,7 @@ 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 @@ // 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:.*]]} Index: test/PCH/pragma-loop.cpp =================================================================== --- test/PCH/pragma-loop.cpp +++ 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 @@ 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 @@ pt.run3(List, 100); pt.run4(List, 100); pt.run5(List, 100); + pt.run6(List, 100); } #endif Index: test/Parser/pragma-unroll.cpp =================================================================== --- test/Parser/pragma-unroll.cpp +++ test/Parser/pragma-unroll.cpp @@ -11,6 +11,11 @@ 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 @@ 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 @@ /* 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 @@ 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) {