[Mlir-commits] [flang] [mlir] [Flang][OpenACC] add loop annotations to acc.loop (PR #216769)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Mon Aug 17 09:49:46 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-openacc
Author: Scott Manley (rscottmanley)
<details>
<summary>Changes</summary>
Add loop annotations that come from directives to acc.loop just like other loops
---
Patch is 23.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/216769.diff
8 Files Affected:
- (modified) flang/docs/Directives.md (+3-2)
- (added) flang/include/flang/Lower/Support/LoopAnnotation.h (+36)
- (modified) flang/lib/Lower/Bridge.cpp (+48-157)
- (modified) flang/lib/Lower/CMakeLists.txt (+1)
- (modified) flang/lib/Lower/OpenACC.cpp (+46-6)
- (added) flang/lib/Lower/Support/LoopAnnotation.cpp (+156)
- (added) flang/test/Lower/OpenACC/acc-loop-directive-annotation.f90 (+17)
- (modified) mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsTiling.cpp (+2)
``````````diff
diff --git a/flang/docs/Directives.md b/flang/docs/Directives.md
index 45080acb778e3..e74db041d01f3 100644
--- a/flang/docs/Directives.md
+++ b/flang/docs/Directives.md
@@ -171,8 +171,9 @@ directives. Directives on loops are used to specify additional transformation to
be performed by the compiler like enabling vectorisation, unrolling, interchange
etc.
-Currently loop directives are not accepted in the presence of OpenMP or OpenACC
-constructs on the loop. This should be implemented as it is used in some
+Currently loop directives are accepted in the presence of OpenACC loop and
+combined constructs on the loop. Loop directives with OpenMP constructs on the
+loop are not yet accepted. This should be implemented as it is used in some
applications.
### Array Expressions
diff --git a/flang/include/flang/Lower/Support/LoopAnnotation.h b/flang/include/flang/Lower/Support/LoopAnnotation.h
new file mode 100644
index 0000000000000..9ff1ec5c521fe
--- /dev/null
+++ b/flang/include/flang/Lower/Support/LoopAnnotation.h
@@ -0,0 +1,36 @@
+//===-- Lower/Support/LoopAnnotation.h -- loop annotation attrs -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers to lower Fortran `!dir$` loop directives to LLVM LoopAnnotationAttr.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_LOWER_SUPPORT_LOOPANNOTATION_H
+#define FORTRAN_LOWER_SUPPORT_LOOPANNOTATION_H
+
+#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace Fortran {
+namespace parser {
+struct CompilerDirective;
+} // namespace parser
+
+namespace lower {
+
+/// Build an LLVM loop annotation attribute from Fortran compiler directives
+/// associated with a loop. Returns null if \p dirs does not contain any
+/// recognized loop directives.
+mlir::LLVM::LoopAnnotationAttr genLoopAnnotationAttr(
+ mlir::MLIRContext *context,
+ llvm::ArrayRef<const Fortran::parser::CompilerDirective *> dirs);
+
+} // namespace lower
+} // namespace Fortran
+
+#endif // FORTRAN_LOWER_SUPPORT_LOOPANNOTATION_H
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index bff6b51e50e18..a0f39d51727d0 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -30,6 +30,7 @@
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Runtime.h"
#include "flang/Lower/StatementContext.h"
+#include "flang/Lower/Support/LoopAnnotation.h"
#include "flang/Lower/Support/ReductionProcessor.h"
#include "flang/Lower/Support/Utils.h"
#include "flang/Optimizer/Builder/BoxValue.h"
@@ -2859,164 +2860,21 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// For unroll directives without a value, force full unrolling.
// For unroll directives with a value, if the value is greater than 1,
// force unrolling with the given factor. Otherwise, disable unrolling.
- mlir::LLVM::LoopUnrollAttr
- genLoopUnrollAttr(std::optional<std::uint64_t> directiveArg) {
- mlir::BoolAttr falseAttr =
- mlir::BoolAttr::get(builder->getContext(), false);
- mlir::BoolAttr trueAttr = mlir::BoolAttr::get(builder->getContext(), true);
- mlir::IntegerAttr countAttr;
- mlir::BoolAttr fullUnrollAttr;
- bool shouldUnroll = true;
- if (directiveArg.has_value()) {
- auto unrollingFactor = directiveArg.value();
- if (unrollingFactor == 0 || unrollingFactor == 1) {
- shouldUnroll = false;
- } else {
- countAttr =
- builder->getIntegerAttr(builder->getI64Type(), unrollingFactor);
- }
- } else {
- fullUnrollAttr = trueAttr;
- }
-
- mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
- return mlir::LLVM::LoopUnrollAttr::get(
- builder->getContext(), /*disable=*/disableAttr, /*count=*/countAttr, {},
- /*full=*/fullUnrollAttr, {}, {}, {});
- }
-
- // Enabling unroll and jamming directive without a value.
- // For directives with a value, if the value is greater than 1,
- // force unrolling with the given factor. Otherwise, disable unrolling and
- // jamming.
- mlir::LLVM::LoopUnrollAndJamAttr
- genLoopUnrollAndJamAttr(std::optional<std::uint64_t> count) {
- mlir::BoolAttr falseAttr =
- mlir::BoolAttr::get(builder->getContext(), false);
- mlir::BoolAttr trueAttr = mlir::BoolAttr::get(builder->getContext(), true);
- mlir::IntegerAttr countAttr;
- bool shouldUnroll = true;
- if (count.has_value()) {
- auto unrollingFactor = count.value();
- if (unrollingFactor == 0 || unrollingFactor == 1) {
- shouldUnroll = false;
- } else {
- countAttr =
- builder->getIntegerAttr(builder->getI64Type(), unrollingFactor);
- }
- }
-
- mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
- return mlir::LLVM::LoopUnrollAndJamAttr::get(
- builder->getContext(), /*disable=*/disableAttr, /*count*/ countAttr, {},
- {}, {}, {}, {});
- }
-
- // Enabling loop vectorization attribute.
- mlir::LLVM::LoopVectorizeAttr
- genLoopVectorizeAttr(mlir::BoolAttr disableAttr,
- mlir::BoolAttr scalableEnable,
- mlir::IntegerAttr vectorWidth) {
- mlir::LLVM::LoopVectorizeAttr va;
- if (disableAttr)
- va = mlir::LLVM::LoopVectorizeAttr::get(
- builder->getContext(),
- /*disable=*/disableAttr, /*predicate=*/{},
- /*scalableEnable=*/scalableEnable,
- /*vectorWidth=*/vectorWidth, {}, {}, {});
- return va;
- }
-
+ // Enabling unroll and jamming / vectorization is handled in
+ // genLoopAnnotationAttr.
void addLoopAnnotationAttr(
IncrementLoopInfo &info,
llvm::SmallVectorImpl<const Fortran::parser::CompilerDirective *> &dirs) {
- mlir::BoolAttr disableVecAttr;
- mlir::BoolAttr scalableEnable;
- mlir::IntegerAttr vectorWidth;
- mlir::LLVM::LoopUnrollAttr ua;
- mlir::LLVM::LoopUnrollAndJamAttr uja;
- llvm::SmallVector<mlir::LLVM::AccessGroupAttr> aga;
- bool has_attrs = false;
- for (const auto *dir : dirs) {
- Fortran::common::visit(
- Fortran::common::visitors{
- [&](const Fortran::parser::CompilerDirective::VectorAlways &) {
- disableVecAttr =
- mlir::BoolAttr::get(builder->getContext(), false);
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::VectorLength &vl) {
- using Kind =
- Fortran::parser::CompilerDirective::VectorLength::Kind;
- Kind kind = std::get<Kind>(vl.t);
- uint64_t length = std::get<uint64_t>(vl.t);
- disableVecAttr =
- mlir::BoolAttr::get(builder->getContext(), false);
- if (length != 0)
- vectorWidth =
- builder->getIntegerAttr(builder->getI64Type(), length);
- switch (kind) {
- case Kind::Scalable:
- scalableEnable =
- mlir::BoolAttr::get(builder->getContext(), true);
- break;
- case Kind::Fixed:
- scalableEnable =
- mlir::BoolAttr::get(builder->getContext(), false);
- break;
- case Kind::Auto:
- break;
- }
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::Unroll &u) {
- ua = genLoopUnrollAttr(u.v);
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::UnrollAndJam &u) {
- uja = genLoopUnrollAndJamAttr(u.v);
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::NoVector &u) {
- disableVecAttr =
- mlir::BoolAttr::get(builder->getContext(), true);
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::NoUnroll &u) {
- ua = genLoopUnrollAttr(/*unrollingFactor=*/0);
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::NoUnrollAndJam &u) {
- uja = genLoopUnrollAndJamAttr(/*unrollingFactor=*/0);
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::IVDep &iv) {
- aga.push_back(
- mlir::LLVM::AccessGroupAttr::get(builder->getContext()));
- has_attrs = true;
- },
- [&](const Fortran::parser::CompilerDirective::Simd &simd) {
- disableVecAttr =
- mlir::BoolAttr::get(builder->getContext(), false);
- has_attrs = true;
- },
- [&](const auto &) {}},
- dir->u);
- }
- mlir::LLVM::LoopVectorizeAttr va =
- genLoopVectorizeAttr(disableVecAttr, scalableEnable, vectorWidth);
- mlir::LLVM::LoopAnnotationAttr la = mlir::LLVM::LoopAnnotationAttr::get(
- builder->getContext(), {}, /*vectorize=*/va, {}, /*unroll*/ ua,
- /*unroll_and_jam*/ uja, {}, {}, {}, {}, {}, {}, {}, {}, {},
- /*parallelAccesses*/ aga);
- if (has_attrs) {
- if (auto loopOp = mlir::dyn_cast<fir::DoLoopOp>(info.loopOp))
- loopOp.setLoopAnnotationAttr(la);
+ mlir::LLVM::LoopAnnotationAttr la =
+ Fortran::lower::genLoopAnnotationAttr(builder->getContext(), dirs);
+ if (!la)
+ return;
+ if (auto loopOp = mlir::dyn_cast<fir::DoLoopOp>(info.loopOp))
+ loopOp.setLoopAnnotationAttr(la);
- if (auto doConcurrentOp =
- mlir::dyn_cast<fir::DoConcurrentLoopOp>(info.loopOp))
- doConcurrentOp.setLoopAnnotationAttr(la);
- }
+ if (auto doConcurrentOp =
+ mlir::dyn_cast<fir::DoConcurrentLoopOp>(info.loopOp))
+ doConcurrentOp.setLoopAnnotationAttr(la);
}
/// Generate FIR to begin a structured or unstructured increment loop nest.
@@ -3548,11 +3406,44 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void attachDirectiveToLoop(const Fortran::parser::CompilerDirective &dir,
Fortran::lower::pft::Evaluation *e) {
- while (e->isDirective())
+ auto attachToDoStmt = [&](Fortran::lower::pft::Evaluation *doEval) {
+ if (doEval && doEval->isA<Fortran::parser::NonLabelDoStmt>())
+ doEval->dirs.push_back(&dir);
+ };
+
+ // Dive into an OpenACC loop/combined construct to find its NonLabelDoStmt.
+ auto findDoStmtInOpenACC = [](Fortran::lower::pft::Evaluation *eval)
+ -> Fortran::lower::pft::Evaluation * {
+ const auto *acc = eval->getIf<Fortran::parser::OpenACCConstruct>();
+ if (!acc)
+ return nullptr;
+ if (!std::holds_alternative<Fortran::parser::OpenACCLoopConstruct>(
+ acc->u) &&
+ !std::holds_alternative<Fortran::parser::OpenACCCombinedConstruct>(
+ acc->u))
+ return nullptr;
+ if (!eval->hasNestedEvaluations())
+ return nullptr;
+ for (Fortran::lower::pft::Evaluation &nested :
+ eval->getNestedEvaluations()) {
+ if (nested.isA<Fortran::parser::DoConstruct>() &&
+ nested.hasNestedEvaluations())
+ return &nested.getFirstNestedEvaluation();
+ }
+ return nullptr;
+ };
+
+ while (e->isDirective()) {
+ if (Fortran::lower::pft::Evaluation *doStmt = findDoStmtInOpenACC(e)) {
+ attachToDoStmt(doStmt);
+ return;
+ }
e = e->lexicalSuccessor;
+ if (!e)
+ return;
+ }
- if (e->isA<Fortran::parser::NonLabelDoStmt>())
- e->dirs.push_back(&dir);
+ attachToDoStmt(e);
}
void markCurrentFuncAsAlwaysInline(
diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt
index 584e4a28e5641..246b6e2f5adff 100644
--- a/flang/lib/Lower/CMakeLists.txt
+++ b/flang/lib/Lower/CMakeLists.txt
@@ -30,6 +30,7 @@ add_flang_library(FortranLower
OpenMP/Utils.cpp
PFTBuilder.cpp
Runtime.cpp
+ Support/LoopAnnotation.cpp
Support/PrivateReductionUtils.cpp
Support/ReductionProcessor.cpp
Support/Utils.cpp
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 2ea3659e8064f..73f1c18bfaac2 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -20,6 +20,7 @@
#include "flang/Lower/Mangler.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/StatementContext.h"
+#include "flang/Lower/Support/LoopAnnotation.h"
#include "flang/Lower/Support/Utils.h"
#include "flang/Lower/SymbolMap.h"
#include "flang/Optimizer/Builder/BoxValue.h"
@@ -2235,6 +2236,32 @@ static bool hasEarlyReturn(Fortran::lower::pft::Evaluation &eval) {
return hasReturnStmt;
}
+/// Return the NonLabelDoStmt evaluation associated with an OpenACC loop or
+/// a DoConstruct being lowered as an acc.loop.
+static Fortran::lower::pft::Evaluation *
+getAccLoopDoStmtEval(Fortran::lower::pft::Evaluation &eval) {
+ Fortran::lower::pft::Evaluation *e = &eval;
+ if (e->isA<Fortran::parser::OpenACCConstruct>()) {
+ if (!e->hasNestedEvaluations())
+ return nullptr;
+ e = nullptr;
+ for (Fortran::lower::pft::Evaluation &nested :
+ eval.getNestedEvaluations()) {
+ if (nested.isA<Fortran::parser::DoConstruct>()) {
+ e = &nested;
+ break;
+ }
+ }
+ if (!e)
+ return nullptr;
+ }
+ if (e->isA<Fortran::parser::DoConstruct>() && e->hasNestedEvaluations())
+ return &e->getFirstNestedEvaluation();
+ if (e->isA<Fortran::parser::NonLabelDoStmt>())
+ return e;
+ return nullptr;
+}
+
static mlir::acc::LoopOp createLoopOp(
Fortran::lower::AbstractConverter &converter,
mlir::Location currentLocation,
@@ -2503,12 +2530,15 @@ static mlir::acc::LoopOp createLoopOp(
loopOp.setCombinedAttr(mlir::acc::CombinedConstructsTypeAttr::get(
builder.getContext(), *combinedConstructs));
- // TODO: retrieve directives from NonLabelDoStmt pft::Evaluation, and add them
- // as attribute to the acc.loop as an extra attribute. It is not quite clear
- // how useful these $dir are in acc contexts, but they could still provide
- // more information about the loop acc codegen. They can be obtained by
- // looking for the first lexicalSuccessor of eval that is a NonLabelDoStmt,
- // and using the related `dirs` member.
+ // Apply `!dir$` loop directives associated with the DO statement as a
+ // discardable LLVM loop annotation attribute on the acc.loop.
+ if (Fortran::lower::pft::Evaluation *doStmtEval =
+ getAccLoopDoStmtEval(eval)) {
+ if (mlir::LLVM::LoopAnnotationAttr la =
+ Fortran::lower::genLoopAnnotationAttr(builder.getContext(),
+ doStmtEval->dirs))
+ loopOp->setDiscardableAttr("loopAnnotation", la);
+ }
return loopOp;
}
@@ -5436,5 +5466,15 @@ mlir::Operation *Fortran::lower::genOpenACCLoopFromDoConstruct(
llvm_unreachable("Unexpected loop par mode");
}
+ // Apply `!dir$` loop directives associated with the DO statement as a
+ // discardable LLVM loop annotation attribute on the acc.loop.
+ if (Fortran::lower::pft::Evaluation *doStmtEval =
+ getAccLoopDoStmtEval(eval)) {
+ if (mlir::LLVM::LoopAnnotationAttr la =
+ Fortran::lower::genLoopAnnotationAttr(builder.getContext(),
+ doStmtEval->dirs))
+ loopOp->setDiscardableAttr("loopAnnotation", la);
+ }
+
return loopOp;
}
diff --git a/flang/lib/Lower/Support/LoopAnnotation.cpp b/flang/lib/Lower/Support/LoopAnnotation.cpp
new file mode 100644
index 0000000000000..976d7495e78dc
--- /dev/null
+++ b/flang/lib/Lower/Support/LoopAnnotation.cpp
@@ -0,0 +1,156 @@
+//===-- Lower/Support/LoopAnnotation.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Lower/Support/LoopAnnotation.h"
+#include "flang/Common/idioms.h"
+#include "flang/Parser/parse-tree.h"
+#include "mlir/IR/BuiltinAttributes.h"
+
+static mlir::LLVM::LoopUnrollAttr
+genLoopUnrollAttr(mlir::MLIRContext *context,
+ std::optional<std::uint64_t> directiveArg) {
+ mlir::BoolAttr falseAttr = mlir::BoolAttr::get(context, false);
+ mlir::BoolAttr trueAttr = mlir::BoolAttr::get(context, true);
+ mlir::IntegerAttr countAttr;
+ mlir::BoolAttr fullUnrollAttr;
+ bool shouldUnroll = true;
+ if (directiveArg.has_value()) {
+ auto unrollingFactor = directiveArg.value();
+ if (unrollingFactor == 0 || unrollingFactor == 1) {
+ shouldUnroll = false;
+ } else {
+ countAttr = mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64),
+ unrollingFactor);
+ }
+ } else {
+ fullUnrollAttr = trueAttr;
+ }
+
+ mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
+ return mlir::LLVM::LoopUnrollAttr::get(context, /*disable=*/disableAttr,
+ /*count=*/countAttr, {},
+ /*full=*/fullUnrollAttr, {}, {}, {});
+}
+
+static mlir::LLVM::LoopUnrollAndJamAttr
+genLoopUnrollAndJamAttr(mlir::MLIRContext *context,
+ std::optional<std::uint64_t> count) {
+ mlir::BoolAttr falseAttr = mlir::BoolAttr::get(context, false);
+ mlir::BoolAttr trueAttr = mlir::BoolAttr::get(context, true);
+ mlir::IntegerAttr countAttr;
+ bool shouldUnroll = true;
+ if (count.has_value()) {
+ auto unrollingFactor = count.value();
+ if (unrollingFactor == 0 || unrollingFactor == 1) {
+ shouldUnroll = false;
+ } else {
+ countAttr = mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64),
+ unrollingFactor);
+ }
+ }
+
+ mlir::BoolAttr disableAttr = shouldUnroll ? falseAttr : trueAttr;
+ return mlir::LLVM::LoopUnrollAndJamAttr::get(context, /*disable=*/disableAttr,
+ /*count*/ countAttr, {}, {}, {},
+ {}, {});
+}
+
+static mlir::LLVM::LoopVectorizeAttr
+genLoopVectorizeAttr(mlir::MLIRContext *context, mlir::BoolAttr disableAttr,
+ mlir::BoolAttr scalableEnable,
+ mlir::IntegerAttr vectorWidth) {
+ mlir::LLVM::LoopVectorizeAttr va;
+ if (disableAttr)
+ va = mlir::LLVM::LoopVectorizeAttr::get(context,
+ /*disable=*/disableAttr,
+ /*predicate=*/{},
+ /*scalableEnable=*/scalableEnable,
+ /*vectorWidth=*/vectorWidth, {}, {},
+ {});
+ return va;
+}
+
+mlir::LLVM::LoopAnnotationAttr Fortran::lower::genLoopAnnotationAttr(
+ mlir::MLIRContext *context,
+ llvm::ArrayRef<const Fortran::parser::CompilerDirective *> dirs) {
+ mlir::BoolAttr disableVecAttr;
+ mlir::BoolAttr scalableEnable;
+ mlir::IntegerAttr vectorWidth;
+ mlir::LLVM::LoopUnrollAttr ua;
+ mlir::LLVM::LoopUnrollAndJamAttr uja;
+ llvm::SmallVector<mlir::LLVM::AccessGroupAttr> aga;
+ bool hasAttrs = false;
+ for (const auto *dir : dirs) {
+ Fortran::common::visit(
+ Fortran::common::visitors{
+ [&](const Fortran::parser::CompilerDirective::VectorAlways &) {
+ disableVecAttr = mlir::BoolAttr::get(context, false);
+ hasAttrs = true;
+ },
+ [&](const Fortran::parser::CompilerDirective::VectorLength &vl) {
+ using Kind =
+ Fortran::parser::CompilerDirective::VectorLength::Kind;
+ Kind kind = std::get<Kind>(vl.t);
+ uint64_t length = std::get<uint64_t>(vl.t);
+ disableV...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/216769
More information about the Mlir-commits
mailing list