Hi aaron.ballman, eliben, TylerNowicki,

This patch adds support for the loop unroll pragma syntax defined in the CUDA 
c/c++ language:

// Fully unroll loop.
#pragma unroll
for (...) { }

// Unroll loop N times.
#pragma unroll N
for (...) { }

This pragma is only supported when compiling in CUDA mode (-x cuda).  The 
"#pragma unroll" and "#pragma unroll N" have identical semantics to "#pragma 
clang loop unroll(enable)" and "#pragma clang loop unroll_count(N)" 
respectively.

Mark

http://reviews.llvm.org/D4297

Files:
  docs/ReleaseNotes.rst
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/TokenKinds.def
  include/clang/Parse/Parser.h
  include/clang/Sema/CudaUnrollHint.h
  lib/CodeGen/CGStmt.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/Parse/ParsePragma.cpp
  lib/Parse/ParseStmt.cpp
  lib/Sema/SemaStmtAttr.cpp
  test/CodeGen/cuda-pragma-unroll.cu
  test/Misc/ast-print-cuda-pragmas.cu
  test/PCH/cuda-pragma-unroll.cu
  test/Parser/cuda-pragma-unroll.cu
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -106,6 +106,9 @@
 as interleave and unrolling count can be manually specified.  See language
 extensions for details.
 
+When compiling the CUDA C/C++ language Clang now supports the `#pragma unroll`
+directive to specify loop unrolling optimization hints.
+
 C Language Changes in Clang
 ---------------------------
 
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1814,3 +1814,20 @@
 
   let Documentation = [LoopHintDocs];
 }
+
+def CudaUnrollHint : Attr {
+  let Spellings = [Pragma<"", "unroll">];
+
+  let Args = [IntArgument<"Value">];
+
+  let AdditionalMembers = [{
+  void printPrettyPragma(raw_ostream &OS, const PrintingPolicy &Policy) const {
+    // The string 'unroll' is emitted earlier when emitting the pragma name.
+    if (value)
+      OS << " " << value;
+    OS << "\n";
+  }
+  }];
+
+  let Documentation = [CudaUnrollHintDocs];
+}
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -1024,3 +1024,36 @@
 for details.
   }];
 }
+
+def CudaUnrollHintDocs : Documentation {
+  let Category = DocCatStmt;
+  let Content = [{
+When compiling the CUDA C/C++ language (indicated by specifying the ``-x cuda``
+command-line argument) the ``#pragma unroll`` directive is supported to specify
+loop unrolling hints.  The pragma is placed immediately before a for, while,
+do-while, or c++11 range-based for loop.  The pragma takes an optional parameter
+which must be a positive integer.
+
+.. code-block:: c++
+
+  #pragma unroll
+  for (...) {
+    ...
+  }
+
+  #pragma unroll 16
+  for (...) {
+    ...
+  }
+
+If ``#pragma unroll`` is specified without a parameter the loop unroller will
+attempt to fully unroll the loop if the trip count is known at compile time.
+Specifying the optional parameter, ``#pragma unroll _value_``, directs the
+unroller to unroll the loop ``_value_`` times.  ``#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.
+  }];
+}
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -549,8 +549,12 @@
 def err_pragma_loop_compatibility : Error<
   "%select{incompatible|duplicate}0 directives '%1(%2)' and '%3(%4)'">;
 def err_pragma_loop_precedes_nonloop : Error<
-  "expected a for, while, or do-while loop to follow the '#pragma clang loop' "
+  "expected a for, while, or do-while loop to follow the '#pragma %0' "
   "directive">;
+def err_incompatible_pragma_loop_unroll : Error<
+  "'#pragma unroll' and '#pragma clang loop %0' cannot both be specified">;
+def err_duplicate_cuda_unroll_pragma : Error<
+  "duplicate '#pragma unroll' directives">;
 
 /// Objective-C parser diagnostics
 def err_duplicate_class_def : Error<
Index: include/clang/Basic/TokenKinds.def
===================================================================
--- include/clang/Basic/TokenKinds.def
+++ include/clang/Basic/TokenKinds.def
@@ -706,6 +706,11 @@
 // handles #pragma loop ... directives.
 ANNOTATION(pragma_loop_hint)
 
+// Annotations for CUDA-specific unroll pragma directives #pragma unroll ...
+// The lexer produces these so that they only take effect when the parser
+// handles #pragma loop ... directives.
+ANNOTATION(pragma_cuda_unroll)
+
 // Annotations for module import translated from #include etc.
 ANNOTATION(module_include)
 ANNOTATION(module_begin)
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -19,6 +19,7 @@
 #include "clang/Basic/Specifiers.h"
 #include "clang/Lex/CodeCompletionHandler.h"
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/CudaUnrollHint.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/LoopHint.h"
 #include "clang/Sema/Sema.h"
@@ -163,6 +164,7 @@
   std::unique_ptr<PragmaHandler> MSSection;
   std::unique_ptr<PragmaHandler> OptimizeHandler;
   std::unique_ptr<PragmaHandler> LoopHintHandler;
+  std::unique_ptr<PragmaHandler> CudaUnrollHandler;
 
   std::unique_ptr<CommentHandler> CommentSemaHandler;
 
@@ -522,9 +524,13 @@
   StmtResult HandlePragmaCaptured();
 
   /// \brief Handle the annotation token produced for
-  /// #pragma vectorize...
+  /// #pragma clang loop...
   LoopHint HandlePragmaLoopHint();
 
+  /// \brief Handle the CUDA-specific annotation token produced for
+  /// #pragma unroll...
+  CudaUnrollHint HandlePragmaCudaUnroll();
+
   /// GetLookAheadToken - This peeks ahead N tokens and returns that token
   /// without consuming any tokens.  LookAhead(0) returns 'Tok', LookAhead(1)
   /// returns the token after Tok, etc.
@@ -1610,6 +1616,9 @@
   StmtResult ParsePragmaLoopHint(StmtVector &Stmts, bool OnlyStatement,
                                  SourceLocation *TrailingElseLoc,
                                  ParsedAttributesWithRange &Attrs);
+  StmtResult ParsePragmaCudaUnroll(StmtVector &Stmts, bool OnlyStatement,
+                                   SourceLocation *TrailingElseLoc,
+                                   ParsedAttributesWithRange &Attrs);
 
   /// \brief Describes the behavior that should be taken for an __if_exists
   /// block.
Index: include/clang/Sema/CudaUnrollHint.h
===================================================================
--- include/clang/Sema/CudaUnrollHint.h
+++ include/clang/Sema/CudaUnrollHint.h
@@ -0,0 +1,28 @@
+//===--- CudaUnrollHint.h - Types for CudaUnrollHint ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SEMA_CUDAUNROLLHINT_H
+#define LLVM_CLANG_SEMA_CUDAUNROLLHINT_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/AttributeList.h"
+
+namespace clang {
+
+/// \brief Loop hint specified by a pragma loop directive.
+struct CudaUnrollHint {
+  IdentifierLoc *UnrollLoc;
+  SourceRange Range;
+  IdentifierLoc *ValueLoc;
+  Expr *ValueExpr;
+};
+
+}  // end namespace clang
+
+#endif  // LLVM_CLANG_SEMA_CUDAUNROLLHINT_H
Index: lib/CodeGen/CGStmt.cpp
===================================================================
--- lib/CodeGen/CGStmt.cpp
+++ lib/CodeGen/CGStmt.cpp
@@ -537,17 +537,40 @@
   // Add vectorize and unroll hints to the metadata on the conditional branch.
   SmallVector<llvm::Value *, 2> Metadata(1);
   for (const auto *Attr : Attrs) {
-    const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(Attr);
-
-    // Skip non loop hint attributes
-    if (!LH)
+    std::pair<llvm::MDString *, llvm::Value *> MetadataPair;
+    if (isa<LoopHintAttr>(Attr))
+      MetadataPair = MetadataForLoopHintAttr(cast<LoopHintAttr>(Attr), Context);
+    else if (isa<CudaUnrollHintAttr>(Attr))
+      MetadataPair = MetadataForCudaUnrollHintAttr(
+          cast<CudaUnrollHintAttr>(Attr), Context);
+    else
       continue;
 
-    LoopHintAttr::OptionType Option = LH->getOption();
-    int ValueInt = LH->getValue();
+    SmallVector<llvm::Value *, 2> OpValues;
+    OpValues.push_back(MetadataPair.first);
+    OpValues.push_back(MetadataPair.second);
+
+    // Set or overwrite metadata indicated by Name.
+    Metadata.push_back(llvm::MDNode::get(Context, OpValues));
+  }
+
+  if (!Metadata.empty()) {
+    // Add llvm.loop MDNode to CondBr.
+    llvm::MDNode *LoopID = llvm::MDNode::get(Context, Metadata);
+    LoopID->replaceOperandWith(0, LoopID); // First op points to itself.
+
+    CondBr->setMetadata("llvm.loop", LoopID);
+  }
+}
+
+std::pair<llvm::MDString *, llvm::Value *>
+CodeGenFunction::MetadataForLoopHintAttr(const LoopHintAttr *LH,
+                                         llvm::LLVMContext &Context) {
+  LoopHintAttr::OptionType Option = LH->getOption();
+  int ValueInt = LH->getValue();
 
-    const char *MetadataName;
-    switch (Option) {
+  const char *MetadataName;
+  switch (Option) {
     case LoopHintAttr::Vectorize:
     case LoopHintAttr::VectorizeWidth:
       MetadataName = "llvm.loop.vectorize.width";
@@ -562,11 +585,11 @@
     case LoopHintAttr::UnrollCount:
       MetadataName = "llvm.loop.unroll.count";
       break;
-    }
+  }
 
-    llvm::Value *Value;
-    llvm::MDString *Name;
-    switch (Option) {
+  llvm::Value *Value;
+  llvm::MDString *Name;
+  switch (Option) {
     case LoopHintAttr::Vectorize:
     case LoopHintAttr::Interleave:
       if (ValueInt == 1) {
@@ -592,23 +615,26 @@
       Name = llvm::MDString::get(Context, MetadataName);
       Value = llvm::ConstantInt::get(Int32Ty, ValueInt);
       break;
-    }
-
-    SmallVector<llvm::Value *, 2> OpValues;
-    OpValues.push_back(Name);
-    OpValues.push_back(Value);
-
-    // Set or overwrite metadata indicated by Name.
-    Metadata.push_back(llvm::MDNode::get(Context, OpValues));
   }
+  return std::make_pair(Name, Value);
+}
 
-  if (!Metadata.empty()) {
-    // Add llvm.loop MDNode to CondBr.
-    llvm::MDNode *LoopID = llvm::MDNode::get(Context, Metadata);
-    LoopID->replaceOperandWith(0, LoopID); // First op points to itself.
-
-    CondBr->setMetadata("llvm.loop", LoopID);
+std::pair<llvm::MDString *, llvm::Value *>
+CodeGenFunction::MetadataForCudaUnrollHintAttr(const CudaUnrollHintAttr *CU,
+                                               llvm::LLVMContext &Context) {
+  const char *MetadataName;
+  llvm::Value *Value;
+  int ValueInt = CU->getValue();
+  if (ValueInt) {
+    MetadataName = "llvm.loop.unroll.count";
+    Value = llvm::ConstantInt::get(Int32Ty, ValueInt);
+  } else {
+    // A value of zero indicates that the optional unroll count was not
+    // specified with the unroll pragma.
+    MetadataName = "llvm.loop.unroll.enable";
+    Value = Builder.getTrue();
   }
+  return std::make_pair(llvm::MDString::get(Context, MetadataName), Value);
 }
 
 void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -1420,6 +1420,13 @@
   /// expression and compare the result against zero, returning an Int1Ty value.
   llvm::Value *EvaluateExprAsBool(const Expr *E);
 
+  /// Return a metadata string/value pair corresponding to the loop or CUDA
+  /// unroll hint attribute.
+  std::pair<llvm::MDString *, llvm::Value *> MetadataForLoopHintAttr(
+      const LoopHintAttr *LH, llvm::LLVMContext &Context);
+  std::pair<llvm::MDString *, llvm::Value *> MetadataForCudaUnrollHintAttr(
+      const CudaUnrollHintAttr *CU, llvm::LLVMContext &Context);
+
   /// EmitIgnoredExpr - Emit an expression in a context which ignores the result.
   void EmitIgnoredExpr(const Expr *E);
 
Index: lib/Parse/ParsePragma.cpp
===================================================================
--- lib/Parse/ParsePragma.cpp
+++ lib/Parse/ParsePragma.cpp
@@ -15,6 +15,7 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Parse/Parser.h"
+#include "clang/Sema/CudaUnrollHint.h"
 #include "clang/Sema/LoopHint.h"
 #include "clang/Sema/Scope.h"
 #include "llvm/ADT/StringSwitch.h"
@@ -148,6 +149,12 @@
                     Token &FirstToken) override;
 };
 
+struct PragmaCudaUnrollHandler : public PragmaHandler {
+  PragmaCudaUnrollHandler() : PragmaHandler("unroll") {}
+  void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
+                    Token &FirstToken) override;
+};
+
 }  // end namespace
 
 void Parser::initializePragmaHandlers() {
@@ -218,6 +225,11 @@
 
   LoopHintHandler.reset(new PragmaLoopHintHandler());
   PP.AddPragmaHandler("clang", LoopHintHandler.get());
+
+  if (getLangOpts().CUDA) {
+    CudaUnrollHandler.reset(new PragmaCudaUnrollHandler());
+    PP.AddPragmaHandler(CudaUnrollHandler.get());
+  }
 }
 
 void Parser::resetPragmaHandlers() {
@@ -278,6 +290,11 @@
 
   PP.RemovePragmaHandler("clang", LoopHintHandler.get());
   LoopHintHandler.reset();
+
+  if (getLangOpts().CUDA) {
+    PP.RemovePragmaHandler(CudaUnrollHandler.get());
+    CudaUnrollHandler.reset();
+  }
 }
 
 /// \brief Handle the annotation token produced for #pragma unused(...)
@@ -633,6 +650,42 @@
   return Hint;
 }
 
+struct PragmaCudaUnrollInfo {
+  Token Unroll;
+  Token Value;
+  bool HasValue;
+};
+
+CudaUnrollHint Parser::HandlePragmaCudaUnroll() {
+  assert(Tok.is(tok::annot_pragma_cuda_unroll));
+  PragmaCudaUnrollInfo *Info =
+      static_cast<PragmaCudaUnrollInfo *>(Tok.getAnnotationValue());
+
+  CudaUnrollHint Hint;
+  Hint.UnrollLoc =
+      IdentifierLoc::create(Actions.Context, Info->Unroll.getLocation(),
+                            Info->Unroll.getIdentifierInfo());
+  if (Info->HasValue) {
+    Hint.Range =
+        SourceRange(Info->Unroll.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->Unroll.getLocation());
+    Hint.ValueLoc = nullptr;
+    Hint.ValueExpr = nullptr;
+  }
+
+  return Hint;
+}
+
 // #pragma GCC visibility comes in two variants:
 //   'push' '(' [visibility] ')'
 //   'pop'
@@ -1755,3 +1808,44 @@
                       /*DisableMacroExpansion=*/false,
                       /*OwnsTokens=*/true);
 }
+
+void PragmaCudaUnrollHandler::HandlePragma(Preprocessor &PP,
+                                           PragmaIntroducerKind Introducer,
+                                           Token &Tok) {
+  // "unroll" token from "#pragma unroll...".
+  Token Unroll = Tok;
+
+  bool HasValue;
+  Token Value;
+  PP.Lex(Tok);
+  if (Tok.is(tok::eod)) {
+    // Bare unroll pragma: #pragma unroll
+    HasValue = false;
+  } else {
+    // Unroll pragma with numeric argument: #pragma unroll N
+    HasValue = true;
+    Value = Tok;
+
+    PP.Lex(Tok);
+    if (Tok.isNot(tok::eod)) {
+      PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol)
+          << "unroll";
+      return;
+    }
+  }
+
+  auto *Info = new (PP.getPreprocessorAllocator()) PragmaCudaUnrollInfo;
+  Info->Unroll = Unroll;
+  Info->HasValue = HasValue;
+  if (HasValue)
+    Info->Value = Value;
+
+  Token *TokenArray = new Token[1];
+  TokenArray[0].startToken();
+  TokenArray[0].setKind(tok::annot_pragma_cuda_unroll);
+  TokenArray[0].setLocation(Unroll.getLocation());
+  TokenArray[0].setAnnotationValue(static_cast<void *>(Info));
+  PP.EnterTokenStream(TokenArray, 1,
+                      /*DisableMacroExpansion=*/false,
+                      /*OwnsTokens=*/true);
+}
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp
+++ lib/Parse/ParseStmt.cpp
@@ -355,6 +355,10 @@
   case tok::annot_pragma_loop_hint:
     ProhibitAttributes(Attrs);
     return ParsePragmaLoopHint(Stmts, OnlyStatement, TrailingElseLoc, Attrs);
+
+  case tok::annot_pragma_cuda_unroll:
+    ProhibitAttributes(Attrs);
+    return ParsePragmaCudaUnroll(Stmts, OnlyStatement, TrailingElseLoc, Attrs);
   }
 
   // If we reached this code, the statement must end in a semicolon.
@@ -1828,6 +1832,26 @@
   return S;
 }
 
+StmtResult Parser::ParsePragmaCudaUnroll(StmtVector &Stmts, bool OnlyStatement,
+                                         SourceLocation *TrailingElseLoc,
+                                         ParsedAttributesWithRange &Attrs) {
+  assert(Tok.is(tok::annot_pragma_cuda_unroll));
+
+  CudaUnrollHint Hint = HandlePragmaCudaUnroll();
+  ConsumeToken();
+
+  // Get the next statement.
+  MaybeParseCXX11Attributes(Attrs);
+
+  StmtResult S = ParseStatementOrDeclarationAfterAttributes(
+      Stmts, OnlyStatement, TrailingElseLoc, Attrs);
+
+  ArgsUnion ArgHints[] = {Hint.ValueLoc, ArgsUnion(Hint.ValueExpr)};
+  Attrs.addNew(Hint.UnrollLoc->Ident, Hint.Range, nullptr, Hint.UnrollLoc->Loc,
+               ArgHints, 3, AttributeList::AS_Pragma);
+  return S;
+}
+
 Decl *Parser::ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope) {
   assert(Tok.is(tok::l_brace));
   SourceLocation LBraceLoc = Tok.getLocation();
Index: lib/Sema/SemaStmtAttr.cpp
===================================================================
--- lib/Sema/SemaStmtAttr.cpp
+++ lib/Sema/SemaStmtAttr.cpp
@@ -49,7 +49,8 @@
       St->getStmtClass() != Stmt::ForStmtClass &&
       St->getStmtClass() != Stmt::CXXForRangeStmtClass &&
       St->getStmtClass() != Stmt::WhileStmtClass) {
-    S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop);
+    S.Diag(St->getLocStart(), diag::err_pragma_loop_precedes_nonloop)
+        << "clang loop";
     return nullptr;
   }
 
@@ -104,8 +105,46 @@
                                       A.getRange());
 }
 
-static void
-CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
+static Attr *handleCudaUnrollHintAttr(Sema &S, Stmt *St, const AttributeList &A,
+                                      SourceRange) {
+  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)
+        << "unroll";
+    return nullptr;
+  }
+
+  IdentifierLoc *ValueLoc = A.getArgAsIdent(0);
+  int ValueInt = 0;
+  if (ValueLoc) {
+    Expr *ValueExpr = A.getArgAsExpr(1);
+    // FIXME: We should support template parameters for the loop hint value.
+    // See bug report #19610.
+    llvm::APSInt ValueAPS;
+    if (!ValueExpr || !ValueExpr->isIntegerConstantExpr(ValueAPS, S.Context) ||
+        (ValueInt = ValueAPS.getSExtValue()) < 1) {
+      S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_value);
+      return nullptr;
+    }
+  }
+  return CudaUnrollHintAttr::CreateImplicit(S.Context, ValueInt, A.getRange());
+}
+
+static void CheckForIncompatibleAttributes(
+    Sema &S, SmallVectorImpl<const Attr *> &Attrs) {
+  bool HasCudaUnroll = false;
+  for (const auto *I : Attrs) {
+    const CudaUnrollHintAttr *CU = dyn_cast<CudaUnrollHintAttr>(I);
+    if (!CU)
+      continue;
+
+    if (HasCudaUnroll)
+      S.Diag(CU->getLocation(), diag::err_duplicate_cuda_unroll_pragma);
+    HasCudaUnroll = true;
+  }
+
   // 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:
@@ -136,19 +175,20 @@
     int Option = LH->getOption();
     int ValueInt = LH->getValue();
 
+    enum { Vectorize = 0, Interleave = 1, Unroll = 2 };
     int Category;
     switch (Option) {
     case LoopHintAttr::Vectorize:
     case LoopHintAttr::VectorizeWidth:
-      Category = 0;
+      Category = Vectorize;
       break;
     case LoopHintAttr::Interleave:
     case LoopHintAttr::InterleaveCount:
-      Category = 1;
+      Category = Interleave;
       break;
     case LoopHintAttr::Unroll:
     case LoopHintAttr::UnrollCount:
-      Category = 2;
+      Category = Unroll;
       break;
     };
 
@@ -191,6 +231,12 @@
           << LoopHintAttr::getOptionName(CategoryState.NumericOptionId)
           << CategoryState.Value;
     }
+
+    // "#pragma loop unroll*()" and CUDA "#pragma unroll" directives are
+    // incompatible.
+    if (Category == Unroll && HasCudaUnroll)
+      S.Diag(ValueLoc, diag::err_incompatible_pragma_loop_unroll)
+          << LoopHintAttr::getOptionName(Option);
   }
 }
 
@@ -206,6 +252,8 @@
     return handleFallThroughAttr(S, St, A, Range);
   case AttributeList::AT_LoopHint:
     return handleLoopHintAttr(S, St, A, Range);
+  case AttributeList::AT_CudaUnrollHint:
+    return handleCudaUnrollHintAttr(S, St, A, Range);
   default:
     // if we're here, then we parsed a known attribute, but didn't recognize
     // it as a statement attribute => it is declaration attribute
Index: test/CodeGen/cuda-pragma-unroll.cu
===================================================================
--- test/CodeGen/cuda-pragma-unroll.cu
+++ test/CodeGen/cuda-pragma-unroll.cu
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -x cuda -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -x c++ -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck -check-prefix=NOTCUDA %s
+
+// If -x cuda is not specified, then the unroll pragma should not be recognized
+// and no loop unroll metadata should be emitted.
+// NOTCUDA-NOT: llvm.loop.unroll
+
+void while_test(int *List, int Length) {
+  int i = 0;
+#pragma unroll
+  while (i < Length) {
+    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_1:.*]]
+    List[i] = i * 2;
+    i++;
+  }
+}
+
+void do_test(int *List, int Length) {
+  int i = 0;
+#pragma unroll
+  do {
+    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_2:.*]]
+    List[i] = i * 2;
+    i++;
+  } while (i < Length);
+}
+
+void for_test(int *List, int Length) {
+#pragma unroll
+  for (int i = 0; i < Length; i++) {
+    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_3:.*]]
+    List[i] = i * 2;
+  }
+}
+
+void range_test(int *List, int Length) {
+  int VList[Length];
+#pragma unroll
+  for (int j : VList) {
+    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_4:.*]]
+    VList[j] = List[j];
+  }
+}
+
+void for_unroll_count_test(int *List, int Length) {
+#pragma unroll 16
+  for (int i = 0; i < Length; i++) {
+    // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_5:.*]]
+    List[i] = i * 2;
+  }
+}
+
+// CHECK: ![[LOOP_1]] = metadata !{metadata ![[LOOP_1]], metadata ![[UNROLL_ENABLE:.*]]}
+// CHECK: ![[UNROLL_ENABLE]] = metadata !{metadata !"llvm.loop.unroll.enable", i1 true}
+// CHECK: ![[LOOP_2]] = metadata !{metadata ![[LOOP_2]], metadata ![[UNROLL_ENABLE:.*]]}
+// CHECK: ![[LOOP_3]] = metadata !{metadata ![[LOOP_3]], metadata ![[UNROLL_ENABLE:.*]]}
+// CHECK: ![[LOOP_4]] = metadata !{metadata ![[LOOP_4]], metadata ![[UNROLL_ENABLE:.*]]}
+// CHECK: ![[LOOP_5]] = metadata !{metadata ![[LOOP_5]], metadata ![[UNROLL_COUNT_16:.*]]}
+// CHECK: ![[UNROLL_COUNT_16]] = metadata !{metadata !"llvm.loop.unroll.count", i32 16}
Index: test/Misc/ast-print-cuda-pragmas.cu
===================================================================
--- test/Misc/ast-print-cuda-pragmas.cu
+++ test/Misc/ast-print-cuda-pragmas.cu
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -x cuda -ast-print -o - %s | FileCheck %s
+// RUN: %clang_cc1 -x c++ -ast-print -o - %s | FileCheck -check-prefix=NOTCUDA %s
+
+// If -x cuda is not specified, then the unroll pragma should not be recognized
+// and loop unroll pragmas should be emitted.
+// NOTCUDA-NOT: #pragma unroll
+
+void test(int *List, int Length) {
+  int i = 0;
+#pragma unroll
+// CHECK: #pragma unroll
+// CHECK-NEXT: for (int i = 0; i < Length; i++)
+  for (int i = 0; i < Length; i++) {
+    List[i] = i * 2;
+  }
+
+#pragma unroll 8
+// CHECK: #pragma unroll 8
+// CHECK-NEXT: for (int i = 0; i < Length; i++)
+  for (int i = 0; i < Length; i++) {
+    List[i] = i * 2;
+  }
+}
Index: test/PCH/cuda-pragma-unroll.cu
===================================================================
--- test/PCH/cuda-pragma-unroll.cu
+++ test/PCH/cuda-pragma-unroll.cu
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -x cuda -emit-pch -o %t.cuda.a %s
+// RUN: %clang_cc1 -x cuda -include-pch %t.cuda.a -ast-print -o - %s | FileCheck %s
+// RUN: %clang_cc1 -x c++ -emit-pch -o %t.notcuda.a %s
+// RUN: %clang_cc1 -x c++ -include-pch %t.notcuda.a -ast-print -o - %s | FileCheck -check-prefix=NOTCUDA %s
+
+// If -x cuda is not specified, then the unroll pragma should not be recognized
+// and loop unroll pragmas should be emitted.
+// NOTCUDA-NOT: #pragma unroll
+
+// CHECK: #pragma unroll
+// CHECK: #pragma unroll 16
+
+#ifndef HEADER
+#define HEADER
+
+void unroll_test(int *List, int Length) {
+#pragma unroll
+  for (int i = 0; i < Length; i++) {
+    List[i] = i * 2;
+  }
+}
+
+void unroll_count_test(int *List, int Length) {
+#pragma unroll 16
+  for (int i = 0; i < Length; i++) {
+    List[i] = i * 2;
+  }
+}
+
+#endif
Index: test/Parser/cuda-pragma-unroll.cu
===================================================================
--- test/Parser/cuda-pragma-unroll.cu
+++ test/Parser/cuda-pragma-unroll.cu
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -x cuda -verify %s
+
+// Note that this puts the expected lines before the directives to work around
+// limitations in the -verify mode.
+
+void test(int *List) {
+
+#pragma unroll
+  for (int i = 0; i < 1024; ++i) {
+    List[i] = i;
+  }
+
+#pragma unroll 16
+  for (int i = 0; i < 1024; ++i) {
+    List[i] = i;
+  }
+
+#pragma unroll
+/* expected-error {{'#pragma unroll' and '#pragma clang loop unroll_count' cannot both be specified}} */ #pragma clang loop unroll_count(4)
+  for (int i = 0; i < 1024; ++i) {
+    List[i] = i;
+  }
+
+#pragma unroll
+/* expected-error {{duplicate '#pragma unroll' directives}} */ #pragma unroll
+  for (int i = 0; i < 1024; ++i) {
+    List[i] = i;
+  }
+
+#pragma unroll 4
+/* expected-error {{duplicate '#pragma unroll' directives}} */ #pragma unroll
+  for (int i = 0; i < 1024; ++i) {
+    List[i] = i;
+  }
+
+/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll 0
+/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll garbage
+/* expected-warning {{extra tokens at end of '#pragma unroll'}} */ #pragma unroll 1 2
+/* expected-error {{invalid argument; expected a positive integer value}} */ #pragma unroll +
+  for (int i = 0; i < 1024; ++i) {
+    List[i] = i;
+  }
+
+#pragma unroll
+/* expected-error {{expected a for, while, or do-while loop to follow the '#pragma unroll' directive}} */ List[0] = List[1];
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to