Skip to content

Commit 6b52fb2

Browse files
[flang] Correctly handle !dir$ unroll with unrolling factors of 0 and 1 (#126170)
#123331 added support for the unrolling directive. In the presence of an explicit unrolling factor, that unrolling factor would be unconditionally passed into the metadata even when it was 1 or 0. These special cases should instead disable unrolling. Adding an explicit unrolling factor of 0 triggered this assertion which is fixed by this patch: ``` unsigned int unrollCountPragmaValue(const llvm::Loop*): Assertion `Count >= 1 && "Unroll count must be positive."' failed. ``` Updated tests and documentation.
1 parent f3cd223 commit 6b52fb2

File tree

3 files changed

+82
-19
lines changed

3 files changed

+82
-19
lines changed

Diff for: flang/docs/Directives.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,22 @@ A list of non-standard directives supported by Flang
3939
* `!dir$ vector always` forces vectorization on the following loop regardless
4040
of cost model decisions. The loop must still be vectorizable.
4141
[This directive currently only works on plain do loops without labels].
42+
* `!dir$ unroll [n]` specifies that the compiler ought to unroll the immediately
43+
following loop `n` times. When `n` is `0` or `1`, the loop should not be unrolled
44+
at all. When `n` is `2` or greater, the loop should be unrolled exactly `n`
45+
times if possible. When `n` is omitted, the compiler should attempt to fully
46+
unroll the loop. Some compilers accept an optional `=` before the `n` when `n`
47+
is present in the directive. Flang does not.
4248

4349
# Directive Details
4450

4551
## Introduction
4652
Directives are commonly used in Fortran programs to specify additional actions
4753
to be performed by the compiler. The directives are always specified with the
48-
`!dir$` or `cdir$` prefix.
54+
`!dir$` or `cdir$` prefix.
4955

5056
## Loop Directives
57+
5158
Some directives are associated with the following construct, for example loop
5259
directives. Directives on loops are used to specify additional transformation to
5360
be performed by the compiler like enabling vectorisation, unrolling, interchange

Diff for: flang/lib/Lower/Bridge.cpp

+35-12
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include "flang/Semantics/tools.h"
6464
#include "flang/Support/Version.h"
6565
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
66+
#include "mlir/IR/BuiltinAttributes.h"
6667
#include "mlir/IR/Matchers.h"
6768
#include "mlir/IR/PatternMatch.h"
6869
#include "mlir/Parser/Parser.h"
@@ -2170,32 +2171,54 @@ class FirConverter : public Fortran::lower::AbstractConverter {
21702171
return builder->createIntegerConstant(loc, controlType, 1); // step
21712172
}
21722173

2174+
// For unroll directives without a value, force full unrolling.
2175+
// For unroll directives with a value, if the value is greater than 1,
2176+
// force unrolling with the given factor. Otherwise, disable unrolling.
2177+
mlir::LLVM::LoopUnrollAttr
2178+
genLoopUnrollAttr(std::optional<std::uint64_t> directiveArg) {
2179+
mlir::BoolAttr falseAttr =
2180+
mlir::BoolAttr::get(builder->getContext(), false);
2181+
mlir::BoolAttr trueAttr = mlir::BoolAttr::get(builder->getContext(), true);
2182+
mlir::IntegerAttr countAttr;
2183+
mlir::BoolAttr fullUnrollAttr;
2184+
bool shouldUnroll = true;
2185+
if (directiveArg.has_value()) {
2186+
auto unrollingFactor = directiveArg.value();
2187+
if (unrollingFactor == 0 || unrollingFactor == 1) {
2188+
shouldUnroll = false;
2189+
} else {
2190+
countAttr =
2191+
builder->getIntegerAttr(builder->getI64Type(), unrollingFactor);
2192+
}
2193+
} else {
2194+
fullUnrollAttr = trueAttr;
2195+
}
2196+
2197+
mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
2198+
return mlir::LLVM::LoopUnrollAttr::get(
2199+
builder->getContext(), /*disable=*/disableAttr, /*count=*/countAttr, {},
2200+
/*full=*/fullUnrollAttr, {}, {}, {});
2201+
}
2202+
21732203
void addLoopAnnotationAttr(
21742204
IncrementLoopInfo &info,
21752205
llvm::SmallVectorImpl<const Fortran::parser::CompilerDirective *> &dirs) {
2176-
mlir::BoolAttr f = mlir::BoolAttr::get(builder->getContext(), false);
2177-
mlir::BoolAttr t = mlir::BoolAttr::get(builder->getContext(), true);
21782206
mlir::LLVM::LoopVectorizeAttr va;
21792207
mlir::LLVM::LoopUnrollAttr ua;
21802208
bool has_attrs = false;
21812209
for (const auto *dir : dirs) {
21822210
Fortran::common::visit(
21832211
Fortran::common::visitors{
21842212
[&](const Fortran::parser::CompilerDirective::VectorAlways &) {
2213+
mlir::BoolAttr falseAttr =
2214+
mlir::BoolAttr::get(builder->getContext(), false);
21852215
va = mlir::LLVM::LoopVectorizeAttr::get(builder->getContext(),
2186-
/*disable=*/f, {}, {},
2187-
{}, {}, {}, {});
2216+
/*disable=*/falseAttr,
2217+
{}, {}, {}, {}, {}, {});
21882218
has_attrs = true;
21892219
},
21902220
[&](const Fortran::parser::CompilerDirective::Unroll &u) {
2191-
mlir::IntegerAttr countAttr;
2192-
if (u.v.has_value()) {
2193-
countAttr = builder->getIntegerAttr(builder->getI64Type(),
2194-
u.v.value());
2195-
}
2196-
ua = mlir::LLVM::LoopUnrollAttr::get(
2197-
builder->getContext(), /*disable=*/f, /*count*/ countAttr,
2198-
{}, /*full*/ u.v.has_value() ? f : t, {}, {}, {});
2221+
ua = genLoopUnrollAttr(u.v);
21992222
has_attrs = true;
22002223
},
22012224
[&](const auto &) {}},

Diff for: flang/test/Integration/unroll.f90

+39-6
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,47 @@
33
! CHECK-LABEL: unroll_dir
44
subroutine unroll_dir
55
integer :: a(10)
6-
!dir$ unroll
7-
! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[ANNOTATION:.*]]
6+
!dir$ unroll
7+
! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[UNROLL_ENABLE_FULL_ANNO:.*]]
88
do i=1,10
9-
a(i)=i
9+
a(i)=i
1010
end do
1111
end subroutine unroll_dir
1212

13-
! CHECK: ![[ANNOTATION]] = distinct !{![[ANNOTATION]], ![[UNROLL:.*]], ![[UNROLL_FULL:.*]]}
14-
! CHECK: ![[UNROLL]] = !{!"llvm.loop.unroll.enable"}
15-
! CHECK: ![[UNROLL_FULL]] = !{!"llvm.loop.unroll.full"}
13+
! CHECK-LABEL: unroll_dir_0
14+
subroutine unroll_dir_0
15+
integer :: a(10)
16+
!dir$ unroll 0
17+
! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[UNROLL_DISABLE_ANNO:.*]]
18+
do i=1,10
19+
a(i)=i
20+
end do
21+
end subroutine unroll_dir_0
22+
23+
! CHECK-LABEL: unroll_dir_1
24+
subroutine unroll_dir_1
25+
integer :: a(10)
26+
!dir$ unroll 1
27+
! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[UNROLL_DISABLE_ANNO]]
28+
do i=1,10
29+
a(i)=i
30+
end do
31+
end subroutine unroll_dir_1
32+
33+
! CHECK-LABEL: unroll_dir_2
34+
subroutine unroll_dir_2
35+
integer :: a(10)
36+
!dir$ unroll 2
37+
! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[UNROLL_ENABLE_COUNT_2:.*]]
38+
do i=1,10
39+
a(i)=i
40+
end do
41+
end subroutine unroll_dir_2
1642

43+
! CHECK: ![[UNROLL_ENABLE_FULL_ANNO]] = distinct !{![[UNROLL_ENABLE_FULL_ANNO]], ![[UNROLL_ENABLE:.*]], ![[UNROLL_FULL:.*]]}
44+
! CHECK: ![[UNROLL_ENABLE:.*]] = !{!"llvm.loop.unroll.enable"}
45+
! CHECK: ![[UNROLL_FULL:.*]] = !{!"llvm.loop.unroll.full"}
46+
! CHECK: ![[UNROLL_DISABLE_ANNO]] = distinct !{![[UNROLL_DISABLE_ANNO]], ![[UNROLL_DISABLE:.*]]}
47+
! CHECK: ![[UNROLL_DISABLE]] = !{!"llvm.loop.unroll.disable"}
48+
! CHECK: ![[UNROLL_ENABLE_COUNT_2]] = distinct !{![[UNROLL_ENABLE_COUNT_2]], ![[UNROLL_ENABLE]], ![[UNROLL_COUNT_2:.*]]}
49+
! CHECK: ![[UNROLL_COUNT_2]] = !{!"llvm.loop.unroll.count", i32 2}

0 commit comments

Comments
 (0)