mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-17 08:21:13 +00:00
[Flang][OpenMP][Sema] Support propagation of REQUIRES information across program units
Re-land commit 3787fd942f3927345320cc97a479f13e44355805 This patch adds support for storing OpenMP REQUIRES information in the semantics symbols for programs/subprograms and modules/submodules, and populates them during directive resolution. A pass is added to name resolution that makes sure this information is also propagated across top-level programs, functions and subprograms. Storing REQUIRES information inside of semantics symbols will also allow supporting the propagation of this information across Fortran modules. This will come as a separate patch. The `bool DirectiveAttributeVisitor::Pre(const parser::SpecificationPart &x)` method is removed since it resulted in specification parts being visited twice. This is patch 3/5 of a series splitting D149337 to simplify review. Differential Revision: https://reviews.llvm.org/D157983
This commit is contained in:
parent
26ca2f47c1
commit
edc2fb0733
@ -23,6 +23,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace Fortran::common;
|
||||
using namespace Fortran::frontend;
|
||||
using namespace Fortran::parser;
|
||||
using namespace Fortran;
|
||||
@ -553,7 +554,7 @@ public:
|
||||
READ_FEATURE(OmpAtomicClause)
|
||||
READ_FEATURE(OmpAtomicClauseList)
|
||||
READ_FEATURE(OmpAtomicDefaultMemOrderClause)
|
||||
READ_FEATURE(OmpAtomicDefaultMemOrderClause::Type)
|
||||
READ_FEATURE(OmpAtomicDefaultMemOrderType)
|
||||
READ_FEATURE(OpenMPFlushConstruct)
|
||||
READ_FEATURE(OpenMPLoopConstruct)
|
||||
READ_FEATURE(OpenMPExecutableAllocate)
|
||||
|
@ -87,6 +87,9 @@ ENUM_CLASS(CUDASubprogramAttrs, Host, Device, HostDevice, Global, Grid_Global)
|
||||
// CUDA data attributes; mutually exclusive
|
||||
ENUM_CLASS(CUDADataAttr, Constant, Device, Managed, Pinned, Shared, Texture)
|
||||
|
||||
// OpenMP atomic_default_mem_order clause allowed values
|
||||
ENUM_CLASS(OmpAtomicDefaultMemOrderType, SeqCst, AcqRel, Relaxed)
|
||||
|
||||
// Fortran names may have up to 63 characters (See Fortran 2018 C601).
|
||||
static constexpr int maxNameLen{63};
|
||||
|
||||
|
@ -589,7 +589,7 @@ public:
|
||||
NODE(parser, OmpAtomicClause)
|
||||
NODE(parser, OmpAtomicClauseList)
|
||||
NODE(parser, OmpAtomicDefaultMemOrderClause)
|
||||
NODE_ENUM(OmpAtomicDefaultMemOrderClause, Type)
|
||||
NODE_ENUM(common, OmpAtomicDefaultMemOrderType)
|
||||
NODE(parser, OpenMPFlushConstruct)
|
||||
NODE(parser, OpenMPLoopConstruct)
|
||||
NODE(parser, OpenMPExecutableAllocate)
|
||||
|
@ -3593,8 +3593,8 @@ struct OmpDependClause {
|
||||
// ATOMIC_DEFAULT_MEM_ORDER (SEQ_CST | ACQ_REL |
|
||||
// RELAXED)
|
||||
struct OmpAtomicDefaultMemOrderClause {
|
||||
ENUM_CLASS(Type, SeqCst, AcqRel, Relaxed)
|
||||
WRAPPER_CLASS_BOILERPLATE(OmpAtomicDefaultMemOrderClause, Type);
|
||||
WRAPPER_CLASS_BOILERPLATE(
|
||||
OmpAtomicDefaultMemOrderClause, common::OmpAtomicDefaultMemOrderType);
|
||||
};
|
||||
|
||||
// OpenMP Clauses
|
||||
|
@ -45,8 +45,38 @@ using SymbolVector = std::vector<SymbolRef>;
|
||||
using MutableSymbolRef = common::Reference<Symbol>;
|
||||
using MutableSymbolVector = std::vector<MutableSymbolRef>;
|
||||
|
||||
// Mixin for details with OpenMP declarative constructs.
|
||||
class WithOmpDeclarative {
|
||||
using OmpAtomicOrderType = common::OmpAtomicDefaultMemOrderType;
|
||||
|
||||
public:
|
||||
ENUM_CLASS(RequiresFlag, ReverseOffload, UnifiedAddress, UnifiedSharedMemory,
|
||||
DynamicAllocators);
|
||||
using RequiresFlags = common::EnumSet<RequiresFlag, RequiresFlag_enumSize>;
|
||||
|
||||
bool has_ompRequires() const { return ompRequires_.has_value(); }
|
||||
const RequiresFlags *ompRequires() const {
|
||||
return ompRequires_ ? &*ompRequires_ : nullptr;
|
||||
}
|
||||
void set_ompRequires(RequiresFlags flags) { ompRequires_ = flags; }
|
||||
|
||||
bool has_ompAtomicDefaultMemOrder() const {
|
||||
return ompAtomicDefaultMemOrder_.has_value();
|
||||
}
|
||||
const OmpAtomicOrderType *ompAtomicDefaultMemOrder() const {
|
||||
return ompAtomicDefaultMemOrder_ ? &*ompAtomicDefaultMemOrder_ : nullptr;
|
||||
}
|
||||
void set_ompAtomicDefaultMemOrder(OmpAtomicOrderType flags) {
|
||||
ompAtomicDefaultMemOrder_ = flags;
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<RequiresFlags> ompRequires_;
|
||||
std::optional<OmpAtomicOrderType> ompAtomicDefaultMemOrder_;
|
||||
};
|
||||
|
||||
// A module or submodule.
|
||||
class ModuleDetails {
|
||||
class ModuleDetails : public WithOmpDeclarative {
|
||||
public:
|
||||
ModuleDetails(bool isSubmodule = false) : isSubmodule_{isSubmodule} {}
|
||||
bool isSubmodule() const { return isSubmodule_; }
|
||||
@ -63,7 +93,7 @@ private:
|
||||
const Scope *scope_{nullptr};
|
||||
};
|
||||
|
||||
class MainProgramDetails {
|
||||
class MainProgramDetails : public WithOmpDeclarative {
|
||||
public:
|
||||
private:
|
||||
};
|
||||
@ -114,7 +144,7 @@ private:
|
||||
// A subroutine or function definition, or a subprogram interface defined
|
||||
// in an INTERFACE block as part of the definition of a dummy procedure
|
||||
// or a procedure pointer (with just POINTER).
|
||||
class SubprogramDetails : public WithBindName {
|
||||
class SubprogramDetails : public WithBindName, public WithOmpDeclarative {
|
||||
public:
|
||||
bool isFunction() const { return result_ != nullptr; }
|
||||
bool isInterface() const { return isInterface_; }
|
||||
|
@ -432,9 +432,9 @@ TYPE_PARSER(sourced(construct<OmpMemoryOrderClause>(
|
||||
// acq_rel
|
||||
// relaxed
|
||||
TYPE_PARSER(construct<OmpAtomicDefaultMemOrderClause>(
|
||||
"SEQ_CST" >> pure(OmpAtomicDefaultMemOrderClause::Type::SeqCst) ||
|
||||
"ACQ_REL" >> pure(OmpAtomicDefaultMemOrderClause::Type::AcqRel) ||
|
||||
"RELAXED" >> pure(OmpAtomicDefaultMemOrderClause::Type::Relaxed)))
|
||||
"SEQ_CST" >> pure(common::OmpAtomicDefaultMemOrderType::SeqCst) ||
|
||||
"ACQ_REL" >> pure(common::OmpAtomicDefaultMemOrderType::AcqRel) ||
|
||||
"RELAXED" >> pure(common::OmpAtomicDefaultMemOrderType::Relaxed)))
|
||||
|
||||
// 2.17.7 Atomic construct
|
||||
// atomic-clause -> memory-order-clause | HINT(hint-expression)
|
||||
|
@ -2307,17 +2307,7 @@ public:
|
||||
}
|
||||
|
||||
void Unparse(const OmpAtomicDefaultMemOrderClause &x) {
|
||||
switch (x.v) {
|
||||
case OmpAtomicDefaultMemOrderClause::Type::SeqCst:
|
||||
Word("SEQ_CST");
|
||||
break;
|
||||
case OmpAtomicDefaultMemOrderClause::Type::AcqRel:
|
||||
Word("ACQ_REL");
|
||||
break;
|
||||
case OmpAtomicDefaultMemOrderClause::Type::Relaxed:
|
||||
Word("RELAXED");
|
||||
break;
|
||||
}
|
||||
Word(ToUpperCaseLetters(common::EnumToString(x.v)));
|
||||
}
|
||||
|
||||
void Unparse(const OmpAtomicClauseList &x) { Walk(" ", x.v, " "); }
|
||||
|
@ -22,6 +22,13 @@
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
template <typename T>
|
||||
static Fortran::semantics::Scope *GetScope(
|
||||
Fortran::semantics::SemanticsContext &context, const T &x) {
|
||||
std::optional<Fortran::parser::CharBlock> source{GetSource(x)};
|
||||
return source ? &context.FindScope(*source) : nullptr;
|
||||
}
|
||||
|
||||
namespace Fortran::semantics {
|
||||
|
||||
template <typename T> class DirectiveAttributeVisitor {
|
||||
@ -324,11 +331,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pre(const parser::SpecificationPart &x) {
|
||||
Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Pre(const parser::StmtFunctionStmt &x) {
|
||||
const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)};
|
||||
if (const auto *expr{GetExpr(context_, parsedExpr)}) {
|
||||
@ -375,7 +377,38 @@ public:
|
||||
void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); }
|
||||
|
||||
bool Pre(const parser::OpenMPRequiresConstruct &x) {
|
||||
using Flags = WithOmpDeclarative::RequiresFlags;
|
||||
using Requires = WithOmpDeclarative::RequiresFlag;
|
||||
PushContext(x.source, llvm::omp::Directive::OMPD_requires);
|
||||
|
||||
// Gather information from the clauses.
|
||||
Flags flags;
|
||||
std::optional<common::OmpAtomicDefaultMemOrderType> memOrder;
|
||||
for (const auto &clause : std::get<parser::OmpClauseList>(x.t).v) {
|
||||
flags |= common::visit(
|
||||
common::visitors{
|
||||
[&memOrder](
|
||||
const parser::OmpClause::AtomicDefaultMemOrder &atomic) {
|
||||
memOrder = atomic.v.v;
|
||||
return Flags{};
|
||||
},
|
||||
[](const parser::OmpClause::ReverseOffload &) {
|
||||
return Flags{Requires::ReverseOffload};
|
||||
},
|
||||
[](const parser::OmpClause::UnifiedAddress &) {
|
||||
return Flags{Requires::UnifiedAddress};
|
||||
},
|
||||
[](const parser::OmpClause::UnifiedSharedMemory &) {
|
||||
return Flags{Requires::UnifiedSharedMemory};
|
||||
},
|
||||
[](const parser::OmpClause::DynamicAllocators &) {
|
||||
return Flags{Requires::DynamicAllocators};
|
||||
},
|
||||
[](const auto &) { return Flags{}; }},
|
||||
clause.u);
|
||||
}
|
||||
// Merge clauses into parents' symbols details.
|
||||
AddOmpRequiresToScope(currScope(), flags, memOrder);
|
||||
return true;
|
||||
}
|
||||
void Post(const parser::OpenMPRequiresConstruct &) { PopContext(); }
|
||||
@ -672,6 +705,9 @@ private:
|
||||
|
||||
bool HasSymbolInEnclosingScope(const Symbol &, Scope &);
|
||||
std::int64_t ordCollapseLevel{0};
|
||||
|
||||
void AddOmpRequiresToScope(Scope &, WithOmpDeclarative::RequiresFlags,
|
||||
std::optional<common::OmpAtomicDefaultMemOrderType>);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -2175,6 +2211,84 @@ void ResolveOmpParts(
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveOmpTopLevelParts(
|
||||
SemanticsContext &context, const parser::Program &program) {
|
||||
if (!context.IsEnabled(common::LanguageFeature::OpenMP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Gather REQUIRES clauses from all non-module top-level program unit symbols,
|
||||
// combine them together ensuring compatibility and apply them to all these
|
||||
// program units. Modules are skipped because their REQUIRES clauses should be
|
||||
// propagated via USE statements instead.
|
||||
WithOmpDeclarative::RequiresFlags combinedFlags;
|
||||
std::optional<common::OmpAtomicDefaultMemOrderType> combinedMemOrder;
|
||||
|
||||
// Function to go through non-module top level program units and extract
|
||||
// REQUIRES information to be processed by a function-like argument.
|
||||
auto processProgramUnits{[&](auto processFn) {
|
||||
for (const parser::ProgramUnit &unit : program.v) {
|
||||
if (!std::holds_alternative<common::Indirection<parser::Module>>(
|
||||
unit.u) &&
|
||||
!std::holds_alternative<common::Indirection<parser::Submodule>>(
|
||||
unit.u)) {
|
||||
Symbol *symbol{common::visit(
|
||||
[&context](auto &x) {
|
||||
Scope *scope = GetScope(context, x.value());
|
||||
return scope ? scope->symbol() : nullptr;
|
||||
},
|
||||
unit.u)};
|
||||
// FIXME There is no symbol defined for MainProgram units in certain
|
||||
// circumstances, so REQUIRES information has no place to be stored in
|
||||
// these cases.
|
||||
if (!symbol) {
|
||||
continue;
|
||||
}
|
||||
common::visit(
|
||||
[&](auto &details) {
|
||||
if constexpr (std::is_convertible_v<decltype(&details),
|
||||
WithOmpDeclarative *>) {
|
||||
processFn(*symbol, details);
|
||||
}
|
||||
},
|
||||
symbol->details());
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// Combine global REQUIRES information from all program units except modules
|
||||
// and submodules.
|
||||
processProgramUnits([&](Symbol &symbol, WithOmpDeclarative &details) {
|
||||
if (const WithOmpDeclarative::RequiresFlags *
|
||||
flags{details.ompRequires()}) {
|
||||
combinedFlags |= *flags;
|
||||
}
|
||||
if (const common::OmpAtomicDefaultMemOrderType *
|
||||
memOrder{details.ompAtomicDefaultMemOrder()}) {
|
||||
if (combinedMemOrder && *combinedMemOrder != *memOrder) {
|
||||
context.Say(symbol.scope()->sourceRange(),
|
||||
"Conflicting '%s' REQUIRES clauses found in compilation "
|
||||
"unit"_err_en_US,
|
||||
parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
|
||||
llvm::omp::Clause::OMPC_atomic_default_mem_order)
|
||||
.str()));
|
||||
}
|
||||
combinedMemOrder = *memOrder;
|
||||
}
|
||||
});
|
||||
|
||||
// Update all program units except modules and submodules with the combined
|
||||
// global REQUIRES information.
|
||||
processProgramUnits([&](Symbol &, WithOmpDeclarative &details) {
|
||||
if (combinedFlags.any()) {
|
||||
details.set_ompRequires(combinedFlags);
|
||||
}
|
||||
if (combinedMemOrder) {
|
||||
details.set_ompAtomicDefaultMemOrder(*combinedMemOrder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OmpAttributeVisitor::CheckDataCopyingClause(
|
||||
const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
|
||||
const auto *checkSymbol{&symbol};
|
||||
@ -2322,4 +2436,44 @@ void OmpAttributeVisitor::CheckNameInAllocateStmt(
|
||||
parser::ToUpperCaseLetters(
|
||||
llvm::omp::getOpenMPDirectiveName(GetContext().directive).str()));
|
||||
}
|
||||
|
||||
void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope,
|
||||
WithOmpDeclarative::RequiresFlags flags,
|
||||
std::optional<common::OmpAtomicDefaultMemOrderType> memOrder) {
|
||||
Scope *scopeIter = &scope;
|
||||
do {
|
||||
if (Symbol * symbol{scopeIter->symbol()}) {
|
||||
common::visit(
|
||||
[&](auto &details) {
|
||||
// Store clauses information into the symbol for the parent and
|
||||
// enclosing modules, programs, functions and subroutines.
|
||||
if constexpr (std::is_convertible_v<decltype(&details),
|
||||
WithOmpDeclarative *>) {
|
||||
if (flags.any()) {
|
||||
if (const WithOmpDeclarative::RequiresFlags *
|
||||
otherFlags{details.ompRequires()}) {
|
||||
flags |= *otherFlags;
|
||||
}
|
||||
details.set_ompRequires(flags);
|
||||
}
|
||||
if (memOrder) {
|
||||
if (details.has_ompAtomicDefaultMemOrder() &&
|
||||
*details.ompAtomicDefaultMemOrder() != *memOrder) {
|
||||
context_.Say(scopeIter->sourceRange(),
|
||||
"Conflicting '%s' REQUIRES clauses found in compilation "
|
||||
"unit"_err_en_US,
|
||||
parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
|
||||
llvm::omp::Clause::OMPC_atomic_default_mem_order)
|
||||
.str()));
|
||||
}
|
||||
details.set_ompAtomicDefaultMemOrder(*memOrder);
|
||||
}
|
||||
}
|
||||
},
|
||||
symbol->details());
|
||||
}
|
||||
scopeIter = &scopeIter->parent();
|
||||
} while (!scopeIter->IsGlobal());
|
||||
}
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Fortran::parser {
|
||||
struct Name;
|
||||
struct Program;
|
||||
struct ProgramUnit;
|
||||
} // namespace Fortran::parser
|
||||
|
||||
@ -21,6 +22,7 @@ class SemanticsContext;
|
||||
// Name resolution for OpenACC and OpenMP directives
|
||||
void ResolveAccParts(SemanticsContext &, const parser::ProgramUnit &);
|
||||
void ResolveOmpParts(SemanticsContext &, const parser::ProgramUnit &);
|
||||
void ResolveOmpTopLevelParts(SemanticsContext &, const parser::Program &);
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
#endif
|
||||
|
@ -8699,11 +8699,14 @@ void ResolveNamesVisitor::ResolveExecutionParts(const ProgramTree &node) {
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveNamesVisitor::Post(const parser::Program &) {
|
||||
void ResolveNamesVisitor::Post(const parser::Program &x) {
|
||||
// ensure that all temps were deallocated
|
||||
CHECK(!attrs_);
|
||||
CHECK(!cudaDataAttr_);
|
||||
CHECK(!GetDeclTypeSpec());
|
||||
// Top-level resolution to propagate information across program units after
|
||||
// each of them has been resolved separately.
|
||||
ResolveOmpTopLevelParts(context(), x);
|
||||
}
|
||||
|
||||
// A singleton instance of the scope -> IMPLICIT rules mapping is
|
||||
|
14
flang/test/Semantics/OpenMP/requires09.f90
Normal file
14
flang/test/Semantics/OpenMP/requires09.f90
Normal file
@ -0,0 +1,14 @@
|
||||
! RUN: %python %S/../test_errors.py %s %flang -fopenmp
|
||||
! OpenMP Version 5.0
|
||||
! 2.4 Requires directive
|
||||
! All atomic_default_mem_order clauses in 'requires' directives found within a
|
||||
! compilation unit must specify the same ordering.
|
||||
|
||||
subroutine f
|
||||
!$omp requires atomic_default_mem_order(seq_cst)
|
||||
end subroutine f
|
||||
|
||||
!ERROR: Conflicting 'ATOMIC_DEFAULT_MEM_ORDER' REQUIRES clauses found in compilation unit
|
||||
subroutine g
|
||||
!$omp requires atomic_default_mem_order(relaxed)
|
||||
end subroutine g
|
Loading…
x
Reference in New Issue
Block a user