212 changes: 158 additions & 54 deletions clang/lib/Parse/ParsePragma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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(...)
Expand Down Expand Up @@ -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() {
Expand All @@ -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;
}
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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
///
Expand Down Expand Up @@ -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.
Expand All @@ -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)) {
Expand All @@ -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);
}
12 changes: 5 additions & 7 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
126 changes: 60 additions & 66 deletions clang/lib/Sema/SemaStmtAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -100,31 +115,22 @@ 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:
// unroll(enable|disable) and unroll_count(N). The following array
// accumulate the hints encountered while iterating through the
// attributes to check for compatibility.
struct {
int EnableOptionId;
int NumericOptionId;
bool EnabledIsSet;
bool ValueIsSet;
bool Enabled;
int Value;
} Options[] = {{LoopHintAttr::Vectorize, LoopHintAttr::VectorizeWidth, false,
false, false, 0},
{LoopHintAttr::Interleave, LoopHintAttr::InterleaveCount,
false, false, false, 0},
{LoopHintAttr::Unroll, LoopHintAttr::UnrollCount, false, false,
false, 0}};
const LoopHintAttr *EnableAttr;
const LoopHintAttr *NumericAttr;
} HintAttrs[] = {{nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}};

for (const auto *I : Attrs) {
const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(I);
Expand All @@ -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:
Expand All @@ -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();
}
}
}
Expand Down
99 changes: 99 additions & 0 deletions clang/test/CodeGen/pragma-unroll.cpp
Original file line number Diff line number Diff line change
@@ -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:.*]]}
22 changes: 22 additions & 0 deletions clang/test/PCH/pragma-loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -51,6 +53,24 @@ class pragma_test {
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
Expand All @@ -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
8 changes: 4 additions & 4 deletions clang/test/Parser/pragma-loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
89 changes: 89 additions & 0 deletions clang/test/Parser/pragma-unroll.cpp
Original file line number Diff line number Diff line change
@@ -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}} */ }
13 changes: 13 additions & 0 deletions clang/test/Parser/warn-cuda-compat.cu
Original file line number Diff line number Diff line change
@@ -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;
}
}