| //===-- 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/Optimizer/Builder/Todo.h" |
| #include "flang/Parser/parse-tree.h" |
| #include "flang/Semantics/expression.h" |
| #include "flang/Semantics/openmp-modifiers.h" |
| #include "flang/Semantics/symbol.h" |
| |
| #include "llvm/Frontend/OpenMP/OMPConstants.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))); |
| } |
| |
| 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); |
| } |
| |
| ObjectList makeObjects(const parser::OmpArgumentList &objects, |
| semantics::SemanticsContext &semaCtx) { |
| return makeList(objects.v, [&](const parser::OmpArgument &arg) { |
| return common::visit( |
| common::visitors{ |
| [&](const parser::OmpLocator &locator) -> Object { |
| if (auto *object = std::get_if<parser::OmpObject>(&locator.u)) { |
| return makeObject(*object, semaCtx); |
| } |
| llvm_unreachable("Expecting object"); |
| }, |
| [](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; |
| } |
| |
| // 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(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(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(SelfMaps, SelfMaps); |
| 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(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); |
| |
| 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 &name = std::get<parser::ObjectName>(ed.t); |
| assert(name.symbol && "Expecting symbol for iterator variable"); |
| auto *stype = name.symbol->GetType(); |
| assert(stype && "Expecting symbol type"); |
| IteratorSpecifier spec{{evaluate::DynamicType::From(*stype), |
| makeObject(name, 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; |
| } |
| |
| 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>{}; |
| |
| return Affinity{{/*Iterator=*/std::move(maybeIter), |
| /*LocatorList=*/makeObjects(t1, semaCtx)}}; |
| } |
| |
| 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 &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 -> 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::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)}; |
| } |
| |
| // 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 overloaded in OpenMP 5.0 and 5.1: it can be either |
| // a data-sharing clause, or a METADIRECTIVE clause. In the latter case, it |
| // has been superseded by the OTHERWISE clause. |
| // Disambiguate this in this representation: for the DSA case, create Default, |
| // and in the other case create Otherwise. |
| Default makeDefault(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 |
| ); |
| |
| auto dsa = std::get<wrapped::DataSharingAttribute>(inp.v.u); |
| return Default{/*DataSharingAttribute=*/convert(dsa)}; |
| } |
| |
| Otherwise makeOtherwise(const parser::OmpClause::Default &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) missing-in-parser |
| // 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 visitSource = [&](const parser::OmpDoacross::Source &) { |
| return Doacross{{/*DependenceType=*/Doacross::DependenceType::Source, |
| /*Vector=*/{}}}; |
| }; |
| |
| auto visitSink = [&](const parser::OmpDoacross::Sink &s) { |
| 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)}}; |
| }; |
| return Doacross{{/*DependenceType=*/Doacross::DependenceType::Sink, |
| /*Vector=*/makeList(s.v.v, convert2)}}; |
| }; |
| |
| return common::visit(common::visitors{visitSink, visitSource}, doa.u); |
| } |
| |
| Depend make(const parser::OmpClause::Depend &inp, |
| semantics::SemanticsContext &semaCtx) { |
| // inp.v -> parser::OmpDependClause |
| using wrapped = parser::OmpDependClause; |
| using Variant = decltype(Depend::u); |
| |
| auto visitTaskDep = [&](const wrapped::TaskDep &s) -> Variant { |
| auto &mods = semantics::OmpGetModifiers(s); |
| auto *m0 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); |
| auto *m1 = |
| semantics::OmpGetUniqueModifier<parser::OmpTaskDependenceType>(mods); |
| auto &t1 = std::get<parser::OmpObjectList>(s.t); |
| assert(m1 && "expecting task dependence type"); |
| |
| auto &&maybeIter = |
| m0 ? makeIterator(*m0, semaCtx) : std::optional<Iterator>{}; |
| return Depend::TaskDep{{/*DependenceType=*/makeDepType(*m1), |
| /*Iterator=*/std::move(maybeIter), |
| /*LocatorList=*/makeObjects(t1, semaCtx)}}; |
| }; |
| |
| return Depend{common::visit( // |
| common::visitors{ |
| // Doacross |
| [&](const parser::OmpDoacross &s) -> Variant { |
| return makeDoacross(s, semaCtx); |
| }, |
| // Depend::TaskDep |
| visitTaskDep, |
| }, |
| inp.v.u)}; |
| } |
| |
| // Depobj: empty |
| |
| 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)}}; |
| } |
| |
| 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: 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.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( // |
| convert, parser::OmpExpectation::Value, From::Expectation, |
| // clang-format off |
| MS(Present, Present) |
| // clang-format on |
| ); |
| |
| auto &mods = semantics::OmpGetModifiers(inp.v); |
| auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpExpectation>(mods); |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods); |
| auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); |
| auto &t3 = std::get<parser::OmpObjectList>(inp.v.t); |
| |
| auto mappers = [&]() -> std::optional<List<Mapper>> { |
| if (t1) |
| return List<Mapper>{Mapper{makeObject(t1->v, semaCtx)}}; |
| return std::nullopt; |
| }(); |
| |
| auto iterator = [&]() -> std::optional<Iterator> { |
| if (t2) |
| return makeIterator(*t2, semaCtx); |
| return std::nullopt; |
| }(); |
| |
| return From{{/*Expectation=*/maybeApplyToV(convert, t0), |
| /*Mappers=*/std::move(mappers), |
| /*Iterator=*/std::move(iterator), |
| /*LocatorList=*/makeObjects(t3, 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 -> empty |
| llvm_unreachable("Empty: indirect"); |
| } |
| |
| Init make(const parser::OmpClause::Init &inp, |
| semantics::SemanticsContext &semaCtx) { |
| // inp -> empty |
| llvm_unreachable("Empty: init"); |
| } |
| |
| Initializer make(const parser::OmpClause::Initializer &inp, |
| semantics::SemanticsContext &semaCtx) { |
| llvm_unreachable("Empty: 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::OmpLinearModifier>(mods); |
| auto &t1 = std::get<parser::OmpObjectList>(inp.v.t); |
| |
| auto &&maybeStep = m0 ? maybeApplyToV(makeExprFn(semaCtx), m0) |
| : m1 ? maybeApplyToV(makeExprFn(semaCtx), m1) |
| : std::optional<Linear::StepComplexModifier>{}; |
| |
| return Linear{{/*StepComplexModifier=*/std::move(maybeStep), |
| /*LinearModifier=*/maybeApplyToV(convert, m2), |
| /*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)}; |
| } |
| |
| Map make(const parser::OmpClause::Map &inp, |
| semantics::SemanticsContext &semaCtx) { |
| // inp.v -> parser::OmpMapClause |
| CLAUSET_ENUM_CONVERT( // |
| convert1, parser::OmpMapType::Value, 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::OmpMapTypeModifier::Value, Map::MapTypeModifier, |
| // clang-format off |
| MS(Always, Always) |
| MS(Close, Close) |
| MS(Ompx_Hold, OmpxHold) |
| MS(Present, Present) |
| // clang-format on |
| ); |
| |
| auto &mods = semantics::OmpGetModifiers(inp.v); |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods); |
| auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); |
| auto *t3 = semantics::OmpGetUniqueModifier<parser::OmpMapType>(mods); |
| auto &t4 = std::get<parser::OmpObjectList>(inp.v.t); |
| |
| auto mappers = [&]() -> std::optional<List<Mapper>> { |
| if (t1) |
| return List<Mapper>{Mapper{makeObject(t1->v, semaCtx)}}; |
| return std::nullopt; |
| }(); |
| |
| auto iterator = [&]() -> std::optional<Iterator> { |
| if (t2) |
| return makeIterator(*t2, semaCtx); |
| return std::nullopt; |
| }(); |
| |
| auto type = [&]() -> std::optional<Map::MapType> { |
| if (t3) |
| return convert1(t3->v); |
| return Map::MapType::Tofrom; |
| }(); |
| |
| Map::MapTypeModifiers typeMods; |
| for (auto *typeMod : |
| semantics::OmpGetRepeatableModifier<parser::OmpMapTypeModifier>(mods)) { |
| typeMods.push_back(convert2(typeMod->v)); |
| } |
| std::optional<Map::MapTypeModifiers> maybeTypeMods{}; |
| if (!typeMods.empty()) |
| maybeTypeMods = std::move(typeMods); |
| |
| return Map{{/*MapType=*/std::move(type), |
| /*MapTypeModifiers=*/std::move(maybeTypeMods), |
| /*Mapper=*/std::move(mappers), /*Iterator=*/std::move(iterator), |
| /*LocatorList=*/makeObjects(t4, 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 -> 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 |
| // 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::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::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: 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::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 |
| |
| 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 &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::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::OmpToClause |
| CLAUSET_ENUM_CONVERT( // |
| convert, parser::OmpExpectation::Value, To::Expectation, |
| // clang-format off |
| MS(Present, Present) |
| // clang-format on |
| ); |
| |
| auto &mods = semantics::OmpGetModifiers(inp.v); |
| auto *t0 = semantics::OmpGetUniqueModifier<parser::OmpExpectation>(mods); |
| auto *t1 = semantics::OmpGetUniqueModifier<parser::OmpMapper>(mods); |
| auto *t2 = semantics::OmpGetUniqueModifier<parser::OmpIterator>(mods); |
| auto &t3 = std::get<parser::OmpObjectList>(inp.v.t); |
| |
| auto mappers = [&]() -> std::optional<List<Mapper>> { |
| if (t1) |
| return List<Mapper>{Mapper{makeObject(t1->v, semaCtx)}}; |
| return std::nullopt; |
| }(); |
| |
| auto iterator = [&]() -> std::optional<Iterator> { |
| if (t2) |
| return makeIterator(*t2, semaCtx); |
| return std::nullopt; |
| }(); |
| |
| return To{{/*Expectation=*/maybeApplyToV(convert, t0), |
| /*Mappers=*/{std::move(mappers)}, |
| /*Iterator=*/std::move(iterator), |
| /*LocatorList=*/makeObjects(t3, 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.v -> parser::OmpUpdateClause |
| auto depType = |
| common::visit([](auto &&s) { return makeDepType(s); }, inp.v.u); |
| return Update{/*DependenceType=*/depType}; |
| } |
| |
| 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 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::Default &s) { |
| using DSA = parser::OmpDefaultClause::DataSharingAttribute; |
| if (std::holds_alternative<DSA>(s.v.u)) { |
| return makeClause(llvm::omp::Clause::OMPC_default, |
| clause::makeDefault(s, semaCtx), cls.source); |
| } else { |
| return makeClause(llvm::omp::Clause::OMPC_otherwise, |
| clause::makeOtherwise(s, semaCtx), cls.source); |
| } |
| }, |
| [&](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 |