summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Heffernan <meheff@google.com>2014-07-21 18:08:34 +0000
committerMark Heffernan <meheff@google.com>2014-07-21 18:08:34 +0000
commit30974231cab08ddc43e78dfe224009f8555d0ec3 (patch)
tree34d0e3ac36d147a146e0ff50dc9a503d1d0d9c22
parentd2b971aa5e42a2d09603085a3b2ab2b5820ef707 (diff)
Add support for '#pragma unroll'.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@213574 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--docs/ReleaseNotes.rst7
-rw-r--r--include/clang/Basic/Attr.td44
-rw-r--r--include/clang/Basic/AttrDocs.td42
-rw-r--r--include/clang/Basic/DiagnosticGroups.td4
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td12
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td5
-rw-r--r--include/clang/Parse/Parser.h3
-rw-r--r--include/clang/Sema/LoopHint.h17
-rw-r--r--lib/Parse/ParsePragma.cpp212
-rw-r--r--lib/Parse/ParseStmt.cpp12
-rw-r--r--lib/Sema/SemaStmtAttr.cpp126
-rw-r--r--test/CodeGen/pragma-unroll.cpp99
-rw-r--r--test/PCH/pragma-loop.cpp22
-rw-r--r--test/Parser/pragma-loop.cpp8
-rw-r--r--test/Parser/pragma-unroll.cpp89
-rw-r--r--test/Parser/warn-cuda-compat.cu13
16 files changed, 566 insertions, 149 deletions
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index 5ca4bbbc7c..f17a1a4b16 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -111,6 +111,13 @@ 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.
+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
---------------------------
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index 640cd4dd40..4850e2675c 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -1779,7 +1779,7 @@ def LoopHint : Attr {
/// 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",
@@ -1809,15 +1809,47 @@ def LoopHint : Attr {
}
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) {
+ return "#pragma unroll" + getValueString();
+ } else {
+ assert(SpellingIndex == Pragma_clang_loop && "Unexpected spelling");
+ return std::string(getOptionName(option)) + getValueString();
+ }
}
}];
- let Documentation = [LoopHintDocs];
+ let Documentation = [LoopHintDocs, UnrollHintDocs];
}
diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td
index 628f22f860..9700232027 100644
--- a/include/clang/Basic/AttrDocs.td
+++ b/include/clang/Basic/AttrDocs.td
@@ -1045,3 +1045,45 @@ as interleave and unrolling count can be manually specified. See
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
+<http://clang.llvm.org/docs/LanguageExtensions.html#extensions-for-loop-hint-optimizations>`_
+for further details including limitations of the unroll hints.
+ }];
+}
+
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index 840b14b07c..58dee482b6 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -709,3 +709,7 @@ def BackendOptimizationFailure : DiagGroup<"pass-failed">;
// 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">;
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index aed53bdc9b..af0baebc13 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -813,6 +813,9 @@ def warn_pragma_expected_punc : Warning<
"expected ')' or ',' in '#pragma %0'">, InGroup<IgnoredPragmas>;
def warn_pragma_expected_non_wide_string : Warning<
"expected non-wide string literal in '#pragma %0'">, InGroup<IgnoredPragmas>;
+// - 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">,
@@ -860,8 +863,6 @@ def err_pragma_pointers_to_members_unknown_kind : Error<
"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'">;
@@ -917,8 +918,11 @@ def err_omp_expected_identifier_for_critical : Error<
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<CudaCompat>;
} // end of Parse Issue category.
let CategoryName = "Modules Issue" in {
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 885cf36163..89749edfac 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -541,10 +541,9 @@ def err_pragma_loop_invalid_value : Error<
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<
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index fa7281b917..73bef66c2c 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -163,6 +163,7 @@ class Parser : public CodeCompletionHandler {
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 @@ private:
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
diff --git a/include/clang/Sema/LoopHint.h b/include/clang/Sema/LoopHint.h
index 928cfb1ee8..d4b985df54 100644
--- a/include/clang/Sema/LoopHint.h
+++ b/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
diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp
index 1b343b4291..a7b5f6ebdd 100644
--- a/lib/Parse/ParsePragma.cpp
+++ b/lib/Parse/ParsePragma.cpp
@@ -148,6 +148,12 @@ struct PragmaLoopHintHandler : public PragmaHandler {
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 @@ void Parser::initializePragmaHandlers() {
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 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", LoopHintHandler.get());
LoopHintHandler.reset();
+
+ PP.RemovePragmaHandler(UnrollHintHandler.get());
+ UnrollHintHandler.reset();
}
/// \brief Handle the annotation token produced for #pragma unused(...)
@@ -649,9 +661,10 @@ bool Parser::HandlePragmaMSInitSeg(StringRef PragmaName,
}
struct PragmaLoopHintInfo {
- Token Loop;
- Token Value;
+ Token PragmaName;
Token Option;
+ Token Value;
+ bool HasValue;
};
LoopHint Parser::HandlePragmaLoopHint() {
@@ -660,24 +673,30 @@ LoopHint Parser::HandlePragmaLoopHint() {
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) {
+ 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;
}
@@ -1650,7 +1669,9 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
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)) {
@@ -1679,6 +1700,48 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
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
///
@@ -1720,7 +1783,8 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
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.
@@ -1736,59 +1800,40 @@ void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP,
IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();
bool OptionValid = llvm::StringSwitch<bool>(OptionInfo->getName())
- .Case("vectorize", true)
- .Case("interleave", true)
- .Case("unroll", true)
- .Case("vectorize_width", true)
- .Case("interleave_count", true)
- .Case("unroll_count", true)
- .Default(false);
+ .Case("vectorize", true)
+ .Case("interleave", true)
+ .Case("unroll", true)
+ .Case("vectorize_width", true)
+ .Case("interleave_count", true)
+ .Case("unroll_count", true)
+ .Default(false);
if (!OptionValid) {
PP.Diag(Tok.getLocation(), diag::err_pragma_loop_invalid_option)
<< /*MissingOption=*/false << OptionInfo;
return;
}
- // Read '('
- PP.Lex(Tok);
- if (Tok.isNot(tok::l_paren)) {
- PP.Diag(Tok.getLocation(), diag::err_expected) << tok::l_paren;
- return;
- }
-
- // FIXME: All tokens between '(' and ')' should be stored and parsed as a
- // constant expression.
+ auto *Info = new (PP.getPreprocessorAllocator()) PragmaLoopHintInfo;
PP.Lex(Tok);
- if (Tok.is(tok::r_paren)) {
- // Nothing between the parentheses.
- PP.Diag(Tok.getLocation(), diag::err_pragma_loop_missing_argument)
- << OptionInfo;
+ 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<void *>(Info));
TokenList.push_back(LoopHintTok);
+
+ // Get next optimization option.
+ PP.Lex(Tok);
}
if (Tok.isNot(tok::eod)) {
@@ -1804,3 +1849,62 @@ void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP,
/*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<void *>(Info));
+ PP.EnterTokenStream(TokenArray, 1, /*DisableMacroExpansion=*/false,
+ /*OwnsTokens=*/true);
+}
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 1e8c4a69d8..ec0ca6b1fd 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -1833,18 +1833,16 @@ StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts, bool OnlyStatement,
// 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.
diff --git a/lib/Sema/SemaStmtAttr.cpp b/lib/Sema/SemaStmtAttr.cpp
index 44169c2fdc..a32e0fbcb6 100644
--- a/lib/Sema/SemaStmtAttr.cpp
+++ b/lib/Sema/SemaStmtAttr.cpp
@@ -45,35 +45,50 @@ static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const AttributeList &A,
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<LoopHintAttr::OptionType>(OptionInfo->getName())
- .Case("vectorize", LoopHintAttr::Vectorize)
- .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
- .Case("interleave", LoopHintAttr::Interleave)
- .Case("interleave_count", LoopHintAttr::InterleaveCount)
- .Case("unroll", LoopHintAttr::Unroll)
- .Case("unroll_count", LoopHintAttr::UnrollCount)
- .Default(LoopHintAttr::Vectorize);
+ LoopHintAttr::OptionType Option;
+ LoopHintAttr::Spelling Spelling;
+ if (PragmaNameLoc->Ident->getName() == "unroll") {
+ Option = ValueLoc ? LoopHintAttr::UnrollCount : LoopHintAttr::Unroll;
+ Spelling = LoopHintAttr::Pragma_unroll;
+ } else {
+ Option = llvm::StringSwitch<LoopHintAttr::OptionType>(OptionInfo->getName())
+ .Case("vectorize", LoopHintAttr::Vectorize)
+ .Case("vectorize_width", LoopHintAttr::VectorizeWidth)
+ .Case("interleave", LoopHintAttr::Interleave)
+ .Case("interleave_count", LoopHintAttr::InterleaveCount)
+ .Case("unroll", LoopHintAttr::Unroll)
+ .Case("unroll_count", LoopHintAttr::UnrollCount)
+ .Default(LoopHintAttr::Vectorize);
+ Spelling = LoopHintAttr::Pragma_clang_loop;
+ }
int ValueInt;
- if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave ||
- Option == LoopHintAttr::Unroll) {
+ if (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 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
} 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<const Attr *> &Attrs) {
+static void CheckForIncompatibleAttributes(
+ Sema &S, const SmallVectorImpl<const Attr *> &Attrs) {
// There are 3 categories of loop hints: vectorize, interleave, and
// unroll. Each comes in two variants: an enable/disable form and a
// form which takes a numeric argument. For example:
@@ -113,18 +128,9 @@ CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
// accumulate the hints encountered while iterating through the
// attributes to check for compatibility.
struct {
- int EnableOptionId;
- int NumericOptionId;
- bool EnabledIsSet;
- bool ValueIsSet;
- bool Enabled;
- int Value;
- } Options[] = {{LoopHintAttr::Vectorize, LoopHintAttr::VectorizeWidth, false,
- false, false, 0},
- {LoopHintAttr::Interleave, LoopHintAttr::InterleaveCount,
- false, false, false, 0},
- {LoopHintAttr::Unroll, LoopHintAttr::UnrollCount, false, false,
- false, 0}};
+ const LoopHintAttr *EnableAttr;
+ const LoopHintAttr *NumericAttr;
+ } HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}};
for (const auto *I : Attrs) {
const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
@@ -134,8 +140,6 @@ CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
continue;
int Option = LH->getOption();
- int ValueInt = LH->getValue();
-
int Category;
switch (Option) {
case LoopHintAttr::Vectorize:
@@ -152,44 +156,34 @@ CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
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();
}
}
}
diff --git a/test/CodeGen/pragma-unroll.cpp b/test/CodeGen/pragma-unroll.cpp
new file mode 100644
index 0000000000..b321e74a11
--- /dev/null
+++ b/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 <typename A>
+void for_template_test(A *List, int Length, A Value) {
+#pragma unroll 8
+ for (int i = 0; i < Length; i++) {
+ // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_6:.*]]
+ List[i] = i * Value;
+ }
+}
+
+// Verify define is resolved correctly when template is used.
+template <typename A>
+void for_template_define_test(A *List, int Length, A Value) {
+#pragma unroll(UNROLLCOUNT)
+ for (int i = 0; i < Length; i++) {
+ // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_7:.*]]
+ List[i] = i * Value;
+ }
+}
+
+#undef UNROLLCOUNT
+
+// Use templates defined above. Test verifies metadata is generated correctly.
+void template_test(double *List, int Length) {
+ double Value = 10;
+
+ for_template_test<double>(List, Length, Value);
+ for_template_define_test<double>(List, Length, Value);
+}
+
+// CHECK: ![[LOOP_1]] = metadata !{metadata ![[LOOP_1]], metadata ![[UNROLLENABLE_1:.*]]}
+// CHECK: ![[UNROLLENABLE_1]] = metadata !{metadata !"llvm.loop.unroll.enable", i1 true}
+// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2:.*]], metadata ![[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:.*]]}
diff --git a/test/PCH/pragma-loop.cpp b/test/PCH/pragma-loop.cpp
index 6258b3781b..1456a2778f 100644
--- a/test/PCH/pragma-loop.cpp
+++ b/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 @@ public:
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 @@ void test() {
pt.run1(List, 100);
pt.run2(List, 100);
pt.run3(List, 100);
+ pt.run4(List, 100);
+ pt.run5(List, 100);
}
#endif
diff --git a/test/Parser/pragma-loop.cpp b/test/Parser/pragma-loop.cpp
index fad4feb870..23f185d522 100644
--- a/test/Parser/pragma-loop.cpp
+++ b/test/Parser/pragma-loop.cpp
@@ -55,9 +55,9 @@ void test(int *List, int Length) {
/* 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 @@ void test(int *List, int Length) {
}
#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) {
diff --git a/test/Parser/pragma-unroll.cpp b/test/Parser/pragma-unroll.cpp
new file mode 100644
index 0000000000..1d89e63028
--- /dev/null
+++ b/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}} */ }
diff --git a/test/Parser/warn-cuda-compat.cu b/test/Parser/warn-cuda-compat.cu
new file mode 100644
index 0000000000..c3aad6f663
--- /dev/null
+++ b/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;
+ }
+}