[llvm-branch-commits] [flang] [flang][OpenMP] Implement checks of intervening code (PR #185295)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Mar 8 08:41:40 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
Author: Krzysztof Parzyszek (kparzysz)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/185295.diff
1 Files Affected:
- (modified) flang/lib/Semantics/openmp-utils.cpp (+145-2)
``````````diff
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) {
``````````
</details>
https://github.com/llvm/llvm-project/pull/185295
More information about the llvm-branch-commits
mailing list