[flang][OpenACC] Add semantic check for GOTO branching out of compute constructs (#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.
This commit is contained in:
parent
e2055bce5c
commit
e53f82716b
@ -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 @@ public:
|
||||
}
|
||||
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 @@ private:
|
||||
.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 @@ private:
|
||||
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 @@ protected:
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user