//===-- Clauses.cpp -- OpenMP clause handling -----------------------------===//
//
// 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 "Clauses.h"
#include "flang/Common/idioms.h"
#include "flang/Evaluate/expression.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
#include "flang/Semantics/symbol.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include <list>
#include <optional>
#include <tuple>
#include <utility>
#include <variant>
namespace detail {
template <typename C>
llvm::omp::Clause getClauseIdForClass(C &&) {
using namespace Fortran;
using A = llvm::remove_cvref_t<C>; // A is referenced in OMP.inc
// The code included below contains a sequence of checks like the following
// for each OpenMP clause
// if constexpr (std::is_same_v<A, parser::OmpClause::AcqRel>)
// return llvm::omp::Clause::OMPC_acq_rel;
// [...]
#define GEN_FLANG_CLAUSE_PARSER_KIND_MAP
#include "llvm/Frontend/OpenMP/OMP.inc"
}
} // namespace detail
static llvm::omp::Clause getClauseId(const Fortran::parser::OmpClause &clause) {
return Fortran::common::visit(
[](auto &&s) { return detail::getClauseIdForClass(s); }, clause.u);
}
namespace Fortran::lower::omp {
using SymbolWithDesignator = std::tuple<semantics::Symbol *, MaybeExpr>;
struct SymbolAndDesignatorExtractor {
template <typename T>
static T &&AsRvalueRef(T &&t) {
return std::move(t);
}
template <typename T>
static T AsRvalueRef(const T &t) {
return t;
}
static semantics::Symbol *symbol_addr(const evaluate::SymbolRef &ref) {
// Symbols cannot be created after semantic checks, so all symbol
// pointers that are non-null must point to one of those pre-existing
// objects. Throughout the code, symbols are often pointed to by
// non-const pointers, so there is no harm in casting the constness
// away.
return const_cast<semantics::Symbol *>(&ref.get());
}
template <typename T>
static SymbolWithDesignator visit(T &&) {
// Use this to see missing overloads:
// llvm::errs() << "NULL: " << __PRETTY_FUNCTION__ << '\n';
return SymbolWithDesignator{};
}
template <typename T>
static SymbolWithDesignator visit(const evaluate::Designator<T> &e) {
return std::make_tuple(symbol_addr(*e.GetLastSymbol()),
evaluate::AsGenericExpr(AsRvalueRef(e)));
}
static SymbolWithDesignator visit(const evaluate::ProcedureDesignator &e) {
return std::make_tuple(symbol_addr(*e.GetSymbol()), std::nullopt);
}
template <typename T>
static SymbolWithDesignator visit(const evaluate::Expr<T> &e) {
return Fortran::common::visit([](auto &&s) { return visit(s); }, e.u);
}
static void verify(const SymbolWithDesignator &sd) {
const semantics::Symbol *symbol = std::get<0>(sd);
assert(symbol && "Expecting symbol");
auto &maybeDsg = std::get<1>(sd);
if (!maybeDsg)
return; // Symbol with no designator -> OK
std::optional<evaluate::DataRef> maybeRef =
evaluate::ExtractDataRef(*maybeDsg);
if (maybeRef) {
if (&maybeRef->GetLastSymbol() == symbol)
return; // Symbol with a designator for it -> OK
llvm_unreachable("Expecting designator for given symbol");
} else {
// This could still be a Substring or ComplexPart, but at least Substring
// is not allowed in OpenMP.
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
maybeDsg->dump();
#endif
llvm_unreachable("Expecting DataRef designator");
}
}
};
SymbolWithDesignator getSymbolAndDesignator(const MaybeExpr &expr) {
if (!expr)
return SymbolWithDesignator{};
return Fortran::common::visit(
[](auto &&s) { return SymbolAndDesignatorExtractor::visit(s); }, expr->u);
}
Object makeObject(const parser::Name &name,
semantics::SemanticsContext &semaCtx) {
assert(name.symbol && "Expecting Symbol");
return Object{name.symbol, std::nullopt};
}
Object makeObject(const parser::Designator &dsg,
semantics::SemanticsContext &semaCtx) {
evaluate::ExpressionAnalyzer ea{semaCtx};
SymbolWithDesignator sd = getSymbolAndDesignator(ea.Analyze(dsg));
SymbolAndDesignatorExtractor::verify(sd);
return Object{std::get<0>(sd), std::move(std::get<1>(sd))};
}
Object makeObject(const parser::StructureComponent &comp,
semantics::SemanticsContext &semaCtx) {
evaluate::ExpressionAnalyzer ea{semaCtx};
SymbolWithDesignator sd = getSymbolAndDesignator(ea.Analyze(comp));
SymbolAndDesignatorExtractor::verify(sd);
return Object{std::get<0>(sd), std::move(std::get<1>(sd))};
}
Object makeObject(const parser::OmpObject &object,
semantics::SemanticsContext &semaCtx) {
// If object is a common block, expression analyzer won't be able to
// do anything.
if (const auto *name = std::get_if<parser::Name>(&object.u)) {
assert(name->symbol && "Expecting Symbol");
return Object{name->symbol, std::nullopt};
}
// OmpObject is std::variant<Designator, /*common block*/ Name>;
return makeObject(std::get<parser::Designator>(object.u), semaCtx);
}
std::optional<Object> getBaseObject(const Object &object,
semantics::SemanticsContext &semaCtx) {
// If it's just the symbol, then there is no base.
if (!object.ref())
return std::nullopt;
auto maybeRef = evaluate::ExtractDataRef(*object.ref());
if (!maybeRef)
return std::nullopt;
evaluate::DataRef ref = *maybeRef;
if (std::get_if<evaluate::SymbolRef>(&ref.u)) {
return std::nullopt;
} else if (auto *comp = std::get_if<evaluate::Component>(&ref.u)) {
const evaluate::DataRef &base = comp->base();
return Object{
SymbolAndDesignatorExtractor::symbol_addr(base.GetLastSymbol()),
evaluate::AsGenericExpr(
SymbolAndDesignatorExtractor::AsRvalueRef(base))};
} else if (auto *arr = std::get_if<evaluate::ArrayRef>(&ref.u)) {
const evaluate::NamedEntity &base = arr->base();
evaluate::ExpressionAnalyzer ea{semaCtx};
if (auto *comp = base.UnwrapComponent()) {
return Object{SymbolAndDesignatorExtractor::symbol_addr(comp->symbol()),
ea.Designate(evaluate::DataRef{
SymbolAndDesignatorExtractor::AsRvalueRef(*comp)})};
} else if (base.UnwrapSymbolRef()) {
return std::nullopt;
}
} else {
assert(std::holds_alternative<evaluate::CoarrayRef>(ref.u) &&
"Unexpected variant alternative");
llvm_unreachable("Coarray reference not supported at the moment");
}
return std::nullopt;
}
// Helper macros
#define MAKE_EMPTY_CLASS(cls, from_cls) \
cls make(const parser::OmpClause::from_cls &, \
semantics::SemanticsContext &) { \
static_assert(cls::EmptyTrait::value); \
return cls{}; \
} \
[[maybe_unused]] extern int xyzzy_semicolon_absorber
#define MAKE_INCOMPLETE_CLASS(cls, from_cls) \
cls make(const parser::OmpClause::from_cls &, \
semantics::SemanticsContext &) { \
static_assert(cls::IncompleteTrait::value); \
return cls{}; \
} \
[[maybe_unused]] extern int xyzzy_semicolon_absorber
#define MS(x, y) CLAUSET_SCOPED_ENUM_MEMBER_CONVERT(x, y)
#define MU(x, y) CLAUSET_UNSCOPED_ENUM_MEMBER_CONVERT(x, y)
namespace clause {
MAKE_EMPTY_CLASS(AcqRel, AcqRel);
MAKE_EMPTY_CLASS(Acquire, Acquire);
MAKE_EMPTY_CLASS(Capture, Capture);
MAKE_EMPTY_CLASS(Compare, Compare);
MAKE_EMPTY_CLASS(DynamicAllocators, DynamicAllocators);
MAKE_EMPTY_CLASS(Full, Full);
MAKE_EMPTY_CLASS(Inbranch, Inbranch);
MAKE_EMPTY_CLASS(Mergeable, Mergeable);
MAKE_EMPTY_CLASS(Nogroup, Nogroup);
MAKE_EMPTY_CLASS(NoOpenmp, NoOpenmp);
MAKE_EMPTY_CLASS(NoOpenmpRoutines, NoOpenmpRoutines);
MAKE_EMPTY_CLASS(NoParallelism, NoParallelism);
MAKE_EMPTY_CLASS(Notinbranch, Notinbranch);
MAKE_EMPTY_CLASS(Nowait, Nowait);
MAKE_EMPTY_CLASS(OmpxAttribute, OmpxAttribute);
MAKE_EMPTY_CLASS(OmpxBare, OmpxBare);
MAKE_EMPTY_CLASS(Read, Read);
MAKE_EMPTY_CLASS(Relaxed, Relaxed);
MAKE_EMPTY_CLASS(Release, Release);
MAKE_EMPTY_CLASS(ReverseOffload, ReverseOffload);
MAKE_EMPTY_CLASS(SeqCst, SeqCst);
MAKE_EMPTY_CLASS(Simd, Simd);
MAKE_EMPTY_CLASS(Threads, Threads);
MAKE_EMPTY_CLASS(UnifiedAddress, UnifiedAddress);
MAKE_EMPTY_CLASS(UnifiedSharedMemory, UnifiedSharedMemory);
MAKE_EMPTY_CLASS(Unknown, Unknown);
MAKE_EMPTY_CLASS(Untied, Untied);
MAKE_EMPTY_CLASS(Weak, Weak);
MAKE_EMPTY_CLASS(Write, Write);
// Artificial clauses
MAKE_EMPTY_CLASS(CancellationConstructType, CancellationConstructType);
MAKE_EMPTY_CLASS(Depobj, Depobj);
MAKE_EMPTY_CLASS(Flush, Flush);
MAKE_EMPTY_CLASS(MemoryOrder, MemoryOrder);
MAKE_EMPTY_CLASS(Threadprivate, Threadprivate);
MAKE_INCOMPLETE_CLASS(AdjustArgs, AdjustArgs);
MAKE_INCOMPLETE_CLASS(AppendArgs, AppendArgs);
MAKE_INCOMPLETE_CLASS(Match, Match);
// MAKE_INCOMPLETE_CLASS(Otherwise, ); // missing-in-parser
MAKE_INCOMPLETE_CLASS(When, When);
DefinedOperator makeDefinedOperator(const parser::DefinedOperator &inp,
semantics::SemanticsContext &semaCtx) {
CLAUSET_ENUM_CONVERT( //
convert, parser::DefinedOperator::IntrinsicOperator,
DefinedOperator::IntrinsicOperator,
// clang-format off
MS(Add, Add)
MS(AND, AND)
MS(Concat, Concat)
MS(Divide, Divide)
MS(EQ, EQ)
MS(EQV, EQV)
MS(GE, GE)
MS(GT, GT)
MS(NOT, NOT)
MS(LE, LE)
MS(LT, LT)
MS(Multiply, Multiply)
MS(NE, NE)
MS(NEQV, NEQV)
MS(OR, OR)
MS(Power, Power)
MS(Subtract, Subtract)
// clang-format on
);
return Fortran::common::visit(
common::visitors{
[&](const parser::DefinedOpName &s) {
return DefinedOperator{
DefinedOperator::DefinedOpName{makeObject(s.v, semaCtx)}};
},
[&](const parser::DefinedOperator::IntrinsicOperator &s) {
return DefinedOperator{convert(s)};
},
},
inp.u);
}
ProcedureDesignator
makeProcedureDesignator(const parser::ProcedureDesignator &inp,
semantics::SemanticsContext &semaCtx) {
return ProcedureDesignator{Fortran::common::visit(
common::visitors{
[&](const parser::Name &t) { return makeObject(t, semaCtx); },
[&](const parser::ProcComponentRef &t) {
return makeObject(t.v.thing, semaCtx);
},
},
inp.u)};
}
ReductionOperator makeReductionOperator(const parser::OmpReductionOperator &inp,
semantics::SemanticsContext &semaCtx) {
return Fortran::common::visit(
common::visitors{
[&](const parser::DefinedOperator &s) {
return ReductionOperator{makeDefinedOperator(s, semaCtx)};
},
[&](const parser::ProcedureDesignator &s) {
return ReductionOperator{makeProcedureDesignator(s, semaCtx)};
},
},
inp.u);
}
// --------------------------------------------------------------------
// Actual clauses. Each T (where tomp::T exists in ClauseT) has its "make".
Absent make(const parser::OmpClause::Absent &inp,
semantics::SemanticsContext &semaCtx) {
llvm_unreachable("Unimplemented: absent");
}
// AcqRel: empty
// Acquire: empty
// AdjustArgs: incomplete
Affinity make(const parser::OmpClause::Affinity &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: affinity");
}
Align make(const parser::OmpClause::Align &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: align");
}
Aligned make(const parser::OmpClause::Aligned &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpAlignedClause
auto &t0 = std::get<parser::OmpObjectList>(inp.v.t);
auto &t1 = std::get<std::optional<parser::ScalarIntConstantExpr>>(inp.v.t);
return Aligned{{
/*Alignment=*/maybeApply(makeExprFn(semaCtx), t1),
/*List=*/makeObjects(t0, semaCtx),
}};
}
Allocate make(const parser::OmpClause::Allocate &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpAllocateClause
using wrapped = parser::OmpAllocateClause;
auto &t0 = std::get<std::optional<wrapped::AllocateModifier>>(inp.v.t);
auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
if (!t0) {
return Allocate{{/*AllocatorSimpleModifier=*/std::nullopt,
/*AllocatorComplexModifier=*/std::nullopt,
/*AlignModifier=*/std::nullopt,
/*List=*/makeObjects(t1, semaCtx)}};
}
using Tuple = decltype(Allocate::t);
return Allocate{Fortran::common::visit(
common::visitors{
// simple-modifier
[&](const wrapped::AllocateModifier::Allocator &v) -> Tuple {
return {/*AllocatorSimpleModifier=*/makeExpr(v.v, semaCtx),
/*AllocatorComplexModifier=*/std::nullopt,
/*AlignModifier=*/std::nullopt,
/*List=*/makeObjects(t1, semaCtx)};
},
// complex-modifier + align-modifier
[&](const wrapped::AllocateModifier::ComplexModifier &v) -> Tuple {
auto &s0 = std::get<wrapped::AllocateModifier::Allocator>(v.t);
auto &s1 = std::get<wrapped::AllocateModifier::Align>(v.t);
return {
/*AllocatorSimpleModifier=*/std::nullopt,
/*AllocatorComplexModifier=*/Allocator{makeExpr(s0.v, semaCtx)},
/*AlignModifier=*/Align{makeExpr(s1.v, semaCtx)},
/*List=*/makeObjects(t1, semaCtx)};
},
// align-modifier
[&](const wrapped::AllocateModifier::Align &v) -> Tuple {
return {/*AllocatorSimpleModifier=*/std::nullopt,
/*AllocatorComplexModifier=*/std::nullopt,
/*AlignModifier=*/Align{makeExpr(v.v, semaCtx)},
/*List=*/makeObjects(t1, semaCtx)};
},
},
t0->u)};
}
Allocator make(const parser::OmpClause::Allocator &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return Allocator{/*Allocator=*/makeExpr(inp.v, semaCtx)};
}
// AppendArgs: incomplete
At make(const parser::OmpClause::At &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: at");
}
// Never called, but needed for using "make" as a Clause visitor.
// See comment about "requires" clauses in Clauses.h.
AtomicDefaultMemOrder make(const parser::OmpClause::AtomicDefaultMemOrder &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpAtomicDefaultMemOrderClause
CLAUSET_ENUM_CONVERT( //
convert, common::OmpAtomicDefaultMemOrderType,
AtomicDefaultMemOrder::MemoryOrder,
// clang-format off
MS(AcqRel, AcqRel)
MS(Relaxed, Relaxed)
MS(SeqCst, SeqCst)
// clang-format on
);
return AtomicDefaultMemOrder{/*MemoryOrder=*/convert(inp.v.v)};
}
Bind make(const parser::OmpClause::Bind &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: bind");
}
// CancellationConstructType: empty
// Capture: empty
Collapse make(const parser::OmpClause::Collapse &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntConstantExpr
return Collapse{/*N=*/makeExpr(inp.v, semaCtx)};
}
// Compare: empty
Contains make(const parser::OmpClause::Contains &inp,
semantics::SemanticsContext &semaCtx) {
llvm_unreachable("Unimplemented: contains");
}
Copyin make(const parser::OmpClause::Copyin &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return Copyin{/*List=*/makeObjects(inp.v, semaCtx)};
}
Copyprivate make(const parser::OmpClause::Copyprivate &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return Copyprivate{/*List=*/makeObjects(inp.v, semaCtx)};
}
Default make(const parser::OmpClause::Default &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpDefaultClause
using wrapped = parser::OmpDefaultClause;
CLAUSET_ENUM_CONVERT( //
convert, wrapped::Type, Default::DataSharingAttribute,
// clang-format off
MS(Firstprivate, Firstprivate)
MS(None, None)
MS(Private, Private)
MS(Shared, Shared)
// clang-format on
);
return Default{/*DataSharingAttribute=*/convert(inp.v.v)};
}
Defaultmap make(const parser::OmpClause::Defaultmap &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpDefaultmapClause
using wrapped = parser::OmpDefaultmapClause;
CLAUSET_ENUM_CONVERT( //
convert1, wrapped::ImplicitBehavior, Defaultmap::ImplicitBehavior,
// clang-format off
MS(Alloc, Alloc)
MS(To, To)
MS(From, From)
MS(Tofrom, Tofrom)
MS(Firstprivate, Firstprivate)
MS(None, None)
MS(Default, Default)
// MS(, Present) missing-in-parser
// clang-format on
);
CLAUSET_ENUM_CONVERT( //
convert2, wrapped::VariableCategory, Defaultmap::VariableCategory,
// clang-format off
MS(Scalar, Scalar)
MS(Aggregate, Aggregate)
MS(Pointer, Pointer)
MS(Allocatable, Allocatable)
// clang-format on
);
auto &t0 = std::get<wrapped::ImplicitBehavior>(inp.v.t);
auto &t1 = std::get<std::optional<wrapped::VariableCategory>>(inp.v.t);
return Defaultmap{{/*ImplicitBehavior=*/convert1(t0),
/*VariableCategory=*/maybeApply(convert2, t1)}};
}
Depend make(const parser::OmpClause::Depend &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpDependClause
using wrapped = parser::OmpDependClause;
using Variant = decltype(Depend::u);
// Iteration is the equivalent of parser::OmpDependSinkVec
using Iteration = Doacross::Vector::value_type; // LoopIterationT
CLAUSET_ENUM_CONVERT( //
convert1, parser::OmpDependenceType::Type, Depend::TaskDependenceType,
// clang-format off
MS(In, In)
MS(Out, Out)
MS(Inout, Inout)
// MS(, Mutexinoutset) // missing-in-parser
// MS(, Inputset) // missing-in-parser
// MS(, Depobj) // missing-in-parser
// clang-format on
);
return Depend{Fortran::common::visit( //
common::visitors{
// Doacross
[&](const wrapped::Source &s) -> Variant {
return Doacross{
{/*DependenceType=*/Doacross::DependenceType::Source,
/*Vector=*/{}}};
},
// Doacross
[&](const wrapped::Sink &s) -> Variant {
using DependLength = parser::OmpDependSinkVecLength;
auto convert2 = [&](const parser::OmpDependSinkVec &v) {
auto &t0 = std::get<parser::Name>(v.t);
auto &t1 = std::get<std::optional<DependLength>>(v.t);
auto convert3 = [&](const DependLength &u) {
auto &s0 = std::get<parser::DefinedOperator>(u.t);
auto &s1 = std::get<parser::ScalarIntConstantExpr>(u.t);
return Iteration::Distance{
{makeDefinedOperator(s0, semaCtx), makeExpr(s1, semaCtx)}};
};
return Iteration{
{makeObject(t0, semaCtx), maybeApply(convert3, t1)}};
};
return Doacross{{/*DependenceType=*/Doacross::DependenceType::Sink,
/*Vector=*/makeList(s.v, convert2)}};
},
// Depend::WithLocators
[&](const wrapped::InOut &s) -> Variant {
auto &t0 = std::get<parser::OmpDependenceType>(s.t);
auto &t1 = std::get<std::list<parser::Designator>>(s.t);
auto convert4 = [&](const parser::Designator &t) {
return makeObject(t, semaCtx);
};
return Depend::WithLocators{
{/*TaskDependenceType=*/convert1(t0.v),
/*Iterator=*/std::nullopt,
/*LocatorList=*/makeList(t1, convert4)}};
},
},
inp.v.u)};
}
// Depobj: empty
Destroy make(const parser::OmpClause::Destroy &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: destroy");
}
Detach make(const parser::OmpClause::Detach &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: detach");
}
Device make(const parser::OmpClause::Device &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpDeviceClause
using wrapped = parser::OmpDeviceClause;
CLAUSET_ENUM_CONVERT( //
convert, parser::OmpDeviceClause::DeviceModifier, Device::DeviceModifier,
// clang-format off
MS(Ancestor, Ancestor)
MS(Device_Num, DeviceNum)
// clang-format on
);
auto &t0 = std::get<std::optional<wrapped::DeviceModifier>>(inp.v.t);
auto &t1 = std::get<parser::ScalarIntExpr>(inp.v.t);
return Device{{/*DeviceModifier=*/maybeApply(convert, t0),
/*DeviceDescription=*/makeExpr(t1, semaCtx)}};
}
DeviceType make(const parser::OmpClause::DeviceType &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpDeviceTypeClause
using wrapped = parser::OmpDeviceTypeClause;
CLAUSET_ENUM_CONVERT( //
convert, wrapped::Type, DeviceType::DeviceTypeDescription,
// clang-format off
MS(Any, Any)
MS(Host, Host)
MS(Nohost, Nohost)
// clang-format om
);
return DeviceType{/*DeviceTypeDescription=*/convert(inp.v.v)};
}
DistSchedule make(const parser::OmpClause::DistSchedule &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> std::optional<parser::ScalarIntExpr>
return DistSchedule{{/*Kind=*/DistSchedule::Kind::Static,
/*ChunkSize=*/maybeApply(makeExprFn(semaCtx), inp.v)}};
}
Doacross make(const parser::OmpClause::Doacross &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: doacross");
}
// DynamicAllocators: empty
Enter make(const parser::OmpClause::Enter &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return Enter{makeObjects(/*List=*/inp.v, semaCtx)};
}
Exclusive make(const parser::OmpClause::Exclusive &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: exclusive");
}
Fail make(const parser::OmpClause::Fail &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: fail");
}
Filter make(const parser::OmpClause::Filter &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return Filter{/*ThreadNum=*/makeExpr(inp.v, semaCtx)};
}
Final make(const parser::OmpClause::Final &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarLogicalExpr
return Final{/*Finalize=*/makeExpr(inp.v, semaCtx)};
}
Firstprivate make(const parser::OmpClause::Firstprivate &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return Firstprivate{/*List=*/makeObjects(inp.v, semaCtx)};
}
// Flush: empty
From make(const parser::OmpClause::From &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return From{{/*Expectation=*/std::nullopt, /*Mapper=*/std::nullopt,
/*Iterator=*/std::nullopt,
/*LocatorList=*/makeObjects(inp.v, semaCtx)}};
}
// Full: empty
Grainsize make(const parser::OmpClause::Grainsize &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return Grainsize{{/*Prescriptiveness=*/std::nullopt,
/*GrainSize=*/makeExpr(inp.v, semaCtx)}};
}
HasDeviceAddr make(const parser::OmpClause::HasDeviceAddr &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return HasDeviceAddr{/*List=*/makeObjects(inp.v, semaCtx)};
}
Hint make(const parser::OmpClause::Hint &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ConstantExpr
return Hint{/*HintExpr=*/makeExpr(inp.v, semaCtx)};
}
Holds make(const parser::OmpClause::Holds &inp,
semantics::SemanticsContext &semaCtx) {
llvm_unreachable("Unimplemented: holds");
}
If make(const parser::OmpClause::If &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpIfClause
using wrapped = parser::OmpIfClause;
CLAUSET_ENUM_CONVERT( //
convert, wrapped::DirectiveNameModifier, llvm::omp::Directive,
// clang-format off
MS(Parallel, OMPD_parallel)
MS(Simd, OMPD_simd)
MS(Target, OMPD_target)
MS(TargetData, OMPD_target_data)
MS(TargetEnterData, OMPD_target_enter_data)
MS(TargetExitData, OMPD_target_exit_data)
MS(TargetUpdate, OMPD_target_update)
MS(Task, OMPD_task)
MS(Taskloop, OMPD_taskloop)
MS(Teams, OMPD_teams)
// clang-format on
);
auto &t0 = std::get<std::optional<wrapped::DirectiveNameModifier>>(inp.v.t);
auto &t1 = std::get<parser::ScalarLogicalExpr>(inp.v.t);
return If{{/*DirectiveNameModifier=*/maybeApply(convert, t0),
/*IfExpression=*/makeExpr(t1, semaCtx)}};
}
// Inbranch: empty
Inclusive make(const parser::OmpClause::Inclusive &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: inclusive");
}
Indirect make(const parser::OmpClause::Indirect &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: indirect");
}
Init make(const parser::OmpClause::Init &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: init");
}
// Initializer: missing-in-parser
InReduction make(const parser::OmpClause::InReduction &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpInReductionClause
auto &t0 = std::get<parser::OmpReductionOperator>(inp.v.t);
auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
return InReduction{
{/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)},
/*List=*/makeObjects(t1, semaCtx)}};
}
IsDevicePtr make(const parser::OmpClause::IsDevicePtr &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return IsDevicePtr{/*List=*/makeObjects(inp.v, semaCtx)};
}
Lastprivate make(const parser::OmpClause::Lastprivate &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpLastprivateClause
using wrapped = parser::OmpLastprivateClause;
CLAUSET_ENUM_CONVERT( //
convert, parser::OmpLastprivateClause::LastprivateModifier,
Lastprivate::LastprivateModifier,
// clang-format off
MS(Conditional, Conditional)
// clang-format on
);
auto &t0 = std::get<std::optional<wrapped::LastprivateModifier>>(inp.v.t);
auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
return Lastprivate{{/*LastprivateModifier=*/maybeApply(convert, t0),
/*List=*/makeObjects(t1, semaCtx)}};
}
Linear make(const parser::OmpClause::Linear &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpLinearClause
using wrapped = parser::OmpLinearClause;
CLAUSET_ENUM_CONVERT( //
convert, parser::OmpLinearModifier::Type, Linear::LinearModifier,
// clang-format off
MS(Ref, Ref)
MS(Val, Val)
MS(Uval, Uval)
// clang-format on
);
using Tuple = decltype(Linear::t);
return Linear{Fortran::common::visit(
common::visitors{
[&](const wrapped::WithModifier &s) -> Tuple {
return {
/*StepSimpleModifier=*/std::nullopt,
/*StepComplexModifier=*/maybeApply(makeExprFn(semaCtx), s.step),
/*LinearModifier=*/convert(s.modifier.v),
/*List=*/makeList(s.names, makeObjectFn(semaCtx))};
},
[&](const wrapped::WithoutModifier &s) -> Tuple {
return {
/*StepSimpleModifier=*/maybeApply(makeExprFn(semaCtx), s.step),
/*StepComplexModifier=*/std::nullopt,
/*LinearModifier=*/std::nullopt,
/*List=*/makeList(s.names, makeObjectFn(semaCtx))};
},
},
inp.v.u)};
}
Link make(const parser::OmpClause::Link &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return Link{/*List=*/makeObjects(inp.v, semaCtx)};
}
Map make(const parser::OmpClause::Map &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpMapClause
using wrapped = parser::OmpMapClause;
CLAUSET_ENUM_CONVERT( //
convert1, parser::OmpMapClause::Type, Map::MapType,
// clang-format off
MS(Alloc, Alloc)
MS(Delete, Delete)
MS(From, From)
MS(Release, Release)
MS(To, To)
MS(Tofrom, Tofrom)
// clang-format on
);
CLAUSET_ENUM_CONVERT( //
convert2, parser::OmpMapClause::TypeModifier, Map::MapTypeModifier,
// clang-format off
MS(Always, Always)
MS(Close, Close)
MS(OmpxHold, OmpxHold)
MS(Present, Present)
// clang-format on
);
auto &t0 = std::get<std::optional<std::list<wrapped::TypeModifier>>>(inp.v.t);
auto &t1 = std::get<std::optional<wrapped::Type>>(inp.v.t);
auto &t2 = std::get<parser::OmpObjectList>(inp.v.t);
std::optional<Map::MapType> maybeType = maybeApply(convert1, t1);
std::optional<Map::MapTypeModifiers> maybeTypeMods = maybeApply(
[&](const std::list<wrapped::TypeModifier> &typeMods) {
Map::MapTypeModifiers mods;
for (wrapped::TypeModifier mod : typeMods)
mods.push_back(convert2(mod));
return mods;
},
t0);
return Map{{/*MapType=*/maybeType,
/*MapTypeModifiers=*/maybeTypeMods,
/*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt,
/*LocatorList=*/makeObjects(t2, semaCtx)}};
}
// Match: incomplete
// MemoryOrder: empty
// Mergeable: empty
Message make(const parser::OmpClause::Message &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: message");
}
Nocontext make(const parser::OmpClause::Nocontext &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarLogicalExpr
return Nocontext{/*DoNotUpdateContext=*/makeExpr(inp.v, semaCtx)};
}
// Nogroup: empty
Nontemporal make(const parser::OmpClause::Nontemporal &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> std::list<parser::Name>
return Nontemporal{/*List=*/makeList(inp.v, makeObjectFn(semaCtx))};
}
// NoOpenmp: empty
// NoOpenmpRoutines: empty
// NoParallelism: empty
// Notinbranch: empty
Novariants make(const parser::OmpClause::Novariants &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarLogicalExpr
return Novariants{/*DoNotUseVariant=*/makeExpr(inp.v, semaCtx)};
}
// Nowait: empty
NumTasks make(const parser::OmpClause::NumTasks &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return NumTasks{{/*Prescriptiveness=*/std::nullopt,
/*NumTasks=*/makeExpr(inp.v, semaCtx)}};
}
NumTeams make(const parser::OmpClause::NumTeams &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
List<NumTeams::Range> v{{{/*LowerBound=*/std::nullopt,
/*UpperBound=*/makeExpr(inp.v, semaCtx)}}};
return NumTeams{/*List=*/v};
}
NumThreads make(const parser::OmpClause::NumThreads &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return NumThreads{/*Nthreads=*/makeExpr(inp.v, semaCtx)};
}
// OmpxAttribute: empty
// OmpxBare: empty
OmpxDynCgroupMem make(const parser::OmpClause::OmpxDynCgroupMem &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return OmpxDynCgroupMem{makeExpr(inp.v, semaCtx)};
}
Order make(const parser::OmpClause::Order &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpOrderClause
using wrapped = parser::OmpOrderClause;
CLAUSET_ENUM_CONVERT( //
convert1, parser::OmpOrderModifier::Kind, Order::OrderModifier,
// clang-format off
MS(Reproducible, Reproducible)
MS(Unconstrained, Unconstrained)
// clang-format on
);
CLAUSET_ENUM_CONVERT( //
convert2, wrapped::Type, Order::Ordering,
// clang-format off
MS(Concurrent, Concurrent)
// clang-format on
);
auto &t0 = std::get<std::optional<parser::OmpOrderModifier>>(inp.v.t);
auto &t1 = std::get<wrapped::Type>(inp.v.t);
auto convert3 = [&](const parser::OmpOrderModifier &s) {
return Fortran::common::visit(
[&](parser::OmpOrderModifier::Kind k) { return convert1(k); }, s.u);
};
return Order{
{/*OrderModifier=*/maybeApply(convert3, t0), /*Ordering=*/convert2(t1)}};
}
Ordered make(const parser::OmpClause::Ordered &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> std::optional<parser::ScalarIntConstantExpr>
return Ordered{/*N=*/maybeApply(makeExprFn(semaCtx), inp.v)};
}
// Otherwise: incomplete, missing-in-parser
Partial make(const parser::OmpClause::Partial &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> std::optional<parser::ScalarIntConstantExpr>
return Partial{/*UnrollFactor=*/maybeApply(makeExprFn(semaCtx), inp.v)};
}
Priority make(const parser::OmpClause::Priority &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return Priority{/*PriorityValue=*/makeExpr(inp.v, semaCtx)};
}
Private make(const parser::OmpClause::Private &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return Private{/*List=*/makeObjects(inp.v, semaCtx)};
}
ProcBind make(const parser::OmpClause::ProcBind &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpProcBindClause
using wrapped = parser::OmpProcBindClause;
CLAUSET_ENUM_CONVERT( //
convert, wrapped::Type, ProcBind::AffinityPolicy,
// clang-format off
MS(Close, Close)
MS(Master, Master)
MS(Spread, Spread)
MS(Primary, Primary)
// clang-format on
);
return ProcBind{/*AffinityPolicy=*/convert(inp.v.v)};
}
// Read: empty
Reduction make(const parser::OmpClause::Reduction &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpReductionClause
using wrapped = parser::OmpReductionClause;
CLAUSET_ENUM_CONVERT( //
convert, wrapped::ReductionModifier, Reduction::ReductionModifier,
// clang-format off
MS(Inscan, Inscan)
MS(Task, Task)
MS(Default, Default)
// clang-format on
);
auto &t0 =
std::get<std::optional<parser::OmpReductionClause::ReductionModifier>>(
inp.v.t);
auto &t1 = std::get<parser::OmpReductionOperator>(inp.v.t);
auto &t2 = std::get<parser::OmpObjectList>(inp.v.t);
return Reduction{
{/*ReductionModifier=*/t0
? std::make_optional<Reduction::ReductionModifier>(convert(*t0))
: std::nullopt,
/*ReductionIdentifiers=*/{makeReductionOperator(t1, semaCtx)},
/*List=*/makeObjects(t2, semaCtx)}};
}
// Relaxed: empty
// Release: empty
// ReverseOffload: empty
Safelen make(const parser::OmpClause::Safelen &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntConstantExpr
return Safelen{/*Length=*/makeExpr(inp.v, semaCtx)};
}
Schedule make(const parser::OmpClause::Schedule &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpScheduleClause
using wrapped = parser::OmpScheduleClause;
CLAUSET_ENUM_CONVERT( //
convert1, wrapped::ScheduleType, Schedule::Kind,
// clang-format off
MS(Static, Static)
MS(Dynamic, Dynamic)
MS(Guided, Guided)
MS(Auto, Auto)
MS(Runtime, Runtime)
// clang-format on
);
CLAUSET_ENUM_CONVERT( //
convert2, parser::OmpScheduleModifierType::ModType,
Schedule::OrderingModifier,
// clang-format off
MS(Monotonic, Monotonic)
MS(Nonmonotonic, Nonmonotonic)
// clang-format on
);
CLAUSET_ENUM_CONVERT( //
convert3, parser::OmpScheduleModifierType::ModType,
Schedule::ChunkModifier,
// clang-format off
MS(Simd, Simd)
// clang-format on
);
auto &t0 = std::get<std::optional<parser::OmpScheduleModifier>>(inp.v.t);
auto &t1 = std::get<wrapped::ScheduleType>(inp.v.t);
auto &t2 = std::get<std::optional<parser::ScalarIntExpr>>(inp.v.t);
if (!t0) {
return Schedule{{/*Kind=*/convert1(t1), /*OrderingModifier=*/std::nullopt,
/*ChunkModifier=*/std::nullopt,
/*ChunkSize=*/maybeApply(makeExprFn(semaCtx), t2)}};
}
// The members of parser::OmpScheduleModifier correspond to OrderingModifier,
// and ChunkModifier, but they can appear in any order.
auto &m1 = std::get<parser::OmpScheduleModifier::Modifier1>(t0->t);
auto &m2 =
std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(t0->t);
std::optional<Schedule::OrderingModifier> omod;
std::optional<Schedule::ChunkModifier> cmod;
if (m1.v.v == parser::OmpScheduleModifierType::ModType::Simd) {
// m1 is chunk-modifier
cmod = convert3(m1.v.v);
if (m2)
omod = convert2(m2->v.v);
} else {
// m1 is ordering-modifier
omod = convert2(m1.v.v);
if (m2)
cmod = convert3(m2->v.v);
}
return Schedule{{/*Kind=*/convert1(t1),
/*OrderingModifier=*/omod,
/*ChunkModifier=*/cmod,
/*ChunkSize=*/maybeApply(makeExprFn(semaCtx), t2)}};
}
// SeqCst: empty
Severity make(const parser::OmpClause::Severity &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: severity");
}
Shared make(const parser::OmpClause::Shared &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return Shared{/*List=*/makeObjects(inp.v, semaCtx)};
}
// Simd: empty
Simdlen make(const parser::OmpClause::Simdlen &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntConstantExpr
return Simdlen{/*Length=*/makeExpr(inp.v, semaCtx)};
}
Sizes make(const parser::OmpClause::Sizes &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> std::list<parser::ScalarIntExpr>
return Sizes{/*SizeList=*/makeList(inp.v, makeExprFn(semaCtx))};
}
Permutation make(const parser::OmpClause::Permutation &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> std::list<parser::ScalarIntConstantExpr>
return Permutation{/*ArgList=*/makeList(inp.v, makeExprFn(semaCtx))};
}
TaskReduction make(const parser::OmpClause::TaskReduction &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpReductionClause
auto &t0 = std::get<parser::OmpReductionOperator>(inp.v.t);
auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
return TaskReduction{
{/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)},
/*List=*/makeObjects(t1, semaCtx)}};
}
ThreadLimit make(const parser::OmpClause::ThreadLimit &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::ScalarIntExpr
return ThreadLimit{/*Threadlim=*/makeExpr(inp.v, semaCtx)};
}
// Threadprivate: empty
// Threads: empty
To make(const parser::OmpClause::To &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return To{{/*Expectation=*/std::nullopt, /*Mapper=*/std::nullopt,
/*Iterator=*/std::nullopt,
/*LocatorList=*/makeObjects(inp.v, semaCtx)}};
}
// UnifiedAddress: empty
// UnifiedSharedMemory: empty
Uniform make(const parser::OmpClause::Uniform &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> std::list<parser::Name>
return Uniform{/*ParameterList=*/makeList(inp.v, makeObjectFn(semaCtx))};
}
// Unknown: empty
// Untied: empty
Update make(const parser::OmpClause::Update &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
return Update{/*TaskDependenceType=*/std::nullopt};
}
Use make(const parser::OmpClause::Use &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: use");
}
UseDeviceAddr make(const parser::OmpClause::UseDeviceAddr &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return UseDeviceAddr{/*List=*/makeObjects(inp.v, semaCtx)};
}
UseDevicePtr make(const parser::OmpClause::UseDevicePtr &inp,
semantics::SemanticsContext &semaCtx) {
// inp.v -> parser::OmpObjectList
return UseDevicePtr{/*List=*/makeObjects(inp.v, semaCtx)};
}
UsesAllocators make(const parser::OmpClause::UsesAllocators &inp,
semantics::SemanticsContext &semaCtx) {
// inp -> empty
llvm_unreachable("Empty: uses_allocators");
}
// Weak: empty
// When: incomplete
// Write: empty
} // namespace clause
Clause makeClause(const parser::OmpClause &cls,
semantics::SemanticsContext &semaCtx) {
return Fortran::common::visit(
[&](auto &&s) {
return makeClause(getClauseId(cls), clause::make(s, semaCtx),
cls.source);
},
cls.u);
}
List<Clause> makeClauses(const parser::OmpClauseList &clauses,
semantics::SemanticsContext &semaCtx) {
return makeList(clauses.v, [&](const parser::OmpClause &s) {
return makeClause(s, semaCtx);
});
}
bool transferLocations(const List<Clause> &from, List<Clause> &to) {
bool allDone = true;
for (Clause &clause : to) {
if (!clause.source.empty())
continue;
auto found =
llvm::find_if(from, [&](const Clause &c) { return c.id == clause.id; });
// This is not completely accurate, but should be good enough for now.
// It can be improved in the future if necessary, but in cases of
// synthesized clauses getting accurate location may be impossible.
if (found != from.end()) {
clause.source = found->source;
} else {
// Found a clause that won't have "source".
allDone = false;
}
}
return allDone;
}
} // namespace Fortran::lower::omp