//===-- 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.h" #include "flang/Semantics/expression.h" #include "flang/Semantics/openmp-modifiers.h" #include "flang/Semantics/symbol.h" #include "llvm/Frontend/OpenMP/OMPConstants.h" #include #include #include #include #include namespace Fortran::lower::omp { using SymbolWithDesignator = std::tuple; struct SymbolAndDesignatorExtractor { template static T &&AsRvalueRef(T &&t) { return std::move(t); } template 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(&ref.get()); } template static SymbolWithDesignator visit(T &&) { // Use this to see missing overloads: // llvm::errs() << "NULL: " << __PRETTY_FUNCTION__ << '\n'; return SymbolWithDesignator{}; } template static SymbolWithDesignator visit(const evaluate::Designator &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 static SymbolWithDesignator visit(const evaluate::Expr &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> &maybeDsg = std::get<1>(sd); if (!maybeDsg) return; // Symbol with no designator -> OK assert(symbol && "Expecting symbol"); std::optional maybeRef = evaluate::ExtractDataRef( *maybeDsg, /*intoSubstring=*/true, /*intoComplexPart=*/true); if (maybeRef) { if (&maybeRef->GetLastSymbol() == symbol) return; // Symbol with a designator for it -> OK llvm_unreachable("Expecting designator for given symbol"); } else { #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(&object.u)) { assert(name->symbol && "Expecting Symbol"); return Object{name->symbol, std::nullopt}; } // OmpObject is std::variant; return makeObject(std::get(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(&locator.u)) { return makeObject(*object, semaCtx); } llvm_unreachable("Expecting object"); }, [](auto &&s) -> Object { // llvm_unreachable("Expecting object"); }, }, arg.u); }); } std::optional 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(&ref.u)) { return std::nullopt; } else if (auto *comp = std::get_if(&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(&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(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 makeIteratorSpecifiers(const parser::OmpIteratorSpecifier &inp, semantics::SemanticsContext &semaCtx) { List specifiers; auto &[begin, end, step] = std::get(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(inp.t); auto &entities = std::get>(tds.t); for (const parser::EntityDecl &ed : entities) { auto &name = std::get(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(mods); auto &t1 = std::get(inp.v.t); auto &&maybeIter = m0 ? makeIterator(*m0, semaCtx) : std::optional{}; 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(inp.v.t); auto *m1 = semantics::OmpGetUniqueModifier(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(mods); auto *m1 = semantics::OmpGetUniqueModifier( mods); auto *m2 = semantics::OmpGetUniqueModifier(mods); auto &t1 = std::get(inp.v.t); auto makeAllocator = [&](auto *mod) -> std::optional { 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(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(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, 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(inp.v.t); auto *t1 = semantics::OmpGetUniqueModifier(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(v.t); auto &t1 = std::get>(v.t); auto convert3 = [&](const IterOffset &u) { auto &s0 = std::get(u.t); auto &s1 = std::get(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(mods); auto *m1 = semantics::OmpGetUniqueModifier(mods); auto &t1 = std::get(s.t); assert(m1 && "expecting task dependence type"); auto &&maybeIter = m0 ? makeIterator(*m0, semaCtx) : std::optional{}; 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 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(mods); auto &t1 = std::get(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 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(mods); auto *t1 = semantics::OmpGetUniqueModifier(mods); auto *t2 = semantics::OmpGetUniqueModifier(mods); auto &t3 = std::get(inp.v.t); auto mappers = [&]() -> std::optional> { if (t1) return List{Mapper{makeObject(t1->v, semaCtx)}}; return std::nullopt; }(); auto iterator = [&]() -> std::optional { 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(mods); auto &t1 = std::get(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(mods); auto &t1 = std::get(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 return Indirect{maybeApply(makeExprFn(semaCtx), inp.v.v)}; } 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(mods); auto &t1 = std::get(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(mods); auto &t1 = std::get(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(mods); auto *m1 = semantics::OmpGetUniqueModifier(mods); assert((!m0 || !m1) && "Simple and complex modifiers both present"); auto *m2 = semantics::OmpGetUniqueModifier(mods); auto &t1 = std::get(inp.v.t); auto &&maybeStep = m0 ? maybeApplyToV(makeExprFn(semaCtx), m0) : m1 ? maybeApplyToV(makeExprFn(semaCtx), m1) : std::optional{}; 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( // 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(Ompx_Hold, OmpxHold) MS(Present, Present) // 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(mods); auto &t2 = std::get(inp.v.t); auto type = [&]() -> std::optional { if (t1) return convertMapType(t1->v); return std::nullopt; }(); llvm::DenseSet modSet; if (t1 && t1->v == parser::OmpMapType::Value::Delete) modSet.insert(Map::MapTypeModifier::Delete); for (auto *typeMod : semantics::OmpGetRepeatableModifier(mods)) { modSet.insert(convertMapTypeMod(typeMod->v)); } if (semantics::OmpGetUniqueModifier(mods)) modSet.insert(Map::MapTypeModifier::Always); if (semantics::OmpGetUniqueModifier(mods)) modSet.insert(Map::MapTypeModifier::Close); if (semantics::OmpGetUniqueModifier(mods)) modSet.insert(Map::MapTypeModifier::Delete); if (semantics::OmpGetUniqueModifier(mods)) modSet.insert(Map::MapTypeModifier::Present); if (semantics::OmpGetUniqueModifier(mods)) modSet.insert(Map::MapTypeModifier::Self); if (semantics::OmpGetUniqueModifier(mods)) modSet.insert(Map::MapTypeModifier::OmpxHold); std::optional maybeTypeMods{}; if (!modSet.empty()) maybeTypeMods = Map::MapTypeModifiers(modSet.begin(), modSet.end()); auto refMod = [&]() -> std::optional { if (auto *t = semantics::OmpGetUniqueModifier(mods)) return convertRefMod(t->v); return std::nullopt; }(); auto mappers = [&]() -> std::optional> { if (auto *t = semantics::OmpGetUniqueModifier(mods)) return List{Mapper{makeObject(t->v, semaCtx)}}; return std::nullopt; }(); auto iterator = [&]() -> std::optional { if (auto *t = semantics::OmpGetUniqueModifier(mods)) return makeIterator(*t, semaCtx); return std::nullopt; }(); return Map{{/*MapType=*/std::move(type), /*MapTypeModifiers=*/std::move(maybeTypeMods), /*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 -> 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 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(mods); auto &t1 = std::get(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 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(mods); auto &t1 = std::get(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 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 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(mods); auto *m1 = semantics::OmpGetUniqueModifier(mods); auto &t1 = std::get(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(mods); auto *t1 = semantics::OmpGetUniqueModifier(mods); auto &t2 = std::get(inp.v.t); auto &t3 = std::get>(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 return Sizes{/*SizeList=*/makeList(inp.v, makeExprFn(semaCtx))}; } Permutation make(const parser::OmpClause::Permutation &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> std::list 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(mods); auto &t1 = std::get(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(mods); auto *t1 = semantics::OmpGetUniqueModifier(mods); auto *t2 = semantics::OmpGetUniqueModifier(mods); auto &t3 = std::get(inp.v.t); auto mappers = [&]() -> std::optional> { if (t1) return List{Mapper{makeObject(t1->v, semaCtx)}}; return std::nullopt; }(); auto iterator = [&]() -> std::optional { 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 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 if (inp.v) { return common::visit( [](auto &&s) { return Update{/*DependenceType=*/makeDepType(s)}; }, inp.v->u); } else { return Update{/*DependenceType=*/std::nullopt}; } } Use make(const parser::OmpClause::Use &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: use"); } UseDeviceAddr make(const parser::OmpClause::UseDeviceAddr &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return UseDeviceAddr{/*List=*/makeObjects(inp.v, semaCtx)}; } UseDevicePtr make(const parser::OmpClause::UseDevicePtr &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return UseDevicePtr{/*List=*/makeObjects(inp.v, semaCtx)}; } UsesAllocators make(const parser::OmpClause::UsesAllocators &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: uses_allocators"); } // Weak: empty When 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(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 makeClauses(const parser::OmpClauseList &clauses, semantics::SemanticsContext &semaCtx) { return makeList(clauses.v, [&](const parser::OmpClause &s) { return makeClause(s, semaCtx); }); } bool transferLocations(const List &from, List &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