diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 11725991e9c9a..8ed44f93a4e8f 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -208,6 +208,7 @@ class ParseTreeDumper { NODE(CompilerDirective, NameValue) NODE(CompilerDirective, Unrecognized) NODE(CompilerDirective, VectorAlways) + NODE(CompilerDirective, Unroll) NODE(parser, ComplexLiteralConstant) NODE(parser, ComplexPart) NODE(parser, ComponentArraySpec) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 00d85aa05fb3a..2a8020a5f9525 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -3368,10 +3368,13 @@ struct CompilerDirective { TUPLE_CLASS_BOILERPLATE(NameValue); std::tuple> t; }; + struct Unroll { + WRAPPER_CLASS_BOILERPLATE(Unroll, std::optional); + }; EMPTY_CLASS(Unrecognized); CharBlock source; std::variant, LoopCount, std::list, - VectorAlways, std::list, Unrecognized> + VectorAlways, std::list, Unroll, Unrecognized> u; }; diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index 37f51d74d23f8..91306f70118bf 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -2153,14 +2153,42 @@ class FirConverter : public Fortran::lower::AbstractConverter { return builder->createIntegerConstant(loc, controlType, 1); // step } - void addLoopAnnotationAttr(IncrementLoopInfo &info) { + void addLoopAnnotationAttr( + IncrementLoopInfo &info, + llvm::SmallVectorImpl &dirs) { mlir::BoolAttr f = mlir::BoolAttr::get(builder->getContext(), false); - mlir::LLVM::LoopVectorizeAttr va = mlir::LLVM::LoopVectorizeAttr::get( - builder->getContext(), /*disable=*/f, {}, {}, {}, {}, {}, {}); + mlir::BoolAttr t = mlir::BoolAttr::get(builder->getContext(), true); + mlir::LLVM::LoopVectorizeAttr va; + mlir::LLVM::LoopUnrollAttr ua; + bool has_attrs = false; + for (const auto *dir : dirs) { + Fortran::common::visit( + Fortran::common::visitors{ + [&](const Fortran::parser::CompilerDirective::VectorAlways &) { + va = mlir::LLVM::LoopVectorizeAttr::get(builder->getContext(), + /*disable=*/f, {}, {}, + {}, {}, {}, {}); + has_attrs = true; + }, + [&](const Fortran::parser::CompilerDirective::Unroll &u) { + mlir::IntegerAttr countAttr; + if (u.v.has_value()) { + countAttr = builder->getIntegerAttr(builder->getI64Type(), + u.v.value()); + } + ua = mlir::LLVM::LoopUnrollAttr::get( + builder->getContext(), /*disable=*/f, /*count*/ countAttr, + {}, /*full*/ u.v.has_value() ? f : t, {}, {}, {}); + has_attrs = true; + }, + [&](const auto &) {}}, + dir->u); + } mlir::LLVM::LoopAnnotationAttr la = mlir::LLVM::LoopAnnotationAttr::get( - builder->getContext(), {}, /*vectorize=*/va, {}, {}, {}, {}, {}, {}, {}, - {}, {}, {}, {}, {}, {}); - info.doLoop.setLoopAnnotationAttr(la); + builder->getContext(), {}, /*vectorize=*/va, {}, /*unroll*/ ua, {}, {}, + {}, {}, {}, {}, {}, {}, {}, {}, {}); + if (has_attrs) + info.doLoop.setLoopAnnotationAttr(la); } /// Generate FIR to begin a structured or unstructured increment loop nest. @@ -2259,14 +2287,7 @@ class FirConverter : public Fortran::lower::AbstractConverter { if (info.hasLocalitySpecs()) handleLocalitySpecs(info); - for (const auto *dir : dirs) { - Fortran::common::visit( - Fortran::common::visitors{ - [&](const Fortran::parser::CompilerDirective::VectorAlways - &d) { addLoopAnnotationAttr(info); }, - [&](const auto &) {}}, - dir->u); - } + addLoopAnnotationAttr(info, dirs); continue; } @@ -2818,6 +2839,9 @@ class FirConverter : public Fortran::lower::AbstractConverter { [&](const Fortran::parser::CompilerDirective::VectorAlways &) { attachDirectiveToLoop(dir, &eval); }, + [&](const Fortran::parser::CompilerDirective::Unroll &) { + attachDirectiveToLoop(dir, &eval); + }, [&](const auto &) {}}, dir.u); } diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp index 7cb35c1f173bb..b5bcb53a12761 100644 --- a/flang/lib/Parser/Fortran-parsers.cpp +++ b/flang/lib/Parser/Fortran-parsers.cpp @@ -1293,6 +1293,7 @@ TYPE_PARSER(construct("STAT =" >> statVariable) || // !DIR$ IGNORE_TKR [ [(tkrdmac...)] name ]... // !DIR$ LOOP COUNT (n1[, n2]...) // !DIR$ name[=value] [, name[=value]]... +// !DIR$ UNROLL [n] // !DIR$ constexpr auto ignore_tkr{ "IGNORE_TKR" >> optionalList(construct( @@ -1305,11 +1306,14 @@ constexpr auto assumeAligned{"ASSUME_ALIGNED" >> indirect(designator), ":"_tok >> digitString64))}; constexpr auto vectorAlways{ "VECTOR ALWAYS" >> construct()}; +constexpr auto unroll{ + "UNROLL" >> construct(maybe(digitString64))}; TYPE_PARSER(beginDirective >> "DIR$ "_tok >> sourced((construct(ignore_tkr) || construct(loopCount) || construct(assumeAligned) || construct(vectorAlways) || + construct(unroll) || construct( many(construct( name, maybe(("="_tok || ":"_tok) >> digitString64))))) / diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index 7bf404bba2c3e..8c70564de1665 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -1847,6 +1847,10 @@ class UnparseVisitor { [&](const std::list &names) { Walk("!DIR$ ", names, " "); }, + [&](const CompilerDirective::Unroll &unroll) { + Word("!DIR$ UNROLL"); + Walk(" ", unroll.v); + }, [&](const CompilerDirective::Unrecognized &) { Word("!DIR$ "); Word(x.source.ToString()); diff --git a/flang/lib/Semantics/canonicalize-directives.cpp b/flang/lib/Semantics/canonicalize-directives.cpp index 739bc3c1992ba..b27a27618808b 100644 --- a/flang/lib/Semantics/canonicalize-directives.cpp +++ b/flang/lib/Semantics/canonicalize-directives.cpp @@ -54,7 +54,9 @@ bool CanonicalizeDirectives( } static bool IsExecutionDirective(const parser::CompilerDirective &dir) { - return std::holds_alternative(dir.u); + return std::holds_alternative( + dir.u) || + std::holds_alternative(dir.u); } void CanonicalizationOfDirectives::Post(parser::SpecificationPart &spec) { @@ -110,6 +112,9 @@ void CanonicalizationOfDirectives::Post(parser::Block &block) { common::visitors{[&](parser::CompilerDirective::VectorAlways &) { CheckLoopDirective(*dir, block, it); }, + [&](parser::CompilerDirective::Unroll &) { + CheckLoopDirective(*dir, block, it); + }, [&](auto &) {}}, dir->u); } diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index f3c2a5bf094d0..705a6e0df04d9 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -9245,7 +9245,8 @@ void ResolveNamesVisitor::Post(const parser::AssignedGotoStmt &x) { } void ResolveNamesVisitor::Post(const parser::CompilerDirective &x) { - if (std::holds_alternative(x.u)) { + if (std::holds_alternative(x.u) || + std::holds_alternative(x.u)) { return; } if (const auto *tkr{ diff --git a/flang/test/Integration/unroll.f90 b/flang/test/Integration/unroll.f90 new file mode 100644 index 0000000000000..9d69605e10d1b --- /dev/null +++ b/flang/test/Integration/unroll.f90 @@ -0,0 +1,16 @@ +! RUN: %flang_fc1 -emit-llvm -o - %s | FileCheck %s + +! CHECK-LABEL: unroll_dir +subroutine unroll_dir + integer :: a(10) + !dir$ unroll + ! CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !llvm.loop ![[ANNOTATION:.*]] + do i=1,10 + a(i)=i + end do +end subroutine unroll_dir + +! CHECK: ![[ANNOTATION]] = distinct !{![[ANNOTATION]], ![[UNROLL:.*]], ![[UNROLL_FULL:.*]]} +! CHECK: ![[UNROLL]] = !{!"llvm.loop.unroll.enable"} +! CHECK: ![[UNROLL_FULL]] = !{!"llvm.loop.unroll.full"} + diff --git a/flang/test/Lower/unroll.f90 b/flang/test/Lower/unroll.f90 new file mode 100644 index 0000000000000..229755200fd8d --- /dev/null +++ b/flang/test/Lower/unroll.f90 @@ -0,0 +1,27 @@ +! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck %s + +! CHECK: #loop_unroll = #llvm.loop_unroll +! CHECK: #loop_annotation = #llvm.loop_annotation + +! CHECK-LABEL: unroll_dir +subroutine unroll_dir + integer :: a(10) + !dir$ unroll + !CHECK: fir.do_loop {{.*}} attributes {loopAnnotation = #loop_annotation} + do i=1,10 + a(i)=i + end do +end subroutine unroll_dir + + +! CHECK-LABEL: intermediate_directive +subroutine intermediate_directive + integer :: a(10) + !dir$ unroll + !dir$ unknown + !CHECK: fir.do_loop {{.*}} attributes {loopAnnotation = #loop_annotation} + do i=1,10 + a(i)=i + end do +end subroutine intermediate_directive + diff --git a/flang/test/Parser/compiler-directives.f90 b/flang/test/Parser/compiler-directives.f90 index 246eaf985251c..f372a9f533a35 100644 --- a/flang/test/Parser/compiler-directives.f90 +++ b/flang/test/Parser/compiler-directives.f90 @@ -35,3 +35,14 @@ subroutine vector_always do i=1,10 enddo end subroutine + +subroutine unroll + !dir$ unroll + ! CHECK: !DIR$ UNROLL + do i=1,10 + enddo + !dir$ unroll 2 + ! CHECK: !DIR$ UNROLL 2 + do i=1,10 + enddo +end subroutine diff --git a/flang/test/Semantics/loop-directives.f90 b/flang/test/Semantics/loop-directives.f90 index 58fb9b8082bc1..e20c0c9d042db 100644 --- a/flang/test/Semantics/loop-directives.f90 +++ b/flang/test/Semantics/loop-directives.f90 @@ -4,11 +4,15 @@ subroutine empty ! WARNING: A DO loop must follow the VECTOR ALWAYS directive !dir$ vector always + ! WARNING: A DO loop must follow the UNROLL directive + !dir$ unroll end subroutine empty subroutine non_do ! WARNING: A DO loop must follow the VECTOR ALWAYS directive !dir$ vector always + ! WARNING: A DO loop must follow the UNROLL directive + !dir$ unroll a = 1 end subroutine non_do @@ -16,6 +20,8 @@ subroutine execution_part do i=1,10 ! WARNING: A DO loop must follow the VECTOR ALWAYS directive !dir$ vector always + ! WARNING: A DO loop must follow the UNROLL directive + !dir$ unroll end do end subroutine execution_part @@ -28,3 +34,13 @@ subroutine test_vector_always_before_acc(a, b, c) a(i) = b(i) + c(i) enddo end subroutine + +! OK +subroutine test_unroll_before_acc(a, b, c) + real, dimension(10) :: a,b,c + !dir$ unroll + !$acc loop + do i=1,N + a(i) = b(i) + c(i) + enddo +end subroutine