[Pragma] Added support for GCC unroll/nounroll

GCC 8 introduced these new pragmas to control loop unrolling. We should support them for compatibility reasons and the implementation itself requires few lines of code, since everything needed is already implemented for #pragma unroll/nounroll.

GitOrigin-RevId: 12a1f1d9d7e4f7ce416d0602d18991973986dfb5
diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td
index 867865e..0af5b79 100644
--- a/include/clang/Basic/AttrDocs.td
+++ b/include/clang/Basic/AttrDocs.td
@@ -3243,7 +3243,9 @@
   let Content = [{
 Loop unrolling optimization hints can be specified with ``#pragma unroll`` and
 ``#pragma nounroll``. The pragma is placed immediately before a for, while,
-do-while, or c++11 range-based for loop.
+do-while, or c++11 range-based for loop. GCC's loop unrolling hints
+``#pragma GCC unroll`` and ``#pragma GCC nounroll`` are also supported and have
+identical semantics to ``#pragma unroll`` and ``#pragma nounroll``.
 
 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 and
diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp
index 4ce8e4c..660d317 100644
--- a/lib/Parse/ParsePragma.cpp
+++ b/lib/Parse/ParsePragma.cpp
@@ -405,9 +405,11 @@
 
   UnrollHintHandler = std::make_unique<PragmaUnrollHintHandler>("unroll");
   PP.AddPragmaHandler(UnrollHintHandler.get());
+  PP.AddPragmaHandler("GCC", UnrollHintHandler.get());
 
   NoUnrollHintHandler = std::make_unique<PragmaUnrollHintHandler>("nounroll");
   PP.AddPragmaHandler(NoUnrollHintHandler.get());
+  PP.AddPragmaHandler("GCC", NoUnrollHintHandler.get());
 
   UnrollAndJamHintHandler =
       std::make_unique<PragmaUnrollHintHandler>("unroll_and_jam");
@@ -523,9 +525,11 @@
   LoopHintHandler.reset();
 
   PP.RemovePragmaHandler(UnrollHintHandler.get());
+  PP.RemovePragmaHandler("GCC", UnrollHintHandler.get());
   UnrollHintHandler.reset();
 
   PP.RemovePragmaHandler(NoUnrollHintHandler.get());
+  PP.RemovePragmaHandler("GCC", NoUnrollHintHandler.get());
   NoUnrollHintHandler.reset();
 
   PP.RemovePragmaHandler(UnrollAndJamHintHandler.get());
diff --git a/test/CodeGenCXX/pragma-gcc-unroll.cpp b/test/CodeGenCXX/pragma-gcc-unroll.cpp
new file mode 100644
index 0000000..ed75e0b
--- /dev/null
+++ b/test/CodeGenCXX/pragma-gcc-unroll.cpp
@@ -0,0 +1,109 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - %s | FileCheck %s
+
+// Check that passing -fno-unroll-loops does not impact the decision made using pragmas.
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++11 -emit-llvm -o - -O1 -disable-llvm-optzns -fno-unroll-loops %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 GCC unroll
+  while (i < Length) {
+    // CHECK: br 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) {
+  // CHECK: define {{.*}} @_Z7do_test
+  int i = 0;
+
+#pragma GCC nounroll
+  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) {
+// CHECK: define {{.*}} @_Z8for_test
+#pragma GCC unroll 8
+  for (int i = 0; i < Length; i++) {
+    // CHECK: br label {{.*}}, !llvm.loop ![[LOOP_3:.*]]
+    List[i] = i * 2;
+  }
+}
+
+// Verify c++11 for range loop is recognized after unroll pragma.
+void for_range_test() {
+  // CHECK: define {{.*}} @_Z14for_range_test
+  double List[100];
+
+#pragma GCC unroll(4)
+  for (int i : List) {
+    // CHECK: br 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) {
+// CHECK: define {{.*}} @_Z15for_define_test
+#pragma GCC unroll(UNROLLCOUNT)
+  for (int i = 0; i < Length; i++) {
+    // CHECK: br 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) {
+// CHECK: define {{.*}} @_Z13template_test
+#pragma GCC unroll 8
+  for (int i = 0; i < Length; i++) {
+    // CHECK: br 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) {
+// CHECK: define {{.*}} @_Z24for_template_define_test
+
+#pragma GCC unroll(UNROLLCOUNT)
+  for (int i = 0; i < Length; i++) {
+    // CHECK: br 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]] = distinct !{![[LOOP_1]], [[MP:![0-9]+]], ![[UNROLL_ENABLE:.*]]}
+// CHECK: ![[UNROLL_ENABLE]] = !{!"llvm.loop.unroll.enable"}
+// CHECK: ![[LOOP_2]] = distinct !{![[LOOP_2:.*]], ![[UNROLL_DISABLE:.*]]}
+// CHECK: ![[UNROLL_DISABLE]] = !{!"llvm.loop.unroll.disable"}
+// CHECK: ![[LOOP_3]] = distinct !{![[LOOP_3]], [[MP]], ![[UNROLL_8:.*]]}
+// CHECK: ![[UNROLL_8]] = !{!"llvm.loop.unroll.count", i32 8}
+// CHECK: ![[LOOP_4]] = distinct !{![[LOOP_4]], ![[UNROLL_4:.*]]}
+// CHECK: ![[UNROLL_4]] = !{!"llvm.loop.unroll.count", i32 4}
+// CHECK: ![[LOOP_5]] = distinct !{![[LOOP_5]], ![[UNROLL_8:.*]]}
+// CHECK: ![[LOOP_6]] = distinct !{![[LOOP_6]], ![[UNROLL_8:.*]]}
+// CHECK: ![[LOOP_7]] = distinct !{![[LOOP_7]], ![[UNROLL_8:.*]]}