[llvm-branch-commits] [flang] [flang][OpenMP] Implement checks of intervening code (PR #185295)
Krzysztof Parzyszek via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Mar 8 08:41:12 PDT 2026
https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/185295
Invalid intervening code will cause the containing loop to be the final loop in the loop nest. Transparent intervening code will not affect perfect nesting if present. Currently compiler directives are considered transparent to allow code mixing OpenMP and such directives to compile.
>From 45b8c77bfcfa9a25590058853af1fb1974585c89 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 26 Feb 2026 15:06:12 -0600
Subject: [PATCH] [flang][OpenMP] Implement checks of intervening code
Invalid intervening code will cause the containing loop to be the final
loop in the loop nest. Transparent intervening code will not affect
perfect nesting if present. Currently compiler directives are considered
transparent to allow code mixing OpenMP and such directives to compile.
---
flang/lib/Semantics/openmp-utils.cpp | 147 ++++++++++++++++++++++++++-
1 file changed, 145 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 533242287a667..0b50160053012 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -19,6 +19,7 @@
#include "flang/Common/visit.h"
#include "flang/Evaluate/check-expression.h"
#include "flang/Evaluate/expression.h"
+#include "flang/Evaluate/match.h"
#include "flang/Evaluate/tools.h"
#include "flang/Evaluate/traverse.h"
#include "flang/Evaluate/type.h"
@@ -244,14 +245,17 @@ bool IsMapExitingType(parser::OmpMapType::Value type) {
}
}
-MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr) {
- const parser::TypedExpr &typedExpr{parserExpr.typedExpr};
+static MaybeExpr GetEvaluateExprFromTyped(const parser::TypedExpr &typedExpr) {
// ForwardOwningPointer typedExpr
// `- GenericExprWrapper ^.get()
// `- std::optional<Expr> ^->v
return DEREF(typedExpr.get()).v;
}
+MaybeExpr GetEvaluateExpr(const parser::Expr &parserExpr) {
+ return GetEvaluateExprFromTyped(parserExpr.typedExpr);
+}
+
std::optional<evaluate::DynamicType> GetDynamicType(
const parser::Expr &parserExpr) {
if (auto maybeExpr{GetEvaluateExpr(parserExpr)}) {
@@ -551,6 +555,145 @@ bool IsFullUnroll(const parser::OpenMPLoopConstruct &x) {
return false;
}
+namespace {
+// Helper class to check if a given evaluate::Expr is an array expression.
+// This does not check any proper subexpressions of the expression (except
+// parentheses).
+struct ArrayExpressionRecognizer {
+ template <TypeCategory C>
+ static bool isArrayExpression(
+ const evaluate::Expr<evaluate::SomeKind<C>> &x) {
+ return common::visit([](auto &&s) { return isArrayExpression(s); }, x.u);
+ }
+
+ template <TypeCategory C, int K>
+ static bool isArrayExpression(const evaluate::Expr<evaluate::Type<C, K>> &x) {
+ return common::visit([](auto &&s) { return isArrayExpression(s); },
+ evaluate::match::deparen(x).u);
+ }
+
+ template <typename T>
+ static bool isArrayExpression(const evaluate::Designator<T> &x) {
+ if (auto *sym{std::get_if<SymbolRef>(&x.u)}) {
+ return (*sym)->Rank() != 0;
+ }
+ if (auto *array{std::get_if<evaluate::ArrayRef>(&x.u)}) {
+ return llvm::any_of(array->subscript(), [](const evaluate::Subscript &s) {
+ // A vector subscript will not be a Triplet, but will have rank > 0.
+ return std::holds_alternative<evaluate::Triplet>(s.u) || s.Rank() > 0;
+ });
+ }
+ return false;
+ }
+
+ template <typename T> static bool isArrayExpression(const T &x) {
+ return false;
+ }
+
+ static bool isArrayExpression(const evaluate::Expr<evaluate::SomeType> &x) {
+ return common::visit([](auto &&s) { return isArrayExpression(s); }, x.u);
+ }
+};
+
+// Helper class to check if a given evaluate::Expr contains a subexpression
+// (not necessarily proper) that is an array expression.
+struct ArrayExpressionFinder
+ : public evaluate::AnyTraverse<ArrayExpressionFinder> {
+ using Base = evaluate::AnyTraverse<ArrayExpressionFinder>;
+ using Base::operator();
+ ArrayExpressionFinder() : Base(*this) {}
+
+ template <typename T>
+ bool operator()(const evaluate::Designator<T> &x) const {
+ return ArrayExpressionRecognizer::isArrayExpression(x);
+ }
+};
+
+// Helper class to check if any array expressions contained in the given
+// evaluate::Expr satisfy the criteria for being in "intervening code".
+struct ArrayExpressionChecker {
+ template <typename T> bool Pre(const T &) { return true; }
+ template <typename T> void Post(const T &) {}
+
+ bool Pre(const parser::Expr &parserExpr) {
+ // If we have found a prohibited expression, skip the rest of the
+ // traversal.
+ if (!rejected) {
+ if (auto expr{GetEvaluateExpr(parserExpr)}) {
+ rejected = ArrayExpressionFinder{}(*expr);
+ }
+ }
+ return !rejected;
+ }
+
+ bool rejected{false};
+};
+} // namespace
+
+static bool ContainsInvalidArrayExpression(
+ const parser::ExecutionPartConstruct &x) {
+ ArrayExpressionChecker checker;
+ parser::Walk(x, checker);
+ return checker.rejected;
+}
+
+bool IsValidInterveningCode(const parser::ExecutionPartConstruct &x) {
+ static auto isScalar = [](const parser::Variable &variable) {
+ if (auto expr{GetEvaluateExprFromTyped(variable.typedExpr)}) {
+ return expr->Rank() == 0;
+ }
+ return false;
+ };
+
+ auto *exec{parser::Unwrap<parser::ExecutableConstruct>(x)};
+ if (!exec) {
+ // DATA, ENTRY, FORMAT, NAMELIST are not explicitly prohibited in a CLN
+ // although they are likely disallowed due to other requirements.
+ // Return true, they should be rejected elsewhere if necessary.
+ return true;
+ }
+
+ if (auto *action{parser::Unwrap<parser::ActionStmt>(exec->u)}) {
+ if (parser::Unwrap<parser::CycleStmt>(action->u) ||
+ parser::Unwrap<parser::ExitStmt>(action->u) ||
+ parser::Unwrap<parser::ForallStmt>(action->u) ||
+ parser::Unwrap<parser::WhereStmt>(action->u)) {
+ return false;
+ }
+ if (auto *assign{parser::Unwrap<parser::AssignmentStmt>(&action->u)}) {
+ if (!isScalar(std::get<parser::Variable>(assign->t))) {
+ return false;
+ }
+ }
+ } else { // Not ActionStmt
+ if (parser::Unwrap<parser::LabelDoStmt>(exec->u) ||
+ parser::Unwrap<parser::DoConstruct>(exec->u) ||
+ parser::Unwrap<parser::ForallConstruct>(exec->u) ||
+ parser::Unwrap<parser::WhereConstruct>(exec->u)) {
+ return false;
+ }
+ if (auto *omp{parser::Unwrap<parser::OpenMPConstruct>(exec->u)}) {
+ auto dirName{GetOmpDirectiveName(*omp)};
+ if (llvm::omp::getDirectiveCategory(dirName.v) ==
+ llvm::omp::Category::Executable) {
+ return false;
+ }
+ }
+ }
+
+ if (ContainsInvalidArrayExpression(x)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool IsTransparentInterveningCode(const parser::ExecutionPartConstruct &x) {
+ // Tolerate compiler directives in perfect nests.
+ return parser::Unwrap<parser::CompilerDirective>(x) ||
+ parser::Unwrap<parser::ContinueStmt>(x);
+}
+
std::optional<int64_t> GetNumGeneratedNestsFrom(
const parser::ExecutionPartConstruct &epc,
std::optional<int64_t> nestedCount) {
More information about the llvm-branch-commits
mailing list