| //===-- 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 "flang/Lower/OpenMP/Clauses.h" | |
| #include "flang/Common/idioms.h" | |
| #include "flang/Evaluate/expression.h" | |
| #include "flang/Optimizer/Builder/Todo.h" | |
| #include "flang/Parser/parse-tree-visitor.h" | |
| #include "flang/Parser/parse-tree.h" | |
| #include "flang/Semantics/expression.h" | |
| #include "flang/Semantics/openmp-modifiers.h" | |
| #include "flang/Semantics/openmp-utils.h" | |
| #include "flang/Semantics/symbol.h" | |
| #include <list> | |
| #include <optional> | |
| #include <tuple> | |
| #include <utility> | |
| #include <variant> | |
| 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))); | |
| } | |
| template <typename T> | |
| static SymbolWithDesignator visit(const evaluate::FunctionRef<T> &e) { | |
| return std::make_tuple(symbol_addr(*e.proc().GetSymbol()), | |
| 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); | |
| const std::optional<evaluate::Expr<evaluate::SomeType>> &maybeDsg = | |
| std::get<1>(sd); | |
| if (!maybeDsg) | |
| return; // Symbol with no designator -> OK | |
| assert(symbol && "Expecting symbol"); | |
| std::optional<evaluate::DataRef> maybeRef = evaluate::ExtractDataRef( | |
| *maybeDsg, /*intoSubstring=*/true, /*intoComplexPart=*/true); | |
| if (maybeRef) { | |
| if (&maybeRef->GetLastSymbol() == symbol) | |
| return; // Symbol with a designator for it -> OK | |
| llvm_unreachable("Symbol mismatch"); | |
| } else if (auto *ref = evaluate::UnwrapProcedureRef(*maybeDsg)) { | |
| if (ref->proc().GetSymbol() == symbol) | |
| return; | |
| llvm_unreachable("Symbol mismatch"); | |
| } else { | |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | |
| maybeDsg->dump(); | |
| #endif | |
| llvm_unreachable("Unexpected expression"); | |
| } | |
| } | |
| }; | |
| 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}; | |
| auto restore{ea.AllowWholeAssumedSizeArray(true)}; | |
| 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::FunctionReference &ref, | |
| semantics::SemanticsContext &semaCtx) { | |
| evaluate::ExpressionAnalyzer ea{semaCtx}; | |
| SymbolWithDesignator sd = getSymbolAndDesignator(ea.Analyze(ref)); | |
| 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}; | |
| auto restore{ea.AllowWholeAssumedSizeArray(true)}; | |
| 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 = parser::omp::GetCommonBlockFromObj(object)) { | |
| assert(name->symbol && "Expecting Symbol"); | |
| return Object{name->symbol, std::nullopt}; | |
| } | |
| assert(!std::holds_alternative<parser::OmpObject::Invalid>(object.u) && | |
| "Invalid object should have been caught in semantics"); | |
| // OmpObject is std::variant<Designator, OmpLocator, Name, Invalid>; | |
| if (auto *desg = parser::omp::GetDesignatorFromObj(object)) | |
| return makeObject(*desg, semaCtx); | |
| if (auto *locator = parser::omp::GetLocatorFromObj(object)) { | |
| return common::visit( // | |
| common::visitors{ | |
| [&](const parser::OmpReservedIdentifier &x) { | |
| return makeObject(x.v, semaCtx); | |
| }, | |
| [&](const parser::FunctionReference &x) { | |
| return makeObject(x, semaCtx); | |
| }, | |
| }, | |
| locator->u); | |
| } | |
| llvm_unreachable("Unexpected OmpObject"); | |
| } | |
| Object makeObject(const parser::EntityDecl &decl, | |
| semantics::SemanticsContext &semaCtx) { | |
| return makeObject(std::get<parser::ObjectName>(decl.t), semaCtx); | |
| } | |
| ObjectList makeObjects(const parser::OmpArgumentList &objects, | |
| semantics::SemanticsContext &semaCtx) { | |
| return makeList(objects.v, [&](const parser::OmpArgument &arg) { | |
| return common::visit( // | |
| common::visitors{ | |
| [&](const parser::OmpObject &object) -> Object { | |
| return makeObject(object, semaCtx); | |
| }, | |
| [](auto &&s) -> Object { // | |
| llvm_unreachable("Expecting object"); | |
| }, | |
| }, | |
| arg.u); | |
| }); | |
| } | |
| 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 (auto *symRef = base.UnwrapSymbolRef()) { | |
| // This is the base symbol of the array reference, which is the same | |
| // as the symbol in the input object, | |
| // e.g. A(i) is represented as {Symbol(A), Designator(ArrayRef(A, i))}. | |
| // Here we have the Symbol(A), which is what we started with. | |
| (void)symRef; | |
| assert(&**symRef == object.sym()); | |
| 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; | |
| } | |
| StylizedInstance makeStylizedInstance(const parser::OmpStylizedInstance &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| ObjectList variables; | |
| llvm::transform(std::get<std::list<parser::OmpStylizedDeclaration>>(inp.t), | |
| std::back_inserter(variables), | |
| [&](const parser::OmpStylizedDeclaration &s) { | |
| return makeObject(s.var, semaCtx); | |
| }); | |
| SomeExpr instance = [&]() { | |
| if (auto &&expr = semantics::omp::MakeEvaluateExpr(inp)) | |
| return std::move(*expr); | |
| llvm_unreachable("Expecting expression instance"); | |
| }(); | |
| return StylizedInstance{{std::move(variables), std::move(instance)}}; | |
| } | |
| // 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(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(NoOpenmpConstructs, NoOpenmpConstructs); | |
| 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(SeqCst, SeqCst); | |
| MAKE_EMPTY_CLASS(Simd, Simd); | |
| MAKE_EMPTY_CLASS(Threads, Threads); | |
| MAKE_EMPTY_CLASS(Unknown, Unknown); | |
| MAKE_EMPTY_CLASS(Untied, Untied); | |
| MAKE_EMPTY_CLASS(Update, Update); | |
| MAKE_EMPTY_CLASS(Weak, Weak); | |
| MAKE_EMPTY_CLASS(Write, Write); | |
| // Artificial clauses | |
| MAKE_EMPTY_CLASS(Depobj, Depobj); | |
| MAKE_EMPTY_CLASS(Flush, Flush); | |
| MAKE_EMPTY_CLASS(Groupprivate, Groupprivate); | |
| MAKE_EMPTY_CLASS(MemoryOrder, MemoryOrder); | |
| MAKE_EMPTY_CLASS(Threadprivate, Threadprivate); | |
| MAKE_INCOMPLETE_CLASS(AdjustArgs, AdjustArgs); | |
| MAKE_INCOMPLETE_CLASS(AppendArgs, AppendArgs); | |
| MAKE_INCOMPLETE_CLASS(Apply, Apply); | |
| MAKE_INCOMPLETE_CLASS(Collector, Collector); | |
| MAKE_INCOMPLETE_CLASS(Counts, Counts); | |
| MAKE_INCOMPLETE_CLASS(GraphId, GraphId); | |
| MAKE_INCOMPLETE_CLASS(GraphReset, GraphReset); | |
| MAKE_INCOMPLETE_CLASS(Induction, Induction); | |
| MAKE_INCOMPLETE_CLASS(Inductor, Inductor); | |
| MAKE_INCOMPLETE_CLASS(InitComplete, InitComplete); | |
| MAKE_INCOMPLETE_CLASS(Interop, Interop); | |
| MAKE_INCOMPLETE_CLASS(Local, Local); | |
| MAKE_INCOMPLETE_CLASS(Memscope, Memscope); | |
| MAKE_INCOMPLETE_CLASS(Replayable, Replayable); | |
| MAKE_INCOMPLETE_CLASS(ReductionKind, ReductionKind); | |
| MAKE_INCOMPLETE_CLASS(Safesync, Safesync); | |
| MAKE_INCOMPLETE_CLASS(Shift, Shift); | |
| MAKE_INCOMPLETE_CLASS(Transparent, Transparent); | |
| List<IteratorSpecifier> | |
| makeIteratorSpecifiers(const parser::OmpIteratorSpecifier &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| List<IteratorSpecifier> specifiers; | |
| auto &[begin, end, step] = std::get<parser::SubscriptTriplet>(inp.t).t; | |
| assert(begin && end && "Expecting begin/end values"); | |
| evaluate::ExpressionAnalyzer ea{semaCtx}; | |
| MaybeExpr rbegin{ea.Analyze(*begin)}, rend{ea.Analyze(*end)}; | |
| MaybeExpr rstep; | |
| if (step) | |
| rstep = ea.Analyze(*step); | |
| assert(rbegin && rend && "Unable to get range bounds"); | |
| Range range{{*rbegin, *rend, rstep}}; | |
| auto &tds = std::get<parser::TypeDeclarationStmt>(inp.t); | |
| auto &entities = std::get<std::list<parser::EntityDecl>>(tds.t); | |
| for (const parser::EntityDecl &ed : entities) { | |
| auto *symbol = std::get<parser::ObjectName>(ed.t).symbol; | |
| auto *type = DEREF(symbol).GetType(); | |
| IteratorSpecifier spec{{evaluate::DynamicType::From(DEREF(type)), | |
| makeObject(ed, semaCtx), range}}; | |
| specifiers.emplace_back(std::move(spec)); | |
| } | |
| return specifiers; | |
| } | |
| Iterator makeIterator(const parser::OmpIterator &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| Iterator iterator; | |
| for (auto &&spec : inp.v) | |
| llvm::append_range(iterator, makeIteratorSpecifiers(spec, semaCtx)); | |
| return iterator; | |
| } | |
| // Walk source syntax rather than the analyzed designator: folding can remove | |
| // an iterator reference that still determines whether a locator is evaluated. | |
| struct IteratorReferenceCollector { | |
| template <typename T> | |
| bool Pre(const T &) { | |
| return true; | |
| } | |
| template <typename T> | |
| void Post(const T &) {} | |
| bool Pre(const parser::Name &name) { | |
| if (name.symbol) { | |
| const semantics::Symbol *symbol = &name.symbol->GetUltimate(); | |
| if (llvm::is_contained(iterators, symbol) && | |
| !llvm::is_contained(references, symbol)) | |
| references.push_back(symbol); | |
| } | |
| return true; | |
| } | |
| const List<const semantics::Symbol *> &iterators; | |
| List<const semantics::Symbol *> &references; | |
| }; | |
| static ObjectList makeIteratorObjects(const parser::OmpObjectList &objects, | |
| const std::optional<Iterator> &iterator, | |
| semantics::SemanticsContext &semaCtx) { | |
| if (!iterator) | |
| return makeObjects(objects, semaCtx); | |
| List<const semantics::Symbol *> symbols; | |
| for (const auto &specifier : *iterator) | |
| symbols.push_back(&std::get<Object>(specifier.t).sym()->GetUltimate()); | |
| return makeList(objects.v, [&](const parser::OmpObject &source) { | |
| Object object = makeObject(source, semaCtx); | |
| auto &references = object.sourceIteratorReferences.emplace(); | |
| IteratorReferenceCollector collector{symbols, references}; | |
| parser::Walk(source, collector); | |
| return object; | |
| }); | |
| } | |
| 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::OmpReductionIdentifier &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); | |
| } | |
| clause::DependenceType makeDepType(const parser::OmpDependenceType &inp) { | |
| switch (inp.v) { | |
| case parser::OmpDependenceType::Value::Sink: | |
| return clause::DependenceType::Sink; | |
| case parser::OmpDependenceType::Value::Source: | |
| return clause::DependenceType::Source; | |
| } | |
| llvm_unreachable("Unexpected dependence type"); | |
| } | |
| clause::DependenceType makeDepType(const parser::OmpTaskDependenceType &inp) { | |
| switch (inp.v) { | |
| case parser::OmpTaskDependenceType::Value::Depobj: | |
| return clause::DependenceType::Depobj; | |
| case parser::OmpTaskDependenceType::Value::In: | |
| return clause::DependenceType::In; | |
| case parser::OmpTaskDependenceType::Value::Inout: | |
| return clause::DependenceType::Inout; | |
| case parser::OmpTaskDependenceType::Value::Inoutset: | |
| return clause::DependenceType::Inoutset; | |
| case parser::OmpTaskDependenceType::Value::Mutexinoutset: | |
| return clause::DependenceType::Mutexinoutset; | |
| case parser::OmpTaskDependenceType::Value::Out: | |
| return clause::DependenceType::Out; | |
| } | |
| llvm_unreachable("Unexpected task dependence type"); | |
| } | |
| clause::Prescriptiveness | |
| makePrescriptiveness(parser::OmpPrescriptiveness::Value v) { | |
| switch (v) { | |
| case parser::OmpPrescriptiveness::Value::Strict: | |
| return clause::Prescriptiveness::Strict; | |
| } | |
| llvm_unreachable("Unexpected prescriptiveness"); | |
| } | |
| // -------------------------------------------------------------------- | |
| // 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.v -> parser::OmpAffinityClause | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); | |
| auto &&maybeIter = | |
| m0 ? makeIterator(*m0, semaCtx) : std::optional<Iterator>{}; | |
| auto objects = makeIteratorObjects(t1, maybeIter, semaCtx); | |
| return Affinity{{/*Iterator=*/std::move(maybeIter), | |
| /*LocatorList=*/std::move(objects)}}; | |
| } | |
| Align make(const parser::OmpClause::Align &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> OmpAlignClause | |
| return Align{/*Alignment=*/makeExpr(inp.v.v, semaCtx)}; | |
| } | |
| Aligned make(const parser::OmpClause::Aligned &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpAlignedClause | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto &t0 = std::get<parser::OmpObjectList>(inp.v.t); | |
| auto *m1 = semantics::OmpGetUniqueModifier<parser::OmpAlignment>(mods); | |
| return Aligned{{ | |
| /*Alignment=*/maybeApplyToV(makeExprFn(semaCtx), m1), | |
| /*List=*/makeObjects(t0, semaCtx), | |
| }}; | |
| } | |
| Allocate make(const parser::OmpClause::Allocate &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpAllocateClause | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpAlignModifier>(mods); | |
| auto *m1 = | |
| semantics::OmpGetUniqueModifier<parser::OmpAllocatorComplexModifier>( | |
| mods); | |
| auto *m2 = | |
| semantics::OmpGetUniqueModifier<parser::OmpAllocatorSimpleModifier>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); | |
| auto makeAllocator = [&](auto *mod) -> std::optional<Allocator> { | |
| if (mod) | |
| return Allocator{makeExpr(mod->v, semaCtx)}; | |
| return std::nullopt; | |
| }; | |
| auto makeAlign = [&](const parser::ScalarIntExpr &expr) { | |
| return Align{makeExpr(expr, semaCtx)}; | |
| }; | |
| auto maybeAllocator = m1 ? makeAllocator(m1) : makeAllocator(m2); | |
| return Allocate{{/*AllocatorComplexModifier=*/std::move(maybeAllocator), | |
| /*AlignModifier=*/maybeApplyToV(makeAlign, m0), | |
| /*List=*/makeObjects(t1, semaCtx)}}; | |
| } | |
| 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.v -> OmpAtClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convertActionTime, parser::OmpAtClause::ActionTime, At::ActionTime, | |
| // clang-format off | |
| MS(Compilation, Compilation) | |
| MS(Execution, Execution) | |
| // clang-format om | |
| ); | |
| return At{/*ActionTime=*/convertActionTime(inp.v.v)}; | |
| } | |
| // 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::OmpMemoryOrderType, AtomicDefaultMemOrder::MemoryOrder, | |
| // clang-format off | |
| MS(Acq_Rel, AcqRel) | |
| MS(Acquire, Acquire) | |
| MS(Relaxed, Relaxed) | |
| MS(Release, Release) | |
| MS(Seq_Cst, SeqCst) | |
| // clang-format on | |
| ); | |
| return AtomicDefaultMemOrder{/*MemoryOrder=*/convert(inp.v.v)}; | |
| } | |
| Bind make(const parser::OmpClause::Bind &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpBindClause | |
| using wrapped = parser::OmpBindClause; | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, wrapped::Binding, Bind::Binding, | |
| // clang-format off | |
| MS(Teams, Teams) | |
| MS(Parallel, Parallel) | |
| MS(Thread, Thread) | |
| // clang-format on | |
| ); | |
| return Bind{/*Binding=*/convert(inp.v.v)}; | |
| } | |
| CancellationConstructType | |
| make(const parser::OmpClause::CancellationConstructType &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| auto name = std::get<parser::OmpDirectiveName>(inp.v.t); | |
| CLAUSET_ENUM_CONVERT( | |
| convert, llvm::omp::Directive, llvm::omp::CancellationConstructType, | |
| // clang-format off | |
| MS(OMPD_parallel, OMP_CANCELLATION_CONSTRUCT_Parallel) | |
| MS(OMPD_do, OMP_CANCELLATION_CONSTRUCT_Loop) | |
| MS(OMPD_sections, OMP_CANCELLATION_CONSTRUCT_Sections) | |
| MS(OMPD_taskgroup, OMP_CANCELLATION_CONSTRUCT_Taskgroup) | |
| // clang-format on | |
| ); | |
| return CancellationConstructType{convert(name.v)}; | |
| } | |
| // Capture: empty | |
| Collapse make(const parser::OmpClause::Collapse &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::ScalarIntConstantExpr | |
| return Collapse{/*N=*/makeExpr(inp.v, semaCtx)}; | |
| } | |
| Combiner make(const parser::OmpClause::Combiner &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| const parser::OmpCombinerExpression &cexpr = inp.v.v; | |
| Combiner combiner; | |
| for (const parser::OmpStylizedInstance &sinst : cexpr.v) | |
| combiner.v.push_back(makeStylizedInstance(sinst, semaCtx)); | |
| return combiner; | |
| } | |
| // 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)}; | |
| } | |
| // The DEFAULT clause is OpenMP 5.0 and 5.1 that represents the default | |
| // directive variant in METADIRECTIVE is represented by the DefaultVariant | |
| // class. | |
| Default make(const parser::OmpClause::Default &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpDefaultClause | |
| using wrapped = parser::OmpDefaultClause; | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, wrapped::DataSharingAttribute, 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)}; | |
| } | |
| // Lower the DefaultVariant (specific to OpenMP 5.0 and 5.1) directly to | |
| // OTHERWISE (which replaced it since 5.2). | |
| Otherwise makeOtherwise(const parser::OmpClause::DefaultVariant &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| return Otherwise{}; | |
| } | |
| 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, Present) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convert2, parser::OmpVariableCategory::Value, | |
| Defaultmap::VariableCategory, | |
| // clang-format off | |
| MS(Aggregate, Aggregate) | |
| MS(All, All) | |
| MS(Allocatable, Allocatable) | |
| MS(Pointer, Pointer) | |
| MS(Scalar, Scalar) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto &t0 = std::get<wrapped::ImplicitBehavior>(inp.v.t); | |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpVariableCategory>(mods); | |
| auto category = t1 ? convert2(t1->v) : Defaultmap::VariableCategory::All; | |
| return Defaultmap{{/*ImplicitBehavior=*/convert1(t0), | |
| /*VariableCategory=*/category}}; | |
| } | |
| Doacross makeDoacross(const parser::OmpDoacross &doa, | |
| semantics::SemanticsContext &semaCtx) { | |
| // Iteration is the equivalent of parser::OmpIteration | |
| using Iteration = Doacross::Vector::value_type; // LoopIterationT | |
| auto &mods = semantics::OmpGetModifiers(doa); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpDependenceType>(mods); | |
| assert(m0 && "Expecting dependence-type"); | |
| if (m0->v == parser::OmpDependenceType::Value::Source) { | |
| return Doacross{{/*DependenceType=*/Doacross::DependenceType::Source, | |
| /*Vector=*/{}}}; | |
| } | |
| using IterOffset = parser::OmpIterationOffset; | |
| auto convert2 = [&](const parser::OmpIteration &v) { | |
| auto &t0 = std::get<parser::Name>(v.t); | |
| auto &t1 = std::get<std::optional<IterOffset>>(v.t); | |
| auto convert3 = [&](const IterOffset &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)}}; | |
| }; | |
| auto &vec = std::get<std::optional<parser::OmpIterationVector>>(doa.t); | |
| assert(vec && "Expecting iteration vector"); | |
| return Doacross{{/*DependenceType=*/Doacross::DependenceType::Sink, | |
| /*Vector=*/makeList(vec->v, convert2)}}; | |
| } | |
| Depend makeDepend(const parser::OmpDependClause::TaskDep &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| auto &mods = semantics::OmpGetModifiers(inp); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); | |
| auto *m1 = | |
| semantics::OmpGetUniqueModifier<parser::OmpTaskDependenceType>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.t); | |
| assert(m1 && "expecting task dependence type"); | |
| auto &&maybeIter = | |
| m0 ? makeIterator(*m0, semaCtx) : std::optional<Iterator>{}; | |
| auto objects = makeIteratorObjects(t1, maybeIter, semaCtx); | |
| return Depend{{/*DependenceType=*/makeDepType(*m1), | |
| /*Iterator=*/std::move(maybeIter), | |
| /*Vector=*/std::nullopt, | |
| /*LocatorList=*/std::move(objects)}}; | |
| } | |
| // depend(source) / depend(sink: vec) on ordered (4.5..5.1 spelling, deprecated | |
| // in 5.2 in favor of the dedicated doacross clause). Internally modelled as a | |
| // Depend with the optional iteration Vector populated and an empty | |
| // LocatorList, mirroring the shape of Doacross. | |
| Depend makeDependDoacross(const parser::OmpDoacross &doa, | |
| semantics::SemanticsContext &semaCtx) { | |
| Doacross doacross = makeDoacross(doa, semaCtx); | |
| return Depend{ | |
| {/*DependenceType=*/std::get<Doacross::DependenceType>(doacross.t), | |
| /*Iterator=*/std::nullopt, | |
| /*Vector=*/std::get<Doacross::Vector>(std::move(doacross.t)), | |
| /*LocatorList=*/{}}}; | |
| } | |
| // Depobj: empty | |
| Depth make(const parser::OmpClause::Depth &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::ScalarIntConstantExpr | |
| return Depth{/*DepthExpr=*/makeExpr(inp.v, semaCtx)}; | |
| } | |
| Destroy make(const parser::OmpClause::Destroy &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> std::optional<OmpDestroyClause> | |
| auto &&maybeObject = maybeApply( | |
| [&](const parser::OmpDestroyClause &c) { | |
| return makeObject(c.v, semaCtx); | |
| }, | |
| inp.v); | |
| return Destroy{/*DestroyVar=*/std::move(maybeObject)}; | |
| } | |
| Detach make(const parser::OmpClause::Detach &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpDetachClause | |
| return Detach{makeObject(inp.v.v, semaCtx)}; | |
| } | |
| Device make(const parser::OmpClause::Device &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpDeviceClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, parser::OmpDeviceModifier::Value, Device::DeviceModifier, | |
| // clang-format off | |
| MS(Ancestor, Ancestor) | |
| MS(Device_Num, DeviceNum) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpDeviceModifier>(mods); | |
| auto &t1 = std::get<parser::ScalarIntExpr>(inp.v.t); | |
| return Device{{/*DeviceModifier=*/maybeApplyToV(convert, m0), | |
| /*DeviceDescription=*/makeExpr(t1, semaCtx)}}; | |
| } | |
| DeviceSafesync make(const parser::OmpClause::DeviceSafesync &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> std::optional<parser::OmpDeviceSafesyncClause> | |
| auto &&maybeRequired = maybeApply( | |
| [&](const parser::OmpDeviceSafesyncClause &c) { | |
| return makeExpr(c.v, semaCtx); | |
| }, | |
| inp.v); | |
| return DeviceSafesync{/*Required=*/std::move(maybeRequired)}; | |
| } | |
| DeviceType make(const parser::OmpClause::DeviceType &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpDeviceTypeClause | |
| using wrapped = parser::OmpDeviceTypeClause; | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, wrapped::DeviceTypeDescription, | |
| 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.v -> OmpDoacrossClause | |
| return makeDoacross(inp.v.v, semaCtx); | |
| } | |
| DynamicAllocators make(const parser::OmpClause::DynamicAllocators &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> td::optional<arser::OmpDynamicAllocatorsClause> | |
| auto &&maybeRequired = maybeApply( | |
| [&](const parser::OmpDynamicAllocatorsClause &c) { | |
| return makeExpr(c.v, semaCtx); | |
| }, | |
| inp.v); | |
| return DynamicAllocators{/*Required=*/std::move(maybeRequired)}; | |
| } | |
| DynGroupprivate make(const parser::OmpClause::DynGroupprivate &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // imp.v -> OmpDyngroupprivateClause | |
| CLAUSET_ENUM_CONVERT( // | |
| makeAccessGroup, parser::OmpAccessGroup::Value, | |
| DynGroupprivate::AccessGroup, | |
| // clang-format off | |
| MS(Cgroup, Cgroup) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| makeFallback, parser::OmpFallbackModifier::Value, | |
| DynGroupprivate::Fallback, | |
| // clang-format off | |
| MS(Abort, Abort) | |
| MS(Default_Mem, Default_Mem) | |
| MS(Null, Null) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpAccessGroup>(mods); | |
| auto *m1 = semantics::OmpGetUniqueModifier<parser::OmpFallbackModifier>(mods); | |
| auto &size = std::get<parser::ScalarIntExpr>(inp.v.t); | |
| return DynGroupprivate{{/*AccessGroup=*/maybeApplyToV(makeAccessGroup, m0), | |
| /*Fallback=*/maybeApplyToV(makeFallback, m1), | |
| /*Size=*/makeExpr(size, semaCtx)}}; | |
| } | |
| Enter make(const parser::OmpClause::Enter &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpEnterClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, parser::OmpAutomapModifier::Value, Enter::Modifier, | |
| // clang-format off | |
| MS(Automap, Automap) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *mod = semantics::OmpGetUniqueModifier<parser::OmpAutomapModifier>(mods); | |
| auto &objList = std::get<parser::OmpObjectList>(inp.v.t); | |
| return Enter{{/*Modifier=*/maybeApplyToV(convert, mod), | |
| /*List=*/makeObjects(objList, semaCtx)}}; | |
| } | |
| Exclusive make(const parser::OmpClause::Exclusive &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpObjectList | |
| return Exclusive{makeObjects(/*List=*/inp.v, semaCtx)}; | |
| } | |
| Fail make(const parser::OmpClause::Fail &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpFalClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, common::OmpMemoryOrderType, Fail::MemoryOrder, | |
| // clang-format off | |
| MS(Acq_Rel, AcqRel) | |
| MS(Acquire, Acquire) | |
| MS(Relaxed, Relaxed) | |
| MS(Release, Release) | |
| MS(Seq_Cst, SeqCst) | |
| // clang-format on | |
| ); | |
| return Fail{/*MemoryOrder=*/convert(inp.v.v)}; | |
| } | |
| 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::OmpFromClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convertMot, parser::OmpMotionModifier::Value, From::Expectation, | |
| // clang-format off | |
| MS(Present, Present) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convertExp, parser::OmpExpectation::Value, From::Expectation, | |
| // clang-format off | |
| MS(Present, Present) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convertPre, parser::OmpPresentModifier::Value, From::Expectation, | |
| // clang-format off | |
| MS(Present, Present) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpMotionModifier>(mods); | |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpExpectation>(mods); | |
| auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpPresentModifier>(mods); | |
| auto *t3 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods); | |
| auto *t4 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); | |
| auto &t5 = std::get<parser::OmpObjectList>(inp.v.t); | |
| std::optional<From::Expectation> maybeExp = // | |
| t0 ? maybeApplyToV(convertMot, t0) | |
| : t1 ? maybeApplyToV(convertExp, t1) | |
| : t2 ? maybeApplyToV(convertPre, t2) | |
| : std::optional<From::Expectation>{}; | |
| auto mappers = [&]() -> std::optional<List<Mapper>> { | |
| if (t3) | |
| return List<Mapper>{Mapper{makeObject(t3->v, semaCtx)}}; | |
| return std::nullopt; | |
| }(); | |
| auto iterator = [&]() -> std::optional<Iterator> { | |
| if (t4) | |
| return makeIterator(*t4, semaCtx); | |
| return std::nullopt; | |
| }(); | |
| return From{{/*Expectation=*/maybeExp, | |
| /*Mappers=*/std::move(mappers), | |
| /*Iterator=*/std::move(iterator), | |
| /*LocatorList=*/makeObjects(t5, semaCtx)}}; | |
| } | |
| // Full: empty | |
| Grainsize make(const parser::OmpClause::Grainsize &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpGrainsizeClause | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpPrescriptiveness>(mods); | |
| auto &t1 = std::get<parser::ScalarIntExpr>(inp.v.t); | |
| return Grainsize{ | |
| {/*Prescriptiveness=*/maybeApplyToV(makePrescriptiveness, m0), | |
| /*Grainsize=*/makeExpr(t1, 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::OmpHintClause | |
| return Hint{/*HintExpr=*/makeExpr(inp.v.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 | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = | |
| semantics::OmpGetUniqueModifier<parser::OmpDirectiveNameModifier>(mods); | |
| auto &t1 = std::get<parser::ScalarLogicalExpr>(inp.v.t); | |
| return If{ | |
| {/*DirectiveNameModifier=*/maybeApplyToV([](auto &&s) { return s; }, m0), | |
| /*IfExpression=*/makeExpr(t1, semaCtx)}}; | |
| } | |
| // Inbranch: empty | |
| Inclusive make(const parser::OmpClause::Inclusive &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpObjectList | |
| return Inclusive{makeObjects(/*List=*/inp.v, semaCtx)}; | |
| } | |
| Indirect make(const parser::OmpClause::Indirect &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v.v -> std::optional<parser::ScalarLogicalExpr> | |
| return Indirect{maybeApply(makeExprFn(semaCtx), inp.v.v)}; | |
| } | |
| Init make(const parser::OmpClause::Init &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // OmpInitClause has modifiers: OmpPreferType, OmpInteropType, | |
| // OmpDepinfoModifier, and an OmpObject (the interop variable). | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| // Extract interop types (repeatable modifier). | |
| Init::InteropTypes interopTypes; | |
| for (auto *interopType : | |
| semantics::OmpGetRepeatableModifier<parser::OmpInteropType>(mods)) { | |
| switch (interopType->v) { | |
| case parser::OmpInteropType::Value::Target: | |
| interopTypes.push_back(Init::InteropType::Target); | |
| break; | |
| case parser::OmpInteropType::Value::Targetsync: | |
| interopTypes.push_back(Init::InteropType::Targetsync); | |
| break; | |
| } | |
| } | |
| // Extract prefer_type (optional modifier). | |
| std::optional<Init::InteropPreference> interopPreference; | |
| if (auto *preferType = | |
| semantics::OmpGetUniqueModifier<parser::OmpPreferType>(mods)) { | |
| Init::InteropPreference prefs; | |
| for (auto &prefSpec : preferType->v) { | |
| common::visit( | |
| common::visitors{ | |
| [&](const parser::OmpPreferenceSpecification:: | |
| ForeignRuntimeIdentifier &fri) { | |
| prefs.push_back(makeExpr(fri.value(), semaCtx)); | |
| }, | |
| [&](const std::list<parser::OmpPreferenceSelector> &selectors) { | |
| // TODO: Handle preference-selector lists. | |
| if (!selectors.empty()) | |
| TODO_NOLOC("prefer_type with preference-selector lists in " | |
| "interop init clause"); | |
| }, | |
| }, | |
| prefSpec.u); | |
| } | |
| if (!prefs.empty()) | |
| interopPreference = std::move(prefs); | |
| } | |
| auto &interopVar = std::get<parser::OmpObject>(inp.v.t); | |
| return Init{{/*InteropPreference=*/std::move(interopPreference), | |
| /*InteropTypes=*/std::move(interopTypes), | |
| /*InteropVar=*/makeObject(interopVar, semaCtx)}}; | |
| } | |
| Initializer make(const parser::OmpClause::Initializer &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| const parser::OmpInitializerExpression &iexpr = inp.v.v; | |
| Initializer initializer; | |
| for (const parser::OmpStylizedInstance &sinst : iexpr.v) | |
| initializer.v.push_back(makeStylizedInstance(sinst, semaCtx)); | |
| return initializer; | |
| } | |
| InReduction make(const parser::OmpClause::InReduction &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpInReductionClause | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = | |
| semantics::OmpGetUniqueModifier<parser::OmpReductionIdentifier>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); | |
| assert(m0 && "OmpReductionIdentifier is required"); | |
| return InReduction{ | |
| {/*ReductionIdentifiers=*/{makeReductionOperator(*m0, 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 | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, parser::OmpLastprivateModifier::Value, | |
| Lastprivate::LastprivateModifier, | |
| // clang-format off | |
| MS(Conditional, Conditional) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = | |
| semantics::OmpGetUniqueModifier<parser::OmpLastprivateModifier>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); | |
| return Lastprivate{{/*LastprivateModifier=*/maybeApplyToV(convert, m0), | |
| /*List=*/makeObjects(t1, semaCtx)}}; | |
| } | |
| Linear make(const parser::OmpClause::Linear &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpLinearClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, parser::OmpLinearModifier::Value, Linear::LinearModifier, | |
| // clang-format off | |
| MS(Ref, Ref) | |
| MS(Val, Val) | |
| MS(Uval, Uval) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = | |
| semantics::OmpGetUniqueModifier<parser::OmpStepComplexModifier>(mods); | |
| auto *m1 = | |
| semantics::OmpGetUniqueModifier<parser::OmpStepSimpleModifier>(mods); | |
| assert((!m0 || !m1) && "Simple and complex modifiers both present"); | |
| auto *m2 = semantics::OmpGetUniqueModifier<parser::OmpLinearStep>(mods); | |
| auto *m3 = semantics::OmpGetUniqueModifier<parser::OmpLinearModifier>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); | |
| auto &&maybeStep = m0 ? maybeApplyToV(makeExprFn(semaCtx), m0) | |
| : m1 ? maybeApplyToV(makeExprFn(semaCtx), m1) | |
| : m2 ? maybeApplyToV(makeExprFn(semaCtx), m2) | |
| : std::optional<Linear::StepComplexModifier>{}; | |
| return Linear{{/*StepComplexModifier=*/std::move(maybeStep), | |
| /*LinearModifier=*/maybeApplyToV(convert, m3), | |
| /*List=*/makeObjects(t1, semaCtx)}}; | |
| } | |
| Link make(const parser::OmpClause::Link &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpObjectList | |
| return Link{/*List=*/makeObjects(inp.v, semaCtx)}; | |
| } | |
| Looprange make(const parser::OmpClause::Looprange &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> OmpLooprangeClause | |
| auto &[begin, count]{inp.v.t}; | |
| return Looprange{ | |
| {/*Begin=*/makeExpr(begin, semaCtx), /*Count=*/makeExpr(count, semaCtx)}}; | |
| } | |
| Map make(const parser::OmpClause::Map &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpMapClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convertMapType, parser::OmpMapType::Value, Map::MapType, | |
| // clang-format off | |
| MS(Alloc, Storage) | |
| MS(Delete, Storage) | |
| MS(Release, Storage) | |
| MS(Storage, Storage) | |
| MS(From, From) | |
| MS(To, To) | |
| MS(Tofrom, Tofrom) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convertMapTypeMod, parser::OmpMapTypeModifier::Value, | |
| Map::MapTypeModifier, | |
| // clang-format off | |
| MS(Always, Always) | |
| MS(Close, Close) | |
| MS(Present, Present) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convertAttachMod, parser::OmpAttachModifier::Value, Map::AttachModifier, | |
| // clang-format off | |
| MS(Always, Always) | |
| MS(Auto, Auto) | |
| MS(Never, Never) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convertRefMod, parser::OmpRefModifier::Value, Map::RefModifier, | |
| // clang-format off | |
| MS(Ref_Ptee, RefPtee) | |
| MS(Ref_Ptr, RefPtr) | |
| MS(Ref_Ptr_Ptee, RefPtrPtee) | |
| // clang-format on | |
| ); | |
| // Treat always, close, present, self, delete modifiers as map-type- | |
| // modifiers. | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpMapType>(mods); | |
| auto &t2 = std::get<parser::OmpObjectList>(inp.v.t); | |
| auto type = [&]() -> std::optional<Map::MapType> { | |
| if (t1) | |
| return convertMapType(t1->v); | |
| return std::nullopt; | |
| }(); | |
| llvm::DenseSet<Map::MapTypeModifier> modSet; | |
| if (t1 && t1->v == parser::OmpMapType::Value::Delete) | |
| modSet.insert(Map::MapTypeModifier::Delete); | |
| for (auto *typeMod : | |
| semantics::OmpGetRepeatableModifier<parser::OmpMapTypeModifier>(mods)) { | |
| modSet.insert(convertMapTypeMod(typeMod->v)); | |
| } | |
| if (semantics::OmpGetUniqueModifier<parser::OmpAlwaysModifier>(mods)) | |
| modSet.insert(Map::MapTypeModifier::Always); | |
| if (semantics::OmpGetUniqueModifier<parser::OmpCloseModifier>(mods)) | |
| modSet.insert(Map::MapTypeModifier::Close); | |
| if (semantics::OmpGetUniqueModifier<parser::OmpDeleteModifier>(mods)) | |
| modSet.insert(Map::MapTypeModifier::Delete); | |
| if (semantics::OmpGetUniqueModifier<parser::OmpPresentModifier>(mods)) | |
| modSet.insert(Map::MapTypeModifier::Present); | |
| if (semantics::OmpGetUniqueModifier<parser::OmpSelfModifier>(mods)) | |
| modSet.insert(Map::MapTypeModifier::Self); | |
| if (semantics::OmpGetUniqueModifier<parser::OmpxHoldModifier>(mods)) | |
| modSet.insert(Map::MapTypeModifier::OmpxHold); | |
| std::optional<Map::MapTypeModifiers> maybeTypeMods{}; | |
| if (!modSet.empty()) | |
| maybeTypeMods = Map::MapTypeModifiers(modSet.begin(), modSet.end()); | |
| auto attachMod = [&]() -> std::optional<Map::AttachModifier> { | |
| if (auto *t = | |
| semantics::OmpGetUniqueModifier<parser::OmpAttachModifier>(mods)) | |
| return convertAttachMod(t->v); | |
| return std::nullopt; | |
| }(); | |
| auto refMod = [&]() -> std::optional<Map::RefModifier> { | |
| if (auto *t = semantics::OmpGetUniqueModifier<parser::OmpRefModifier>(mods)) | |
| return convertRefMod(t->v); | |
| return std::nullopt; | |
| }(); | |
| auto mappers = [&]() -> std::optional<List<Mapper>> { | |
| if (auto *t = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods)) | |
| return List<Mapper>{Mapper{makeObject(t->v, semaCtx)}}; | |
| return std::nullopt; | |
| }(); | |
| auto iterator = [&]() -> std::optional<Iterator> { | |
| if (auto *t = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods)) | |
| return makeIterator(*t, semaCtx); | |
| return std::nullopt; | |
| }(); | |
| return Map{{/*MapType=*/std::move(type), | |
| /*MapTypeModifiers=*/std::move(maybeTypeMods), | |
| /*AttachModifier=*/std::move(attachMod), | |
| /*RefModifier=*/std::move(refMod), /*Mapper=*/std::move(mappers), | |
| /*Iterator=*/std::move(iterator), | |
| /*LocatorList=*/makeObjects(t2, semaCtx)}}; | |
| } | |
| Match make(const parser::OmpClause::Match &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| return Match{}; | |
| } | |
| // MemoryOrder: empty | |
| // Mergeable: empty | |
| Message make(const parser::OmpClause::Message &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> OmpMessageClause | |
| return Message{/*MsgString=*/makeExpr(inp.v.v, semaCtx)}; | |
| } | |
| 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 -> parser::OmpObjectList | |
| return Nontemporal{/*List=*/makeObjects(inp.v, semaCtx)}; | |
| } | |
| // NoOpenmp: empty | |
| // NoOpenmpRoutines: empty | |
| // NoOpenmpConstructs: 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::OmpNumTasksClause | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpPrescriptiveness>(mods); | |
| auto &t1 = std::get<parser::ScalarIntExpr>(inp.v.t); | |
| return NumTasks{{/*Prescriptiveness=*/maybeApplyToV(makePrescriptiveness, m0), | |
| /*NumTasks=*/makeExpr(t1, semaCtx)}}; | |
| } | |
| NumTeams make(const parser::OmpClause::NumTeams &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpNumTeamsClause | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *lowerBound = | |
| semantics::OmpGetUniqueModifier<parser::OmpLowerBound>(mods); | |
| auto &values = std::get<std::list<parser::ScalarIntExpr>>(inp.v.t); | |
| assert(!values.empty()); | |
| // Extract optional lower bound (only valid without dims modifier) | |
| auto lb = maybeApplyToV(makeExprFn(semaCtx), lowerBound); | |
| // Extract all upper bounds | |
| NumTeams::UpperBoundList upperBounds; | |
| for (const auto &val : values) { | |
| upperBounds.push_back(makeExpr(val, semaCtx)); | |
| } | |
| return NumTeams{ | |
| {/*LowerBound=*/lb, /*UpperBoundList=*/std::move(upperBounds)}}; | |
| } | |
| NumThreads make(const parser::OmpClause::NumThreads &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpNumThreadsClause | |
| // With dims modifier (OpenMP 6.1): multiple values | |
| // Without dims modifier: single value | |
| auto &values = std::get<std::list<parser::ScalarIntExpr>>(inp.v.t); | |
| assert(!values.empty()); | |
| List<NumThreads::Nthreads> v; | |
| for (const auto &val : values) { | |
| v.push_back(makeExpr(val, semaCtx)); | |
| } | |
| return NumThreads{/*Nthreads=*/v}; | |
| } | |
| // 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::Value, Order::OrderModifier, | |
| // clang-format off | |
| MS(Reproducible, Reproducible) | |
| MS(Unconstrained, Unconstrained) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convert2, wrapped::Ordering, Order::Ordering, | |
| // clang-format off | |
| MS(Concurrent, Concurrent) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpOrderModifier>(mods); | |
| auto &t1 = std::get<wrapped::Ordering>(inp.v.t); | |
| return Order{{/*OrderModifier=*/maybeApplyToV(convert1, 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)}; | |
| } | |
| // See also Default. | |
| Otherwise make(const parser::OmpClause::Otherwise &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| return Otherwise{}; | |
| } | |
| 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::AffinityPolicy, 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 | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, parser::OmpReductionModifier::Value, | |
| Reduction::ReductionModifier, | |
| // clang-format off | |
| MS(Inscan, Inscan) | |
| MS(Task, Task) | |
| MS(Default, Default) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = | |
| semantics::OmpGetUniqueModifier<parser::OmpReductionModifier>(mods); | |
| auto *m1 = | |
| semantics::OmpGetUniqueModifier<parser::OmpReductionIdentifier>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); | |
| assert(m1 && "OmpReductionIdentifier is required"); | |
| return Reduction{ | |
| {/*ReductionModifier=*/maybeApplyToV(convert, m0), | |
| /*ReductionIdentifiers=*/{makeReductionOperator(*m1, semaCtx)}, | |
| /*List=*/makeObjects(t1, semaCtx)}}; | |
| } | |
| // Relaxed: empty | |
| // Release: empty | |
| ReverseOffload make(const parser::OmpClause::ReverseOffload &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> std::optional<parser::OmpReverseOffloadClause> | |
| auto &&maybeRequired = maybeApply( | |
| [&](const parser::OmpReverseOffloadClause &c) { | |
| return makeExpr(c.v, semaCtx); | |
| }, | |
| inp.v); | |
| return ReverseOffload{/*Required=*/std::move(maybeRequired)}; | |
| } | |
| 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::Kind, 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::OmpOrderingModifier::Value, Schedule::OrderingModifier, | |
| // clang-format off | |
| MS(Monotonic, Monotonic) | |
| MS(Nonmonotonic, Nonmonotonic) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convert3, parser::OmpChunkModifier::Value, Schedule::ChunkModifier, | |
| // clang-format off | |
| MS(Simd, Simd) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpOrderingModifier>(mods); | |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpChunkModifier>(mods); | |
| auto &t2 = std::get<wrapped::Kind>(inp.v.t); | |
| auto &t3 = std::get<std::optional<parser::ScalarIntExpr>>(inp.v.t); | |
| return Schedule{{/*Kind=*/convert1(t2), | |
| /*OrderingModifier=*/maybeApplyToV(convert2, t0), | |
| /*ChunkModifier=*/maybeApplyToV(convert3, t1), | |
| /*ChunkSize=*/maybeApply(makeExprFn(semaCtx), t3)}}; | |
| } | |
| // SeqCst: empty | |
| SelfMaps make(const parser::OmpClause::SelfMaps &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> std::optional<parser::OmpSelfMapsClause> | |
| auto &&maybeRequired = maybeApply( | |
| [&](const parser::OmpSelfMapsClause &c) { | |
| return makeExpr(c.v, semaCtx); | |
| }, | |
| inp.v); | |
| return SelfMaps{/*Required=*/std::move(maybeRequired)}; | |
| } | |
| Severity make(const parser::OmpClause::Severity &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> OmpSeverityClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convertSevLevel, parser::OmpSeverityClause::SevLevel, Severity::SevLevel, | |
| // clang-format off | |
| MS(Fatal, Fatal) | |
| MS(Warning, Warning) | |
| // clang-format om | |
| ); | |
| return Severity{/*SevLevel=*/convertSevLevel(inp.v.v)}; | |
| } | |
| 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 &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *m0 = | |
| semantics::OmpGetUniqueModifier<parser::OmpReductionIdentifier>(mods); | |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); | |
| assert(m0 && "OmpReductionIdentifier is required"); | |
| return TaskReduction{ | |
| {/*ReductionIdentifiers=*/{makeReductionOperator(*m0, semaCtx)}, | |
| /*List=*/makeObjects(t1, semaCtx)}}; | |
| } | |
| ThreadLimit make(const parser::OmpClause::ThreadLimit &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpThreadLimitClause | |
| // With dims modifier: multiple values | |
| // Without dims modifier: single value | |
| auto &values = std::get<std::list<parser::ScalarIntExpr>>(inp.v.t); | |
| assert(!values.empty()); | |
| List<ThreadLimit::Threadlim> v; | |
| for (const auto &val : values) { | |
| v.push_back(makeExpr(val, semaCtx)); | |
| } | |
| return ThreadLimit{/*Threadlim=*/std::move(v)}; | |
| } | |
| Threadset make(const parser::OmpClause::Threadset &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpThreadsetClause | |
| using wrapped = parser::OmpThreadsetClause; | |
| CLAUSET_ENUM_CONVERT( // | |
| convert, wrapped::ThreadsetPolicy, Threadset::ThreadsetPolicy, | |
| // clang-format off | |
| MS(Omp_Pool, Omp_Pool) | |
| MS(Omp_Team, Omp_Team) | |
| // clang-format on | |
| ); | |
| return Threadset{/*ThreadsetPolicy=*/convert(inp.v.v)}; | |
| } | |
| // Threadprivate: empty | |
| // Threads: empty | |
| To make(const parser::OmpClause::To &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpToClause | |
| CLAUSET_ENUM_CONVERT( // | |
| convertMot, parser::OmpMotionModifier::Value, To::Expectation, | |
| // clang-format off | |
| MS(Present, Present) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convertExp, parser::OmpExpectation::Value, To::Expectation, | |
| // clang-format off | |
| MS(Present, Present) | |
| // clang-format on | |
| ); | |
| CLAUSET_ENUM_CONVERT( // | |
| convertPre, parser::OmpPresentModifier::Value, To::Expectation, | |
| // clang-format off | |
| MS(Present, Present) | |
| // clang-format on | |
| ); | |
| auto &mods = semantics::OmpGetModifiers(inp.v); | |
| auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpMotionModifier>(mods); | |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpExpectation>(mods); | |
| auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpPresentModifier>(mods); | |
| auto *t3 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods); | |
| auto *t4 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); | |
| auto &t5 = std::get<parser::OmpObjectList>(inp.v.t); | |
| std::optional<To::Expectation> maybeExp = // | |
| t0 ? maybeApplyToV(convertMot, t0) | |
| : t1 ? maybeApplyToV(convertExp, t1) | |
| : t2 ? maybeApplyToV(convertPre, t2) | |
| : std::optional<To::Expectation>{}; | |
| auto mappers = [&]() -> std::optional<List<Mapper>> { | |
| if (t3) | |
| return List<Mapper>{Mapper{makeObject(t3->v, semaCtx)}}; | |
| return std::nullopt; | |
| }(); | |
| auto iterator = [&]() -> std::optional<Iterator> { | |
| if (t4) | |
| return makeIterator(*t4, semaCtx); | |
| return std::nullopt; | |
| }(); | |
| return To{{/*Expectation=*/maybeExp, | |
| /*Mappers=*/{std::move(mappers)}, | |
| /*Iterator=*/std::move(iterator), | |
| /*LocatorList=*/makeObjects(t5, semaCtx)}}; | |
| } | |
| UnifiedAddress make(const parser::OmpClause::UnifiedAddress &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> std::optional<parser::OmpUnifiedAddressClause> | |
| auto &&maybeRequired = maybeApply( | |
| [&](const parser::OmpUnifiedAddressClause &c) { | |
| return makeExpr(c.v, semaCtx); | |
| }, | |
| inp.v); | |
| return UnifiedAddress{/*Required=*/std::move(maybeRequired)}; | |
| } | |
| UnifiedSharedMemory make(const parser::OmpClause::UnifiedSharedMemory &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> std::optional<parser::OmpUnifiedSharedMemoryClause> | |
| auto &&maybeRequired = maybeApply( | |
| [&](const parser::OmpUnifiedSharedMemoryClause &c) { | |
| return makeExpr(c.v, semaCtx); | |
| }, | |
| inp.v); | |
| return UnifiedSharedMemory{/*Required=*/std::move(maybeRequired)}; | |
| } | |
| 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 | |
| UpdateDependObjects make(const parser::OmpClause::UpdateDependObjects &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> parser::OmpUpdateDependObjectsClause | |
| return common::visit( | |
| [](auto &&s) { | |
| return UpdateDependObjects{/*DependenceType=*/makeDepType(s)}; | |
| }, | |
| inp.v.u); | |
| } | |
| Use make(const parser::OmpClause::Use &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| // inp.v -> OmpUseClause | |
| return Use{/*InteropVar=*/makeObject(inp.v.v, semaCtx)}; | |
| } | |
| 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.v -> parser::OmpUsesAllocatorsClause | |
| using AllocatorSpec = parser::OmpUsesAllocatorsClause::AllocatorSpec; | |
| auto makeSpec = [&](const AllocatorSpec &spec) { | |
| auto &mods = semantics::OmpGetModifiers(spec); | |
| auto *memSpace = semantics::OmpGetUniqueModifier<parser::OmpMemSpace>(mods); | |
| auto *traits = | |
| semantics::OmpGetUniqueModifier<parser::OmpTraitsArray>(mods); | |
| std::optional<UsesAllocators::TraitsArray> traitsArray; | |
| if (traits) { | |
| // Semantics has verified that the traits array is a named constant. | |
| const parser::Name *name{parser::Unwrap<parser::Name>(traits->v.value())}; | |
| traitsArray = makeObject(DEREF(name), semaCtx); | |
| } | |
| return UsesAllocators::AllocatorSpec{ | |
| {/*MemSpace=*/maybeApplyToV(makeExprFn(semaCtx), memSpace), | |
| /*TraitsArray=*/std::move(traitsArray), | |
| /*Allocator=*/ | |
| makeExpr(std::get<parser::ScalarIntExpr>(spec.t), semaCtx)}}; | |
| }; | |
| return UsesAllocators{/*Allocators=*/makeList(inp.v.v, makeSpec)}; | |
| } | |
| // Weak: empty | |
| When make(const parser::OmpClause::When &inp, | |
| semantics::SemanticsContext &semaCtx) { | |
| return When{}; | |
| } | |
| // Write: empty | |
| } // namespace clause | |
| Clause makeClause(const parser::OmpClause &cls, | |
| semantics::SemanticsContext &semaCtx) { | |
| return Fortran::common::visit( // | |
| common::visitors{ | |
| [&](const parser::OmpClause::DefaultVariant &s) { | |
| return makeClause(llvm::omp::Clause::OMPC_default_variant, | |
| clause::makeOtherwise(s, semaCtx), cls.source); | |
| }, | |
| [&](const parser::OmpClause::Depend &s) { | |
| using TaskDep = parser::OmpDependClause::TaskDep; | |
| if (auto *dep = std::get_if<TaskDep>(&s.v.u)) { | |
| return makeClause(llvm::omp::Clause::OMPC_depend, | |
| clause::makeDepend(*dep, semaCtx), cls.source); | |
| } else if (auto *doa = std::get_if<parser::OmpDoacross>(&s.v.u)) { | |
| // depend(source) / depend(sink:) on ordered is the | |
| // 4.5 - 5.1 spelling of what 5.2 renamed to the doacross | |
| // clause. Represent it as OMPC_depend (the surface clause is | |
| // depend) rather than rewriting to OMPC_doacross, otherwise | |
| // construct decomposition rejects the clause at OpenMP < 5.2 | |
| // even though the construct itself is valid since 4.5. | |
| return makeClause(llvm::omp::Clause::OMPC_depend, | |
| clause::makeDependDoacross(*doa, semaCtx), | |
| cls.source); | |
| } else { | |
| llvm_unreachable("Unexpected alternative"); | |
| } | |
| }, | |
| [&](auto &&s) { | |
| return makeClause(cls.Id(), 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 |