[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.
This commit is contained in:
Dávid Bolvanský 2021-04-17 17:27:11 +02:00
parent d5c0f00e21
commit 12a1f1d9d7
3 changed files with 116 additions and 1 deletions

View File

@ -3243,7 +3243,9 @@ def UnrollHintDocs : Documentation {
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

View File

@ -405,9 +405,11 @@ void Parser::initializePragmaHandlers() {
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 @@ void Parser::resetPragmaHandlers() {
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());

View File

@ -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:.*]]}