[PATCH] Add support for unroll pragma

Aaron Ballman aaron.ballman at gmail.com
Wed Jul 9 07:42:48 PDT 2014


On Wed, Jul 9, 2014 at 2:14 AM, Mark Heffernan <meheff at google.com> wrote:
> Thanks for the comments.  Responses are below.  With this patch "#pragma unroll" and "#pragma clang loop unroll" syntaxes are both supported.

Great, thank you!

> I also added "#pragma nounroll" as both icc and xlc support this pragma.

Please split nounroll out into a separate patch. The idea is certainly
reasonable, but it's preferable for patches to cover a limited,
incremental change.

>
> On Wed, Jul 2, 2014 at 7:54 AM, Aaron Ballman <aaron.ballman at gmail.com> wrote:
>>
>> >    /// State of the loop optimization specified by the spelling.
>> >    let Args = [EnumArgument<"Option", "OptionType",
>> >                            ["vectorize", "vectorize_width", "interleave", "interleave_count",
>> >                             "unroll", "unroll_count"],
>> >                            ["Vectorize", "VectorizeWidth", "Interleave", "InterleaveCount",
>> >                             "Unroll", "UnrollCount"]>,
>> > -              DefaultIntArgument<"Value", 1>];
>> > +              DefaultIntArgument<"Value", 1>,
>> > +              DefaultBoolArgument<"ValueInParens", 0>];
>>
>> Hmm... we usually want the arguments to match the syntax of the
>> attribute unless the argument is really key to the semantics.
>> ValueInParens doesn't really fit that model. I would prefer for this
>> to be an AdditionalMembers entry.
>
> I tried putting ValueInParens in the AdditionalMembers, but then ran into issues with test/PCH/pragma-loop.  I just added a member variable and a method to set it, but then ValueInParens is then not set when reading in the PCH file.  It only works if ValueInParens is not in Args.  Am I missing something?

Okay, I can see why that would happen. ClangAtterEmitter.cpp generates
code for serialization and deserialization, but it doesn't have any
way of handling additional members (since those are just copy/pasted
into the attribute class definition itself).

I wonder how horrible it would be to simply canonicalize based on
compiler options when pretty printing, and skip this field entirely.
Eg) when CUDA is on, pretty print does not emit the parens. When CUDA
mode is off, it does emit the parens. Yes, this isn't *exactly* what
the user wrote, but the semantics are identical either way.

>
>>
>> >    "%select{invalid|missing}0 option%select{ %1|}0; expected vectorize, "
>> >    "vectorize_width, interleave, interleave_count, unroll, or unroll_count">;
>> >  def err_pragma_loop_missing_argument : Error<
>> > -  "missing argument to loop pragma %0">;
>> > +  "missing argument to %0 pragma">;
>>
>> The name of this diagnostic should probably change given how generic
>> the text now is.
>
>
> Done.
>
>> > +def err_pragma_loop_duplicate : Error<
>> > +  "duplicate '%0' directives">;
>> > +def err_pragma_loop_incompatible : Error<
>> > +  "incompatible directives '%0(%1)' and '%2(%3)'">;
>>
>> Why was this split into two errors?
>
> This was originally done because the duplicate form was also used when there was no parameter such as '#pragma unroll'.  I've refactored it and now they are back to being one error.
>
>> +// Parses loop or unroll pragma hint value and fills in Info.
>>
>> > +bool ParseLoopHintValue(Preprocessor &PP, Token Tok, Token &PragmaName,
>> > +                        Token &Option, PragmaLoopHintInfo &Info) {
>> > +  bool ValueInParens;
>>
>> Could simply initialize to Tok.is(tok::l_paren), then not need the
>> else clause at all.
>
> Good point.  Done.
>
>>
>> > -static void
>> > -CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
>> > +static void CheckForIncompatibleAttributes(
>> > +    Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
>>
>> Attrs should be const.
>
> Done.
>
> http://reviews.llvm.org/D4297
>
> Files:
>   docs/ReleaseNotes.rst
>   include/clang/Basic/Attr.td
>   include/clang/Basic/AttrDocs.td
>   include/clang/Basic/DiagnosticParseKinds.td
>   include/clang/Basic/DiagnosticSemaKinds.td
>   include/clang/Parse/Parser.h
>   include/clang/Sema/LoopHint.h
>   lib/Parse/ParsePragma.cpp
>   lib/Parse/ParseStmt.cpp
>   lib/Sema/SemaStmtAttr.cpp
>   test/CodeGen/pragma-unroll.cpp
>   test/PCH/pragma-loop.cpp
>   test/Parser/pragma-loop.cpp
>   test/Parser/pragma-unroll.cpp

Some comments below:

> Index: docs/ReleaseNotes.rst
> ===================================================================
> --- docs/ReleaseNotes.rst
> +++ docs/ReleaseNotes.rst
> @@ -106,6 +106,14 @@
>  as interleave and unrolling count can be manually specified.  See language
>  extensions for details.
>
> +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
> @@ -1772,15 +1772,17 @@
>    /// unroll: unroll loop if 'value != 0'.
>    /// unroll_count: unrolls loop 'value' times.
>
> -  let Spellings = [Pragma<"clang", "loop">];
> +  let Spellings = [Pragma<"clang", "loop">, Pragma<"", "unroll">,
> +                   Pragma<"", "nounroll">];
>
>    /// State of the loop optimization specified by the spelling.
>    let Args = [EnumArgument<"Option", "OptionType",
>                            ["vectorize", "vectorize_width", "interleave", "interleave_count",
>                             "unroll", "unroll_count"],
>                            ["Vectorize", "VectorizeWidth", "Interleave", "InterleaveCount",
>                             "Unroll", "UnrollCount"]>,
> -              DefaultIntArgument<"Value", 1>];
> +              DefaultIntArgument<"Value", 1>,
> +              DefaultBoolArgument<"ValueInParens", 0>];
>
>    let AdditionalMembers = [{
>    static StringRef getOptionName(int Option) {
> @@ -1795,22 +1797,59 @@
>      llvm_unreachable("Unhandled LoopHint option.");
>    }
>
> -  static StringRef getValueName(int Value) {
> -    if (Value)
> -      return "enable";
> -    return "disable";
> +  void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
> +    unsigned SpellingIndex = getSpellingListIndex();
> +    if (SpellingIndex == Pragma_nounroll ||
> +        (SpellingIndex == Pragma_unroll && option == Unroll)) {
> +      // "#pragma unroll" or "#pragma nounroll".  String "unroll" or "nounroll"
> +      // is already emitted as the pragma name and there is no argument to emit.
> +      OS << "\n";
> +      return;
> +    }
> +    if (SpellingIndex == Pragma_unroll) {
> +      // "#pragma unroll N" or "#pragma unroll (N)".
> +      assert(option == UnrollCount && "Unexpected option type");
> +      OS << getValueString() << "\n";
> +      return;
> +    }
> +    assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling");
> +    OS << getOptionName(option) << getValueString() << "\n";
>    }
>
> -  void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
> -    OS << getOptionName(option) << "(";
> +  // Return a string containing the loop hint argument including the
> +  // optional enclosing parentheses.
> +  std::string getValueString() const {
> +    std::string ValueName;
>      if (option == VectorizeWidth || option == InterleaveCount ||
>          option == UnrollCount)
> -      OS << value;
> +      ValueName = std::to_string(value);
> +    else if (value)
> +      ValueName = "enable";
>      else
> -      OS << getValueName(value);
> -    OS << ")\n";
> +      ValueName = "disable";
> +
> +    if (valueInParens)
> +      return "(" + ValueName + ")";
> +    else
> +      return ValueName;
> +  }
> +
> +  // Return a string suitable for identifying this attribute in diagnostics.
> +  std::string getDiagnosticName() const {
> +    unsigned SpellingIndex = getSpellingListIndex();
> +    if (SpellingIndex == Pragma_nounroll)
> +      return "#pragma nounroll";
> +    else if (SpellingIndex == Pragma_unroll && option == Unroll)
> +      return "#pragma unroll";
> +    else if (SpellingIndex == Pragma_unroll && option == UnrollCount) {
> +      std::string DiagnosticName = "#pragma unroll ";
> +      return DiagnosticName + getValueString();
> +    } else {
> +      std::string DiagnosticName = getOptionName(option);
> +      return DiagnosticName + getValueString();
> +    }
>    }
>    }];
>
> -  let Documentation = [LoopHintDocs];
> +  let Documentation = [LoopHintDocs, UnrollHintDocs];
>  }
> Index: include/clang/Basic/AttrDocs.td
> ===================================================================
> --- include/clang/Basic/AttrDocs.td
> +++ include/clang/Basic/AttrDocs.td
> @@ -1024,3 +1024,54 @@
>  for details.
>    }];
>  }
> +
> +def UnrollHintDocs : Documentation {
> +  let Category = DocCatStmt;
> +  let Content = [{
> +Loop unrolling optimization hints can be specified with the ``#pragma unroll``
> +and ``#pragma nounroll`` directives.  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:
> +
> +.. code-block:: c++
> +
> +  #pragma unroll
> +  for (...) {
> +    ...
> +  }
> +
> +Specifying the optional parameter, ``#pragma unroll _value_``, directs the
> +unroller to unroll the loop ``_value_`` times.  The parameter may optionally be
> +enclosed in parentheses:
> +
> +.. code-block:: c++
> +
> +  #pragma unroll 16
> +  for (...) {
> +    ...
> +  }
> +
> +  #pragma unroll(16)
> +  for (...) {
> +    ...
> +  }
> +
> +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(enable)`` and
> +``#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>`_
> +for further details including limitations of the unroll hints.  }]; }
> +
> Index: include/clang/Basic/DiagnosticParseKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticParseKinds.td
> +++ include/clang/Basic/DiagnosticParseKinds.td
> @@ -811,6 +811,9 @@
>    InGroup<IgnoredPragmas>;
>  def warn_pragma_expected_punc : Warning<
>    "expected ')' or ',' in '#pragma %0'">, InGroup<IgnoredPragmas>;
> +// - Generic errors
> +def err_pragma_missing_argument : Error<
> +  "missing argument to %0 directive">;

Since it's a generic parsing diagnostic, presumably we know what kind
of argument is missing, and we could tell the user that information.
What's more, since pragmas can have multiple arguments, this doesn't
say *which* argument is missing (there could be more than one).

I also think the word "directive" could be dropped.

>  // - #pragma options
>  def warn_pragma_options_expected_align : Warning<
>    "expected 'align' following '#pragma options' - ignored">,
> @@ -911,8 +914,6 @@
>  def err_pragma_loop_invalid_option : Error<
>    "%select{invalid|missing}0 option%select{ %1|}0; expected vectorize, "
>    "vectorize_width, interleave, interleave_count, unroll, or unroll_count">;
> -def err_pragma_loop_missing_argument : Error<
> -  "missing argument to loop pragma %0">;
>  } // end of Parse Issue category.
>
>  let CategoryName = "Modules Issue" in {
> Index: include/clang/Basic/DiagnosticSemaKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticSemaKinds.td
> +++ include/clang/Basic/DiagnosticSemaKinds.td
> @@ -547,10 +547,9 @@
>  def err_pragma_loop_invalid_keyword : Error<
>    "invalid argument; expected 'enable' or 'disable'">;
>  def err_pragma_loop_compatibility : Error<
> -  "%select{incompatible|duplicate}0 directives '%1(%2)' and '%3(%4)'">;
> +  "%select{incompatible|duplicate}0 directives '%1' and '%2'">;
>  def err_pragma_loop_precedes_nonloop : Error<
> -  "expected a for, while, or do-while loop to follow the '#pragma clang loop' "
> -  "directive">;
> +  "expected a for, while, or do-while loop to follow the '%0' directive">;

I would drop "directive" (and "the") here too.

>
>  /// Objective-C parser diagnostics
>  def err_duplicate_class_def : Error<
> Index: include/clang/Parse/Parser.h
> ===================================================================
> --- include/clang/Parse/Parser.h
> +++ include/clang/Parse/Parser.h
> @@ -163,6 +163,8 @@
>    std::unique_ptr<PragmaHandler> MSSection;
>    std::unique_ptr<PragmaHandler> OptimizeHandler;
>    std::unique_ptr<PragmaHandler> LoopHintHandler;
> +  std::unique_ptr<PragmaHandler> UnrollHintHandler;
> +  std::unique_ptr<PragmaHandler> NoUnrollHintHandler;
>
>    std::unique_ptr<CommentHandler> CommentSemaHandler;
>
> @@ -522,7 +524,7 @@
>    StmtResult HandlePragmaCaptured();
>
>    /// \brief Handle the annotation token produced for
> -  /// #pragma vectorize...
> +  /// #pragma clang loop and #pragma unroll.
>    LoopHint HandlePragmaLoopHint();
>
>    /// GetLookAheadToken - This peeks ahead N tokens and returns that token
> Index: include/clang/Sema/LoopHint.h
> ===================================================================
> --- include/clang/Sema/LoopHint.h
> +++ include/clang/Sema/LoopHint.h
> @@ -17,13 +17,26 @@
>
>  namespace clang {
>
> -/// \brief Loop hint specified by a pragma loop directive.
> +/// \brief Loop optimization hint for loop and unroll pragmas.
>  struct LoopHint {
> +  // Source range of the directive.
>    SourceRange Range;
> -  Expr *ValueExpr;
> -  IdentifierLoc *LoopLoc;
> -  IdentifierLoc *ValueLoc;
> +  // Identifier corresponding to the name of the pragma.  "loop" for
> +  // "#pragma clang loop" directives and "unroll" for "#pragma unroll"
> +  // hints.
> +  IdentifierLoc *PragmaNameLoc;
> +  // Name of the loop hint.  Examples: "unroll", "vectorize".  In the
> +  // "#pragma unroll" case, this is identical to PragmaNameLoc.
>    IdentifierLoc *OptionLoc;
> +  // Identifier for the hint argument.  If null, then the hint has no argument
> +  // such as "#pragma unroll".
> +  IdentifierLoc *ValueLoc;
> +  // If the hint argument exists and is contained in parentheses (for example,
> +  // "vectorize_width(8)") this contains the identifier for the closing
> +  // parentheses.  Value is null otherwise.
> +  IdentifierLoc *CloseParenLoc;
> +  // Expression for the hint argument if it exists, null otherwise.
> +  Expr *ValueExpr;
>  };
>
>  } // end namespace clang
> Index: lib/Parse/ParsePragma.cpp
> ===================================================================
> --- lib/Parse/ParsePragma.cpp
> +++ lib/Parse/ParsePragma.cpp
> @@ -148,6 +148,12 @@
>                      Token &FirstToken) override;
>  };
>
> +struct PragmaUnrollHintHandler : public PragmaHandler {
> +  PragmaUnrollHintHandler(const char *name) : PragmaHandler(name) {}
> +  void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
> +                    Token &FirstToken) override;
> +};
> +
>  }  // end namespace
>
>  void Parser::initializePragmaHandlers() {
> @@ -218,6 +224,12 @@
>
>    LoopHintHandler.reset(new PragmaLoopHintHandler());
>    PP.AddPragmaHandler("clang", LoopHintHandler.get());
> +
> +  UnrollHintHandler.reset(new PragmaUnrollHintHandler("unroll"));
> +  PP.AddPragmaHandler(UnrollHintHandler.get());
> +
> +  NoUnrollHintHandler.reset(new PragmaUnrollHintHandler("nounroll"));
> +  PP.AddPragmaHandler(NoUnrollHintHandler.get());
>  }
>
>  void Parser::resetPragmaHandlers() {
> @@ -278,6 +290,12 @@
>
>    PP.RemovePragmaHandler("clang", LoopHintHandler.get());
>    LoopHintHandler.reset();
> +
> +  PP.RemovePragmaHandler(UnrollHintHandler.get());
> +  UnrollHintHandler.reset();
> +
> +  PP.RemovePragmaHandler(NoUnrollHintHandler.get());
> +  NoUnrollHintHandler.reset();
>  }
>
>  /// \brief Handle the annotation token produced for #pragma unused(...)
> @@ -600,35 +618,54 @@
>  }
>
>  struct PragmaLoopHintInfo {
> -  Token Loop;
> -  Token Value;
> +  Token PragmaName;
>    Token Option;
> +  Token Value;
> +  Token CloseParen;
> +  bool HasValue;
> +  bool ValueInParens;
>  };
>
>  LoopHint Parser::HandlePragmaLoopHint() {
>    assert(Tok.is(tok::annot_pragma_loop_hint));
>    PragmaLoopHintInfo *Info =
>        static_cast<PragmaLoopHintInfo *>(Tok.getAnnotationValue());
>
>    LoopHint Hint;
> -  Hint.LoopLoc =
> -      IdentifierLoc::create(Actions.Context, Info->Loop.getLocation(),
> -                            Info->Loop.getIdentifierInfo());
> +  Hint.PragmaNameLoc =
> +      IdentifierLoc::create(Actions.Context, Info->PragmaName.getLocation(),
> +                            Info->PragmaName.getIdentifierInfo());
>    Hint.OptionLoc =
>        IdentifierLoc::create(Actions.Context, Info->Option.getLocation(),
>                              Info->Option.getIdentifierInfo());
> -  Hint.ValueLoc =
> -      IdentifierLoc::create(Actions.Context, Info->Value.getLocation(),
> -                            Info->Value.getIdentifierInfo());
> -  Hint.Range =
> -      SourceRange(Info->Option.getLocation(), Info->Value.getLocation());
> -
> -  // FIXME: We should allow non-type template parameters for the loop hint
> -  // value. See bug report #19610
> -  if (Info->Value.is(tok::numeric_constant))
> -    Hint.ValueExpr = Actions.ActOnNumericConstant(Info->Value).get();
> -  else
> +  if (Info->HasValue) {
> +    if (Info->ValueInParens) {
> +      Hint.Range = SourceRange(Info->Option.getLocation(),
> +                               Info->CloseParen.getLocation());
> +      Hint.CloseParenLoc =
> +          IdentifierLoc::create(Actions.Context, Info->CloseParen.getLocation(),
> +                                Info->CloseParen.getIdentifierInfo());
> +    } else {
> +      Hint.Range =
> +          SourceRange(Info->Option.getLocation(), Info->Value.getLocation());
> +      Hint.CloseParenLoc = nullptr;
> +    }
> +    Hint.ValueLoc =
> +        IdentifierLoc::create(Actions.Context, Info->Value.getLocation(),
> +                              Info->Value.getIdentifierInfo());
> +
> +    // FIXME: We should allow non-type template parameters for the loop hint
> +    // value. See bug report #19610
> +    if (Info->Value.is(tok::numeric_constant))
> +      Hint.ValueExpr = Actions.ActOnNumericConstant(Info->Value).get();
> +    else
> +      Hint.ValueExpr = nullptr;
> +  } else {
> +    Hint.Range = SourceRange(Info->PragmaName.getLocation());
> +    Hint.ValueLoc = nullptr;
>      Hint.ValueExpr = nullptr;
> +    Hint.CloseParenLoc = nullptr;
> +  }
>
>    return Hint;
>  }
> @@ -1630,6 +1667,44 @@
>    Actions.ActOnPragmaOptimize(IsOn, FirstToken.getLocation());
>  }
>
> +// Parses loop or unroll pragma hint value and fills in Info.

Can you use /// \brief comments instead?

> +bool ParseLoopHintValue(Preprocessor &PP, Token Tok, Token PragmaName,
> +                        Token &Option, PragmaLoopHintInfo &Info) {

Function should be made static.

> +  bool ValueInParens = Tok.is(tok::l_paren);
> +  if (ValueInParens) {
> +    PP.Lex(Tok);
> +    if (Tok.is(tok::r_paren)) {
> +      // Nothing between the parentheses.
> +      PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument)
> +          << Option.getIdentifierInfo();
> +      return true;
> +    }
> +  }
> +
> +  // FIXME: Value should be stored and parsed as a constant expression.
> +  Token Value = Tok;
> +
> +  Token CloseParen;
> +  if (ValueInParens) {
> +    PP.Lex(Tok);
> +    if (Tok.isNot(tok::r_paren)) {
> +      PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
> +      return true;
> +    }
> +    CloseParen = Tok;
> +  }
> +
> +  Info.PragmaName = PragmaName;
> +  Info.Option = Option;
> +  Info.Value = Value;
> +  Info.HasValue = true;
> +  Info.ValueInParens = ValueInParens;
> +  if (Info.ValueInParens)
> +    Info.CloseParen = CloseParen;
> +
> +  return false;
> +}
> +
>  /// \brief Handle the \#pragma clang loop directive.
>  ///  #pragma clang 'loop' loop-hints
>  ///
> @@ -1668,10 +1743,12 @@
>  /// try to unroll the loop the number of times indicated by the value.
>  /// If unroll(enable) and unroll_count are both specified only
>  /// unroll_count takes effect.
> +

Spurious newline.

>  void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP,
>                                           PragmaIntroducerKind Introducer,
>                                           Token &Tok) {
> -  Token Loop = Tok;
> +  // Incoming token is "loop" from "#pragma clang loop".
> +  Token PragmaName = Tok;
>    SmallVector<Token, 1> TokenList;
>
>    // Lex the optimization option and verify it is an identifier.
> @@ -1687,59 +1764,39 @@
>      IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();
>
>      bool OptionValid = llvm::StringSwitch<bool>(OptionInfo->getName())
> -        .Case("vectorize", true)
> -        .Case("interleave", true)
> -        .Case("unroll", true)
> -        .Case("vectorize_width", true)
> -        .Case("interleave_count", true)
> -        .Case("unroll_count", true)
> -        .Default(false);
> +                           .Case("vectorize", true)
> +                           .Case("interleave", true)
> +                           .Case("unroll", true)
> +                           .Case("vectorize_width", true)
> +                           .Case("interleave_count", true)
> +                           .Case("unroll_count", true)
> +                           .Default(false);
>      if (!OptionValid) {
>        PP.Diag(Tok.getLocation(), diag::err_pragma_loop_invalid_option)
>            << /*MissingOption=*/false << OptionInfo;
>        return;
>      }
>
> -    // Read '('
> -    PP.Lex(Tok);
> -    if (Tok.isNot(tok::l_paren)) {
> -      PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
> -      return;
> -    }
> -
> -    // FIXME: All tokens between '(' and ')' should be stored and parsed as a
> -    // constant expression.
> +    auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo;
>      PP.Lex(Tok);
> -    if (Tok.is(tok::r_paren)) {
> -      // Nothing between the parentheses.
> -      PP.Diag(Tok.getLocation(), diag::err_pragma_loop_missing_argument)
> -          << OptionInfo;
> +    if (ParseLoopHintValue(PP, Tok, PragmaName, Option, *Info))
>        return;
> -    }
> -    Token Value = Tok;
>
> -    // Read ')'
> -    PP.Lex(Tok);
> -    if (Tok.isNot(tok::r_paren)) {
> -      PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren;
> +    if (!Info->ValueInParens) {
> +      PP.Diag(Info->Value.getLocation(), diag::err_expected) << tok::l_paren;
>        return;
>      }
>
> -    // Get next optimization option.
> -    PP.Lex(Tok);
> -
> -    auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo;
> -    Info->Loop = Loop;
> -    Info->Option = Option;
> -    Info->Value = Value;
> -
> -    // Generate the vectorization hint token.
> +    // Generate the loop hint token.
>      Token LoopHintTok;
>      LoopHintTok.startToken();
>      LoopHintTok.setKind(tok::annot_pragma_loop_hint);
> -    LoopHintTok.setLocation(Loop.getLocation());
> +    LoopHintTok.setLocation(PragmaName.getLocation());
>      LoopHintTok.setAnnotationValue(static_cast<void *>(Info));
>      TokenList.push_back(LoopHintTok);
> +
> +    // Get next optimization option.
> +    PP.Lex(Tok);
>    }
>
>    if (Tok.isNot(tok::eod)) {
> @@ -1755,3 +1812,62 @@
>                        /*DisableMacroExpansion=*/false,
>                        /*OwnsTokens=*/true);
>  }
> +
> +/// \brief Handle the loop unroll optimization pragmas.
> +///  #pragma unroll
> +///  #pragma nounroll
> +///  #pragma unroll unroll-hint-value
> +///  #pragma unroll '(' unroll-hint-value ')'
> +///
> +///  unroll-hint-value:
> +///    constant-expression
> +///
> +/// Loop unrolling hints are specified with '#pragma unroll' and
> +/// '#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. '#pragma nounroll' indicates that
> +/// the loop should not be unrolled.
> +void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP,
> +                                           PragmaIntroducerKind Introducer,
> +                                           Token &Tok) {
> +  // 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)) {
> +    // nounroll pragma or unroll pragma without a argument.

Typo: "an argument."

> +    Info->PragmaName = PragmaName;
> +    Info->Option = PragmaName;
> +    Info->HasValue = false;
> +    Info->ValueInParens = 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)".
> +    if (ParseLoopHintValue(PP, Tok, PragmaName, PragmaName, *Info))
> +      return;
> +    PP.Lex(Tok);
> +
> +    if (Tok.isNot(tok::eod)) {
> +      PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
> +          << "unroll";
> +      return;
> +    }
> +  }
> +
> +  // Generate the hint token.
> +  Token *TokenArray = new Token[1];
> +  TokenArray[0].startToken();
> +  TokenArray[0].setKind(tok::annot_pragma_loop_hint);
> +  TokenArray[0].setLocation(PragmaName.getLocation());
> +  TokenArray[0].setAnnotationValue(static_cast<void *>(Info));
> +  PP.EnterTokenStream(TokenArray, 1, /*DisableMacroExpansion=*/false,
> +                      /*OwnsTokens=*/true);
> +}
> Index: lib/Parse/ParseStmt.cpp
> ===================================================================
> --- lib/Parse/ParseStmt.cpp
> +++ lib/Parse/ParseStmt.cpp
> @@ -1820,18 +1820,16 @@
>    // Create temporary attribute list.
>    ParsedAttributesWithRange TempAttrs(AttrFactory);
>
> -  // Get vectorize hints and consume annotated token.
> +  // Get loop hints and consume annotated token.
>    while (Tok.is(tok::annot_pragma_loop_hint)) {
>      LoopHint Hint = HandlePragmaLoopHint();
>      ConsumeToken();
>
> -    if (!Hint.LoopLoc || !Hint.OptionLoc || !Hint.ValueLoc)
> -      continue;
> -
> -    ArgsUnion ArgHints[] = {Hint.OptionLoc, Hint.ValueLoc,
> -                            ArgsUnion(Hint.ValueExpr)};
> -    TempAttrs.addNew(Hint.LoopLoc->Ident, Hint.Range, nullptr,
> -                     Hint.LoopLoc->Loc, ArgHints, 3, AttributeList::AS_Pragma);
> +    ArgsUnion ArgHints[] = {Hint.PragmaNameLoc, Hint.OptionLoc, Hint.ValueLoc,
> +                            Hint.CloseParenLoc, ArgsUnion(Hint.ValueExpr)};
> +    TempAttrs.addNew(Hint.PragmaNameLoc->Ident, Hint.Range, nullptr,
> +                     Hint.PragmaNameLoc->Loc, ArgHints, 5,
> +                     AttributeList::AS_Pragma);
>    }
>
>    // Get the next statement.
> Index: lib/Sema/SemaStmtAttr.cpp
> ===================================================================
> --- lib/Sema/SemaStmtAttr.cpp
> +++ lib/Sema/SemaStmtAttr.cpp
> @@ -45,35 +45,57 @@
>
>  static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
>                                  SourceRange) {
> +  IdentifierLoc *PragmaNameLoc = A.getArgAsIdent(0);
> +  IdentifierLoc *OptionLoc = A.getArgAsIdent(1);
> +  IdentifierInfo *OptionInfo = OptionLoc->Ident;
> +  IdentifierLoc *ValueLoc = A.getArgAsIdent(2);
> +  IdentifierInfo *ValueInfo = ValueLoc ? ValueLoc->Ident : nullptr;
> +  IdentifierLoc *CloseParenLoc = A.getArgAsIdent(3);
> +  Expr *ValueExpr = A.getArgAsExpr(4);
> +
> +  assert(OptionInfo && "Attribute must have valid option info.");
> +
>    if (St->getStmtClass() != Stmt::DoStmtClass &&
>        St->getStmtClass() != Stmt::ForStmtClass &&
>        St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
>        St->getStmtClass() != Stmt::WhileStmtClass) {
> -    S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop);
> +    const char *Pragma =
> +        llvm::StringSwitch<const char *>(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;
>    }
>
> -  IdentifierLoc *OptionLoc = A.getArgAsIdent(0);
> -  IdentifierInfo *OptionInfo = OptionLoc->Ident;
> -  IdentifierLoc *ValueLoc = A.getArgAsIdent(1);
> -  IdentifierInfo *ValueInfo = ValueLoc->Ident;
> -  Expr *ValueExpr = A.getArgAsExpr(2);
> -
> -  assert(OptionInfo && "Attribute must have valid option info.");
> -
> -  LoopHintAttr::OptionType Option =
> -      llvm::StringSwitch<LoopHintAttr::OptionType>(OptionInfo->getName())
> -          .Case("vectorize", LoopHintAttr::Vectorize)
> -          .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
> -          .Case("interleave", LoopHintAttr::Interleave)
> -          .Case("interleave_count", LoopHintAttr::InterleaveCount)
> -          .Case("unroll", LoopHintAttr::Unroll)
> -          .Case("unroll_count", LoopHintAttr::UnrollCount)
> -          .Default(LoopHintAttr::Vectorize);
> +  LoopHintAttr::OptionType Option;
> +  LoopHintAttr::Spelling Spelling;
> +  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<LoopHintAttr::OptionType>(OptionInfo->getName())
> +                 .Case("vectorize", LoopHintAttr::Vectorize)
> +                 .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
> +                 .Case("interleave", LoopHintAttr::Interleave)
> +                 .Case("interleave_count", LoopHintAttr::InterleaveCount)
> +                 .Case("unroll", LoopHintAttr::Unroll)
> +                 .Case("unroll_count", LoopHintAttr::UnrollCount)
> +                 .Default(LoopHintAttr::Vectorize);
> +    Spelling = LoopHintAttr::Pragma_clang_loop;
> +  }
>
>    int ValueInt;
> -  if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave ||
> -      Option == LoopHintAttr::Unroll) {
> +  if (Spelling == LoopHintAttr::Pragma_nounroll ||
> +      (Option == LoopHintAttr::Unroll &&
> +       Spelling == LoopHintAttr::Pragma_unroll)) {
> +    ValueInt = (Spelling == LoopHintAttr::Pragma_nounroll ? 0 : 1);
> +  } else if (Option == LoopHintAttr::Vectorize ||
> +             Option == LoopHintAttr::Interleave ||
> +             Option == LoopHintAttr::Unroll) {
>      if (!ValueInfo) {
>        S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword);
>        return nullptr;
> @@ -100,31 +122,23 @@
>    } else
>      llvm_unreachable("Unknown loop hint option");
>
> -  return LoopHintAttr::CreateImplicit(S.Context, Option, ValueInt,
> -                                      A.getRange());
> +  return LoopHintAttr::CreateImplicit(
> +      S.Context, Spelling, Option, ValueInt,
> +      /*ValueInParens=*/CloseParenLoc != nullptr, A.getRange());
>  }
>
> -static void
> -CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
> +static void CheckForIncompatibleAttributes(
> +    Sema &S, const SmallVectorImpl<const Attr *> &Attrs) {
>    // There are 3 categories of loop hints: vectorize, interleave, and
>    // unroll. Each comes in two variants: an enable/disable form and a
>    // form which takes a numeric argument. For example:
>    // unroll(enable|disable) and unroll_count(N). The following array
>    // accumulate the hints encountered while iterating through the
>    // attributes to check for compatibility.
>    struct {
> -    int EnableOptionId;
> -    int NumericOptionId;
> -    bool EnabledIsSet;
> -    bool ValueIsSet;
> -    bool Enabled;
> -    int Value;
> -  } Options[] = {{LoopHintAttr::Vectorize, LoopHintAttr::VectorizeWidth, false,
> -                  false, false, 0},
> -                 {LoopHintAttr::Interleave, LoopHintAttr::InterleaveCount,
> -                  false, false, false, 0},
> -                 {LoopHintAttr::Unroll, LoopHintAttr::UnrollCount, false, false,
> -                  false, 0}};
> +    const LoopHintAttr *EnableAttr;
> +    const LoopHintAttr *NumericAttr;
> +  } HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}};
>
>    for (const auto *I : Attrs) {
>      const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
> @@ -134,8 +148,6 @@
>        continue;
>
>      int Option = LH->getOption();
> -    int ValueInt = LH->getValue();
> -
>      int Category;
>      switch (Option) {
>      case LoopHintAttr::Vectorize:
> @@ -152,44 +164,39 @@
>        break;
>      };
>
> -    auto &CategoryState = Options[Category];
> -    SourceLocation ValueLoc = LH->getRange().getEnd();
> +    auto &CategoryState = HintAttrs[Category];
> +    SourceLocation OptionLoc = LH->getRange().getBegin();
>      if (Option == LoopHintAttr::Vectorize ||
>          Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) {
>        // Enable|disable hint.  For example, vectorize(enable).
> -      if (CategoryState.EnabledIsSet) {
> +      if (CategoryState.EnableAttr) {
>          // Cannot specify enable/disable state twice.
> -        S.Diag(ValueLoc, diag::err_pragma_loop_compatibility)
> -            << /*Duplicate=*/true << LoopHintAttr::getOptionName(Option)
> -            << LoopHintAttr::getValueName(CategoryState.Enabled)
> -            << LoopHintAttr::getOptionName(Option)
> -            << LoopHintAttr::getValueName(ValueInt);
> +        S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
> +            << /*Duplicate=*/true
> +            << CategoryState.EnableAttr->getDiagnosticName()
> +            << LH->getDiagnosticName();
>        }
> -      CategoryState.EnabledIsSet = true;
> -      CategoryState.Enabled = ValueInt;
> +      CategoryState.EnableAttr = LH;
>      } else {
> -      // Numeric hint.  For example, unroll_count(8).
> -      if (CategoryState.ValueIsSet) {
> +      // Numeric hint.  For example, vectorize_width(8).
> +      if (CategoryState.NumericAttr) {
>          // Cannot specify numeric hint twice.
> -        S.Diag(ValueLoc, diag::err_pragma_loop_compatibility)
> -            << /*Duplicate=*/true << LoopHintAttr::getOptionName(Option)
> -            << CategoryState.Value << LoopHintAttr::getOptionName(Option)
> -            << ValueInt;
> +        S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
> +            << /*Duplicate=*/true
> +            << CategoryState.NumericAttr->getDiagnosticName()
> +            << LH->getDiagnosticName();
>        }
> -      CategoryState.ValueIsSet = true;
> -      CategoryState.Value = ValueInt;
> +      CategoryState.NumericAttr = LH;
>      }
>
> -    if (CategoryState.EnabledIsSet && !CategoryState.Enabled &&
> -        CategoryState.ValueIsSet) {
> +    if (CategoryState.EnableAttr && !CategoryState.EnableAttr->getValue() &&
> +        CategoryState.NumericAttr) {
>        // Disable hints are not compatible with numeric hints of the
>        // same category.
> -      S.Diag(ValueLoc, diag::err_pragma_loop_compatibility)
> +      S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
>            << /*Duplicate=*/false
> -          << LoopHintAttr::getOptionName(CategoryState.EnableOptionId)
> -          << LoopHintAttr::getValueName(CategoryState.Enabled)
> -          << LoopHintAttr::getOptionName(CategoryState.NumericOptionId)
> -          << CategoryState.Value;
> +          << CategoryState.EnableAttr->getDiagnosticName()
> +          << CategoryState.NumericAttr->getDiagnosticName();
>      }
>    }
>  }
> Index: test/CodeGen/pragma-unroll.cpp
> ===================================================================
> --- test/CodeGen/pragma-unroll.cpp
> +++ test/CodeGen/pragma-unroll.cpp
> @@ -0,0 +1,99 @@
> +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s
> +
> +// Verify while loop is recognized after unroll pragma.
> +void while_test(int *List, int Length) {
> +  // CHECK: define {{.*}} @_Z10while_test
> +  int i = 0;
> +
> +#pragma unroll
> +  while (i < Length) {
> +    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_1:.*]]
> +    List[i] = i * 2;
> +    i++;
> +  }
> +}
> +
> +// Verify do loop is recognized after multi-option pragma clang loop directive.
> +void do_test(int *List, int Length) {
> +  int i = 0;
> +
> +#pragma nounroll
> +  do {
> +    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_2:.*]]
> +    List[i] = i * 2;
> +    i++;
> +  } while (i < Length);
> +}
> +
> +// Verify for loop is recognized after unroll pragma.
> +void for_test(int *List, int Length) {
> +#pragma unroll 8
> +  for (int i = 0; i < Length; i++) {
> +    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_3:.*]]
> +    List[i] = i * 2;
> +  }
> +}
> +
> +// Verify c++11 for range loop is recognized after unroll pragma.
> +void for_range_test() {
> +  double List[100];
> +
> +#pragma unroll(4)
> +  for (int i : List) {
> +    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_4:.*]]
> +    List[i] = i;
> +  }
> +}
> +
> +#define UNROLLCOUNT 8
> +
> +// Verify defines are correctly resolved in unroll pragmas.
> +void for_define_test(int *List, int Length, int Value) {
> +#pragma unroll(UNROLLCOUNT)
> +  for (int i = 0; i < Length; i++) {
> +    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_5:.*]]
> +    List[i] = i * Value;
> +  }
> +}
> +
> +// Verify metadata is generated when template is used.
> +template <typename A>
> +void for_template_test(A *List, int Length, A Value) {
> +#pragma unroll 8
> +  for (int i = 0; i < Length; i++) {
> +    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_6:.*]]
> +    List[i] = i * Value;
> +  }
> +}
> +
> +// Verify define is resolved correctly when template is used.
> +template <typename A>
> +void for_template_define_test(A *List, int Length, A Value) {
> +#pragma unroll(UNROLLCOUNT)
> +  for (int i = 0; i < Length; i++) {
> +    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_7:.*]]
> +    List[i] = i * Value;
> +  }
> +}
> +
> +#undef UNROLLCOUNT
> +
> +// Use templates defined above. Test verifies metadata is generated correctly.
> +void template_test(double *List, int Length) {
> +  double Value = 10;
> +
> +  for_template_test<double>(List, Length, Value);
> +  for_template_define_test<double>(List, Length, Value);
> +}
> +
> +// CHECK: ![[LOOP_1]] = metadata !{metadata ![[LOOP_1]], metadata ![[UNROLLENABLE_1:.*]]}
> +// CHECK: ![[UNROLLENABLE_1]] = metadata !{metadata !"llvm.loop.unroll.enable", i1 true}
> +// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[UNROLLENABLE_0:.*]]}
> +// CHECK: ![[UNROLLENABLE_0]] = metadata !{metadata !"llvm.loop.unroll.enable", i1 false}
> +// 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:.*]]}
> +// CHECK: ![[UNROLL_4]] = metadata !{metadata !"llvm.loop.unroll.count", i32 4}
> +// CHECK: ![[LOOP_5]] = metadata !{metadata ![[LOOP_5]], metadata ![[UNROLL_8:.*]]}
> +// CHECK: ![[LOOP_6]] = metadata !{metadata ![[LOOP_6]], metadata ![[UNROLL_8:.*]]}
> +// CHECK: ![[LOOP_7]] = metadata !{metadata ![[LOOP_7]], metadata ![[UNROLL_8:.*]]}
> Index: test/PCH/pragma-loop.cpp
> ===================================================================
> --- test/PCH/pragma-loop.cpp
> +++ test/PCH/pragma-loop.cpp
> @@ -13,6 +13,10 @@
>  // CHECK: #pragma clang loop unroll(enable)
>  // CHECK: #pragma clang loop interleave(enable)
>  // CHECK: #pragma clang loop vectorize(disable)
> +// CHECK: #pragma unroll
> +// CHECK: #pragma nounroll
> +// CHECK: #pragma unroll 32
> +// CHECK: #pragma unroll (4)
>
>  #ifndef HEADER
>  #define HEADER
> @@ -51,6 +55,42 @@
>        i++;
>      }
>    }
> +
> +  inline void run4(int *List, int Length) {
> +    int i = 0;
> +#pragma unroll
> +    while (i - 3 < Length) {
> +      List[i] = i;
> +      i++;
> +    }
> +  }
> +
> +  inline void run5(int *List, int Length) {
> +    int i = 0;
> +#pragma nounroll
> +    while (i - 3 < Length) {
> +      List[i] = i;
> +      i++;
> +    }
> +  }
> +
> +  inline void run6(int *List, int Length) {
> +    int i = 0;
> +#pragma unroll 32
> +    while (i - 3 < Length) {
> +      List[i] = i;
> +      i++;
> +    }
> +  }
> +
> +  inline void run7(int *List, int Length) {
> +    int i = 0;
> +#pragma unroll(4)
> +    while (i - 3 < Length) {
> +      List[i] = i;
> +      i++;
> +    }
> +  }
>  };
>
>  #else
> @@ -63,6 +103,10 @@
>    pt.run1(List, 100);
>    pt.run2(List, 100);
>    pt.run3(List, 100);
> +  pt.run4(List, 100);
> +  pt.run5(List, 100);
> +  pt.run6(List, 100);
> +  pt.run7(List, 100);
>  }
>
>  #endif
> Index: test/Parser/pragma-loop.cpp
> ===================================================================
> --- test/Parser/pragma-loop.cpp
> +++ test/Parser/pragma-loop.cpp
> @@ -55,9 +55,9 @@
>  /* expected-error {{expected ')'}} */ #pragma clang loop interleave_count(4
>  /* expected-error {{expected ')'}} */ #pragma clang loop unroll_count(4
>
> -/* expected-error {{missing argument to loop pragma 'vectorize'}} */ #pragma clang loop vectorize()
> -/* expected-error {{missing argument to loop pragma 'interleave_count'}} */ #pragma clang loop interleave_count()
> -/* expected-error {{missing argument to loop pragma 'unroll'}} */ #pragma clang loop unroll()
> +/* expected-error {{missing argument to 'vectorize' directive}} */ #pragma clang loop vectorize()
> +/* expected-error {{missing argument to 'interleave_count' directive}} */ #pragma clang loop interleave_count()
> +/* expected-error {{missing argument to 'unroll' directive}} */ #pragma unroll()
>
>  /* expected-error {{missing option}} */ #pragma clang loop
>  /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop badkeyword
> Index: test/Parser/pragma-unroll.cpp
> ===================================================================
> --- test/Parser/pragma-unroll.cpp
> +++ test/Parser/pragma-unroll.cpp
> @@ -0,0 +1,125 @@
> +// RUN: %clang_cc1 -std=c++11 -verify %s
> +
> +// Note that this puts the expected lines before the directives to work around
> +// limitations in the -verify mode.
> +
> +void test(int *List, int Length) {
> +  int i = 0;
> +
> +#pragma unroll
> +  while (i + 1 < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma nounroll
> +  while (i < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma unroll 4
> +  while (i - 1 < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma unroll(8)
> +  while (i - 2 < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma unroll
> +#pragma unroll(8)
> +  while (i - 3 < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma clang loop unroll(enable)
> +#pragma unroll(8)
> +  while (i - 4 < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma unroll
> +#pragma clang loop unroll_count(4)
> +  while (i - 5 < Length) {
> +    List[i] = i;
> +  }
> +
> +/* expected-error {{expected ')'}} */ #pragma unroll(4
> +/* expected-error {{missing argument to 'unroll' directive}} */ #pragma unroll()
> +/* expected-warning {{extra tokens at end of '#pragma unroll'}} */ #pragma unroll 1 2
> +  while (i-6 < 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)
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll 0
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(3000000000)
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll 3000000000
> +  while (i-8 < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma unroll
> +/* expected-error {{expected a for, while, or do-while loop to follow the '#pragma unroll' directive}} */ int j = Length;
> +#pragma unroll 4
> +/* expected-error {{expected a for, while, or do-while loop to follow the '#pragma unroll' directive}} */ int k = Length;
> +#pragma nounroll
> +/* expected-error {{expected a for, while, or do-while loop to follow the '#pragma nounroll' directive}} */ int l = Length;
> +
> +/* expected-error {{incompatible directives '#pragma nounroll' and '#pragma unroll 4'}} */ #pragma unroll 4
> +#pragma nounroll
> +  while (i-9 < Length) {
> +    List[i] = i;
> +  }
> +
> +/* expected-error {{incompatible directives 'unroll(disable)' and '#pragma unroll 4'}} */ #pragma unroll 4
> +#pragma clang loop unroll(disable)
> +  while (i-10 < Length) {
> +    List[i] = i;
> +  }
> +
> +/* expected-error {{incompatible directives '#pragma nounroll' and 'unroll_count(4)'}} */ #pragma clang loop unroll_count(4)
> +#pragma nounroll
> +  while (i-11 < Length) {
> +    List[i] = i;
> +  }
> +
> +/* expected-error {{duplicate directives '#pragma nounroll' and '#pragma nounroll'}} */ #pragma nounroll
> +#pragma nounroll
> +  while (i-12 < Length) {
> +    List[i] = i;
> +  }
> +
> +/* expected-error {{duplicate directives '#pragma nounroll' and '#pragma unroll'}} */ #pragma unroll
> +#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) {
> +    List[i] = i;
> +  }
> +
> +/* expected-error {{duplicate directives 'unroll(enable)' and '#pragma unroll'}} */ #pragma unroll
> +#pragma clang loop unroll(enable)
> +  while (i-15 < Length) {
> +    List[i] = i;
> +  }
> +
> +/* expected-error {{duplicate directives '#pragma unroll (4)' and '#pragma unroll 4'}} */ #pragma unroll 4
> +#pragma unroll (4)
> +  while (i-16 < Length) {
> +    List[i] = i;
> +  }
> +
> +#pragma unroll
> +/* expected-error {{expected statement}} */ }
>

~Aaron




More information about the cfe-commits mailing list