[PATCH] Add support for unroll pragma

Aaron Ballman aaron.ballman at gmail.com
Wed Jul 2 07:54:21 PDT 2014


On Tue, Jul 1, 2014 at 6:59 PM, Mark Heffernan <meheff at google.com> wrote:
> Thanks all for your comments.  This patch reduces much of the implementation duplication from the previous patch.  Unroll and loop optimization hints now both share the same attribute.
>
> Also, the loop unroll pragma now replaces the "#pragma clang loop unroll/unroll_count" directives which have been removed.  The following unroll pragma forms are supported:
>
> #pragma unroll
> #pragma unroll N
> #pragma unroll(N)

I may have led you slightly astray on this point. I believe the
consensus wound up being that having two different syntaxes for the
same thing was okay, and so #pragma loop unroll[_count] should remain.
Richard, Chandler, do I have that correct?

Some comments:

> Index: docs/LanguageExtensions.rst
> ===================================================================
> --- docs/LanguageExtensions.rst
> +++ docs/LanguageExtensions.rst
> @@ -1767,15 +1767,10 @@
>
>  Extensions for loop hint optimizations
>  ======================================
> -
>  The ``#pragma clang loop`` directive is used to specify hints for optimizing the
>  subsequent for, while, do-while, or c++11 range-based for loop. The directive
> -provides options for vectorization, interleaving, and unrolling. Loop hints can
> -be specified before any loop and will be ignored if the optimization is not safe
> -to apply.
> -
> -Vectorization and Interleaving
> -------------------------------
> +provides options for vectorization and interleaving. Loop hints can be specified
> +before any loop and will be ignored if the optimization is not safe to apply.
>
>  A vectorized loop performs multiple iterations of the original loop
>  in parallel using vector instructions. The instruction set of the target
> @@ -1818,43 +1813,6 @@
>  Specifying a width/count of 1 disables the optimization, and is equivalent to
>  ``vectorize(disable)`` or ``interleave(disable)``.
>
> -Loop Unrolling
> ---------------
> -
> -Unrolling a loop reduces the loop control overhead and exposes more
> -opportunities for ILP. Loops can be fully or partially unrolled. Full unrolling
> -eliminates the loop and replaces it with an enumerated sequence of loop
> -iterations. Full unrolling is only possible if the loop trip count is known at
> -compile time. Partial unrolling replicates the loop body within the loop and
> -reduces the trip count.
> -
> -If ``unroll(enable)`` is specified the unroller will attempt to fully unroll the
> -loop if the trip count is known at compile time. If the loop count is not known
> -or the fully unrolled code size is greater than the limit specified by the
> -`-pragma-unroll-threshold` command line option the loop will be partially
> -unrolled subject to the same limit.
> -
> -.. code-block:: c++
> -
> -  #pragma clang loop unroll(enable)
> -  for(...) {
> -    ...
> -  }
> -
> -The unroll count can be specified explicitly with ``unroll_count(_value_)`` where
> -_value_ is a positive integer. If this value is greater than the trip count the
> -loop will be fully unrolled. Otherwise the loop is partially unrolled subject
> -to the `-pragma-unroll-threshold` limit.
> -
> -.. code-block:: c++
> -
> -  #pragma clang loop unroll_count(8)
> -  for(...) {
> -    ...
> -  }
> -
> -Unrolling of a loop can be prevented by specifying ``unroll(disable)``.
> -
>  Additional Information
>  ----------------------
>
> Index: docs/ReleaseNotes.rst
> ===================================================================
> --- docs/ReleaseNotes.rst
> +++ docs/ReleaseNotes.rst
> @@ -101,10 +101,16 @@
>  -----------------------
>
>  Loop optimization hints can be specified using the new `#pragma clang loop`
> -directive just prior to the desired loop. The directive allows vectorization,
> -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.
> +directive just prior to the desired loop. The directive allows vectorization and
> +interleaving to be enabled or disabled. Vector width and interleave 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 N`.  The unroll count parameter can be optionally
> +enclosed in parentheses.
>
>  C Language Changes in Clang
>  ---------------------------
> Index: include/clang/Basic/Attr.td
> ===================================================================
> --- include/clang/Basic/Attr.td
> +++ include/clang/Basic/Attr.td
> @@ -1772,25 +1772,28 @@
>    /// unroll: unroll loop if 'value != 0'.
>    /// unroll_count: unrolls loop 'value' times.
>
> -  let Spellings = [Pragma<"clang", "loop">];
> +  let Spellings = [Pragma<"clang", "loop">, Pragma<"", "unroll">];
>
>    /// 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.

>
>    let AdditionalMembers = [{
>    static StringRef getOptionName(int Option) {
>      switch(Option) {
>      case Vectorize: return "vectorize";
>      case VectorizeWidth: return "vectorize_width";
>      case Interleave: return "interleave";
>      case InterleaveCount: return "interleave_count";
> +    // Unroll and UnrollCount attributes are added for '#pragma unroll'
> +    // directives which share the option name 'unroll'.
>      case Unroll: return "unroll";
> -    case UnrollCount: return "unroll_count";
> +    case UnrollCount: return "unroll";
>      }
>      llvm_unreachable("Unhandled LoopHint option.");
>    }
> @@ -1802,15 +1805,29 @@
>    }
>
>    void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
> +    if (option == Unroll) {
> +      // "#pragma unroll".  String "unroll" is already emitted as the
> +      // pragma name.
> +      OS << "\n";
> +      return;
> +    }
> +    if (option == UnrollCount) {
> +      // "#pragma unroll N" or "#pragma unroll(N)".
> +      if (ValueInParens)
> +        OS << "(" << value << ")";
> +      else
> +        OS << value;
> +      OS << "\n";
> +      return;
> +    }
>      OS << getOptionName(option) << "(";
> -    if (option == VectorizeWidth || option == InterleaveCount ||
> -        option == UnrollCount)
> +    if (option == VectorizeWidth || option == InterleaveCount)
>        OS << value;
>      else
>        OS << getValueName(value);
>      OS << ")\n";
>    }
>    }];
>
> -  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,45 @@
>  for details.
>    }];
>  }
> +
> +def UnrollHintDocs : Documentation {
> +  let Category = DocCatStmt;
> +  let Content = [{
> +Loop unrolling optimization hints can be specified with the ``#pragma unroll``
> +directive.  The pragma is placed immediately before a for, while, do-while, or
> +c++11 range-based for loop.  The pragma takes an optional parameter which must
> +be a positive integer.
> +
> +.. code-block:: c++
> +
> +  #pragma unroll
> +  for (...) {
> +    ...
> +  }
> +
> +If ``#pragma unroll`` is specified without a parameter the loop unroller will
> +attempt to fully unroll the loop if the trip count is known at compile time.  If
> +the loop count is not known or the fully unrolled code size is greater than the
> +limit specified by the ``-pragma-unroll-threshold`` command-line option the loop
> +will be partially unrolled subject to the same limit.
> +
> +.. code-block:: c++
> +
> +  #pragma unroll 16
> +  for (...) {
> +    ...
> +  }
> +
> +  #pragma unroll(16)
> +  for (...) {
> +    ...
> +  }
> +
> +Specifying ``#pragma unroll _value_`` where _value_ is a positive integer directs
> +the unroller to unroll the loop _value_ times.  The value may be optionally
> +enclosed in parentheses such as ``#pragma unroll(_value_)``.  If this value is
> +greater than the trip count the loop will be fully unrolled. Otherwise the loop
> +is partially unrolled subject to the ``-pragma-unroll-threshold`` limit.
> +  }];
> +}
> +
> Index: include/clang/Basic/DiagnosticParseKinds.td
> ===================================================================
> --- include/clang/Basic/DiagnosticParseKinds.td
> +++ include/clang/Basic/DiagnosticParseKinds.td
> @@ -912,7 +912,7 @@
>    "%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.

>  } // 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
> @@ -546,11 +546,12 @@
>    "invalid argument; expected a positive integer value">;
>  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)'">;
> +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?

>  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' pragma">;
>
>  /// 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,7 @@
>    std::unique_ptr<PragmaHandler> MSSection;
>    std::unique_ptr<PragmaHandler> OptimizeHandler;
>    std::unique_ptr<PragmaHandler> LoopHintHandler;
> +  std::unique_ptr<PragmaHandler> UnrollHintHandler;
>
>    std::unique_ptr<CommentHandler> CommentSemaHandler;
>
> @@ -522,7 +523,7 @@
>    StmtResult HandlePragmaCaptured();
>
>    /// \brief Handle the annotation token produced for
> -  /// #pragma vectorize...
> +  /// #pragma clang loop...
>    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 unroll
> +  // case, this is identical to PragmaNameLoc.
>    IdentifierLoc *OptionLoc;
> +  // Identifier for the hint argument.  If null, then the hint has no argument
> +  // (for example, "#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() : PragmaHandler("unroll") {}
> +  void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
> +                    Token &FirstToken) override;
> +};
> +
>  }  // end namespace
>
>  void Parser::initializePragmaHandlers() {
> @@ -218,6 +224,9 @@
>
>    LoopHintHandler.reset(new PragmaLoopHintHandler());
>    PP.AddPragmaHandler("clang", LoopHintHandler.get());
> +
> +  UnrollHintHandler.reset(new PragmaUnrollHintHandler());
> +  PP.AddPragmaHandler(UnrollHintHandler.get());
>  }
>
>  void Parser::resetPragmaHandlers() {
> @@ -278,6 +287,9 @@
>
>    PP.RemovePragmaHandler("clang", LoopHintHandler.get());
>    LoopHintHandler.reset();
> +
> +  PP.RemovePragmaHandler(UnrollHintHandler.get());
> +  UnrollHintHandler.reset();
>  }
>
>  /// \brief Handle the annotation token produced for #pragma unused(...)
> @@ -600,35 +612,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 +1661,47 @@
>    Actions.ActOnPragmaOptimize(IsOn, FirstToken.getLocation());
>  }
>
> +// 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.

> +  if (Tok.is(tok::l_paren)) {
> +    ValueInParens = true;
> +    PP.Lex(Tok);
> +    if (Tok.is(tok::r_paren)) {
> +      // Nothing between the parentheses.
> +      PP.Diag(Tok.getLocation(), diag::err_pragma_loop_missing_argument)
> +          << PragmaName.getIdentifierInfo();
> +      return true;
> +    }
> +  } else {
> +    ValueInParens = false;
> +  }
> +
> +  // 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
>  ///
> @@ -1639,10 +1711,8 @@
>  ///  loop-hint:
>  ///    'vectorize' '(' loop-hint-keyword ')'
>  ///    'interleave' '(' loop-hint-keyword ')'
> -///    'unroll' '(' loop-hint-keyword ')'
>  ///    'vectorize_width' '(' loop-hint-value ')'
>  ///    'interleave_count' '(' loop-hint-value ')'
> -///    'unroll_count' '(' loop-hint-value ')'
>  ///
>  ///  loop-hint-keyword:
>  ///    'enable'
> @@ -1660,18 +1730,11 @@
>  /// value of 1 effectively disables vectorization/interleaving, even if it is
>  /// possible and profitable, and 0 is invalid. The loop vectorizer currently
>  /// only works on inner loops.
> -///
> -/// The unroll and unroll_count directives control the concatenation
> -/// unroller. Specifying unroll(enable) instructs llvm to try to
> -/// unroll the loop completely, and unroll(disable) disables unrolling
> -/// for the loop. Specifying unroll_count(_value_) instructs llvm to
> -/// 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.
>  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 +1750,37 @@
>      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("vectorize_width", true)
> +                           .Case("interleave_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 +1796,54 @@
>                        /*DisableMacroExpansion=*/false,
>                        /*OwnsTokens=*/true);
>  }
> +
> +/// \brief Handle the loop unroll optimization pragmas.
> +///  #pragma unroll
> +///  #pragma unroll unroll-hint-value
> +///  #pragma unroll '(' unroll-hint-value ')'
> +///
> +///  unroll-hint-value:
> +///    constant-expression
> +///
> +/// Loop unrolling hints are specified with '#pragma unroll' and can include 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.
> +void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP,
> +                                           PragmaIntroducerKind Introducer,
> +                                           Token &Tok) {
> +  // Incoming token is "unroll" from "#pragma unroll".
> +  Token PragmaName = Tok;
> +  PP.Lex(Tok);
> +  auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo;
> +  if (Tok.is(tok::eod)) {
> +    // Bare unroll pragma: "#pragma unroll".
> +    Info->PragmaName = PragmaName;
> +    Info->Option = PragmaName;
> +    Info->HasValue = false;
> +    Info->ValueInParens = false;
> +  } 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
> @@ -1804,18 +1804,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,45 @@
>
>  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);
> +    S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop)
> +        << PragmaNameLoc->Ident->getName();
>      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 (OptionInfo->getName() == "unroll") {
> +    Option = ValueLoc ? LoopHintAttr::UnrollCount : LoopHintAttr::Unroll;
> +    Spelling = LoopHintAttr::Pragma_unroll;
> +  } 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)
> +                 .Default(LoopHintAttr::Vectorize);
> +    Spelling = LoopHintAttr::Pragma_clang_loop;
> +  }
>
>    int ValueInt;
> -  if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave ||
> -      Option == LoopHintAttr::Unroll) {
> +  if (Option == LoopHintAttr::Unroll) {
> +    ValueInt = 1;
> +  } else if (Option == LoopHintAttr::Vectorize ||
> +             Option == LoopHintAttr::Interleave) {
>      if (!ValueInfo) {
>        S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword);
>        return nullptr;
> @@ -100,12 +110,13 @@
>    } 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, SmallVectorImpl<const Attr *> &Attrs) {

Attrs should be const.

>    // 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:
> @@ -159,22 +170,17 @@
>        // Enable|disable hint.  For example, vectorize(enable).
>        if (CategoryState.EnabledIsSet) {
>          // 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(ValueLoc, diag::err_pragma_loop_duplicate)
> +            << LoopHintAttr::getOptionName(Option);
>        }
>        CategoryState.EnabledIsSet = true;
>        CategoryState.Enabled = ValueInt;
>      } else {
> -      // Numeric hint.  For example, unroll_count(8).
> +      // Numeric hint.  For example, vectorize_width(8).
>        if (CategoryState.ValueIsSet) {
>          // 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(ValueLoc, diag::err_pragma_loop_duplicate)
> +            << LoopHintAttr::getOptionName(Option);
>        }
>        CategoryState.ValueIsSet = true;
>        CategoryState.Value = ValueInt;
> @@ -184,8 +190,7 @@
>          CategoryState.ValueIsSet) {
>        // Disable hints are not compatible with numeric hints of the
>        // same category.
> -      S.Diag(ValueLoc, diag::err_pragma_loop_compatibility)
> -          << /*Duplicate=*/false
> +      S.Diag(ValueLoc, diag::err_pragma_loop_incompatible)
>            << LoopHintAttr::getOptionName(CategoryState.EnableOptionId)
>            << LoopHintAttr::getValueName(CategoryState.Enabled)
>            << LoopHintAttr::getOptionName(CategoryState.NumericOptionId)
> Index: test/CodeGen/pragma-loop.cpp
> ===================================================================
> --- test/CodeGen/pragma-loop.cpp
> +++ test/CodeGen/pragma-loop.cpp
> @@ -8,7 +8,7 @@
>  #pragma clang loop vectorize(enable)
>  #pragma clang loop interleave_count(4)
>  #pragma clang loop vectorize_width(4)
> -#pragma clang loop unroll(enable)
> +#pragma unroll
>    while (i < Length) {
>      // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_1:.*]]
>      List[i] = i * 2;
> @@ -20,7 +20,8 @@
>  void do_test(int *List, int Length) {
>    int i = 0;
>
> -#pragma clang loop vectorize_width(8) interleave_count(4) unroll(disable)
> +#pragma clang loop vectorize_width(8) interleave_count(4)
> +#pragma unroll 4
>    do {
>      // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_2:.*]]
>      List[i] = i * 2;
> @@ -32,7 +33,7 @@
>  void for_test(int *List, int Length) {
>  #pragma clang loop interleave(enable)
>  #pragma clang loop interleave_count(4)
> -#pragma clang loop unroll_count(8)
> +#pragma unroll(8)
>    for (int i = 0; i < Length; i++) {
>      // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_3:.*]]
>      List[i] = i * 2;
> @@ -53,7 +54,7 @@
>
>  // Verify disable pragma clang loop directive generates correct metadata
>  void disable_test(int *List, int Length) {
> -#pragma clang loop vectorize(disable) unroll(disable)
> +#pragma clang loop vectorize(disable)
>    for (int i = 0; i < Length; i++) {
>      // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_5:.*]]
>      List[i] = i * 2;
> @@ -67,7 +68,7 @@
>  // Verify defines are correctly resolved in pragma clang loop directive
>  void for_define_test(int *List, int Length, int Value) {
>  #pragma clang loop vectorize_width(VECWIDTH) interleave_count(INTCOUNT)
> -#pragma clang loop unroll_count(UNROLLCOUNT)
> +#pragma unroll(UNROLLCOUNT)
>    for (int i = 0; i < Length; i++) {
>      // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_6:.*]]
>      List[i] = i * Value;
> @@ -78,7 +79,8 @@
>  template <typename A>
>  void for_template_test(A *List, int Length, A Value) {
>
> -#pragma clang loop vectorize_width(8) interleave_count(8) unroll_count(8)
> +#pragma clang loop vectorize_width(8) interleave_count(8)
> +#pragma unroll 8
>    for (int i = 0; i < Length; i++) {
>      // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_7:.*]]
>      List[i] = i * Value;
> @@ -89,7 +91,7 @@
>  template <typename A>
>  void for_template_define_test(A *List, int Length, A Value) {
>  #pragma clang loop vectorize_width(VECWIDTH) interleave_count(INTCOUNT)
> -#pragma clang loop unroll_count(UNROLLCOUNT)
> +#pragma unroll UNROLLCOUNT
>    for (int i = 0; i < Length; i++) {
>      // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_8:.*]]
>      List[i] = i * Value;
> @@ -113,15 +115,15 @@
>  // CHECK: ![[WIDTH_4]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 4}
>  // CHECK: ![[INTERLEAVE_4]] = metadata !{metadata !"llvm.loop.vectorize.unroll", i32 4}
>  // CHECK: ![[INTENABLE_1]] = metadata !{metadata !"llvm.loop.vectorize.enable", i1 true}
> -// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[UNROLLENABLE_0:.*]], metadata ![[INTERLEAVE_4:.*]], metadata ![[WIDTH_8:.*]]}
> -// CHECK: ![[UNROLLENABLE_0]] = metadata !{metadata !"llvm.loop.unroll.enable", i1 false}
> +// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[UNROLL_4:.*]], metadata ![[INTERLEAVE_4:.*]], metadata ![[WIDTH_8:.*]]}
> +// CHECK: ![[UNROLL_4]] = metadata !{metadata !"llvm.loop.unroll.count", i32 4}
>  // CHECK: ![[WIDTH_8]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 8}
>  // CHECK: ![[LOOP_3]] = metadata !{metadata ![[LOOP_3]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_4:.*]], metadata ![[ENABLE_1:.*]]}
>  // CHECK: ![[UNROLL_8]] = metadata !{metadata !"llvm.loop.unroll.count", i32 8}
>  // CHECK: ![[LOOP_4]] = metadata !{metadata ![[LOOP_4]], metadata ![[INTERLEAVE_2:.*]], metadata ![[WIDTH_2:.*]]}
>  // CHECK: ![[INTERLEAVE_2]] = metadata !{metadata !"llvm.loop.vectorize.unroll", i32 2}
>  // CHECK: ![[WIDTH_2]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 2}
> -// CHECK: ![[LOOP_5]] = metadata !{metadata ![[LOOP_5]], metadata ![[UNROLLENABLE_0:.*]], metadata ![[WIDTH_1:.*]]}
> +// CHECK: ![[LOOP_5]] = metadata !{metadata ![[LOOP_5]], metadata ![[WIDTH_1:.*]]}
>  // CHECK: ![[WIDTH_1]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 1}
>  // CHECK: ![[LOOP_6]] = metadata !{metadata ![[LOOP_6]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_2:.*]], metadata ![[WIDTH_2:.*]]}
>  // CHECK: ![[LOOP_7]] = metadata !{metadata ![[LOOP_7]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_8:.*]], metadata ![[WIDTH_8:.*]]}
> Index: test/PCH/pragma-loop.cpp
> ===================================================================
> --- test/PCH/pragma-loop.cpp
> +++ test/PCH/pragma-loop.cpp
> @@ -4,13 +4,13 @@
>  // FIXME: A bug in ParsedAttributes causes the order of the attributes to be
>  // reversed. The checks are consequently in the reverse order below.
>
> -// CHECK: #pragma clang loop unroll_count(16)
> +// CHECK: #pragma unroll (16)
>  // CHECK: #pragma clang loop interleave_count(8)
>  // CHECK: #pragma clang loop vectorize_width(4)
> -// CHECK: #pragma clang loop unroll(disable)
> +// CHECK: #pragma unroll
>  // CHECK: #pragma clang loop interleave(disable)
>  // CHECK: #pragma clang loop vectorize(enable)
> -// CHECK: #pragma clang loop unroll(enable)
> +// CHECK: #pragma unroll 8
>  // CHECK: #pragma clang loop interleave(enable)
>  // CHECK: #pragma clang loop vectorize(disable)
>
> @@ -23,7 +23,7 @@
>      int i = 0;
>  #pragma clang loop vectorize_width(4)
>  #pragma clang loop interleave_count(8)
> -#pragma clang loop unroll_count(16)
> +#pragma unroll(16)
>      while (i < Length) {
>        List[i] = i;
>        i++;
> @@ -34,7 +34,7 @@
>      int i = 0;
>  #pragma clang loop vectorize(enable)
>  #pragma clang loop interleave(disable)
> -#pragma clang loop unroll(disable)
> +#pragma unroll
>      while (i - 1 < Length) {
>        List[i] = i;
>        i++;
> @@ -45,7 +45,7 @@
>      int i = 0;
>  #pragma clang loop vectorize(disable)
>  #pragma clang loop interleave(enable)
> -#pragma clang loop unroll(enable)
> +#pragma unroll 8
>      while (i - 3 < Length) {
>        List[i] = i;
>        i++;
> Index: test/Parser/pragma-loop.cpp
> ===================================================================
> --- test/Parser/pragma-loop.cpp
> +++ test/Parser/pragma-loop.cpp
> @@ -8,26 +8,26 @@
>
>  #pragma clang loop vectorize(enable)
>  #pragma clang loop interleave(enable)
> -#pragma clang loop unroll(enable)
> +#pragma unroll
>    while (i + 1 < Length) {
>      List[i] = i;
>    }
>
>  #pragma clang loop vectorize_width(4)
>  #pragma clang loop interleave_count(8)
> -#pragma clang loop unroll_count(16)
> +#pragma unroll 16
>    while (i < Length) {
>      List[i] = i;
>    }
>
>  #pragma clang loop vectorize(disable)
>  #pragma clang loop interleave(disable)
> -#pragma clang loop unroll(disable)
> +#pragma unroll(4)
>    while (i - 1 < Length) {
>      List[i] = i;
>    }
>
> -#pragma clang loop vectorize_width(4) interleave_count(8) unroll_count(16)
> +#pragma clang loop vectorize_width(4) interleave_count(8)
>    while (i - 2 < Length) {
>      List[i] = i;
>    }
> @@ -38,81 +38,82 @@
>    }
>
>    int VList[Length];
> -#pragma clang loop vectorize(disable) interleave(disable) unroll(disable)
> +#pragma clang loop vectorize(disable) interleave(disable)
>    for (int j : VList) {
>      VList[j] = List[j];
>    }
>
>  /* expected-error {{expected '('}} */ #pragma clang loop vectorize
>  /* expected-error {{expected '('}} */ #pragma clang loop interleave
> -/* expected-error {{expected '('}} */ #pragma clang loop unroll
>
>  /* expected-error {{expected ')'}} */ #pragma clang loop vectorize(enable
>  /* expected-error {{expected ')'}} */ #pragma clang loop interleave(enable
> -/* expected-error {{expected ')'}} */ #pragma clang loop unroll(enable
>
>  /* expected-error {{expected ')'}} */ #pragma clang loop vectorize_width(4
>  /* expected-error {{expected ')'}} */ #pragma clang loop interleave_count(4
> -/* expected-error {{expected ')'}} */ #pragma clang loop unroll_count(4
> +/* expected-error {{expected ')'}} */ #pragma unroll(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 'loop' pragma}} */ #pragma clang loop vectorize()
> +/* expected-error {{missing argument to 'loop' pragma}} */ #pragma clang loop interleave_count()
> +/* expected-error {{missing argument to 'unroll' pragma}} */ #pragma unroll()
>
>  /* expected-error {{missing option}} */ #pragma clang loop
>  /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop badkeyword
>  /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop badkeyword(enable)
>  /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop vectorize(enable) badkeyword(4)
>  /* expected-warning {{extra tokens at end of '#pragma clang loop'}} */ #pragma clang loop vectorize(enable) ,
> +/* expected-warning {{extra tokens at end of '#pragma unroll'}} */ #pragma unroll 1 foobar
>
>    while (i-4 < Length) {
>      List[i] = i;
>    }
>
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(0)
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(0)
> -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(0)
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll 0
>    while (i-5 < Length) {
>      List[i] = i;
>    }
>
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(3000000000)
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(3000000000)
> -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(3000000000)
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll 3000000000
>    while (i-6 < Length) {
>      List[i] = i;
>    }
>
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(badvalue)
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(badvalue)
> -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(badvalue)
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll badvalue
>    while (i-6 < Length) {
>      List[i] = i;
>    }
>
>  /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop vectorize(badidentifier)
>  /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop interleave(badidentifier)
> -/* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop unroll(badidentifier)
>    while (i-7 < Length) {
>      List[i] = i;
>    }
>
>  // PR20069 - Loop pragma arguments that are not identifiers or numeric
>  // constants crash FE.
>  /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop vectorize(()
>  /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop interleave(*)
> -/* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop unroll(=)
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(^)
>  /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(/)
> -/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(==)
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll(==)
> +/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll -
>    while (i-8 < Length) {
>      List[i] = i;
>    }
>
>  #pragma clang loop vectorize(enable)
> -/* expected-error {{expected a for, while, or do-while loop to follow the '#pragma clang loop' directive}} */ int j = Length;
> +/* expected-error {{expected a for, while, or do-while loop to follow the 'loop' pragma}} */ int j = Length;
>    List[0] = List[1];
>
> +#pragma unroll
> +/* expected-error {{expected a for, while, or do-while loop to follow the 'unroll' pragma}} */ List[2] = List[3];
> +
>    while (j-1 < Length) {
>      List[j] = j;
>    }
> @@ -126,38 +127,34 @@
>  #pragma clang loop vectorize(disable)
>  /* expected-error {{incompatible directives 'interleave(disable)' and 'interleave_count(4)'}} */ #pragma clang loop interleave_count(4)
>  #pragma clang loop interleave(disable)
> -/* expected-error {{incompatible directives 'unroll(disable)' and 'unroll_count(4)'}} */ #pragma clang loop unroll_count(4)
> -#pragma clang loop unroll(disable)
>    while (i-8 < Length) {
>      List[i] = i;
>    }
>
> -/* expected-error {{duplicate directives 'vectorize(disable)' and 'vectorize(enable)'}} */ #pragma clang loop vectorize(enable)
> +/* expected-error {{duplicate 'vectorize' directives}} */ #pragma clang loop vectorize(enable)
>  #pragma clang loop vectorize(disable)
> -/* expected-error {{duplicate directives 'interleave(disable)' and 'interleave(enable)'}} */ #pragma clang loop interleave(enable)
> -#pragma clang loop interleave(disable)
> -/* expected-error {{duplicate directives 'unroll(disable)' and 'unroll(enable)'}} */ #pragma clang loop unroll(enable)
> -#pragma clang loop unroll(disable)
> +/* expected-error {{duplicate 'interleave' directives}} */ #pragma clang loop interleave(enable)
> +#pragma clang loop interleave(enable)
> +/* expected-error {{duplicate 'unroll' directives}} */ #pragma unroll
> +#pragma unroll
>    while (i-9 < Length) {
>      List[i] = i;
>    }
>
>  /* expected-error {{incompatible directives 'vectorize(disable)' and 'vectorize_width(4)'}} */ #pragma clang loop vectorize(disable)
>  #pragma clang loop vectorize_width(4)
>  /* expected-error {{incompatible directives 'interleave(disable)' and 'interleave_count(4)'}} */ #pragma clang loop interleave(disable)
>  #pragma clang loop interleave_count(4)
> -/* expected-error {{incompatible directives 'unroll(disable)' and 'unroll_count(4)'}} */ #pragma clang loop unroll(disable)
> -#pragma clang loop unroll_count(4)
>    while (i-10 < Length) {
>      List[i] = i;
>    }
>
> -/* expected-error {{duplicate directives 'vectorize_width(4)' and 'vectorize_width(8)'}} */ #pragma clang loop vectorize_width(8)
> +/* expected-error {{duplicate 'vectorize_width' directives}} */ #pragma clang loop vectorize_width(8)
>  #pragma clang loop vectorize_width(4)
> -/* expected-error {{duplicate directives 'interleave_count(4)' and 'interleave_count(8)'}} */ #pragma clang loop interleave_count(8)
> +/* expected-error {{duplicate 'interleave_count' directives}} */ #pragma clang loop interleave_count(8)
>  #pragma clang loop interleave_count(4)
> -/* expected-error {{duplicate directives 'unroll_count(4)' and 'unroll_count(8)'}} */ #pragma clang loop unroll_count(8)
> -#pragma clang loop unroll_count(4)
> +/* expected-error {{duplicate 'unroll' directives}} */ #pragma unroll 8
> +#pragma unroll(4)
>    while (i-11 < Length) {
>      List[i] = i;
>    }
>

~Aaron



More information about the cfe-commits mailing list