]> granicus.if.org Git - clang/commitdiff
In unroll pragma syntax and loop hint metadata, change "enable" forms to a new form...
authorMark Heffernan <meheff@google.com>
Wed, 23 Jul 2014 17:31:31 +0000 (17:31 +0000)
committerMark Heffernan <meheff@google.com>
Wed, 23 Jul 2014 17:31:31 +0000 (17:31 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@213771 91177308-0d34-0410-b5e6-96231b3b80d8

13 files changed:
docs/LanguageExtensions.rst
include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
lib/CodeGen/CGStmt.cpp
lib/Parse/ParsePragma.cpp
lib/Sema/SemaStmtAttr.cpp
test/CodeGen/pragma-loop.cpp
test/CodeGen/pragma-unroll.cpp
test/PCH/pragma-loop.cpp
test/Parser/pragma-loop.cpp
test/Parser/pragma-unroll.cpp

index 50e1ccebe87171146ba6016950eba04f8aee7dd7..26c1418cb61f5f2fa71940cff33e5fb01c4dbad0 100644 (file)
@@ -1843,7 +1843,7 @@ iterations. Full unrolling is only possible if the loop trip count is known at
 compile time. Partial unrolling replicates the loop body within the loop and
 reduces the trip count.
 
-If ``unroll(enable)`` is specified the unroller will attempt to fully unroll the
+If ``unroll(full)`` is specified the unroller will attempt to fully unroll the
 loop if the trip count is known at compile time. If the loop count is not known
 or the fully unrolled code size is greater than the limit specified by the
 `-pragma-unroll-threshold` command line option the loop will be partially
@@ -1851,7 +1851,7 @@ unrolled subject to the same limit.
 
 .. code-block:: c++
 
-  #pragma clang loop unroll(enable)
+  #pragma clang loop unroll(full)
   for(...) {
     ...
   }
index 704a375ba29184b701c3066cda807b3ad403ef7c..708f4f49bdbabdbf581b0a3fb49cc102c4137213 100644 (file)
@@ -1788,7 +1788,7 @@ def LoopHint : Attr {
   /// vectorize_width: vectorize loop operations with width 'value'.
   /// interleave: interleave multiple loop iterations if 'value != 0'.
   /// interleave_count: interleaves 'value' loop interations.
-  /// unroll: unroll loop if 'value != 0'.
+  /// unroll: fully unroll loop if 'value != 0'.
   /// unroll_count: unrolls loop 'value' times.
 
   let Spellings = [Pragma<"clang", "loop">, Pragma<"", "unroll">];
@@ -1842,6 +1842,10 @@ def LoopHint : Attr {
     if (option == VectorizeWidth || option == InterleaveCount ||
         option == UnrollCount)
       OS << value;
+    else if (option == Unroll && value)
+      // Unroll loop hint does not use the keyword "enable". Instead, a nonzero value
+      // indicates full unrolling which uses the keyword "full".
+      OS << "full";
     else if (value)
       OS << "enable";
     else
index 758705b4ad1a2d34daf4b8221d56fb04a043b4f3..655cc45ab94c6178bc509683bb3d364f4e9ddd6b 100644 (file)
@@ -1097,7 +1097,7 @@ enclosed in parentheses:
   }
 
 ``#pragma unroll`` and ``#pragma unroll _value_`` have identical semantics to
-``#pragma clang loop unroll(enable)`` and ``#pragma clang loop
+``#pragma clang loop unroll(full)`` 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 35ed795c9fd7e6f8b344653442e4edfee1421197..38a4eac123f73e3b2c2742a7fe8b6a9c40795343 100644 (file)
@@ -818,7 +818,7 @@ 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">;
+  "missing argument to '#pragma %0'%select{|; expected %2}1">;
 // - #pragma options
 def warn_pragma_options_expected_align : Warning<
   "expected 'align' following '#pragma options' - ignored">,
index 85072f1d1567f5091ae2efaebfabf50d9db4aff6..9c6ffc0517857445a5cbe005d76f62f16f8ca1e6 100644 (file)
@@ -539,7 +539,7 @@ def note_surrounding_namespace_starts_here : Note<
 def err_pragma_loop_invalid_value : Error<
   "invalid argument; expected a positive integer value">;
 def err_pragma_loop_invalid_keyword : Error<
-  "invalid argument; expected 'enable' or 'disable'">;
+  "invalid argument; expected '%0' or 'disable'">;
 def err_pragma_loop_compatibility : Error<
   "%select{incompatible|duplicate}0 directives '%1' and '%2'">;
 def err_pragma_loop_precedes_nonloop : Error<
index fdeaaf6bb8882fef0d5c052c79da81153a281ea7..62a80a334ef5b85557d3b4c6a6559b4160c41199 100644 (file)
@@ -601,13 +601,14 @@ void CodeGenFunction::EmitCondBrHints(llvm::LLVMContext &Context,
       MetadataName = "llvm.loop.interleave.count";
       break;
     case LoopHintAttr::Unroll:
-      MetadataName = "llvm.loop.unroll.enable";
+      // With the unroll loop hint, a non-zero value indicates full unrolling.
+      MetadataName =
+          ValueInt == 0 ? "llvm.loop.unroll.disable" : "llvm.loop.unroll.full";
       break;
     case LoopHintAttr::UnrollCount:
       MetadataName = "llvm.loop.unroll.count";
       break;
     }
-
     llvm::Value *Value;
     llvm::MDString *Name;
     switch (Option) {
@@ -625,22 +626,20 @@ void CodeGenFunction::EmitCondBrHints(llvm::LLVMContext &Context,
       // Fallthrough.
     case LoopHintAttr::VectorizeWidth:
     case LoopHintAttr::InterleaveCount:
+    case LoopHintAttr::UnrollCount:
       Name = llvm::MDString::get(Context, MetadataName);
       Value = llvm::ConstantInt::get(Int32Ty, ValueInt);
       break;
     case LoopHintAttr::Unroll:
       Name = llvm::MDString::get(Context, MetadataName);
-      Value = (ValueInt == 0) ? Builder.getFalse() : Builder.getTrue();
-      break;
-    case LoopHintAttr::UnrollCount:
-      Name = llvm::MDString::get(Context, MetadataName);
-      Value = llvm::ConstantInt::get(Int32Ty, ValueInt);
+      Value = nullptr;
       break;
     }
 
     SmallVector<llvm::Value *, 2> OpValues;
     OpValues.push_back(Name);
-    OpValues.push_back(Value);
+    if (Value)
+      OpValues.push_back(Value);
 
     // Set or overwrite metadata indicated by Name.
     Metadata.push_back(llvm::MDNode::get(Context, OpValues));
index b94c4bc9448ab2f016b2492e9be88d993ca16692..5fb999d749c6cf4f9fa8a3cedce9bb25612a6fe6 100644 (file)
@@ -1719,8 +1719,7 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP,
   PP.Lex(Tok);
   if (Tok.is(tok::eod)) {
     PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument)
-        << "clang optimize"
-        << "'on' or 'off'";
+        << "clang optimize" << /*Expected=*/true << "'on' or 'off'";
     return;
   }
   if (Tok.isNot(tok::identifier)) {
@@ -1767,8 +1766,12 @@ static bool ParseLoopHintValue(Preprocessor &PP, Token Tok, Token &PragmaName,
                "Unexpected pragma name");
         PragmaString = "unroll";
       }
+      // Don't try to emit what the pragma is expecting with the diagnostic
+      // because the logic is non-trivial and we give expected values in sema
+      // diagnostics if an invalid argument is given.  Here, just note that the
+      // pragma is missing an argument.
       PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument)
-          << PragmaString << "a positive integer value";
+          << PragmaString << /*Expected=*/false;
       return true;
     }
   }
@@ -1800,7 +1803,7 @@ static bool ParseLoopHintValue(Preprocessor &PP, Token Tok, Token &PragmaName,
 ///  loop-hint:
 ///    'vectorize' '(' loop-hint-keyword ')'
 ///    'interleave' '(' loop-hint-keyword ')'
-///    'unroll' '(' loop-hint-keyword ')'
+///    'unroll' '(' unroll-hint-keyword ')'
 ///    'vectorize_width' '(' loop-hint-value ')'
 ///    'interleave_count' '(' loop-hint-value ')'
 ///    'unroll_count' '(' loop-hint-value ')'
@@ -1809,6 +1812,10 @@ static bool ParseLoopHintValue(Preprocessor &PP, Token Tok, Token &PragmaName,
 ///    'enable'
 ///    'disable'
 ///
+///  unroll-hint-keyword:
+///    'full'
+///    'disable'
+///
 ///  loop-hint-value:
 ///    constant-expression
 ///
@@ -1823,12 +1830,10 @@ static bool ParseLoopHintValue(Preprocessor &PP, Token Tok, Token &PragmaName,
 /// only works on inner loops.
 ///
 /// The unroll and unroll_count directives control the concatenation
-/// unroller. Specifying unroll(enable) instructs llvm to try to
+/// unroller. Specifying unroll(full) instructs llvm to try to
 /// unroll the loop completely, and unroll(disable) disables unrolling
 /// for the loop. Specifying unroll_count(_value_) instructs llvm to
 /// try to unroll the loop the number of times indicated by the value.
-/// If unroll(enable) and unroll_count are both specified only
-/// unroll_count takes effect.
 void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP,
                                          PragmaIntroducerKind Introducer,
                                          Token &Tok) {
index a32e0fbcb622934d59e5ebd3795d83a2dd871ac4..603581d117be0a7e0922d40aa515e5b1ae3d5469 100644 (file)
@@ -89,16 +89,22 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
   } else if (Option == LoopHintAttr::Vectorize ||
              Option == LoopHintAttr::Interleave ||
              Option == LoopHintAttr::Unroll) {
+    // Unrolling uses the keyword "full" rather than "enable" to indicate full
+    // unrolling.
+    const char *TrueKeyword =
+        Option == LoopHintAttr::Unroll ? "full" : "enable";
     if (!ValueInfo) {
-      S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword);
+      S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword)
+          << TrueKeyword;
       return nullptr;
     }
     if (ValueInfo->isStr("disable"))
       ValueInt = 0;
-    else if (ValueInfo->isStr("enable"))
+    else if (ValueInfo->getName() == TrueKeyword)
       ValueInt = 1;
     else {
-      S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword);
+      S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword)
+          << TrueKeyword;
       return nullptr;
     }
   } else if (Option == LoopHintAttr::VectorizeWidth ||
@@ -121,12 +127,14 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const AttributeList &A,
 
 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.
+  // There are 3 categories of loop hints attributes: vectorize, interleave, and
+  // unroll. Each comes in two variants: a boolean form and a numeric form.  The
+  // boolean hints selectively enables/disables the transformation for the loop
+  // (for unroll, a nonzero value indicates full unrolling rather than enabling
+  // the transformation).  The numeric hint provides an integer hint (for
+  // example, unroll count) to the transformer. The following array accumulates
+  // the hints encountered while iterating through the attributes to check for
+  // compatibility.
   struct {
     const LoopHintAttr *EnableAttr;
     const LoopHintAttr *NumericAttr;
@@ -141,18 +149,19 @@ static void CheckForIncompatibleAttributes(
 
     int Option = LH->getOption();
     int Category;
+    enum { Vectorize, Interleave, Unroll };
     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;
     };
 
@@ -176,10 +185,11 @@ static void CheckForIncompatibleAttributes(
           << /*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.
+    if (CategoryState.EnableAttr && CategoryState.NumericAttr &&
+        (Category == Unroll || !CategoryState.EnableAttr->getValue())) {
+      // Disable hints are not compatible with numeric hints of the same
+      // category.  As a special case, numeric unroll hints are also not
+      // compatible with "enable" form of the unroll pragma, unroll(full).
       S.Diag(OptionLoc, diag::err_pragma_loop_compatibility)
           << /*Duplicate=*/false
           << CategoryState.EnableAttr->getDiagnosticName()
index bdcd304263150e30236d17a7f33b904fea561280..331c5cf20442315cf0773ba78b46a33146b6759f 100644 (file)
@@ -8,7 +8,7 @@ void while_test(int *List, int Length) {
 #pragma clang loop vectorize(enable)
 #pragma clang loop interleave_count(4)
 #pragma clang loop vectorize_width(4)
-#pragma clang loop unroll(enable)
+#pragma clang loop unroll(full)
   while (i < Length) {
     // CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[LOOP_1:.*]]
     List[i] = i * 2;
@@ -121,7 +121,7 @@ void template_test(double *List, int Length) {
 // CHECK: ![[LOOP_4]] = metadata !{metadata ![[LOOP_4]], metadata ![[INTERLEAVE_2:.*]], metadata ![[WIDTH_2:.*]]}
 // CHECK: ![[INTERLEAVE_2]] = metadata !{metadata !"llvm.loop.interleave.count", i32 2}
 // CHECK: ![[WIDTH_2]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 2}
-// CHECK: ![[LOOP_5]] = metadata !{metadata ![[LOOP_5]], metadata ![[UNROLLENABLE_0:.*]], metadata ![[WIDTH_1:.*]]}
+// CHECK: ![[LOOP_5]] = metadata !{metadata ![[LOOP_5]], metadata ![[UNROLL_DISABLE:.*]], metadata ![[WIDTH_1:.*]]}
 // CHECK: ![[WIDTH_1]] = metadata !{metadata !"llvm.loop.vectorize.width", i32 1}
 // CHECK: ![[LOOP_6]] = metadata !{metadata ![[LOOP_6]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_2:.*]], metadata ![[WIDTH_2:.*]]}
 // CHECK: ![[LOOP_7]] = metadata !{metadata ![[LOOP_7]], metadata ![[UNROLL_8:.*]], metadata ![[INTERLEAVE_8:.*]], metadata ![[WIDTH_8:.*]]}
index b321e74a11540335317a3e44bb6c37859eeb51af..9c3617e564c9269cf5f401f59338b1644249a675 100644 (file)
@@ -86,8 +86,8 @@ void template_test(double *List, int Length) {
   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_1]] = metadata !{metadata ![[LOOP_1]], metadata ![[UNROLL_FULL:.*]]}
+// CHECK: ![[UNROLL_FULL]] = metadata !{metadata !"llvm.loop.unroll.full"}
 // 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:.*]]}
index 1456a2778f33aaa2189636d2bb8ecfe160e29d37..670830d912a7a28c0c45561773da497ab813aa71 100644 (file)
@@ -10,7 +10,7 @@
 // CHECK: #pragma clang loop unroll(disable)
 // CHECK: #pragma clang loop interleave(disable)
 // CHECK: #pragma clang loop vectorize(enable)
-// CHECK: #pragma clang loop unroll(enable)
+// CHECK: #pragma clang loop unroll(full)
 // CHECK: #pragma clang loop interleave(enable)
 // CHECK: #pragma clang loop vectorize(disable)
 // CHECK: #pragma unroll
@@ -47,7 +47,7 @@ public:
     int i = 0;
 #pragma clang loop vectorize(disable)
 #pragma clang loop interleave(enable)
-#pragma clang loop unroll(enable)
+#pragma clang loop unroll(full)
     while (i - 3 < Length) {
       List[i] = i;
       i++;
index 23f185d52284b7afa18aba63467552dff82403d0..ac07af9bc778af4265c2c239cdf3e6d5b0ef6ce8 100644 (file)
@@ -8,7 +8,7 @@ void test(int *List, int Length) {
 
 #pragma clang loop vectorize(enable)
 #pragma clang loop interleave(enable)
-#pragma clang loop unroll(enable)
+#pragma clang loop unroll(full)
   while (i + 1 < Length) {
     List[i] = i;
   }
@@ -49,15 +49,15 @@ void test(int *List, int Length) {
 
 /* expected-error {{expected ')'}} */ #pragma clang loop vectorize(enable
 /* expected-error {{expected ')'}} */ #pragma clang loop interleave(enable
-/* expected-error {{expected ')'}} */ #pragma clang loop unroll(enable
+/* expected-error {{expected ')'}} */ #pragma clang loop unroll(full
 
 /* expected-error {{expected ')'}} */ #pragma clang loop vectorize_width(4
 /* expected-error {{expected ')'}} */ #pragma clang loop interleave_count(4
 /* expected-error {{expected ')'}} */ #pragma clang loop unroll_count(4
 
-/* expected-error {{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 argument to '#pragma clang loop vectorize'}} */ #pragma clang loop vectorize()
+/* expected-error {{missing argument to '#pragma clang loop interleave_count'}} */ #pragma clang loop interleave_count()
+/* expected-error {{missing argument to '#pragma clang loop unroll'}} */ #pragma clang loop unroll()
 
 /* expected-error {{missing option}} */ #pragma clang loop
 /* expected-error {{invalid option 'badkeyword'}} */ #pragma clang loop badkeyword
@@ -92,7 +92,7 @@ void test(int *List, int Length) {
 
 /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop vectorize(badidentifier)
 /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop interleave(badidentifier)
-/* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop unroll(badidentifier)
+/* expected-error {{invalid argument; expected 'full' or 'disable'}} */ #pragma clang loop unroll(badidentifier)
   while (i-7 < Length) {
     List[i] = i;
   }
@@ -101,7 +101,7 @@ void test(int *List, int Length) {
 // constants crash FE.
 /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop vectorize(()
 /* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop interleave(*)
-/* expected-error {{invalid argument; expected 'enable' or 'disable'}} */ #pragma clang loop unroll(=)
+/* expected-error {{invalid argument; expected 'full' or 'disable'}} */ #pragma clang loop unroll(=)
 /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop vectorize_width(^)
 /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop interleave_count(/)
 /* expected-error {{invalid argument; expected a positive integer value}} */ #pragma clang loop unroll_count(==)
@@ -136,7 +136,7 @@ void test(int *List, int Length) {
 #pragma clang loop vectorize(disable)
 /* expected-error {{duplicate directives 'interleave(disable)' and 'interleave(enable)'}} */ #pragma clang loop interleave(enable)
 #pragma clang loop interleave(disable)
-/* expected-error {{duplicate directives 'unroll(disable)' and 'unroll(enable)'}} */ #pragma clang loop unroll(enable)
+/* expected-error {{duplicate directives 'unroll(disable)' and 'unroll(full)'}} */ #pragma clang loop unroll(full)
 #pragma clang loop unroll(disable)
   while (i-9 < Length) {
     List[i] = i;
@@ -162,5 +162,12 @@ void test(int *List, int Length) {
     List[i] = i;
   }
 
+
+/* expected-error {{incompatible directives 'unroll(full)' and 'unroll_count(4)'}} */ #pragma clang loop unroll(full)
+#pragma clang loop unroll_count(4)
+  while (i-11 < Length) {
+    List[i] = i;
+  }
+
 #pragma clang loop interleave(enable)
 /* expected-error {{expected statement}} */ }
index 1d89e63028c48abe0a08f5d97f7e339d85e05176..1fa23ef4673b7a59d50cc237ee1f273814dc3710 100644 (file)
@@ -21,26 +21,8 @@ void test(int *List, int 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-error {{missing argument to '#pragma unroll'}} */ #pragma unroll()
 /* expected-warning {{extra tokens at end of '#pragma unroll'}} */ #pragma unroll 1 2
   while (i-6 < Length) {
     List[i] = i;
@@ -67,14 +49,26 @@ void test(int *List, int Length) {
     List[i] = i;
   }
 
+/* expected-error {{incompatible directives 'unroll(full)' and '#pragma unroll(4)'}} */ #pragma unroll(4)
+#pragma clang loop unroll(full)
+  while (i-11 < Length) {
+    List[i] = i;
+  }
+
+/* expected-error {{incompatible directives '#pragma unroll' and '#pragma unroll(4)'}} */ #pragma unroll(4)
+#pragma unroll
+  while (i-11 < 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)
+/* expected-error {{duplicate directives 'unroll(full)' and '#pragma unroll'}} */ #pragma unroll
+#pragma clang loop unroll(full)
   while (i-15 < Length) {
     List[i] = i;
   }