Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -111,6 +111,13 @@ as interleave and unrolling count can be manually specified. See language extensions for details. +Clang now supports the `#pragma unroll` directive to specify loop unrolling +optimization hints. Placed just prior to the desired loop, `#pragma unroll` +directs the loop unroller to attempt to fully unroll the loop. The pragma may +also be specified with a positive integer parameter indicating the desired +unroll count: `#pragma unroll _value_`. The unroll count parameter can be +optionally enclosed in parentheses. + 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", @@ -1802,15 +1802,49 @@ } void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const { - OS << getOptionName(option) << "("; + unsigned SpellingIndex = getSpellingListIndex(); + if (SpellingIndex == Pragma_unroll) { + // String "unroll" of "#pragma unroll" is already emitted as the + // pragma name. + if (option == UnrollCount) + OS << getValueString(); + OS << "\n"; + return; + } + assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling"); + OS << getOptionName(option) << getValueString() << "\n"; + } + + // Return a string containing the loop hint argument including the + // 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"; + + return "(" + ValueName + ")"; + } + + // Return a string suitable for identifying this attribute in diagnostics. + std::string getDiagnosticName() const { + unsigned SpellingIndex = getSpellingListIndex(); + 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 { + assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling"); + 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,45 @@ for details. }]; } + +def UnrollHintDocs : Documentation { + let Category = DocCatStmt; + let Content = [{ +Loop unrolling optimization hints can be specified with ``#pragma unroll``. The +pragma is placed immediately before a for, while, do-while, or c++11 range-based +for loop. + +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 (...) { + ... + } + +``#pragma unroll`` and ``#pragma unroll _value_`` have identical semantics to +``#pragma clang loop unroll(enable)`` and ``#pragma clang loop +unroll_count(_value_)`` respectively. See `language extensions +`_ +for further details including limitations of the unroll hints. + }]; +} + Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -708,3 +708,7 @@ // Instrumentation based profiling warnings. def ProfileInstrOutOfDate : DiagGroup<"profile-instr-out-of-date">; def ProfileInstrUnprofiled : DiagGroup<"profile-instr-unprofiled">; + +// A warning group for warnings about code that clang accepts when +// compiling CUDA C/C++ but which is not compatible with the CUDA spec. +def CudaCompat : DiagGroup<"cuda-compat">; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -811,6 +811,9 @@ InGroup; def warn_pragma_expected_punc : Warning< "expected ')' or ',' in '#pragma %0'">, InGroup; +// - Generic errors +def err_pragma_missing_argument : Error< + "missing argument to '#pragma %0'; expected %1">; // - #pragma options def warn_pragma_options_expected_align : Warning< "expected 'align' following '#pragma options' - ignored">, @@ -858,8 +861,6 @@ "unexpected %0, expected to see one of %select{|'best_case', 'full_generality', }1" "'single_inheritance', 'multiple_inheritance', or 'virtual_inheritance'">; // - #pragma clang optimize on/off -def err_pragma_optimize_missing_argument : Error< - "missing argument to '#pragma clang optimize'; expected 'on' or 'off'">; def err_pragma_optimize_invalid_argument : Error< "unexpected argument '%0' to '#pragma clang optimize'; " "expected 'on' or 'off'">; @@ -911,8 +912,11 @@ 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">; + +// Pragma unroll support. +def warn_pragma_unroll_cuda_value_in_parens : Warning< + "argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">, + InGroup; } // 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 @@ -537,10 +537,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 '%0'">; /// 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 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,22 @@ 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 for "#pragma unroll". + IdentifierLoc *ValueLoc; + // 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,9 @@ LoopHintHandler.reset(new PragmaLoopHintHandler()); PP.AddPragmaHandler("clang", LoopHintHandler.get()); + + UnrollHintHandler.reset(new PragmaUnrollHintHandler("unroll")); + 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,10 @@ } struct PragmaLoopHintInfo { - Token Loop; - Token Value; + Token PragmaName; Token Option; + Token Value; + bool HasValue; }; LoopHint Parser::HandlePragmaLoopHint() { @@ -611,24 +624,30 @@ 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) { + Hint.Range = + SourceRange(Info->Option.getLocation(), Info->Value.getLocation()); + 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; + } return Hint; } @@ -1601,7 +1620,9 @@ Token Tok; PP.Lex(Tok); if (Tok.is(tok::eod)) { - PP.Diag(Tok.getLocation(), diag::err_pragma_optimize_missing_argument); + PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument) + << "clang optimize" + << "'on' or 'off'"; return; } if (Tok.isNot(tok::identifier)) { @@ -1630,6 +1651,48 @@ Actions.ActOnPragmaOptimize(IsOn, FirstToken.getLocation()); } +/// \brief Parses loop or unroll pragma hint value and fills in Info. +static bool ParseLoopHintValue(Preprocessor &PP, Token Tok, Token &PragmaName, + Token &Option, bool &ValueInParens, + PragmaLoopHintInfo &Info) { + ValueInParens = Tok.is(tok::l_paren); + if (ValueInParens) { + PP.Lex(Tok); + if (Tok.is(tok::r_paren)) { + // Nothing between the parentheses. + std::string PragmaString; + if (PragmaName.getIdentifierInfo()->getName() == "loop") { + PragmaString = "clang loop "; + PragmaString += Option.getIdentifierInfo()->getName(); + } else { + assert(PragmaName.getIdentifierInfo()->getName() == "unroll" && + "Unexpected pragma name"); + PragmaString = "unroll"; + } + PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument) + << PragmaString << "a positive integer value"; + return true; + } + } + + // FIXME: Value should be stored and parsed as a constant expression. + Token Value = Tok; + + if (ValueInParens) { + PP.Lex(Tok); + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_expected) << tok::r_paren; + return true; + } + } + + Info.PragmaName = PragmaName; + Info.Option = Option; + Info.Value = Value; + Info.HasValue = true; + return false; +} + /// \brief Handle the \#pragma clang loop directive. /// #pragma clang 'loop' loop-hints /// @@ -1671,7 +1734,8 @@ 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 +1751,40 @@ 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("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; + bool ValueInParens; + if (ParseLoopHintValue(PP, Tok, PragmaName, Option, ValueInParens, *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 (!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 +1800,62 @@ /*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'. '#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. +void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducerKind Introducer, + Token &Tok) { + // Incoming token is "unroll" of "#pragma unroll". + Token PragmaName = Tok; + PP.Lex(Tok); + auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo; + if (Tok.is(tok::eod)) { + // Unroll pragma without an argument. + Info->PragmaName = PragmaName; + Info->Option = PragmaName; + Info->HasValue = false; + } else { + // Unroll pragma with an argument: "#pragma unroll N" or + // "#pragma unroll(N)". + bool ValueInParens; + if (ParseLoopHintValue(PP, Tok, PragmaName, PragmaName, ValueInParens, + *Info)) + return; + + // In CUDA, the argument to '#pragma unroll' should not be contained in + // parentheses. + if (PP.getLangOpts().CUDA && ValueInParens) + PP.Diag(Info->Value.getLocation(), + diag::warn_pragma_unroll_cuda_value_in_parens); + + 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 @@ -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 ArgHints[] = {Hint.PragmaNameLoc, Hint.OptionLoc, Hint.ValueLoc, ArgsUnion(Hint.ValueExpr)}; - TempAttrs.addNew(Hint.LoopLoc->Ident, Hint.Range, nullptr, - Hint.LoopLoc->Loc, ArgHints, 3, AttributeList::AS_Pragma); + TempAttrs.addNew(Hint.PragmaNameLoc->Ident, Hint.Range, nullptr, + Hint.PragmaNameLoc->Loc, ArgHints, 4, + AttributeList::AS_Pragma); } // Get the next statement. Index: lib/Sema/SemaStmtAttr.cpp =================================================================== --- lib/Sema/SemaStmtAttr.cpp +++ lib/Sema/SemaStmtAttr.cpp @@ -45,35 +45,50 @@ 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; + Expr *ValueExpr = A.getArgAsExpr(3); + + 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 = PragmaNameLoc->Ident->getName() == "unroll" + ? "#pragma unroll" + : "#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(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 { + 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); + Spelling = LoopHintAttr::Pragma_clang_loop; + } int ValueInt; - if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave || - Option == LoopHintAttr::Unroll) { + if (Option == LoopHintAttr::Unroll && + Spelling == LoopHintAttr::Pragma_unroll) { + ValueInt = 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,12 +115,12 @@ } else llvm_unreachable("Unknown loop hint option"); - return LoopHintAttr::CreateImplicit(S.Context, Option, ValueInt, + return LoopHintAttr::CreateImplicit(S.Context, Spelling, Option, ValueInt, A.getRange()); } -static void -CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl &Attrs) { +static void CheckForIncompatibleAttributes( + Sema &S, const 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: @@ -113,18 +128,9 @@ // 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(I); @@ -134,8 +140,6 @@ continue; int Option = LH->getOption(); - int ValueInt = LH->getValue(); - int Category; switch (Option) { case LoopHintAttr::Vectorize: @@ -152,44 +156,34 @@ break; }; - auto &CategoryState = Options[Category]; - SourceLocation ValueLoc = LH->getRange().getEnd(); + auto &CategoryState = HintAttrs[Category]; + SourceLocation OptionLoc = LH->getRange().getBegin(); + const LoopHintAttr *PrevAttr; if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave || Option == LoopHintAttr::Unroll) { // 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); - } - CategoryState.EnabledIsSet = true; - CategoryState.Enabled = ValueInt; + PrevAttr = CategoryState.EnableAttr; + CategoryState.EnableAttr = LH; } else { - // Numeric hint. For example, unroll_count(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; - } - CategoryState.ValueIsSet = true; - CategoryState.Value = ValueInt; + // Numeric hint. For example, vectorize_width(8). + PrevAttr = CategoryState.NumericAttr; + CategoryState.NumericAttr = LH; } - if (CategoryState.EnabledIsSet && !CategoryState.Enabled && - CategoryState.ValueIsSet) { + if (PrevAttr) + // Cannot specify same type of attribute twice. + S.Diag(OptionLoc, diag::err_pragma_loop_compatibility) + << /*Duplicate=*/true << PrevAttr->getDiagnosticName() + << LH->getDiagnosticName(); + + 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 unroll 16 + 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 +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 +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(List, Length, Value); + for_template_define_test(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 ![[UNROLL_16:.*]]} +// CHECK: ![[UNROLL_16]] = metadata !{metadata !"llvm.loop.unroll.count", i32 16} +// 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,8 @@ // CHECK: #pragma clang loop unroll(enable) // CHECK: #pragma clang loop interleave(enable) // CHECK: #pragma clang loop vectorize(disable) +// CHECK: #pragma unroll +// CHECK: #pragma unroll (32) #ifndef HEADER #define HEADER @@ -51,6 +53,24 @@ 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 unroll 32 + while (i - 3 < Length) { + List[i] = i; + i++; + } + } }; #else @@ -63,6 +83,8 @@ pt.run1(List, 100); pt.run2(List, 100); pt.run3(List, 100); + pt.run4(List, 100); + pt.run5(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 '#pragma clang loop vectorize'; expected a positive integer value}} */ #pragma clang loop vectorize() +/* expected-error {{missing argument to '#pragma clang loop interleave_count'; expected a positive integer value}} */ #pragma clang loop interleave_count() +/* expected-error {{missing argument to '#pragma clang loop unroll'; expected a positive integer value}} */ #pragma clang loop unroll() /* expected-error {{missing option}} */ #pragma clang loop /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop badkeyword @@ -110,7 +110,7 @@ } #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 '#pragma clang loop'}} */ int j = Length; List[0] = List[1]; while (j-1 < Length) { Index: test/Parser/pragma-unroll.cpp =================================================================== --- test/Parser/pragma-unroll.cpp +++ test/Parser/pragma-unroll.cpp @@ -0,0 +1,89 @@ +// 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 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 '#pragma unroll'; expected a positive integer value}} */ #pragma unroll() +/* expected-warning {{extra tokens at end of '#pragma unroll'}} */ #pragma unroll 1 2 + while (i-6 < 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 '#pragma unroll'}} */ int j = Length; +#pragma unroll 4 +/* expected-error {{expected a for, while, or do-while loop to follow '#pragma unroll'}} */ int k = Length; + +/* 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 {{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}} */ } Index: test/Parser/warn-cuda-compat.cu =================================================================== --- test/Parser/warn-cuda-compat.cu +++ test/Parser/warn-cuda-compat.cu @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -Wno-cuda-compat -Werror %s +// RUN: %clang_cc1 -Wcuda-compat -verify %s +// RUN: %clang_cc1 -x c++ -Wcuda-compat -Werror %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) { +/* expected-warning {{argument to '#pragma unroll' should not be in parentheses in CUDA C/C++}} */#pragma unroll(4) + for (int i = 0; i < Length; ++i) { + List[i] = i; + } +}