blob: f631308819c4a56909543dfe4bad156794401bdf [file] [edit]
//===-- lib/Semantics/check-omp-syntax.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
//
//===----------------------------------------------------------------------===//
#include "check-omp-structure.h"
#include "flang/Common/visit.h"
#include "flang/Parser/char-block.h"
#include "flang/Parser/openmp-utils.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/openmp-modifiers.h"
#include "flang/Semantics/openmp-utils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Frontend/Directive/Spelling.h"
#include "llvm/Frontend/OpenMP/OMP.h"
#include "llvm/Frontend/OpenMP/OMPDescriptors.h"
#include <algorithm>
#include <list>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <variant>
namespace Fortran::semantics {
using namespace Fortran::parser::omp;
template <typename T> struct SetTypeFor {
using type = llvm::omp::EnumSet<T,
llvm::to_underlying(T::Last_) - llvm::to_underlying(T::First_) + 1>;
};
static llvm::omp::Modifiers getElements(
const llvm::omp::descriptor::Clause &cdesc, llvm::omp::Version version) {
return cdesc.getModifiers(version);
}
static llvm::omp::Modifiers getElements(
const llvm::omp::descriptor::ModifierSet &sdesc,
llvm::omp::Version version) {
return sdesc.getModifiers(version);
}
static llvm::omp::ModifierSets getSets(
const llvm::omp::descriptor::Clause &cdesc, llvm::omp::Version version) {
return cdesc.getModifierSets(version);
}
template < //
typename ElemTy, typename SetsSetTy, typename OwnerTy,
typename ResultTy = llvm::DenseMap<ElemTy,
std::pair<parser::CharBlock, llvm::directive::VersionRange>>>
static ResultTy VerifyVersions(
const AppliedElementInfo<ElemTy, SetsSetTy> &info, OwnerTy ownerId,
llvm::omp::Version version) {
using AppliedElementTy = AppliedElement<ElemTy, SetsSetTy>;
ResultTy result;
auto &odesc{llvm::omp::getDescriptor(ownerId)};
auto elements{getElements(odesc, version)};
for (const AppliedElementTy &elem : info.elements) {
if (elements.test(elem.id.value)) {
continue;
}
llvm::omp::Version since{~0u}, until{0u};
for (llvm::omp::Version v : odesc.getVersions()) {
if (getElements(odesc, v).test(elem.id.value)) {
if (v < version) {
until = std::max(until, v);
} else if (v > version) {
since = std::min(since, v);
}
}
}
int minVer = static_cast<unsigned>(since);
int maxVer = static_cast<unsigned>(until);
result.insert({elem.id.value,
{elem.id.source, llvm::directive::VersionRange{minVer, maxVer}}});
}
return result;
}
template < //
typename ElemTy, typename SetsSetTy, typename OwnerTy,
typename ElemSetTy = typename SetTypeFor<ElemTy>::type,
typename ResultTy = std::pair<ElemSetTy, SetsSetTy>>
static ResultTy VerifyRequired(
const AppliedElementInfo<ElemTy, SetsSetTy> &info, OwnerTy ownerId,
llvm::omp::Version version) {
using AppliedElementTy = AppliedElement<ElemTy, SetsSetTy>;
ResultTy required;
auto &odesc{llvm::omp::getDescriptor(ownerId)};
for (auto e : getElements(odesc, version)) {
auto &edesc{llvm::omp::getDescriptor(e)};
if (edesc.getProperties(version).test(llvm::omp::Property::Required)) {
required.first.set(e);
}
}
for (auto s : getSets(odesc, version)) {
auto &sdesc{llvm::omp::getDescriptor(s)};
if (sdesc.getProperties(version).test(llvm::omp::Property::Required)) {
required.second.set(s);
}
}
for (const AppliedElementTy &elem : info.elements) {
required.first.reset(elem.id.value);
required.second &= ~elem.sets;
}
return required;
}
template < //
typename ElemTy, typename SetsSetTy, typename OwnerTy,
typename ResultTy =
llvm::DenseMap<ElemTy, std::pair<parser::CharBlock, parser::CharBlock>>>
static ResultTy VerifyUnique(const AppliedElementInfo<ElemTy, SetsSetTy> &info,
OwnerTy ownerId, llvm::omp::Version version) {
using AppliedElementTy = AppliedElement<ElemTy, SetsSetTy>;
using ElemSetTy = typename SetTypeFor<ElemTy>::type;
ElemSetTy unique;
auto &odesc{llvm::omp::getDescriptor(ownerId)};
auto elements{getElements(odesc, version)};
for (auto e : elements) {
auto &edesc{llvm::omp::getDescriptor(e)};
// Exclusive modifiers should have the "unique" property present as well.
if (edesc.getProperties(version).test(llvm::omp::Property::Unique)) {
unique.set(e);
}
}
for (auto s : getSets(odesc, version)) {
auto &sdesc{llvm::omp::getDescriptor(s)};
if (sdesc.getProperties(version).test(llvm::omp::Property::Unique)) {
unique |= getElements(sdesc, version);
}
}
ResultTy repeated;
llvm::DenseMap<ElemTy, parser::CharBlock> present;
for (const AppliedElementTy &elem : info.elements) {
if (!elements.test(elem.id.value)) {
// Skip invalid elements.
continue;
}
if (unique.test(elem.id.value)) {
auto [where, inserted]{present.insert({elem.id.value, elem.id.source})};
if (!inserted) {
repeated.insert({elem.id.value, {where->second, elem.id.source}});
}
}
}
return repeated;
}
template < //
typename ElemTy, typename SetsSetTy, typename OwnerTy,
typename ResultTy = llvm::DenseMap<ElemTy,
std::tuple<ElemTy, parser::CharBlock, parser::CharBlock>>>
static ResultTy VerifyExclusive(
const AppliedElementInfo<ElemTy, SetsSetTy> &info, OwnerTy ownerId,
llvm::omp::Version version) {
using AppliedElementTy = AppliedElement<ElemTy, SetsSetTy>;
ResultTy result;
auto &odesc{llvm::omp::getDescriptor(ownerId)};
auto elements{getElements(odesc, version)};
llvm::DenseMap<ElemTy, parser::CharBlock> present;
for (const AppliedElementTy &elem : info.elements) {
if (!elements.test(elem.id.value)) {
// Skip invalid elements.
continue;
}
present.insert({elem.id.value, elem.id.source});
}
for (auto [id, source] : present) {
auto &edesc{llvm::omp::getDescriptor(id)};
if (!edesc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
continue;
}
// Element is exclusive, it cannot coexist with any other element.
for (auto [otherId, otherSource] : present) {
if (otherId != id) {
result.insert({id, {otherId, source, otherSource}});
break;
}
}
}
return result;
}
template < //
typename ElemTy, typename SetsSetTy, typename OwnerTy,
typename ResultTy = llvm::DenseMap<ElemTy,
std::tuple<ElemTy, parser::CharBlock, parser::CharBlock>>>
static ResultTy VerifyMutuallyExclusive(
const AppliedElementInfo<ElemTy, SetsSetTy> &info, OwnerTy ownerId,
llvm::omp::Version version) {
using AppliedElementTy = AppliedElement<ElemTy, SetsSetTy>;
using SetTy = typename SetsSetTy::value_type;
ResultTy result;
auto &odesc{llvm::omp::getDescriptor(ownerId)};
auto elements{getElements(odesc, version)};
llvm::DenseMap<SetTy, const AppliedElementTy *> exclusive;
for (const AppliedElementTy &elem : info.elements) {
if (!elements.test(elem.id.value)) {
// Skip invalid elements.
continue;
}
for (auto s : elem.sets) {
auto &sdesc{llvm::omp::getDescriptor(s)};
if (!sdesc.getProperties(version).test(llvm::omp::Property::Exclusive)) {
continue;
}
auto [where, inserted]{exclusive.insert({s, &elem})};
if (!inserted) {
const AppliedElementTy *prev{where->second};
if (prev->id.value != elem.id.value) {
result.insert({elem.id.value,
{prev->id.value, elem.id.source, prev->id.source}});
}
}
}
}
return result;
}
template < //
typename ElemTy, typename SetsSetTy, typename OwnerTy,
typename ResultTy = llvm::DenseMap<ElemTy, parser::CharBlock>>
static ResultTy VerifyUltimate(
const AppliedElementInfo<ElemTy, SetsSetTy> &info, OwnerTy ownerId,
llvm::omp::Version version, bool last = true) {
ResultTy result;
if (info.elements.empty()) {
return result;
}
using AppliedElementTy = AppliedElement<ElemTy, SetsSetTy>;
using ElemSetTy = typename SetTypeFor<ElemTy>::type;
ElemSetTy ultimate;
auto &odesc{llvm::omp::getDescriptor(ownerId)};
auto elements{getElements(odesc, version)};
for (auto e : elements) {
auto &edesc{llvm::omp::getDescriptor(e)};
if (edesc.getProperties(version).test(llvm::omp::Property::Ultimate)) {
ultimate.set(e);
}
}
for (auto s : getSets(odesc, version)) {
auto &sdesc{llvm::omp::getDescriptor(s)};
if (sdesc.getProperties(version).test(llvm::omp::Property::Ultimate)) {
ultimate |= getElements(sdesc, version);
}
}
// Check if there is an ultimate modifier that is in a wrong position.
auto rest{last
? llvm::ArrayRef<AppliedElementTy>(info.elements).drop_back(1)
: llvm::ArrayRef<AppliedElementTy>(info.elements).drop_front(1)};
for (const AppliedElementTy &elem : rest) {
if (!elements.test(elem.id.value)) {
// Skip invalid elements.
continue;
}
if (ultimate.test(elem.id.value)) {
result.insert({elem.id.value, elem.id.source});
}
}
return result;
}
bool OmpStructureChecker::VerifyModifierVersion(
WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
// Verify that the specified modifiers are allowed in this version.
llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
auto result = VerifyVersions(info, clause.value, version);
for (auto &[m, svr] : result) {
std::string modName{llvm::omp::getDescriptor(m).getName().str()};
std::string clauseName{GetUpperName(clause.value, version)};
llvm::omp::Version since(svr.second.Min);
llvm::omp::Version until(svr.second.Max);
if (since == ~0u && until == 0u) {
// This shouldn't really happen, but have it just in case.
context_.Say(svr.first,
"'%s' modifier is not supported on %s clause"_err_en_US, modName,
clauseName);
} else if (since != ~0u && version < since) {
context_.Say(svr.first,
"'%s' modifier is not supported in %s on %s clause, %s"_warn_en_US,
modName, omp::ThisVersion(version), clauseName,
omp::TryVersion(since));
} else if (until != 0u && version > until) {
context_.Say(svr.first,
"'%s' modifier is no longer supported in %s on %s clause"_warn_en_US,
modName, omp::ThisVersion(version), clauseName);
}
}
return result.empty();
}
bool OmpStructureChecker::VerifyModifierRequired(
WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
auto result = VerifyRequired(info, clause.value, version);
for (llvm::omp::Modifier m : result.first) {
auto &mdesc{llvm::omp::getDescriptor(m)};
context_.Say(clause.source, "'%s' modifier is required"_err_en_US,
mdesc.getName().str());
}
for (llvm::omp::ModifierSet s : result.second) {
auto &sdesc{llvm::omp::getDescriptor(s)};
// If the group is required, at least one modifier from that group must
// be present.
if (llvm::omp::isModifierGroup(s)) {
context_.Say(clause.source,
"modifier from '%s' modifier group is required"_err_en_US,
sdesc.getName().str());
} else {
context_.Say(clause.source,
"modifier from the modifier set on %s clause is required"_err_en_US,
GetUpperName(clause.value, version));
}
}
return result.first.empty() && result.second.empty();
}
bool OmpStructureChecker::VerifyModifierUnique(
WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
auto result = VerifyUnique(info, clause.value, version);
for (auto [id, where] : result) {
auto &mdesc{llvm::omp::getDescriptor(id)};
context_
.Say(where.first, "'%s' modifier cannot occur multiple times"_err_en_US,
mdesc.getName().str())
.Attach(where.second, "previous occurrence of this modifier"_en_US);
}
return result.empty();
}
bool OmpStructureChecker::VerifyModifierExclusive(
WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
auto resultExcl = VerifyExclusive(info, clause.value, version);
for (auto [id, wrong] : resultExcl) {
auto [otherId, source, otherSource] = wrong;
context_
.Say(source,
"An exclusive '%s' modifier cannot be specified together with a modifier of a different type"_err_en_US,
llvm::omp::getDescriptor(id).getName().str())
.Attach(otherSource, "'%s' provided here"_en_US,
llvm::omp::getDescriptor(otherId).getName().str());
}
auto resultMut = VerifyMutuallyExclusive(info, clause.value, version);
for (auto [id, wrong] : resultMut) {
auto [otherId, source, otherSource] = wrong;
auto thisName{llvm::omp::getDescriptor(id).getName().str()};
context_
.Say(otherSource,
"The '%s' and '%s' modifiers are mutually exclusive"_err_en_US,
llvm::omp::getDescriptor(otherId).getName().str(), thisName)
.Attach(source, "'%s' modifier specified here"_en_US, thisName);
}
return resultExcl.empty() && resultMut.empty();
}
bool OmpStructureChecker::VerifyModifierUltimate(
WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
auto &cdesc{llvm::omp::getDescriptor(clause.value)};
bool last{
!cdesc.getProperties(version).test(llvm::omp::Property::PostModified)};
std::string expected{last ? "last" : "first"};
auto result = VerifyUltimate(info, clause.value, version, last);
for (auto [id, where] : result) {
context_.Say(where, "'%s' should be the %s modifier"_err_en_US,
llvm::omp::getDescriptor(id).getName().str(), expected);
}
return result.empty();
}
template <typename UnionTy>
AppliedModifierInfo GetAppliedModifiers(llvm::omp::Clause clauseId,
llvm::omp::Version version,
const std::optional<std::list<UnionTy>> &modifiers) {
AppliedModifierInfo info;
if (modifiers) {
auto cdesc{llvm::omp::getDescriptor(clauseId)};
for (auto &m : *modifiers) {
common::visit(
[&](auto &&t) {
auto &am{info.elements.emplace_back(AppliedModifier{})};
am.id = WithSource{t.Id, m.source};
for (auto s : cdesc.getModifierSets(version)) {
auto &sdesc{llvm::omp::getDescriptor(s)};
if (sdesc.getModifiers(version).test(am.id.value)) {
am.sets.set(s);
}
}
},
m.u);
}
}
return info;
}
static AppliedModifierInfo GetAppliedModifiersFromWrapper(
llvm::omp::Clause clauseId, llvm::omp::Version version,
const parser::OmpDependClause &depend) {
using TaskDep = parser::OmpDependClause::TaskDep;
if (auto *task{std::get_if<TaskDep>(&depend.u)}) {
using Modifiers = std::optional<std::list<TaskDep::Modifier>>;
return GetAppliedModifiers(
llvm::omp::Clause::OMPC_depend, version, std::get<Modifiers>(task->t));
} else if (auto *doa{std::get_if<parser::OmpDoacross>(&depend.u)}) {
using Modifiers = std::optional<std::list<parser::OmpDoacross::Modifier>>;
return GetAppliedModifiers(
llvm::omp::Clause::OMPC_depend, version, std::get<Modifiers>(doa->t));
}
llvm_unreachable("Unexpected alternative in depend");
}
static AppliedModifierInfo GetAppliedModifiersFromWrapper(
llvm::omp::Clause clauseId, llvm::omp::Version version,
const parser::OmpDoacrossClause &doacross) {
using Modifiers = std::optional<std::list<parser::OmpDoacross::Modifier>>;
return GetAppliedModifiers(llvm::omp::Clause::OMPC_doacross, version,
std::get<Modifiers>(doacross.v.t));
}
template <typename T>
static AppliedModifierInfo GetAppliedModifiersFromWrapper(
llvm::omp::Clause clauseId, llvm::omp::Version version, const T &wrapper) {
if constexpr (HasModifier<T>) {
using Modifiers = std::optional<std::list<typename T::Modifier>>;
return GetAppliedModifiers(
clauseId, version, std::get<Modifiers>(wrapper.t));
} else {
return AppliedModifierInfo{};
}
}
AppliedModifierInfo GetAppliedModifiers(
const parser::OmpClause &clause, llvm::omp::Version version) {
return common::visit(
[&](auto &&s) {
using TypeS = llvm::remove_cvref_t<decltype(s)>;
if constexpr (WrapperTrait<TypeS>) {
return GetAppliedModifiersFromWrapper(clause.Id(), version, s.v);
} else {
return AppliedModifierInfo{};
}
},
clause.u);
}
bool OmpStructureChecker::VerifyModifiers(
WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
// Run all checks without short-circuiting, return 'true' if all succeed.
bool valid[]{
VerifyModifierVersion(clause, info),
VerifyModifierRequired(clause, info),
VerifyModifierUnique(clause, info),
VerifyModifierUltimate(clause, info),
VerifyModifierExclusive(clause, info),
};
return llvm::all_of(valid, [](bool x) { return x; });
}
void OmpStructureChecker::VerifyModifiers(const parser::OmpClause &x) {
llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
llvm::omp::Clause id{x.Id()};
auto clauseId{WithSource(id, x.source)};
switch (id) {
case llvm::omp::Clause::OMPC_ompx_bare:
case llvm::omp::Clause::OMPC_cancellation_construct_type:
// Those are extensions/synthetic clauses and they don't have descriptors.
break;
case llvm::omp::Clause::OMPC_uses_allocators: {
// The traits of the deprecated syntax are stored as a traits-array
// modifier, but they are not the 5.2 modifier, so they must not be
// version-checked. A modifier that postdates the OpenMP version in effect
// is only warned about, so the specification is accepted as an extension
// and must still be checked, otherwise a malformed one would reach lowering
// unvalidated.
auto &uac{parser::UnwrapRef<parser::OmpUsesAllocatorsClause>(x)};
for (auto &&as : uac.v) {
bool legacy{std::get<bool>(as.t)};
if (!legacy) {
VerifyModifiers(
clauseId, GetAppliedModifiers(id, version, OmpGetModifiers(as)));
}
}
break;
}
default:
VerifyModifiers(clauseId, GetAppliedModifiers(x, version));
break;
}
}
} // namespace Fortran::semantics