[flang][OpenMP] Split check-omp-structure.cpp into smaller files, NFC (#146359)
Create these new files in flang/lib/Semantics: openmp-utils.cpp/.h - Common utilities check-omp-atomic.cpp - Atomic-related checks check-omp-loop.cpp - Loop constructs/clauses check-omp-metadirective.cpp - Metadirective-related checks Update lists of included headers, std in particular. --------- Co-authored-by: Jack Styles <jack.styles@arm.com>
This commit is contained in:
committed by
GitHub
parent
1a7b7e24bc
commit
ba116a8bed
@@ -20,6 +20,9 @@ add_flang_library(FortranSemantics
|
||||
check-io.cpp
|
||||
check-namelist.cpp
|
||||
check-nullify.cpp
|
||||
check-omp-atomic.cpp
|
||||
check-omp-loop.cpp
|
||||
check-omp-metadirective.cpp
|
||||
check-omp-structure.cpp
|
||||
check-purity.cpp
|
||||
check-return.cpp
|
||||
@@ -34,12 +37,13 @@ add_flang_library(FortranSemantics
|
||||
mod-file.cpp
|
||||
openmp-dsa.cpp
|
||||
openmp-modifiers.cpp
|
||||
openmp-utils.cpp
|
||||
pointer-assignment.cpp
|
||||
program-tree.cpp
|
||||
resolve-labels.cpp
|
||||
resolve-directives.cpp
|
||||
resolve-names-utils.cpp
|
||||
resolve-labels.cpp
|
||||
resolve-names.cpp
|
||||
resolve-names-utils.cpp
|
||||
rewrite-parse-tree.cpp
|
||||
runtime-type-info.cpp
|
||||
scope.cpp
|
||||
|
||||
1295
flang/lib/Semantics/check-omp-atomic.cpp
Normal file
1295
flang/lib/Semantics/check-omp-atomic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
671
flang/lib/Semantics/check-omp-loop.cpp
Normal file
671
flang/lib/Semantics/check-omp-loop.cpp
Normal file
@@ -0,0 +1,671 @@
|
||||
//===-- lib/Semantics/check-omp-loop.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Semantic checks for constructs and clauses related to loops.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "check-omp-structure.h"
|
||||
|
||||
#include "check-directive-structure.h"
|
||||
#include "openmp-utils.h"
|
||||
|
||||
#include "flang/Common/idioms.h"
|
||||
#include "flang/Common/visit.h"
|
||||
#include "flang/Parser/char-block.h"
|
||||
#include "flang/Parser/parse-tree-visitor.h"
|
||||
#include "flang/Parser/parse-tree.h"
|
||||
#include "flang/Parser/tools.h"
|
||||
#include "flang/Semantics/openmp-modifiers.h"
|
||||
#include "flang/Semantics/semantics.h"
|
||||
#include "flang/Semantics/symbol.h"
|
||||
#include "flang/Semantics/tools.h"
|
||||
#include "flang/Semantics/type.h"
|
||||
|
||||
#include "llvm/Frontend/OpenMP/OMP.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
namespace {
|
||||
using namespace Fortran;
|
||||
|
||||
class AssociatedLoopChecker {
|
||||
public:
|
||||
AssociatedLoopChecker(
|
||||
semantics::SemanticsContext &context, std::int64_t level)
|
||||
: context_{context}, level_{level} {}
|
||||
|
||||
template <typename T> bool Pre(const T &) { return true; }
|
||||
template <typename T> void Post(const T &) {}
|
||||
|
||||
bool Pre(const parser::DoConstruct &dc) {
|
||||
level_--;
|
||||
const auto &doStmt{
|
||||
std::get<parser::Statement<parser::NonLabelDoStmt>>(dc.t)};
|
||||
const auto &constructName{
|
||||
std::get<std::optional<parser::Name>>(doStmt.statement.t)};
|
||||
if (constructName) {
|
||||
constructNamesAndLevels_.emplace(
|
||||
constructName.value().ToString(), level_);
|
||||
}
|
||||
if (level_ >= 0) {
|
||||
if (dc.IsDoWhile()) {
|
||||
context_.Say(doStmt.source,
|
||||
"The associated loop of a loop-associated directive cannot be a DO WHILE."_err_en_US);
|
||||
}
|
||||
if (!dc.GetLoopControl()) {
|
||||
context_.Say(doStmt.source,
|
||||
"The associated loop of a loop-associated directive cannot be a DO without control."_err_en_US);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Post(const parser::DoConstruct &dc) { level_++; }
|
||||
|
||||
bool Pre(const parser::CycleStmt &cyclestmt) {
|
||||
std::map<std::string, std::int64_t>::iterator it;
|
||||
bool err{false};
|
||||
if (cyclestmt.v) {
|
||||
it = constructNamesAndLevels_.find(cyclestmt.v->source.ToString());
|
||||
err = (it != constructNamesAndLevels_.end() && it->second > 0);
|
||||
} else { // If there is no label then use the level of the last enclosing DO
|
||||
err = level_ > 0;
|
||||
}
|
||||
if (err) {
|
||||
context_.Say(*source_,
|
||||
"CYCLE statement to non-innermost associated loop of an OpenMP DO "
|
||||
"construct"_err_en_US);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pre(const parser::ExitStmt &exitStmt) {
|
||||
std::map<std::string, std::int64_t>::iterator it;
|
||||
bool err{false};
|
||||
if (exitStmt.v) {
|
||||
it = constructNamesAndLevels_.find(exitStmt.v->source.ToString());
|
||||
err = (it != constructNamesAndLevels_.end() && it->second >= 0);
|
||||
} else { // If there is no label then use the level of the last enclosing DO
|
||||
err = level_ >= 0;
|
||||
}
|
||||
if (err) {
|
||||
context_.Say(*source_,
|
||||
"EXIT statement terminates associated loop of an OpenMP DO "
|
||||
"construct"_err_en_US);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pre(const parser::Statement<parser::ActionStmt> &actionstmt) {
|
||||
source_ = &actionstmt.source;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
semantics::SemanticsContext &context_;
|
||||
const parser::CharBlock *source_;
|
||||
std::int64_t level_;
|
||||
std::map<std::string, std::int64_t> constructNamesAndLevels_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace Fortran::semantics {
|
||||
|
||||
using namespace Fortran::semantics::omp;
|
||||
|
||||
void OmpStructureChecker::HasInvalidDistributeNesting(
|
||||
const parser::OpenMPLoopConstruct &x) {
|
||||
bool violation{false};
|
||||
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
||||
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
|
||||
if (llvm::omp::topDistributeSet.test(beginDir.v)) {
|
||||
// `distribute` region has to be nested
|
||||
if (!CurrentDirectiveIsNested()) {
|
||||
violation = true;
|
||||
} else {
|
||||
// `distribute` region has to be strictly nested inside `teams`
|
||||
if (!llvm::omp::bottomTeamsSet.test(GetContextParent().directive)) {
|
||||
violation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (violation) {
|
||||
context_.Say(beginDir.source,
|
||||
"`DISTRIBUTE` region has to be strictly nested inside `TEAMS` "
|
||||
"region."_err_en_US);
|
||||
}
|
||||
}
|
||||
void OmpStructureChecker::HasInvalidLoopBinding(
|
||||
const parser::OpenMPLoopConstruct &x) {
|
||||
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
||||
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
|
||||
|
||||
auto teamsBindingChecker = [&](parser::MessageFixedText msg) {
|
||||
const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
|
||||
for (const auto &clause : clauseList.v) {
|
||||
if (const auto *bindClause{
|
||||
std::get_if<parser::OmpClause::Bind>(&clause.u)}) {
|
||||
if (bindClause->v.v != parser::OmpBindClause::Binding::Teams) {
|
||||
context_.Say(beginDir.source, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (llvm::omp::Directive::OMPD_loop == beginDir.v &&
|
||||
CurrentDirectiveIsNested() &&
|
||||
llvm::omp::bottomTeamsSet.test(GetContextParent().directive)) {
|
||||
teamsBindingChecker(
|
||||
"`BIND(TEAMS)` must be specified since the `LOOP` region is "
|
||||
"strictly nested inside a `TEAMS` region."_err_en_US);
|
||||
}
|
||||
|
||||
if (OmpDirectiveSet{
|
||||
llvm::omp::OMPD_teams_loop, llvm::omp::OMPD_target_teams_loop}
|
||||
.test(beginDir.v)) {
|
||||
teamsBindingChecker(
|
||||
"`BIND(TEAMS)` must be specified since the `LOOP` directive is "
|
||||
"combined with a `TEAMS` construct."_err_en_US);
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
|
||||
// Check the following:
|
||||
// The only OpenMP constructs that can be encountered during execution of
|
||||
// a simd region are the `atomic` construct, the `loop` construct, the `simd`
|
||||
// construct and the `ordered` construct with the `simd` clause.
|
||||
|
||||
// Check if the parent context has the SIMD clause
|
||||
// Please note that we use GetContext() instead of GetContextParent()
|
||||
// because PushContextAndClauseSets() has not been called on the
|
||||
// current context yet.
|
||||
// TODO: Check for declare simd regions.
|
||||
bool eligibleSIMD{false};
|
||||
common::visit(
|
||||
common::visitors{
|
||||
// Allow `!$OMP ORDERED SIMD`
|
||||
[&](const parser::OpenMPBlockConstruct &c) {
|
||||
const auto &beginBlockDir{
|
||||
std::get<parser::OmpBeginBlockDirective>(c.t)};
|
||||
const auto &beginDir{
|
||||
std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
|
||||
if (beginDir.v == llvm::omp::Directive::OMPD_ordered) {
|
||||
const auto &clauses{
|
||||
std::get<parser::OmpClauseList>(beginBlockDir.t)};
|
||||
for (const auto &clause : clauses.v) {
|
||||
if (std::get_if<parser::OmpClause::Simd>(&clause.u)) {
|
||||
eligibleSIMD = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[&](const parser::OpenMPStandaloneConstruct &c) {
|
||||
if (auto *ssc{std::get_if<parser::OpenMPSimpleStandaloneConstruct>(
|
||||
&c.u)}) {
|
||||
llvm::omp::Directive dirId{ssc->v.DirId()};
|
||||
if (dirId == llvm::omp::Directive::OMPD_ordered) {
|
||||
for (const parser::OmpClause &x : ssc->v.Clauses().v) {
|
||||
if (x.Id() == llvm::omp::Clause::OMPC_simd) {
|
||||
eligibleSIMD = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (dirId == llvm::omp::Directive::OMPD_scan) {
|
||||
eligibleSIMD = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
// Allowing SIMD and loop construct
|
||||
[&](const parser::OpenMPLoopConstruct &c) {
|
||||
const auto &beginLoopDir{
|
||||
std::get<parser::OmpBeginLoopDirective>(c.t)};
|
||||
const auto &beginDir{
|
||||
std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
|
||||
if ((beginDir.v == llvm::omp::Directive::OMPD_simd) ||
|
||||
(beginDir.v == llvm::omp::Directive::OMPD_do_simd) ||
|
||||
(beginDir.v == llvm::omp::Directive::OMPD_loop)) {
|
||||
eligibleSIMD = true;
|
||||
}
|
||||
},
|
||||
[&](const parser::OpenMPAtomicConstruct &c) {
|
||||
// Allow `!$OMP ATOMIC`
|
||||
eligibleSIMD = true;
|
||||
},
|
||||
[&](const auto &c) {},
|
||||
},
|
||||
c.u);
|
||||
if (!eligibleSIMD) {
|
||||
context_.Say(parser::FindSourceLocation(c),
|
||||
"The only OpenMP constructs that can be encountered during execution "
|
||||
"of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, "
|
||||
"the `SIMD` construct, the `SCAN` construct and the `ORDERED` "
|
||||
"construct with the `SIMD` clause."_err_en_US);
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
|
||||
loopStack_.push_back(&x);
|
||||
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
||||
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
|
||||
|
||||
PushContextAndClauseSets(beginDir.source, beginDir.v);
|
||||
|
||||
// check matching, End directive is optional
|
||||
if (const auto &endLoopDir{
|
||||
std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
|
||||
const auto &endDir{
|
||||
std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
|
||||
|
||||
CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
|
||||
|
||||
AddEndDirectiveClauses(std::get<parser::OmpClauseList>(endLoopDir->t));
|
||||
}
|
||||
|
||||
if (llvm::omp::allSimdSet.test(GetContext().directive)) {
|
||||
EnterDirectiveNest(SIMDNest);
|
||||
}
|
||||
|
||||
// Combined target loop constructs are target device constructs. Keep track of
|
||||
// whether any such construct has been visited to later check that REQUIRES
|
||||
// directives for target-related options don't appear after them.
|
||||
if (llvm::omp::allTargetSet.test(beginDir.v)) {
|
||||
deviceConstructFound_ = true;
|
||||
}
|
||||
|
||||
if (beginDir.v == llvm::omp::Directive::OMPD_do) {
|
||||
// 2.7.1 do-clause -> private-clause |
|
||||
// firstprivate-clause |
|
||||
// lastprivate-clause |
|
||||
// linear-clause |
|
||||
// reduction-clause |
|
||||
// schedule-clause |
|
||||
// collapse-clause |
|
||||
// ordered-clause
|
||||
|
||||
// nesting check
|
||||
HasInvalidWorksharingNesting(
|
||||
beginDir.source, llvm::omp::nestedWorkshareErrSet);
|
||||
}
|
||||
SetLoopInfo(x);
|
||||
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &doConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
const auto &doBlock{std::get<parser::Block>(doConstruct->t)};
|
||||
CheckNoBranching(doBlock, beginDir.v, beginDir.source);
|
||||
}
|
||||
}
|
||||
CheckLoopItrVariableIsInt(x);
|
||||
CheckAssociatedLoopConstraints(x);
|
||||
HasInvalidDistributeNesting(x);
|
||||
HasInvalidLoopBinding(x);
|
||||
if (CurrentDirectiveIsNested() &&
|
||||
llvm::omp::bottomTeamsSet.test(GetContextParent().directive)) {
|
||||
HasInvalidTeamsNesting(beginDir.v, beginDir.source);
|
||||
}
|
||||
if ((beginDir.v == llvm::omp::Directive::OMPD_distribute_parallel_do_simd) ||
|
||||
(beginDir.v == llvm::omp::Directive::OMPD_distribute_simd)) {
|
||||
CheckDistLinear(x);
|
||||
}
|
||||
}
|
||||
|
||||
const parser::Name OmpStructureChecker::GetLoopIndex(
|
||||
const parser::DoConstruct *x) {
|
||||
using Bounds = parser::LoopControl::Bounds;
|
||||
return std::get<Bounds>(x->GetLoopControl()->u).name.thing;
|
||||
}
|
||||
|
||||
void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
const parser::DoConstruct *loop{&*loopConstruct};
|
||||
if (loop && loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
SetLoopIv(itrVal.symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckLoopItrVariableIsInt(
|
||||
const parser::OpenMPLoopConstruct &x) {
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
|
||||
for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
|
||||
if (loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
if (itrVal.symbol) {
|
||||
const auto *type{itrVal.symbol->GetType()};
|
||||
if (!type->IsNumeric(TypeCategory::Integer)) {
|
||||
context_.Say(itrVal.source,
|
||||
"The DO loop iteration"
|
||||
" variable must be of the type integer."_err_en_US,
|
||||
itrVal.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get the next DoConstruct if block is not empty.
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
|
||||
: nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::int64_t OmpStructureChecker::GetOrdCollapseLevel(
|
||||
const parser::OpenMPLoopConstruct &x) {
|
||||
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
||||
const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
|
||||
std::int64_t orderedCollapseLevel{1};
|
||||
std::int64_t orderedLevel{1};
|
||||
std::int64_t collapseLevel{1};
|
||||
|
||||
for (const auto &clause : clauseList.v) {
|
||||
if (const auto *collapseClause{
|
||||
std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
|
||||
if (const auto v{GetIntValue(collapseClause->v)}) {
|
||||
collapseLevel = *v;
|
||||
}
|
||||
}
|
||||
if (const auto *orderedClause{
|
||||
std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
|
||||
if (const auto v{GetIntValue(orderedClause->v)}) {
|
||||
orderedLevel = *v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (orderedLevel >= collapseLevel) {
|
||||
orderedCollapseLevel = orderedLevel;
|
||||
} else {
|
||||
orderedCollapseLevel = collapseLevel;
|
||||
}
|
||||
return orderedCollapseLevel;
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckAssociatedLoopConstraints(
|
||||
const parser::OpenMPLoopConstruct &x) {
|
||||
std::int64_t ordCollapseLevel{GetOrdCollapseLevel(x)};
|
||||
AssociatedLoopChecker checker{context_, ordCollapseLevel};
|
||||
parser::Walk(x, checker);
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckDistLinear(
|
||||
const parser::OpenMPLoopConstruct &x) {
|
||||
|
||||
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
||||
const auto &clauses{std::get<parser::OmpClauseList>(beginLoopDir.t)};
|
||||
|
||||
SymbolSourceMap indexVars;
|
||||
|
||||
// Collect symbols of all the variables from linear clauses
|
||||
for (auto &clause : clauses.v) {
|
||||
if (auto *linearClause{std::get_if<parser::OmpClause::Linear>(&clause.u)}) {
|
||||
auto &objects{std::get<parser::OmpObjectList>(linearClause->v.t)};
|
||||
GetSymbolsInObjectList(objects, indexVars);
|
||||
}
|
||||
}
|
||||
|
||||
if (!indexVars.empty()) {
|
||||
// Get collapse level, if given, to find which loops are "associated."
|
||||
std::int64_t collapseVal{GetOrdCollapseLevel(x)};
|
||||
// Include the top loop if no collapse is specified
|
||||
if (collapseVal == 0) {
|
||||
collapseVal = 1;
|
||||
}
|
||||
|
||||
// Match the loop index variables with the collected symbols from linear
|
||||
// clauses.
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
|
||||
if (loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
if (itrVal.symbol) {
|
||||
// Remove the symbol from the collected set
|
||||
indexVars.erase(&itrVal.symbol->GetUltimate());
|
||||
}
|
||||
collapseVal--;
|
||||
if (collapseVal == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Get the next DoConstruct if block is not empty.
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
|
||||
: nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show error for the remaining variables
|
||||
for (auto &[symbol, source] : indexVars) {
|
||||
const Symbol &root{GetAssociationRoot(*symbol)};
|
||||
context_.Say(source,
|
||||
"Variable '%s' not allowed in LINEAR clause, only loop iterator can be specified in LINEAR clause of a construct combined with DISTRIBUTE"_err_en_US,
|
||||
root.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) {
|
||||
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
||||
const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
|
||||
|
||||
// A few semantic checks for InScan reduction are performed below as SCAN
|
||||
// constructs inside LOOP may add the relevant information. Scan reduction is
|
||||
// supported only in loop constructs, so same checks are not applicable to
|
||||
// other directives.
|
||||
using ReductionModifier = parser::OmpReductionModifier;
|
||||
for (const auto &clause : clauseList.v) {
|
||||
if (const auto *reductionClause{
|
||||
std::get_if<parser::OmpClause::Reduction>(&clause.u)}) {
|
||||
auto &modifiers{OmpGetModifiers(reductionClause->v)};
|
||||
auto *maybeModifier{OmpGetUniqueModifier<ReductionModifier>(modifiers)};
|
||||
if (maybeModifier &&
|
||||
maybeModifier->v == ReductionModifier::Value::Inscan) {
|
||||
const auto &objectList{
|
||||
std::get<parser::OmpObjectList>(reductionClause->v.t)};
|
||||
auto checkReductionSymbolInScan = [&](const parser::Name *name) {
|
||||
if (auto &symbol = name->symbol) {
|
||||
if (!symbol->test(Symbol::Flag::OmpInclusiveScan) &&
|
||||
!symbol->test(Symbol::Flag::OmpExclusiveScan)) {
|
||||
context_.Say(name->source,
|
||||
"List item %s must appear in EXCLUSIVE or "
|
||||
"INCLUSIVE clause of an "
|
||||
"enclosed SCAN directive"_err_en_US,
|
||||
name->ToString());
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const auto &ompObj : objectList.v) {
|
||||
common::visit(
|
||||
common::visitors{
|
||||
[&](const parser::Designator &designator) {
|
||||
if (const auto *name{semantics::getDesignatorNameIfDataRef(
|
||||
designator)}) {
|
||||
checkReductionSymbolInScan(name);
|
||||
}
|
||||
},
|
||||
[&](const auto &name) { checkReductionSymbolInScan(&name); },
|
||||
},
|
||||
ompObj.u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (llvm::omp::allSimdSet.test(GetContext().directive)) {
|
||||
ExitDirectiveNest(SIMDNest);
|
||||
}
|
||||
dirContext_.pop_back();
|
||||
|
||||
assert(!loopStack_.empty() && "Expecting non-empty loop stack");
|
||||
#ifndef NDEBUG
|
||||
const LoopConstruct &top{loopStack_.back()};
|
||||
auto *loopc{std::get_if<const parser::OpenMPLoopConstruct *>(&top)};
|
||||
assert(loopc != nullptr && *loopc == &x && "Mismatched loop constructs");
|
||||
#endif
|
||||
loopStack_.pop_back();
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
|
||||
const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
|
||||
ResetPartialContext(dir.source);
|
||||
switch (dir.v) {
|
||||
// 2.7.1 end-do -> END DO [nowait-clause]
|
||||
// 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
|
||||
case llvm::omp::Directive::OMPD_do:
|
||||
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_do);
|
||||
break;
|
||||
case llvm::omp::Directive::OMPD_do_simd:
|
||||
PushContextAndClauseSets(
|
||||
dir.source, llvm::omp::Directive::OMPD_end_do_simd);
|
||||
break;
|
||||
default:
|
||||
// no clauses are allowed
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Leave(const parser::OmpEndLoopDirective &x) {
|
||||
if ((GetContext().directive == llvm::omp::Directive::OMPD_end_do) ||
|
||||
(GetContext().directive == llvm::omp::Directive::OMPD_end_do_simd)) {
|
||||
dirContext_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
|
||||
CheckAllowedClause(llvm::omp::Clause::OMPC_linear);
|
||||
unsigned version{context_.langOptions().OpenMPVersion};
|
||||
llvm::omp::Directive dir{GetContext().directive};
|
||||
parser::CharBlock clauseSource{GetContext().clauseSource};
|
||||
const parser::OmpLinearModifier *linearMod{nullptr};
|
||||
|
||||
SymbolSourceMap symbols;
|
||||
auto &objects{std::get<parser::OmpObjectList>(x.v.t)};
|
||||
CheckCrayPointee(objects, "LINEAR", false);
|
||||
GetSymbolsInObjectList(objects, symbols);
|
||||
|
||||
auto CheckIntegerNoRef{[&](const Symbol *symbol, parser::CharBlock source) {
|
||||
if (!symbol->GetType()->IsNumeric(TypeCategory::Integer)) {
|
||||
auto &desc{OmpGetDescriptor<parser::OmpLinearModifier>()};
|
||||
context_.Say(source,
|
||||
"The list item '%s' specified without the REF '%s' must be of INTEGER type"_err_en_US,
|
||||
symbol->name(), desc.name.str());
|
||||
}
|
||||
}};
|
||||
|
||||
if (OmpVerifyModifiers(x.v, llvm::omp::OMPC_linear, clauseSource, context_)) {
|
||||
auto &modifiers{OmpGetModifiers(x.v)};
|
||||
linearMod = OmpGetUniqueModifier<parser::OmpLinearModifier>(modifiers);
|
||||
if (linearMod) {
|
||||
// 2.7 Loop Construct Restriction
|
||||
if ((llvm::omp::allDoSet | llvm::omp::allSimdSet).test(dir)) {
|
||||
context_.Say(clauseSource,
|
||||
"A modifier may not be specified in a LINEAR clause on the %s directive"_err_en_US,
|
||||
ContextDirectiveAsFortran());
|
||||
return;
|
||||
}
|
||||
|
||||
auto &desc{OmpGetDescriptor<parser::OmpLinearModifier>()};
|
||||
for (auto &[symbol, source] : symbols) {
|
||||
if (linearMod->v != parser::OmpLinearModifier::Value::Ref) {
|
||||
CheckIntegerNoRef(symbol, source);
|
||||
} else {
|
||||
if (!IsAllocatable(*symbol) && !IsAssumedShape(*symbol) &&
|
||||
!IsPolymorphic(*symbol)) {
|
||||
context_.Say(source,
|
||||
"The list item `%s` specified with the REF '%s' must be polymorphic variable, assumed-shape array, or a variable with the `ALLOCATABLE` attribute"_err_en_US,
|
||||
symbol->name(), desc.name.str());
|
||||
}
|
||||
}
|
||||
if (linearMod->v == parser::OmpLinearModifier::Value::Ref ||
|
||||
linearMod->v == parser::OmpLinearModifier::Value::Uval) {
|
||||
if (!IsDummy(*symbol) || IsValue(*symbol)) {
|
||||
context_.Say(source,
|
||||
"If the `%s` is REF or UVAL, the list item '%s' must be a dummy argument without the VALUE attribute"_err_en_US,
|
||||
desc.name.str(), symbol->name());
|
||||
}
|
||||
}
|
||||
} // for (symbol, source)
|
||||
|
||||
if (version >= 52 && !std::get</*PostModified=*/bool>(x.v.t)) {
|
||||
context_.Say(OmpGetModifierSource(modifiers, linearMod),
|
||||
"The 'modifier(<list>)' syntax is deprecated in %s, use '<list> : modifier' instead"_warn_en_US,
|
||||
ThisVersion(version));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OpenMP 5.2: Ordered clause restriction
|
||||
if (const auto *clause{
|
||||
FindClause(GetContext(), llvm::omp::Clause::OMPC_ordered)}) {
|
||||
const auto &orderedClause{std::get<parser::OmpClause::Ordered>(clause->u)};
|
||||
if (orderedClause.v) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// OpenMP 5.2: Linear clause Restrictions
|
||||
for (auto &[symbol, source] : symbols) {
|
||||
if (!linearMod) {
|
||||
// Already checked this with the modifier present.
|
||||
CheckIntegerNoRef(symbol, source);
|
||||
}
|
||||
if (dir == llvm::omp::Directive::OMPD_declare_simd && !IsDummy(*symbol)) {
|
||||
context_.Say(source,
|
||||
"The list item `%s` must be a dummy argument"_err_en_US,
|
||||
symbol->name());
|
||||
}
|
||||
if (IsPointer(*symbol) || symbol->test(Symbol::Flag::CrayPointer)) {
|
||||
context_.Say(source,
|
||||
"The list item `%s` in a LINEAR clause must not be Cray Pointer or a variable with POINTER attribute"_err_en_US,
|
||||
symbol->name());
|
||||
}
|
||||
if (FindCommonBlockContaining(*symbol)) {
|
||||
context_.Say(source,
|
||||
"'%s' is a common block name and must not appear in an LINEAR clause"_err_en_US,
|
||||
symbol->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
|
||||
Base::Enter(x);
|
||||
loopStack_.push_back(&x);
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Leave(const parser::DoConstruct &x) {
|
||||
assert(!loopStack_.empty() && "Expecting non-empty loop stack");
|
||||
#ifndef NDEBUG
|
||||
const LoopConstruct &top = loopStack_.back();
|
||||
auto *doc{std::get_if<const parser::DoConstruct *>(&top)};
|
||||
assert(doc != nullptr && *doc == &x && "Mismatched loop constructs");
|
||||
#endif
|
||||
loopStack_.pop_back();
|
||||
Base::Leave(x);
|
||||
}
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
548
flang/lib/Semantics/check-omp-metadirective.cpp
Normal file
548
flang/lib/Semantics/check-omp-metadirective.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
//===-- lib/Semantics/check-omp-metadirective.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Semantic checks for METADIRECTIVE and related constructs/clauses.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "check-omp-structure.h"
|
||||
|
||||
#include "openmp-utils.h"
|
||||
|
||||
#include "flang/Common/idioms.h"
|
||||
#include "flang/Common/indirection.h"
|
||||
#include "flang/Common/visit.h"
|
||||
#include "flang/Parser/characters.h"
|
||||
#include "flang/Parser/message.h"
|
||||
#include "flang/Parser/parse-tree.h"
|
||||
#include "flang/Semantics/openmp-modifiers.h"
|
||||
#include "flang/Semantics/tools.h"
|
||||
|
||||
#include "llvm/Frontend/OpenMP/OMP.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace Fortran::semantics {
|
||||
|
||||
using namespace Fortran::semantics::omp;
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::OmpClause::When &x) {
|
||||
CheckAllowedClause(llvm::omp::Clause::OMPC_when);
|
||||
OmpVerifyModifiers(
|
||||
x.v, llvm::omp::OMPC_when, GetContext().clauseSource, context_);
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::OmpContextSelector &ctx) {
|
||||
EnterDirectiveNest(ContextSelectorNest);
|
||||
|
||||
using SetName = parser::OmpTraitSetSelectorName;
|
||||
std::map<SetName::Value, const SetName *> visited;
|
||||
|
||||
for (const parser::OmpTraitSetSelector &traitSet : ctx.v) {
|
||||
auto &name{std::get<SetName>(traitSet.t)};
|
||||
auto [prev, unique]{visited.insert(std::make_pair(name.v, &name))};
|
||||
if (!unique) {
|
||||
std::string showName{parser::ToUpperCaseLetters(name.ToString())};
|
||||
parser::MessageFormattedText txt(
|
||||
"Repeated trait set name %s in a context specifier"_err_en_US,
|
||||
showName);
|
||||
parser::Message message(name.source, txt);
|
||||
message.Attach(prev->second->source,
|
||||
"Previous trait set %s provided here"_en_US, showName);
|
||||
context_.Say(std::move(message));
|
||||
}
|
||||
CheckTraitSetSelector(traitSet);
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Leave(const parser::OmpContextSelector &) {
|
||||
ExitDirectiveNest(ContextSelectorNest);
|
||||
}
|
||||
|
||||
const std::list<parser::OmpTraitProperty> &
|
||||
OmpStructureChecker::GetTraitPropertyList(
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
static const std::list<parser::OmpTraitProperty> empty{};
|
||||
auto &[_, maybeProps]{trait.t};
|
||||
if (maybeProps) {
|
||||
using PropertyList = std::list<parser::OmpTraitProperty>;
|
||||
return std::get<PropertyList>(maybeProps->t);
|
||||
} else {
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<llvm::omp::Clause> OmpStructureChecker::GetClauseFromProperty(
|
||||
const parser::OmpTraitProperty &property) {
|
||||
using MaybeClause = std::optional<llvm::omp::Clause>;
|
||||
|
||||
// The parser for OmpClause will only succeed if the clause was
|
||||
// given with all required arguments.
|
||||
// If this is a string or complex extension with a clause name,
|
||||
// treat it as a clause and let the trait checker deal with it.
|
||||
|
||||
auto getClauseFromString{[&](const std::string &s) -> MaybeClause {
|
||||
auto id{llvm::omp::getOpenMPClauseKind(parser::ToLowerCaseLetters(s))};
|
||||
if (id != llvm::omp::Clause::OMPC_unknown) {
|
||||
return id;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}};
|
||||
|
||||
return common::visit( //
|
||||
common::visitors{
|
||||
[&](const parser::OmpTraitPropertyName &x) -> MaybeClause {
|
||||
return getClauseFromString(x.v);
|
||||
},
|
||||
[&](const common::Indirection<parser::OmpClause> &x) -> MaybeClause {
|
||||
return x.value().Id();
|
||||
},
|
||||
[&](const parser::ScalarExpr &x) -> MaybeClause {
|
||||
return std::nullopt;
|
||||
},
|
||||
[&](const parser::OmpTraitPropertyExtension &x) -> MaybeClause {
|
||||
using ExtProperty = parser::OmpTraitPropertyExtension;
|
||||
if (auto *name{std::get_if<parser::OmpTraitPropertyName>(&x.u)}) {
|
||||
return getClauseFromString(name->v);
|
||||
} else if (auto *cpx{std::get_if<ExtProperty::Complex>(&x.u)}) {
|
||||
return getClauseFromString(
|
||||
std::get<parser::OmpTraitPropertyName>(cpx->t).v);
|
||||
}
|
||||
return std::nullopt;
|
||||
},
|
||||
},
|
||||
property.u);
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitSelectorList(
|
||||
const std::list<parser::OmpTraitSelector> &traits) {
|
||||
// [6.0:322:20]
|
||||
// Each trait-selector-name may only be specified once in a trait selector
|
||||
// set.
|
||||
|
||||
// Cannot store OmpTraitSelectorName directly, because it's not copyable.
|
||||
using TraitName = parser::OmpTraitSelectorName;
|
||||
using BareName = decltype(TraitName::u);
|
||||
std::map<BareName, const TraitName *> visited;
|
||||
|
||||
for (const parser::OmpTraitSelector &trait : traits) {
|
||||
auto &name{std::get<TraitName>(trait.t)};
|
||||
|
||||
auto [prev, unique]{visited.insert(std::make_pair(name.u, &name))};
|
||||
if (!unique) {
|
||||
std::string showName{parser::ToUpperCaseLetters(name.ToString())};
|
||||
parser::MessageFormattedText txt(
|
||||
"Repeated trait name %s in a trait set"_err_en_US, showName);
|
||||
parser::Message message(name.source, txt);
|
||||
message.Attach(prev->second->source,
|
||||
"Previous trait %s provided here"_en_US, showName);
|
||||
context_.Say(std::move(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitSetSelector(
|
||||
const parser::OmpTraitSetSelector &traitSet) {
|
||||
|
||||
// Trait Set | Allowed traits | D-traits | X-traits | Score |
|
||||
//
|
||||
// Construct | Simd, directive-name | Yes | No | No |
|
||||
// Device | Arch, Isa, Kind | No | Yes | No |
|
||||
// Implementation | Atomic_Default_Mem_Order | No | Yes | Yes |
|
||||
// | Extension, Requires | | | |
|
||||
// | Vendor | | | |
|
||||
// Target_Device | Arch, Device_Num, Isa | No | Yes | No |
|
||||
// | Kind, Uid | | | |
|
||||
// User | Condition | No | No | Yes |
|
||||
|
||||
struct TraitSetConfig {
|
||||
std::set<parser::OmpTraitSelectorName::Value> allowed;
|
||||
bool allowsDirectiveTraits;
|
||||
bool allowsExtensionTraits;
|
||||
bool allowsScore;
|
||||
};
|
||||
|
||||
using SName = parser::OmpTraitSetSelectorName::Value;
|
||||
using TName = parser::OmpTraitSelectorName::Value;
|
||||
|
||||
static const std::map<SName, TraitSetConfig> configs{
|
||||
{SName::Construct, //
|
||||
{{TName::Simd}, true, false, false}},
|
||||
{SName::Device, //
|
||||
{{TName::Arch, TName::Isa, TName::Kind}, false, true, false}},
|
||||
{SName::Implementation, //
|
||||
{{TName::Atomic_Default_Mem_Order, TName::Extension, TName::Requires,
|
||||
TName::Vendor},
|
||||
false, true, true}},
|
||||
{SName::Target_Device, //
|
||||
{{TName::Arch, TName::Device_Num, TName::Isa, TName::Kind,
|
||||
TName::Uid},
|
||||
false, true, false}},
|
||||
{SName::User, //
|
||||
{{TName::Condition}, false, false, true}},
|
||||
};
|
||||
|
||||
auto checkTraitSet{[&](const TraitSetConfig &config) {
|
||||
auto &[setName, traits]{traitSet.t};
|
||||
auto usn{parser::ToUpperCaseLetters(setName.ToString())};
|
||||
|
||||
// Check if there are any duplicate traits.
|
||||
CheckTraitSelectorList(traits);
|
||||
|
||||
for (const parser::OmpTraitSelector &trait : traits) {
|
||||
// Don't use structured bindings here, because they cannot be captured
|
||||
// before C++20.
|
||||
auto &traitName = std::get<parser::OmpTraitSelectorName>(trait.t);
|
||||
auto &maybeProps =
|
||||
std::get<std::optional<parser::OmpTraitSelector::Properties>>(
|
||||
trait.t);
|
||||
|
||||
// Check allowed traits
|
||||
common::visit( //
|
||||
common::visitors{
|
||||
[&](parser::OmpTraitSelectorName::Value v) {
|
||||
if (!config.allowed.count(v)) {
|
||||
context_.Say(traitName.source,
|
||||
"%s is not a valid trait for %s trait set"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()), usn);
|
||||
}
|
||||
},
|
||||
[&](llvm::omp::Directive) {
|
||||
if (!config.allowsDirectiveTraits) {
|
||||
context_.Say(traitName.source,
|
||||
"Directive name is not a valid trait for %s trait set"_err_en_US,
|
||||
usn);
|
||||
}
|
||||
},
|
||||
[&](const std::string &) {
|
||||
if (!config.allowsExtensionTraits) {
|
||||
context_.Say(traitName.source,
|
||||
"Extension traits are not valid for %s trait set"_err_en_US,
|
||||
usn);
|
||||
}
|
||||
},
|
||||
},
|
||||
traitName.u);
|
||||
|
||||
// Check score
|
||||
if (maybeProps) {
|
||||
auto &[maybeScore, _]{maybeProps->t};
|
||||
if (maybeScore) {
|
||||
CheckTraitScore(*maybeScore);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the properties of the individual traits
|
||||
CheckTraitSelector(traitSet, trait);
|
||||
}
|
||||
}};
|
||||
|
||||
checkTraitSet(
|
||||
configs.at(std::get<parser::OmpTraitSetSelectorName>(traitSet.t).v));
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitScore(const parser::OmpTraitScore &score) {
|
||||
// [6.0:322:23]
|
||||
// A score-expression must be a non-negative constant integer expression.
|
||||
if (auto value{GetIntValue(score)}; !value || value < 0) {
|
||||
context_.Say(score.source,
|
||||
"SCORE expression must be a non-negative constant integer expression"_err_en_US);
|
||||
}
|
||||
}
|
||||
|
||||
bool OmpStructureChecker::VerifyTraitPropertyLists(
|
||||
const parser::OmpTraitSetSelector &traitSet,
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
using TraitName = parser::OmpTraitSelectorName;
|
||||
using PropertyList = std::list<parser::OmpTraitProperty>;
|
||||
auto &[traitName, maybeProps]{trait.t};
|
||||
|
||||
auto checkPropertyList{[&](const PropertyList &properties, auto isValid,
|
||||
const std::string &message) {
|
||||
bool foundInvalid{false};
|
||||
for (const parser::OmpTraitProperty &prop : properties) {
|
||||
if (!isValid(prop)) {
|
||||
if (foundInvalid) {
|
||||
context_.Say(
|
||||
prop.source, "More invalid properties are present"_err_en_US);
|
||||
break;
|
||||
}
|
||||
context_.Say(prop.source, "%s"_err_en_US, message);
|
||||
foundInvalid = true;
|
||||
}
|
||||
}
|
||||
return !foundInvalid;
|
||||
}};
|
||||
|
||||
bool invalid{false};
|
||||
|
||||
if (std::holds_alternative<llvm::omp::Directive>(traitName.u)) {
|
||||
// Directive-name traits don't have properties.
|
||||
if (maybeProps) {
|
||||
context_.Say(trait.source,
|
||||
"Directive-name traits cannot have properties"_err_en_US);
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
// Ignore properties on extension traits.
|
||||
|
||||
// See `TraitSelectorParser` in openmp-parser.cpp
|
||||
if (auto *v{std::get_if<TraitName::Value>(&traitName.u)}) {
|
||||
switch (*v) {
|
||||
// name-list properties
|
||||
case parser::OmpTraitSelectorName::Value::Arch:
|
||||
case parser::OmpTraitSelectorName::Value::Extension:
|
||||
case parser::OmpTraitSelectorName::Value::Isa:
|
||||
case parser::OmpTraitSelectorName::Value::Kind:
|
||||
case parser::OmpTraitSelectorName::Value::Uid:
|
||||
case parser::OmpTraitSelectorName::Value::Vendor:
|
||||
if (maybeProps) {
|
||||
auto isName{[](const parser::OmpTraitProperty &prop) {
|
||||
return std::holds_alternative<parser::OmpTraitPropertyName>(prop.u);
|
||||
}};
|
||||
invalid = !checkPropertyList(std::get<PropertyList>(maybeProps->t),
|
||||
isName, "Trait property should be a name");
|
||||
}
|
||||
break;
|
||||
// clause-list
|
||||
case parser::OmpTraitSelectorName::Value::Atomic_Default_Mem_Order:
|
||||
case parser::OmpTraitSelectorName::Value::Requires:
|
||||
case parser::OmpTraitSelectorName::Value::Simd:
|
||||
if (maybeProps) {
|
||||
auto isClause{[&](const parser::OmpTraitProperty &prop) {
|
||||
return GetClauseFromProperty(prop).has_value();
|
||||
}};
|
||||
invalid = !checkPropertyList(std::get<PropertyList>(maybeProps->t),
|
||||
isClause, "Trait property should be a clause");
|
||||
}
|
||||
break;
|
||||
// expr-list
|
||||
case parser::OmpTraitSelectorName::Value::Condition:
|
||||
case parser::OmpTraitSelectorName::Value::Device_Num:
|
||||
if (maybeProps) {
|
||||
auto isExpr{[](const parser::OmpTraitProperty &prop) {
|
||||
return std::holds_alternative<parser::ScalarExpr>(prop.u);
|
||||
}};
|
||||
invalid = !checkPropertyList(std::get<PropertyList>(maybeProps->t),
|
||||
isExpr, "Trait property should be a scalar expression");
|
||||
}
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
return !invalid;
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitSelector(
|
||||
const parser::OmpTraitSetSelector &traitSet,
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
using TraitName = parser::OmpTraitSelectorName;
|
||||
auto &[traitName, maybeProps]{trait.t};
|
||||
|
||||
// Only do the detailed checks if the property lists are valid.
|
||||
if (VerifyTraitPropertyLists(traitSet, trait)) {
|
||||
if (std::holds_alternative<llvm::omp::Directive>(traitName.u) ||
|
||||
std::holds_alternative<std::string>(traitName.u)) {
|
||||
// No properties here: directives don't have properties, and
|
||||
// we don't implement any extension traits now.
|
||||
return;
|
||||
}
|
||||
|
||||
// Specific traits we want to check.
|
||||
// Limitations:
|
||||
// (1) The properties for these traits are defined in "Additional
|
||||
// Definitions for the OpenMP API Specification". It's not clear how
|
||||
// to define them in a portable way, and how to verify their validity,
|
||||
// especially if they get replaced by their integer values (in case
|
||||
// they are defined as enums).
|
||||
// (2) These are entirely implementation-defined, and at the moment
|
||||
// there is no known schema to validate these values.
|
||||
auto v{std::get<TraitName::Value>(traitName.u)};
|
||||
switch (v) {
|
||||
case TraitName::Value::Arch:
|
||||
// Unchecked, TBD(1)
|
||||
break;
|
||||
case TraitName::Value::Atomic_Default_Mem_Order:
|
||||
CheckTraitADMO(traitSet, trait);
|
||||
break;
|
||||
case TraitName::Value::Condition:
|
||||
CheckTraitCondition(traitSet, trait);
|
||||
break;
|
||||
case TraitName::Value::Device_Num:
|
||||
CheckTraitDeviceNum(traitSet, trait);
|
||||
break;
|
||||
case TraitName::Value::Extension:
|
||||
// Ignore
|
||||
break;
|
||||
case TraitName::Value::Isa:
|
||||
// Unchecked, TBD(1)
|
||||
break;
|
||||
case TraitName::Value::Kind:
|
||||
// Unchecked, TBD(1)
|
||||
break;
|
||||
case TraitName::Value::Requires:
|
||||
CheckTraitRequires(traitSet, trait);
|
||||
break;
|
||||
case TraitName::Value::Simd:
|
||||
CheckTraitSimd(traitSet, trait);
|
||||
break;
|
||||
case TraitName::Value::Uid:
|
||||
// Unchecked, TBD(2)
|
||||
break;
|
||||
case TraitName::Value::Vendor:
|
||||
// Unchecked, TBD(1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitADMO(
|
||||
const parser::OmpTraitSetSelector &traitSet,
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
|
||||
auto &properties{GetTraitPropertyList(trait)};
|
||||
|
||||
if (properties.size() != 1) {
|
||||
context_.Say(trait.source,
|
||||
"%s trait requires a single clause property"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()));
|
||||
} else {
|
||||
const parser::OmpTraitProperty &property{properties.front()};
|
||||
auto clauseId{*GetClauseFromProperty(property)};
|
||||
// Check that the clause belongs to the memory-order clause-set.
|
||||
// Clause sets will hopefully be autogenerated at some point.
|
||||
switch (clauseId) {
|
||||
case llvm::omp::Clause::OMPC_acq_rel:
|
||||
case llvm::omp::Clause::OMPC_acquire:
|
||||
case llvm::omp::Clause::OMPC_relaxed:
|
||||
case llvm::omp::Clause::OMPC_release:
|
||||
case llvm::omp::Clause::OMPC_seq_cst:
|
||||
break;
|
||||
default:
|
||||
context_.Say(property.source,
|
||||
"%s trait requires a clause from the memory-order clause set"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()));
|
||||
}
|
||||
|
||||
using ClauseProperty = common::Indirection<parser::OmpClause>;
|
||||
if (!std::holds_alternative<ClauseProperty>(property.u)) {
|
||||
context_.Say(property.source,
|
||||
"Invalid clause specification for %s"_err_en_US,
|
||||
parser::ToUpperCaseLetters(getClauseName(clauseId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitCondition(
|
||||
const parser::OmpTraitSetSelector &traitSet,
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
|
||||
auto &properties{GetTraitPropertyList(trait)};
|
||||
|
||||
if (properties.size() != 1) {
|
||||
context_.Say(trait.source,
|
||||
"%s trait requires a single expression property"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()));
|
||||
} else {
|
||||
const parser::OmpTraitProperty &property{properties.front()};
|
||||
auto &scalarExpr{std::get<parser::ScalarExpr>(property.u)};
|
||||
|
||||
auto maybeType{GetDynamicType(scalarExpr.thing.value())};
|
||||
if (!maybeType || maybeType->category() != TypeCategory::Logical) {
|
||||
context_.Say(property.source,
|
||||
"%s trait requires a single LOGICAL expression"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitDeviceNum(
|
||||
const parser::OmpTraitSetSelector &traitSet,
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
|
||||
auto &properties{GetTraitPropertyList(trait)};
|
||||
|
||||
if (properties.size() != 1) {
|
||||
context_.Say(trait.source,
|
||||
"%s trait requires a single expression property"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()));
|
||||
}
|
||||
// No other checks at the moment.
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitRequires(
|
||||
const parser::OmpTraitSetSelector &traitSet,
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
unsigned version{context_.langOptions().OpenMPVersion};
|
||||
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
|
||||
auto &properties{GetTraitPropertyList(trait)};
|
||||
|
||||
for (const parser::OmpTraitProperty &property : properties) {
|
||||
auto clauseId{*GetClauseFromProperty(property)};
|
||||
if (!llvm::omp::isAllowedClauseForDirective(
|
||||
llvm::omp::OMPD_requires, clauseId, version)) {
|
||||
context_.Say(property.source,
|
||||
"%s trait requires a clause from the requirement clause set"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()));
|
||||
}
|
||||
|
||||
using ClauseProperty = common::Indirection<parser::OmpClause>;
|
||||
if (!std::holds_alternative<ClauseProperty>(property.u)) {
|
||||
context_.Say(property.source,
|
||||
"Invalid clause specification for %s"_err_en_US,
|
||||
parser::ToUpperCaseLetters(getClauseName(clauseId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::CheckTraitSimd(
|
||||
const parser::OmpTraitSetSelector &traitSet,
|
||||
const parser::OmpTraitSelector &trait) {
|
||||
unsigned version{context_.langOptions().OpenMPVersion};
|
||||
auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
|
||||
auto &properties{GetTraitPropertyList(trait)};
|
||||
|
||||
for (const parser::OmpTraitProperty &property : properties) {
|
||||
auto clauseId{*GetClauseFromProperty(property)};
|
||||
if (!llvm::omp::isAllowedClauseForDirective(
|
||||
llvm::omp::OMPD_declare_simd, clauseId, version)) {
|
||||
context_.Say(property.source,
|
||||
"%s trait requires a clause that is allowed on the %s directive"_err_en_US,
|
||||
parser::ToUpperCaseLetters(traitName.ToString()),
|
||||
parser::ToUpperCaseLetters(
|
||||
getDirectiveName(llvm::omp::OMPD_declare_simd)));
|
||||
}
|
||||
|
||||
using ClauseProperty = common::Indirection<parser::OmpClause>;
|
||||
if (!std::holds_alternative<ClauseProperty>(property.u)) {
|
||||
context_.Say(property.source,
|
||||
"Invalid clause specification for %s"_err_en_US,
|
||||
parser::ToUpperCaseLetters(getClauseName(clauseId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
|
||||
EnterDirectiveNest(MetadirectiveNest);
|
||||
PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_metadirective);
|
||||
}
|
||||
|
||||
void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &) {
|
||||
ExitDirectiveNest(MetadirectiveNest);
|
||||
dirContext_.pop_back();
|
||||
}
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
File diff suppressed because it is too large
Load Diff
@@ -162,10 +162,6 @@ public:
|
||||
|
||||
private:
|
||||
bool CheckAllowedClause(llvmOmpClause clause);
|
||||
bool IsVariableListItem(const Symbol &sym);
|
||||
bool IsExtendedListItem(const Symbol &sym);
|
||||
bool IsCommonBlock(const Symbol &sym);
|
||||
std::optional<bool> IsContiguous(const parser::OmpObject &object);
|
||||
void CheckVariableListItem(const SymbolSourceMap &symbols);
|
||||
void CheckMultipleOccurrence(semantics::UnorderedSymbolSet &listVars,
|
||||
const std::list<parser::Name> &nameList, const parser::CharBlock &item,
|
||||
@@ -215,10 +211,6 @@ private:
|
||||
typename IterTy = decltype(std::declval<RangeTy>().begin())>
|
||||
std::optional<IterTy> FindDuplicate(RangeTy &&);
|
||||
|
||||
const Symbol *GetObjectSymbol(const parser::OmpObject &object);
|
||||
const Symbol *GetArgumentSymbol(const parser::OmpArgument &argument);
|
||||
std::optional<parser::CharBlock> GetObjectSource(
|
||||
const parser::OmpObject &object);
|
||||
void CheckDependList(const parser::DataRef &);
|
||||
void CheckDependArraySection(
|
||||
const common::Indirection<parser::ArrayElement> &, const parser::Name &);
|
||||
|
||||
393
flang/lib/Semantics/openmp-utils.cpp
Normal file
393
flang/lib/Semantics/openmp-utils.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
//===-- lib/Semantics/openmp-utils.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Common utilities used in OpenMP semantic checks.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "openmp-utils.h"
|
||||
|
||||
#include "flang/Common/indirection.h"
|
||||
#include "flang/Common/reference.h"
|
||||
#include "flang/Common/visit.h"
|
||||
#include "flang/Evaluate/check-expression.h"
|
||||
#include "flang/Evaluate/expression.h"
|
||||
#include "flang/Evaluate/tools.h"
|
||||
#include "flang/Evaluate/traverse.h"
|
||||
#include "flang/Evaluate/type.h"
|
||||
#include "flang/Evaluate/variable.h"
|
||||
#include "flang/Parser/parse-tree.h"
|
||||
#include "flang/Semantics/expression.h"
|
||||
#include "flang/Semantics/semantics.h"
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace Fortran::semantics::omp {
|
||||
|
||||
std::string ThisVersion(unsigned version) {
|
||||
std::string tv{
|
||||
std::to_string(version / 10) + "." + std::to_string(version % 10)};
|
||||
return "OpenMP v" + tv;
|
||||
}
|
||||
|
||||
std::string TryVersion(unsigned version) {
|
||||
return "try -fopenmp-version=" + std::to_string(version);
|
||||
}
|
||||
|
||||
const parser::Designator *GetDesignatorFromObj(
|
||||
const parser::OmpObject &object) {
|
||||
return std::get_if<parser::Designator>(&object.u);
|
||||
}
|
||||
|
||||
const parser::DataRef *GetDataRefFromObj(const parser::OmpObject &object) {
|
||||
if (auto *desg{GetDesignatorFromObj(object)}) {
|
||||
return std::get_if<parser::DataRef>(&desg->u);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const parser::ArrayElement *GetArrayElementFromObj(
|
||||
const parser::OmpObject &object) {
|
||||
if (auto *dataRef{GetDataRefFromObj(object)}) {
|
||||
using ElementIndirection = common::Indirection<parser::ArrayElement>;
|
||||
if (auto *ind{std::get_if<ElementIndirection>(&dataRef->u)}) {
|
||||
return &ind->value();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Symbol *GetObjectSymbol(const parser::OmpObject &object) {
|
||||
// Some symbols may be missing if the resolution failed, e.g. when an
|
||||
// undeclared name is used with implicit none.
|
||||
if (auto *name{std::get_if<parser::Name>(&object.u)}) {
|
||||
return name->symbol ? &name->symbol->GetUltimate() : nullptr;
|
||||
} else if (auto *desg{std::get_if<parser::Designator>(&object.u)}) {
|
||||
auto &last{GetLastName(*desg)};
|
||||
return last.symbol ? &GetLastName(*desg).symbol->GetUltimate() : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Symbol *GetArgumentSymbol(const parser::OmpArgument &argument) {
|
||||
if (auto *locator{std::get_if<parser::OmpLocator>(&argument.u)}) {
|
||||
if (auto *object{std::get_if<parser::OmpObject>(&locator->u)}) {
|
||||
return GetObjectSymbol(*object);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<parser::CharBlock> GetObjectSource(
|
||||
const parser::OmpObject &object) {
|
||||
if (auto *name{std::get_if<parser::Name>(&object.u)}) {
|
||||
return name->source;
|
||||
} else if (auto *desg{std::get_if<parser::Designator>(&object.u)}) {
|
||||
return GetLastName(*desg).source;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool IsCommonBlock(const Symbol &sym) {
|
||||
return sym.detailsIf<CommonBlockDetails>() != nullptr;
|
||||
}
|
||||
|
||||
bool IsVariableListItem(const Symbol &sym) {
|
||||
return evaluate::IsVariable(sym) || sym.attrs().test(Attr::POINTER);
|
||||
}
|
||||
|
||||
bool IsExtendedListItem(const Symbol &sym) {
|
||||
return IsVariableListItem(sym) || sym.IsSubprogram();
|
||||
}
|
||||
|
||||
bool IsVarOrFunctionRef(const MaybeExpr &expr) {
|
||||
if (expr) {
|
||||
return evaluate::UnwrapProcedureRef(*expr) != nullptr ||
|
||||
evaluate::IsVariable(*expr);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr) {
|
||||
const parser::TypedExpr &typedExpr{parserExpr.typedExpr};
|
||||
// ForwardOwningPointer typedExpr
|
||||
// `- GenericExprWrapper ^.get()
|
||||
// `- std::optional<Expr> ^->v
|
||||
return typedExpr.get()->v;
|
||||
}
|
||||
|
||||
std::optional<evaluate::DynamicType> GetDynamicType(
|
||||
const parser::Expr &parserExpr) {
|
||||
if (auto maybeExpr{GetEvaluateExpr(parserExpr)}) {
|
||||
return maybeExpr->GetType();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct ContiguousHelper {
|
||||
ContiguousHelper(SemanticsContext &context)
|
||||
: fctx_(context.foldingContext()) {}
|
||||
|
||||
template <typename Contained>
|
||||
std::optional<bool> Visit(const common::Indirection<Contained> &x) {
|
||||
return Visit(x.value());
|
||||
}
|
||||
template <typename Contained>
|
||||
std::optional<bool> Visit(const common::Reference<Contained> &x) {
|
||||
return Visit(x.get());
|
||||
}
|
||||
template <typename T> std::optional<bool> Visit(const evaluate::Expr<T> &x) {
|
||||
return common::visit([&](auto &&s) { return Visit(s); }, x.u);
|
||||
}
|
||||
template <typename T>
|
||||
std::optional<bool> Visit(const evaluate::Designator<T> &x) {
|
||||
return common::visit(
|
||||
[this](auto &&s) { return evaluate::IsContiguous(s, fctx_); }, x.u);
|
||||
}
|
||||
template <typename T> std::optional<bool> Visit(const T &) {
|
||||
// Everything else.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
evaluate::FoldingContext &fctx_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Return values:
|
||||
// - std::optional<bool>{true} if the object is known to be contiguous
|
||||
// - std::optional<bool>{false} if the object is known not to be contiguous
|
||||
// - std::nullopt if the object contiguity cannot be determined
|
||||
std::optional<bool> IsContiguous(
|
||||
SemanticsContext &semaCtx, const parser::OmpObject &object) {
|
||||
return common::visit( //
|
||||
common::visitors{
|
||||
[&](const parser::Name &x) {
|
||||
// Any member of a common block must be contiguous.
|
||||
return std::optional<bool>{true};
|
||||
},
|
||||
[&](const parser::Designator &x) {
|
||||
evaluate::ExpressionAnalyzer ea{semaCtx};
|
||||
if (MaybeExpr maybeExpr{ea.Analyze(x)}) {
|
||||
return ContiguousHelper{semaCtx}.Visit(*maybeExpr);
|
||||
}
|
||||
return std::optional<bool>{};
|
||||
},
|
||||
},
|
||||
object.u);
|
||||
}
|
||||
|
||||
struct DesignatorCollector : public evaluate::Traverse<DesignatorCollector,
|
||||
std::vector<SomeExpr>, false> {
|
||||
using Result = std::vector<SomeExpr>;
|
||||
using Base = evaluate::Traverse<DesignatorCollector, Result, false>;
|
||||
DesignatorCollector() : Base(*this) {}
|
||||
|
||||
Result Default() const { return {}; }
|
||||
|
||||
using Base::operator();
|
||||
|
||||
template <typename T> //
|
||||
Result operator()(const evaluate::Designator<T> &x) const {
|
||||
// Once in a designator, don't traverse it any further (i.e. only
|
||||
// collect top-level designators).
|
||||
auto copy{x};
|
||||
return Result{AsGenericExpr(std::move(copy))};
|
||||
}
|
||||
|
||||
template <typename... Rs> //
|
||||
Result Combine(Result &&result, Rs &&...results) const {
|
||||
Result v(std::move(result));
|
||||
auto moveAppend{[](auto &accum, auto &&other) {
|
||||
for (auto &&s : other) {
|
||||
accum.push_back(std::move(s));
|
||||
}
|
||||
}};
|
||||
(moveAppend(v, std::move(results)), ...);
|
||||
return v;
|
||||
}
|
||||
};
|
||||
|
||||
struct VariableFinder : public evaluate::AnyTraverse<VariableFinder> {
|
||||
using Base = evaluate::AnyTraverse<VariableFinder>;
|
||||
VariableFinder(const SomeExpr &v) : Base(*this), var(v) {}
|
||||
|
||||
using Base::operator();
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const evaluate::Designator<T> &x) const {
|
||||
auto copy{x};
|
||||
return evaluate::AsGenericExpr(std::move(copy)) == var;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const evaluate::FunctionRef<T> &x) const {
|
||||
auto copy{x};
|
||||
return evaluate::AsGenericExpr(std::move(copy)) == var;
|
||||
}
|
||||
|
||||
private:
|
||||
const SomeExpr &var;
|
||||
};
|
||||
|
||||
std::vector<SomeExpr> GetAllDesignators(const SomeExpr &expr) {
|
||||
return DesignatorCollector{}(expr);
|
||||
}
|
||||
|
||||
static bool HasCommonDesignatorSymbols(
|
||||
const evaluate::SymbolVector &baseSyms, const SomeExpr &other) {
|
||||
// Compare the designators used in "other" with the designators whose
|
||||
// symbols are given in baseSyms.
|
||||
// This is a part of the check if these two expressions can access the same
|
||||
// storage: if the designators used in them are different enough, then they
|
||||
// will be assumed not to access the same memory.
|
||||
//
|
||||
// Consider an (array element) expression x%y(w%z), the corresponding symbol
|
||||
// vector will be {x, y, w, z} (i.e. the symbols for these names).
|
||||
// Check whether this exact sequence appears anywhere in any the symbol
|
||||
// vector for "other". This will be true for x(y) and x(y+1), so this is
|
||||
// not a sufficient condition, but can be used to eliminate candidates
|
||||
// before doing more exhaustive checks.
|
||||
//
|
||||
// If any of the symbols in this sequence are function names, assume that
|
||||
// there is no storage overlap, mostly because it would be impossible in
|
||||
// general to determine what storage the function will access.
|
||||
// Note: if f is pure, then two calls to f will access the same storage
|
||||
// when called with the same arguments. This check is not done yet.
|
||||
|
||||
if (llvm::any_of(
|
||||
baseSyms, [](const SymbolRef &s) { return s->IsSubprogram(); })) {
|
||||
// If there is a function symbol in the chain then we can't infer much
|
||||
// about the accessed storage.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto isSubsequence{// Is u a subsequence of v.
|
||||
[](const evaluate::SymbolVector &u, const evaluate::SymbolVector &v) {
|
||||
size_t us{u.size()}, vs{v.size()};
|
||||
if (us > vs) {
|
||||
return false;
|
||||
}
|
||||
for (size_t off{0}; off != vs - us + 1; ++off) {
|
||||
bool same{true};
|
||||
for (size_t i{0}; i != us; ++i) {
|
||||
if (u[i] != v[off + i]) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (same) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}};
|
||||
|
||||
evaluate::SymbolVector otherSyms{evaluate::GetSymbolVector(other)};
|
||||
return isSubsequence(baseSyms, otherSyms);
|
||||
}
|
||||
|
||||
static bool HasCommonTopLevelDesignators(
|
||||
const std::vector<SomeExpr> &baseDsgs, const SomeExpr &other) {
|
||||
// Compare designators directly as expressions. This will ensure
|
||||
// that x(y) and x(y+1) are not flagged as overlapping, whereas
|
||||
// the symbol vectors for both of these would be identical.
|
||||
std::vector<SomeExpr> otherDsgs{GetAllDesignators(other)};
|
||||
|
||||
for (auto &s : baseDsgs) {
|
||||
if (llvm::any_of(otherDsgs, [&](auto &&t) { return s == t; })) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const SomeExpr *HasStorageOverlap(
|
||||
const SomeExpr &base, llvm::ArrayRef<SomeExpr> exprs) {
|
||||
evaluate::SymbolVector baseSyms{evaluate::GetSymbolVector(base)};
|
||||
std::vector<SomeExpr> baseDsgs{GetAllDesignators(base)};
|
||||
|
||||
for (const SomeExpr &expr : exprs) {
|
||||
if (!HasCommonDesignatorSymbols(baseSyms, expr)) {
|
||||
continue;
|
||||
}
|
||||
if (HasCommonTopLevelDesignators(baseDsgs, expr)) {
|
||||
return &expr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsSubexpressionOf(const SomeExpr &sub, const SomeExpr &super) {
|
||||
return VariableFinder{sub}(super);
|
||||
}
|
||||
|
||||
// Check if the ActionStmt is actually a [Pointer]AssignmentStmt. This is
|
||||
// to separate cases where the source has something that looks like an
|
||||
// assignment, but is semantically wrong (diagnosed by general semantic
|
||||
// checks), and where the source has some other statement (which we want
|
||||
// to report as "should be an assignment").
|
||||
bool IsAssignment(const parser::ActionStmt *x) {
|
||||
if (x == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using AssignmentStmt = common::Indirection<parser::AssignmentStmt>;
|
||||
using PointerAssignmentStmt =
|
||||
common::Indirection<parser::PointerAssignmentStmt>;
|
||||
|
||||
return common::visit(
|
||||
[](auto &&s) -> bool {
|
||||
using BareS = llvm::remove_cvref_t<decltype(s)>;
|
||||
return std::is_same_v<BareS, AssignmentStmt> ||
|
||||
std::is_same_v<BareS, PointerAssignmentStmt>;
|
||||
},
|
||||
x->u);
|
||||
}
|
||||
|
||||
bool IsPointerAssignment(const evaluate::Assignment &x) {
|
||||
return std::holds_alternative<evaluate::Assignment::BoundsSpec>(x.u) ||
|
||||
std::holds_alternative<evaluate::Assignment::BoundsRemapping>(x.u);
|
||||
}
|
||||
|
||||
/// parser::Block is a list of executable constructs, parser::BlockConstruct
|
||||
/// is Fortran's BLOCK/ENDBLOCK construct.
|
||||
/// Strip the outermost BlockConstructs, return the reference to the Block
|
||||
/// in the executable part of the innermost of the stripped constructs.
|
||||
/// Specifically, if the given `block` has a single entry (it's a list), and
|
||||
/// the entry is a BlockConstruct, get the Block contained within. Repeat
|
||||
/// this step as many times as possible.
|
||||
const parser::Block &GetInnermostExecPart(const parser::Block &block) {
|
||||
const parser::Block *iter{&block};
|
||||
while (iter->size() == 1) {
|
||||
const parser::ExecutionPartConstruct &ep{iter->front()};
|
||||
if (auto *exec{std::get_if<parser::ExecutableConstruct>(&ep.u)}) {
|
||||
using BlockConstruct = common::Indirection<parser::BlockConstruct>;
|
||||
if (auto *bc{std::get_if<BlockConstruct>(&exec->u)}) {
|
||||
iter = &std::get<parser::Block>(bc->value().t);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return *iter;
|
||||
}
|
||||
|
||||
} // namespace Fortran::semantics::omp
|
||||
66
flang/lib/Semantics/openmp-utils.h
Normal file
66
flang/lib/Semantics/openmp-utils.h
Normal file
@@ -0,0 +1,66 @@
|
||||
//===-- lib/Semantics/openmp-utils.h --------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Common utilities used in OpenMP semantic checks.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef FORTRAN_SEMANTICS_OPENMP_UTILS_H
|
||||
#define FORTRAN_SEMANTICS_OPENMP_UTILS_H
|
||||
|
||||
#include "flang/Evaluate/type.h"
|
||||
#include "flang/Parser/char-block.h"
|
||||
#include "flang/Parser/parse-tree.h"
|
||||
#include "flang/Semantics/tools.h"
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace Fortran::semantics {
|
||||
class SemanticsContext;
|
||||
class Symbol;
|
||||
|
||||
// Add this namespace to avoid potential conflicts
|
||||
namespace omp {
|
||||
std::string ThisVersion(unsigned version);
|
||||
std::string TryVersion(unsigned version);
|
||||
|
||||
const parser::Designator *GetDesignatorFromObj(const parser::OmpObject &object);
|
||||
const parser::DataRef *GetDataRefFromObj(const parser::OmpObject &object);
|
||||
const parser::ArrayElement *GetArrayElementFromObj(
|
||||
const parser::OmpObject &object);
|
||||
const Symbol *GetObjectSymbol(const parser::OmpObject &object);
|
||||
const Symbol *GetArgumentSymbol(const parser::OmpArgument &argument);
|
||||
std::optional<parser::CharBlock> GetObjectSource(
|
||||
const parser::OmpObject &object);
|
||||
|
||||
bool IsCommonBlock(const Symbol &sym);
|
||||
bool IsExtendedListItem(const Symbol &sym);
|
||||
bool IsVariableListItem(const Symbol &sym);
|
||||
bool IsVarOrFunctionRef(const MaybeExpr &expr);
|
||||
|
||||
std::optional<SomeExpr> GetEvaluateExpr(const parser::Expr &parserExpr);
|
||||
std::optional<evaluate::DynamicType> GetDynamicType(
|
||||
const parser::Expr &parserExpr);
|
||||
|
||||
std::optional<bool> IsContiguous(
|
||||
SemanticsContext &semaCtx, const parser::OmpObject &object);
|
||||
|
||||
std::vector<SomeExpr> GetAllDesignators(const SomeExpr &expr);
|
||||
const SomeExpr *HasStorageOverlap(
|
||||
const SomeExpr &base, llvm::ArrayRef<SomeExpr> exprs);
|
||||
bool IsSubexpressionOf(const SomeExpr &sub, const SomeExpr &super);
|
||||
bool IsAssignment(const parser::ActionStmt *x);
|
||||
bool IsPointerAssignment(const evaluate::Assignment &x);
|
||||
const parser::Block &GetInnermostExecPart(const parser::Block &block);
|
||||
} // namespace omp
|
||||
} // namespace Fortran::semantics
|
||||
|
||||
#endif // FORTRAN_SEMANTICS_OPENMP_UTILS_H
|
||||
Reference in New Issue
Block a user