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,7 +1772,7 @@ /// 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", @@ -1780,7 +1780,8 @@ "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) { @@ -1789,8 +1790,10 @@ 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,9 +1805,23 @@ } 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); @@ -1812,5 +1829,5 @@ } }]; - 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">; } // 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)'">; 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 MSSection; std::unique_ptr OptimizeHandler; std::unique_ptr LoopHintHandler; + std::unique_ptr UnrollHintHandler; std::unique_ptr 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,9 +612,12 @@ } struct PragmaLoopHintInfo { - Token Loop; - Token Value; + Token PragmaName; Token Option; + Token Value; + Token CloseParen; + bool HasValue; + bool ValueInParens; }; LoopHint Parser::HandlePragmaLoopHint() { @@ -611,24 +626,40 @@ static_cast(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; + 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 TokenList; // Lex the optimization option and verify it is an identifier. @@ -1687,59 +1750,37 @@ IdentifierInfo *OptionInfo = Tok.getIdentifierInfo(); bool OptionValid = llvm::StringSwitch(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(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(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(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(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 &Attrs) { +static void CheckForIncompatibleAttributes( + Sema &S, SmallVectorImpl &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: @@ -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 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 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,32 +38,31 @@ } 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; @@ -71,28 +70,27 @@ /* 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; } @@ -101,18 +99,21 @@ // 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,18 +127,16 @@ #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; } @@ -146,18 +145,16 @@ #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; }