[flang-commits] [flang] [flang][OpenACC] Add semantic check for GOTO branching out of compute constructs (PR #189385)
via flang-commits
flang-commits at lists.llvm.org
Mon Mar 30 06:55:14 PDT 2026
https://github.com/khaki3 created https://github.com/llvm/llvm-project/pull/189385
Per OpenACC spec 2.5.4, branching out of `parallel`/`serial`/`kernels` constructs is not allowed. Add a GOTO check to `NoBranchingEnforce` that collects labels within the construct block and flags GOTOs targeting labels outside. In-region GOTOs are allowed.
The check applies only to compute constructs (`parallel`, `serial`, `kernels`), not to data constructs where GOTO out is valid.
>From 160dcaf0edffd2f97ff69ff5f4c11b6ddc32baa1 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Mon, 30 Mar 2026 06:49:59 -0700
Subject: [PATCH] [flang][OpenACC] Add semantic check for GOTO branching out of
compute constructs
Per OpenACC spec 2.5.4, branching out of parallel/serial/kernels
constructs is not allowed. Add a GOTO check to NoBranchingEnforce
that collects labels within the construct block and flags GOTOs
targeting labels outside. In-region GOTOs are allowed.
The check applies only to compute constructs (parallel, serial,
kernels), not to data constructs where GOTO out is valid.
Made-with: Cursor
---
.../lib/Semantics/check-directive-structure.h | 41 +++++++++++++++++
flang/test/Semantics/OpenACC/acc-branch.f90 | 44 +++++++++++++++++++
2 files changed, 85 insertions(+)
diff --git a/flang/lib/Semantics/check-directive-structure.h b/flang/lib/Semantics/check-directive-structure.h
index bd78d3cfe91e7..46fbc5191b38a 100644
--- a/flang/lib/Semantics/check-directive-structure.h
+++ b/flang/lib/Semantics/check-directive-structure.h
@@ -17,6 +17,7 @@
#include "flang/Semantics/tools.h"
#include "llvm/ADT/iterator_range.h"
+#include <set>
#include <unordered_map>
namespace Fortran::semantics {
@@ -53,6 +54,21 @@ template <typename D> class NoBranchingEnforce {
}
void Post(const parser::DoConstruct &) { numDoConstruct_--; }
void Post(const parser::ReturnStmt &) { EmitBranchOutError("RETURN"); }
+ void Post(const parser::GotoStmt &gotoStmt) {
+ if constexpr (std::is_same_v<D, llvm::acc::Directive>) {
+ switch ((llvm::acc::Directive)currentDirective_) {
+ case llvm::acc::Directive::ACCD_parallel:
+ case llvm::acc::Directive::ACCD_serial:
+ case llvm::acc::Directive::ACCD_kernels:
+ if (labelsInBlock_.count(gotoStmt.v) == 0)
+ EmitBranchOutOfComputeConstructError("GOTO");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ void CollectLabel(parser::Label label) { labelsInBlock_.insert(label); }
void Post(const parser::ExitStmt &exitStmt) {
if (const auto &exitName{exitStmt.v}) {
CheckConstructNameBranching("EXIT", exitName.value());
@@ -115,6 +131,14 @@ template <typename D> class NoBranchingEnforce {
.Attach(sourcePosition_, GetEnclosingMsg());
}
+ void EmitBranchOutOfComputeConstructError(const char *stmt) const {
+ context_
+ .Say(currentStatementSourcePosition_,
+ "%s to a label outside of a %s construct is not allowed"_err_en_US,
+ stmt, upperCaseDirName_)
+ .Attach(sourcePosition_, GetEnclosingMsg());
+ }
+
inline void EmitUnlabelledBranchOutError(const char *stmt) {
context_
.Say(currentStatementSourcePosition_,
@@ -172,6 +196,7 @@ template <typename D> class NoBranchingEnforce {
D currentDirective_;
int numDoConstruct_; // tracks number of DoConstruct found AFTER encountering
// an OpenMP/OpenACC directive
+ std::set<parser::Label> labelsInBlock_;
};
// Generic structure checker for directives/clauses language such as OpenMP
@@ -401,12 +426,28 @@ class DirectiveStructureChecker : public virtual BaseChecker {
std::string ClauseSetToString(const common::EnumSet<C, ClauseEnumSize> set);
};
+// Collect all labels defined in a block.
+struct LabelCollector {
+ std::set<parser::Label> labels;
+ template <typename T> bool Pre(const T &) { return true; }
+ template <typename T> void Post(const T &) {}
+ template <typename T> bool Pre(const parser::Statement<T> &stmt) {
+ if (stmt.label)
+ labels.insert(*stmt.label);
+ return true;
+ }
+};
+
template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckNoBranching(
const parser::Block &block, D directive,
const parser::CharBlock &directiveSource) {
+ LabelCollector labelCollector;
+ parser::Walk(block, labelCollector);
NoBranchingEnforce<D> noBranchingEnforce{
context_, directiveSource, directive, ContextDirectiveAsFortran()};
+ for (auto label : labelCollector.labels)
+ noBranchingEnforce.CollectLabel(label);
parser::Walk(block, noBranchingEnforce);
}
diff --git a/flang/test/Semantics/OpenACC/acc-branch.f90 b/flang/test/Semantics/OpenACC/acc-branch.f90
index 0a1bdc3afd65a..a39fd4609d87a 100644
--- a/flang/test/Semantics/OpenACC/acc-branch.f90
+++ b/flang/test/Semantics/OpenACC/acc-branch.f90
@@ -196,4 +196,48 @@ subroutine openacc_clause_validity
!$acc end data
+ ! GOTO branching out of compute constructs is not allowed (spec 2.5.4).
+ !$acc parallel
+ do i = 1, N
+ a(i) = 3.14d0
+ !ERROR: GOTO to a label outside of a PARALLEL construct is not allowed
+ if (i == N-1) goto 999
+ end do
+ !$acc end parallel
+999 continue
+
+ !$acc kernels
+ do i = 1, N
+ a(i) = 3.14d0
+ !ERROR: GOTO to a label outside of a KERNELS construct is not allowed
+ if (i == N-1) goto 998
+ end do
+ !$acc end kernels
+998 continue
+
+ !$acc serial
+ do i = 1, N
+ a(i) = 3.14d0
+ !ERROR: GOTO to a label outside of a SERIAL construct is not allowed
+ if (i == N-1) goto 997
+ end do
+ !$acc end serial
+997 continue
+
+ ! GOTO within a compute construct is allowed.
+ !$acc parallel
+ do i = 1, N
+ if (i == N-1) goto 996
+996 a(i) = 3.14d0
+ end do
+ !$acc end parallel
+
+ ! GOTO out of a data construct is allowed.
+ !$acc data create(a)
+ do i = 1, N
+ if (i == N-1) goto 995
+ end do
+ !$acc end data
+995 continue
+
end subroutine openacc_clause_validity
More information about the flang-commits
mailing list