//===-- Clauses.cpp -- OpenMP clause handling -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Clauses.h" #include "flang/Common/idioms.h" #include "flang/Evaluate/expression.h" #include "flang/Parser/parse-tree.h" #include "flang/Semantics/expression.h" #include "flang/Semantics/symbol.h" #include "llvm/Frontend/OpenMP/OMPConstants.h" #include #include #include #include #include namespace detail { template llvm::omp::Clause getClauseIdForClass(C &&) { using namespace Fortran; using A = llvm::remove_cvref_t; // A is referenced in OMP.inc // The code included below contains a sequence of checks like the following // for each OpenMP clause // if constexpr (std::is_same_v) // return llvm::omp::Clause::OMPC_acq_rel; // [...] #define GEN_FLANG_CLAUSE_PARSER_KIND_MAP #include "llvm/Frontend/OpenMP/OMP.inc" } } // namespace detail static llvm::omp::Clause getClauseId(const Fortran::parser::OmpClause &clause) { return std::visit([](auto &&s) { return detail::getClauseIdForClass(s); }, clause.u); } 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 std::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 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 std::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); } std::optional getBaseObject(const Object &object, Fortran::semantics::SemanticsContext &semaCtx) { // If it's just the symbol, then there is no base. if (!object.id()) 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 (base.UnwrapSymbolRef()) { 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, ); // missing-in-parser // MAKE_EMPTY_CLASS(NoOpenmpRoutines, ); // missing-in-parser // MAKE_EMPTY_CLASS(NoParallelism, ); // missing-in-parser MAKE_EMPTY_CLASS(Notinbranch, Notinbranch); MAKE_EMPTY_CLASS(Nowait, Nowait); MAKE_EMPTY_CLASS(OmpxAttribute, OmpxAttribute); MAKE_EMPTY_CLASS(OmpxBare, OmpxBare); MAKE_EMPTY_CLASS(Read, Read); MAKE_EMPTY_CLASS(Relaxed, Relaxed); MAKE_EMPTY_CLASS(Release, Release); MAKE_EMPTY_CLASS(ReverseOffload, ReverseOffload); MAKE_EMPTY_CLASS(SeqCst, SeqCst); MAKE_EMPTY_CLASS(Simd, Simd); MAKE_EMPTY_CLASS(Threads, Threads); MAKE_EMPTY_CLASS(UnifiedAddress, UnifiedAddress); MAKE_EMPTY_CLASS(UnifiedSharedMemory, UnifiedSharedMemory); MAKE_EMPTY_CLASS(Unknown, Unknown); MAKE_EMPTY_CLASS(Untied, Untied); MAKE_EMPTY_CLASS(Weak, Weak); MAKE_EMPTY_CLASS(Write, Write); // Artificial clauses MAKE_EMPTY_CLASS(CancellationConstructType, CancellationConstructType); MAKE_EMPTY_CLASS(Depobj, Depobj); MAKE_EMPTY_CLASS(Flush, Flush); MAKE_EMPTY_CLASS(MemoryOrder, MemoryOrder); MAKE_EMPTY_CLASS(Threadprivate, Threadprivate); MAKE_INCOMPLETE_CLASS(AdjustArgs, AdjustArgs); MAKE_INCOMPLETE_CLASS(AppendArgs, AppendArgs); MAKE_INCOMPLETE_CLASS(Match, Match); // MAKE_INCOMPLETE_CLASS(Otherwise, ); // missing-in-parser MAKE_INCOMPLETE_CLASS(When, When); DefinedOperator makeDefinedOperator(const parser::DefinedOperator &inp, semantics::SemanticsContext &semaCtx) { CLAUSET_ENUM_CONVERT( // convert, parser::DefinedOperator::IntrinsicOperator, DefinedOperator::IntrinsicOperator, // clang-format off MS(Add, Add) MS(AND, AND) MS(Concat, Concat) MS(Divide, Divide) MS(EQ, EQ) MS(EQV, EQV) MS(GE, GE) MS(GT, GT) MS(NOT, NOT) MS(LE, LE) MS(LT, LT) MS(Multiply, Multiply) MS(NE, NE) MS(NEQV, NEQV) MS(OR, OR) MS(Power, Power) MS(Subtract, Subtract) // clang-format on ); return std::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{std::visit( common::visitors{ [&](const parser::Name &t) { return makeObject(t, semaCtx); }, [&](const parser::ProcComponentRef &t) { return makeObject(t.v.thing, semaCtx); }, }, inp.u)}; } ReductionOperator makeReductionOperator(const parser::OmpReductionOperator &inp, semantics::SemanticsContext &semaCtx) { return std::visit( common::visitors{ [&](const parser::DefinedOperator &s) { return ReductionOperator{makeDefinedOperator(s, semaCtx)}; }, [&](const parser::ProcedureDesignator &s) { return ReductionOperator{makeProcedureDesignator(s, semaCtx)}; }, }, inp.u); } // -------------------------------------------------------------------- // Actual clauses. Each T (where tomp::T exists in ClauseT) has its "make". // Absent: missing-in-parser // AcqRel: empty // Acquire: empty // AdjustArgs: incomplete Affinity make(const parser::OmpClause::Affinity &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: affinity"); } Align make(const parser::OmpClause::Align &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: align"); } Aligned make(const parser::OmpClause::Aligned &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpAlignedClause auto &t0 = std::get(inp.v.t); auto &t1 = std::get>(inp.v.t); return Aligned{{ /*Alignment=*/maybeApply(makeExprFn(semaCtx), t1), /*List=*/makeObjects(t0, semaCtx), }}; } Allocate make(const parser::OmpClause::Allocate &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpAllocateClause using wrapped = parser::OmpAllocateClause; auto &t0 = std::get>(inp.v.t); auto &t1 = std::get(inp.v.t); if (!t0) { return Allocate{{/*AllocatorSimpleModifier=*/std::nullopt, /*AllocatorComplexModifier=*/std::nullopt, /*AlignModifier=*/std::nullopt, /*List=*/makeObjects(t1, semaCtx)}}; } using Tuple = decltype(Allocate::t); return Allocate{std::visit( common::visitors{ // simple-modifier [&](const wrapped::AllocateModifier::Allocator &v) -> Tuple { return {/*AllocatorSimpleModifier=*/makeExpr(v.v, semaCtx), /*AllocatorComplexModifier=*/std::nullopt, /*AlignModifier=*/std::nullopt, /*List=*/makeObjects(t1, semaCtx)}; }, // complex-modifier + align-modifier [&](const wrapped::AllocateModifier::ComplexModifier &v) -> Tuple { auto &s0 = std::get(v.t); auto &s1 = std::get(v.t); return { /*AllocatorSimpleModifier=*/std::nullopt, /*AllocatorComplexModifier=*/Allocator{makeExpr(s0.v, semaCtx)}, /*AlignModifier=*/Align{makeExpr(s1.v, semaCtx)}, /*List=*/makeObjects(t1, semaCtx)}; }, // align-modifier [&](const wrapped::AllocateModifier::Align &v) -> Tuple { return {/*AllocatorSimpleModifier=*/std::nullopt, /*AllocatorComplexModifier=*/std::nullopt, /*AlignModifier=*/Align{makeExpr(v.v, semaCtx)}, /*List=*/makeObjects(t1, semaCtx)}; }, }, t0->u)}; } Allocator make(const parser::OmpClause::Allocator &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return Allocator{/*Allocator=*/makeExpr(inp.v, semaCtx)}; } // AppendArgs: incomplete At make(const parser::OmpClause::At &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: at"); } // Never called, but needed for using "make" as a Clause visitor. // See comment about "requires" clauses in Clauses.h. AtomicDefaultMemOrder make(const parser::OmpClause::AtomicDefaultMemOrder &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpAtomicDefaultMemOrderClause CLAUSET_ENUM_CONVERT( // convert, common::OmpAtomicDefaultMemOrderType, AtomicDefaultMemOrder::MemoryOrder, // clang-format off MS(AcqRel, AcqRel) MS(Relaxed, Relaxed) MS(SeqCst, SeqCst) // clang-format on ); return AtomicDefaultMemOrder{/*MemoryOrder=*/convert(inp.v.v)}; } Bind make(const parser::OmpClause::Bind &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: bind"); } // CancellationConstructType: empty // Capture: empty Collapse make(const parser::OmpClause::Collapse &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntConstantExpr return Collapse{/*N=*/makeExpr(inp.v, semaCtx)}; } // Compare: empty // Contains: missing-in-parser Copyin make(const parser::OmpClause::Copyin &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Copyin{/*List=*/makeObjects(inp.v, semaCtx)}; } Copyprivate make(const parser::OmpClause::Copyprivate &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Copyprivate{/*List=*/makeObjects(inp.v, semaCtx)}; } Default make(const parser::OmpClause::Default &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpDefaultClause using wrapped = parser::OmpDefaultClause; CLAUSET_ENUM_CONVERT( // convert, wrapped::Type, Default::DataSharingAttribute, // clang-format off MS(Firstprivate, Firstprivate) MS(None, None) MS(Private, Private) MS(Shared, Shared) // clang-format on ); return Default{/*DataSharingAttribute=*/convert(inp.v.v)}; } Defaultmap make(const parser::OmpClause::Defaultmap &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpDefaultmapClause using wrapped = parser::OmpDefaultmapClause; CLAUSET_ENUM_CONVERT( // convert1, wrapped::ImplicitBehavior, Defaultmap::ImplicitBehavior, // clang-format off MS(Alloc, Alloc) MS(To, To) MS(From, From) MS(Tofrom, Tofrom) MS(Firstprivate, Firstprivate) MS(None, None) MS(Default, Default) // MS(, Present) missing-in-parser // clang-format on ); CLAUSET_ENUM_CONVERT( // convert2, wrapped::VariableCategory, Defaultmap::VariableCategory, // clang-format off MS(Scalar, Scalar) MS(Aggregate, Aggregate) MS(Pointer, Pointer) MS(Allocatable, Allocatable) // clang-format on ); auto &t0 = std::get(inp.v.t); auto &t1 = std::get>(inp.v.t); return Defaultmap{{/*ImplicitBehavior=*/convert1(t0), /*VariableCategory=*/maybeApply(convert2, t1)}}; } Depend make(const parser::OmpClause::Depend &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpDependClause using wrapped = parser::OmpDependClause; using Variant = decltype(Depend::u); // Iteration is the equivalent of parser::OmpDependSinkVec using Iteration = Doacross::Vector::value_type; // LoopIterationT CLAUSET_ENUM_CONVERT( // convert1, parser::OmpDependenceType::Type, Depend::TaskDependenceType, // clang-format off MS(In, In) MS(Out, Out) MS(Inout, Inout) // MS(, Mutexinoutset) // missing-in-parser // MS(, Inputset) // missing-in-parser // MS(, Depobj) // missing-in-parser // clang-format on ); return Depend{std::visit( // common::visitors{ // Doacross [&](const wrapped::Source &s) -> Variant { return Doacross{ {/*DependenceType=*/Doacross::DependenceType::Source, /*Vector=*/{}}}; }, // Doacross [&](const wrapped::Sink &s) -> Variant { using DependLength = parser::OmpDependSinkVecLength; auto convert2 = [&](const parser::OmpDependSinkVec &v) { auto &t0 = std::get(v.t); auto &t1 = std::get>(v.t); auto convert3 = [&](const DependLength &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, convert2)}}; }, // Depend::WithLocators [&](const wrapped::InOut &s) -> Variant { auto &t0 = std::get(s.t); auto &t1 = std::get>(s.t); auto convert4 = [&](const parser::Designator &t) { return makeObject(t, semaCtx); }; return Depend::WithLocators{ {/*TaskDependenceType=*/convert1(t0.v), /*Iterator=*/std::nullopt, /*LocatorList=*/makeList(t1, convert4)}}; }, }, inp.v.u)}; } // Depobj: empty Destroy make(const parser::OmpClause::Destroy &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: destroy"); } Detach make(const parser::OmpClause::Detach &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: detach"); } Device make(const parser::OmpClause::Device &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpDeviceClause using wrapped = parser::OmpDeviceClause; CLAUSET_ENUM_CONVERT( // convert, parser::OmpDeviceClause::DeviceModifier, Device::DeviceModifier, // clang-format off MS(Ancestor, Ancestor) MS(Device_Num, DeviceNum) // clang-format on ); auto &t0 = std::get>(inp.v.t); auto &t1 = std::get(inp.v.t); return Device{{/*DeviceModifier=*/maybeApply(convert, t0), /*DeviceDescription=*/makeExpr(t1, semaCtx)}}; } DeviceType make(const parser::OmpClause::DeviceType &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpDeviceTypeClause using wrapped = parser::OmpDeviceTypeClause; CLAUSET_ENUM_CONVERT( // convert, wrapped::Type, DeviceType::DeviceTypeDescription, // clang-format off MS(Any, Any) MS(Host, Host) MS(Nohost, Nohost) // clang-format om ); return DeviceType{/*DeviceTypeDescription=*/convert(inp.v.v)}; } DistSchedule make(const parser::OmpClause::DistSchedule &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> std::optional return DistSchedule{{/*Kind=*/DistSchedule::Kind::Static, /*ChunkSize=*/maybeApply(makeExprFn(semaCtx), inp.v)}}; } Doacross make(const parser::OmpClause::Doacross &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: doacross"); } // DynamicAllocators: empty Enter make(const parser::OmpClause::Enter &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Enter{makeObjects(/*List=*/inp.v, semaCtx)}; } Exclusive make(const parser::OmpClause::Exclusive &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: exclusive"); } Fail make(const parser::OmpClause::Fail &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: fail"); } Filter make(const parser::OmpClause::Filter &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return Filter{/*ThreadNum=*/makeExpr(inp.v, semaCtx)}; } Final make(const parser::OmpClause::Final &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarLogicalExpr return Final{/*Finalize=*/makeExpr(inp.v, semaCtx)}; } Firstprivate make(const parser::OmpClause::Firstprivate &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Firstprivate{/*List=*/makeObjects(inp.v, semaCtx)}; } // Flush: empty From make(const parser::OmpClause::From &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return From{{/*Expectation=*/std::nullopt, /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt, /*LocatorList=*/makeObjects(inp.v, semaCtx)}}; } // Full: empty Grainsize make(const parser::OmpClause::Grainsize &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return Grainsize{{/*Prescriptiveness=*/std::nullopt, /*GrainSize=*/makeExpr(inp.v, semaCtx)}}; } HasDeviceAddr make(const parser::OmpClause::HasDeviceAddr &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return HasDeviceAddr{/*List=*/makeObjects(inp.v, semaCtx)}; } Hint make(const parser::OmpClause::Hint &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ConstantExpr return Hint{/*HintExpr=*/makeExpr(inp.v, semaCtx)}; } // Holds: missing-in-parser If make(const parser::OmpClause::If &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpIfClause using wrapped = parser::OmpIfClause; CLAUSET_ENUM_CONVERT( // convert, wrapped::DirectiveNameModifier, llvm::omp::Directive, // clang-format off MS(Parallel, OMPD_parallel) MS(Simd, OMPD_simd) MS(Target, OMPD_target) MS(TargetData, OMPD_target_data) MS(TargetEnterData, OMPD_target_enter_data) MS(TargetExitData, OMPD_target_exit_data) MS(TargetUpdate, OMPD_target_update) MS(Task, OMPD_task) MS(Taskloop, OMPD_taskloop) MS(Teams, OMPD_teams) // clang-format on ); auto &t0 = std::get>(inp.v.t); auto &t1 = std::get(inp.v.t); return If{{/*DirectiveNameModifier=*/maybeApply(convert, t0), /*IfExpression=*/makeExpr(t1, semaCtx)}}; } // Inbranch: empty Inclusive make(const parser::OmpClause::Inclusive &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: inclusive"); } Indirect make(const parser::OmpClause::Indirect &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: indirect"); } Init make(const parser::OmpClause::Init &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: init"); } // Initializer: missing-in-parser InReduction make(const parser::OmpClause::InReduction &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpInReductionClause auto &t0 = std::get(inp.v.t); auto &t1 = std::get(inp.v.t); return InReduction{ {/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)}, /*List=*/makeObjects(t1, semaCtx)}}; } IsDevicePtr make(const parser::OmpClause::IsDevicePtr &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return IsDevicePtr{/*List=*/makeObjects(inp.v, semaCtx)}; } Lastprivate make(const parser::OmpClause::Lastprivate &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Lastprivate{{/*LastprivateModifier=*/std::nullopt, /*List=*/makeObjects(inp.v, semaCtx)}}; } Linear make(const parser::OmpClause::Linear &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpLinearClause using wrapped = parser::OmpLinearClause; CLAUSET_ENUM_CONVERT( // convert, parser::OmpLinearModifier::Type, Linear::LinearModifier, // clang-format off MS(Ref, Ref) MS(Val, Val) MS(Uval, Uval) // clang-format on ); using Tuple = decltype(Linear::t); return Linear{std::visit( common::visitors{ [&](const wrapped::WithModifier &s) -> Tuple { return { /*StepSimpleModifier=*/std::nullopt, /*StepComplexModifier=*/maybeApply(makeExprFn(semaCtx), s.step), /*LinearModifier=*/convert(s.modifier.v), /*List=*/makeList(s.names, makeObjectFn(semaCtx))}; }, [&](const wrapped::WithoutModifier &s) -> Tuple { return { /*StepSimpleModifier=*/maybeApply(makeExprFn(semaCtx), s.step), /*StepComplexModifier=*/std::nullopt, /*LinearModifier=*/std::nullopt, /*List=*/makeList(s.names, makeObjectFn(semaCtx))}; }, }, inp.v.u)}; } Link make(const parser::OmpClause::Link &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Link{/*List=*/makeObjects(inp.v, semaCtx)}; } Map make(const parser::OmpClause::Map &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpMapClause CLAUSET_ENUM_CONVERT( // convert1, parser::OmpMapType::Type, Map::MapType, // clang-format off MS(To, To) MS(From, From) MS(Tofrom, Tofrom) MS(Alloc, Alloc) MS(Release, Release) MS(Delete, Delete) // clang-format on ); // No convert2: MapTypeModifier is not an enum in parser. auto &t0 = std::get>(inp.v.t); auto &t1 = std::get(inp.v.t); if (!t0) { return Map{{/*MapType=*/std::nullopt, /*MapTypeModifiers=*/std::nullopt, /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt, /*LocatorList=*/makeObjects(t1, semaCtx)}}; } auto &s0 = std::get>(t0->t); auto &s1 = std::get(t0->t); std::optional maybeList; if (s0) maybeList = Map::MapTypeModifiers{Map::MapTypeModifier::Always}; return Map{{/*MapType=*/convert1(s1), /*MapTypeModifiers=*/maybeList, /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt, /*LocatorList=*/makeObjects(t1, semaCtx)}}; } // Match: incomplete // MemoryOrder: empty // Mergeable: empty Message make(const parser::OmpClause::Message &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: message"); } Nocontext make(const parser::OmpClause::Nocontext &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarLogicalExpr return Nocontext{/*DoNotUpdateContext=*/makeExpr(inp.v, semaCtx)}; } // Nogroup: empty Nontemporal make(const parser::OmpClause::Nontemporal &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> std::list return Nontemporal{/*List=*/makeList(inp.v, makeObjectFn(semaCtx))}; } // NoOpenmp: missing-in-parser // NoOpenmpRoutines: missing-in-parser // NoParallelism: missing-in-parser // Notinbranch: empty Novariants make(const parser::OmpClause::Novariants &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarLogicalExpr return Novariants{/*DoNotUseVariant=*/makeExpr(inp.v, semaCtx)}; } // Nowait: empty NumTasks make(const parser::OmpClause::NumTasks &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return NumTasks{{/*Prescriptiveness=*/std::nullopt, /*NumTasks=*/makeExpr(inp.v, semaCtx)}}; } NumTeams make(const parser::OmpClause::NumTeams &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return NumTeams{{/*LowerBound=*/std::nullopt, /*UpperBound=*/makeExpr(inp.v, semaCtx)}}; } NumThreads make(const parser::OmpClause::NumThreads &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return NumThreads{/*Nthreads=*/makeExpr(inp.v, semaCtx)}; } // OmpxAttribute: empty // OmpxBare: empty OmpxDynCgroupMem make(const parser::OmpClause::OmpxDynCgroupMem &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return OmpxDynCgroupMem{makeExpr(inp.v, semaCtx)}; } Order make(const parser::OmpClause::Order &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpOrderClause using wrapped = parser::OmpOrderClause; CLAUSET_ENUM_CONVERT( // convert1, parser::OmpOrderModifier::Kind, Order::OrderModifier, // clang-format off MS(Reproducible, Reproducible) MS(Unconstrained, Unconstrained) // clang-format on ); CLAUSET_ENUM_CONVERT( // convert2, wrapped::Type, Order::Ordering, // clang-format off MS(Concurrent, Concurrent) // clang-format on ); auto &t0 = std::get>(inp.v.t); auto &t1 = std::get(inp.v.t); auto convert3 = [&](const parser::OmpOrderModifier &s) { return std::visit( [&](parser::OmpOrderModifier::Kind k) { return convert1(k); }, s.u); }; return Order{ {/*OrderModifier=*/maybeApply(convert3, t0), /*Ordering=*/convert2(t1)}}; } Ordered make(const parser::OmpClause::Ordered &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> std::optional return Ordered{/*N=*/maybeApply(makeExprFn(semaCtx), inp.v)}; } // Otherwise: incomplete, missing-in-parser Partial make(const parser::OmpClause::Partial &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> std::optional return Partial{/*UnrollFactor=*/maybeApply(makeExprFn(semaCtx), inp.v)}; } Priority make(const parser::OmpClause::Priority &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return Priority{/*PriorityValue=*/makeExpr(inp.v, semaCtx)}; } Private make(const parser::OmpClause::Private &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Private{/*List=*/makeObjects(inp.v, semaCtx)}; } ProcBind make(const parser::OmpClause::ProcBind &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpProcBindClause using wrapped = parser::OmpProcBindClause; CLAUSET_ENUM_CONVERT( // convert, wrapped::Type, ProcBind::AffinityPolicy, // clang-format off MS(Close, Close) MS(Master, Master) MS(Spread, Spread) MS(Primary, Primary) // clang-format on ); return ProcBind{/*AffinityPolicy=*/convert(inp.v.v)}; } // Read: empty Reduction make(const parser::OmpClause::Reduction &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpReductionClause auto &t0 = std::get(inp.v.t); auto &t1 = std::get(inp.v.t); return Reduction{ {/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)}, /*ReductionModifier=*/std::nullopt, /*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::ScheduleType, Schedule::Kind, // clang-format off MS(Static, Static) MS(Dynamic, Dynamic) MS(Guided, Guided) MS(Auto, Auto) MS(Runtime, Runtime) // clang-format on ); CLAUSET_ENUM_CONVERT( // convert2, parser::OmpScheduleModifierType::ModType, Schedule::OrderingModifier, // clang-format off MS(Monotonic, Monotonic) MS(Nonmonotonic, Nonmonotonic) // clang-format on ); CLAUSET_ENUM_CONVERT( // convert3, parser::OmpScheduleModifierType::ModType, Schedule::ChunkModifier, // clang-format off MS(Simd, Simd) // clang-format on ); auto &t0 = std::get>(inp.v.t); auto &t1 = std::get(inp.v.t); auto &t2 = std::get>(inp.v.t); if (!t0) { return Schedule{{/*Kind=*/convert1(t1), /*OrderingModifier=*/std::nullopt, /*ChunkModifier=*/std::nullopt, /*ChunkSize=*/maybeApply(makeExprFn(semaCtx), t2)}}; } // The members of parser::OmpScheduleModifier correspond to OrderingModifier, // and ChunkModifier, but they can appear in any order. auto &m1 = std::get(t0->t); auto &m2 = std::get>(t0->t); std::optional omod; std::optional cmod; if (m1.v.v == parser::OmpScheduleModifierType::ModType::Simd) { // m1 is chunk-modifier cmod = convert3(m1.v.v); if (m2) omod = convert2(m2->v.v); } else { // m1 is ordering-modifier omod = convert2(m1.v.v); if (m2) cmod = convert3(m2->v.v); } return Schedule{{/*Kind=*/convert1(t1), /*OrderingModifier=*/omod, /*ChunkModifier=*/cmod, /*ChunkSize=*/maybeApply(makeExprFn(semaCtx), t2)}}; } // SeqCst: empty Severity make(const parser::OmpClause::Severity &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: severity"); } Shared make(const parser::OmpClause::Shared &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return Shared{/*List=*/makeObjects(inp.v, semaCtx)}; } // Simd: empty Simdlen make(const parser::OmpClause::Simdlen &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntConstantExpr return Simdlen{/*Length=*/makeExpr(inp.v, semaCtx)}; } Sizes make(const parser::OmpClause::Sizes &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> std::list return Sizes{/*SizeList=*/makeList(inp.v, makeExprFn(semaCtx))}; } TaskReduction make(const parser::OmpClause::TaskReduction &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpReductionClause auto &t0 = std::get(inp.v.t); auto &t1 = std::get(inp.v.t); return TaskReduction{ {/*ReductionIdentifiers=*/{makeReductionOperator(t0, semaCtx)}, /*List=*/makeObjects(t1, semaCtx)}}; } ThreadLimit make(const parser::OmpClause::ThreadLimit &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::ScalarIntExpr return ThreadLimit{/*Threadlim=*/makeExpr(inp.v, semaCtx)}; } // Threadprivate: empty // Threads: empty To make(const parser::OmpClause::To &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return To{{/*Expectation=*/std::nullopt, /*Mapper=*/std::nullopt, /*Iterator=*/std::nullopt, /*LocatorList=*/makeObjects(inp.v, semaCtx)}}; } // UnifiedAddress: empty // UnifiedSharedMemory: empty Uniform make(const parser::OmpClause::Uniform &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> std::list return Uniform{/*ParameterList=*/makeList(inp.v, makeObjectFn(semaCtx))}; } // Unknown: empty // Untied: empty Update make(const parser::OmpClause::Update &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty return Update{/*TaskDependenceType=*/std::nullopt}; } Use make(const parser::OmpClause::Use &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: use"); } UseDeviceAddr make(const parser::OmpClause::UseDeviceAddr &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return UseDeviceAddr{/*List=*/makeObjects(inp.v, semaCtx)}; } UseDevicePtr make(const parser::OmpClause::UseDevicePtr &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpObjectList return UseDevicePtr{/*List=*/makeObjects(inp.v, semaCtx)}; } UsesAllocators make(const parser::OmpClause::UsesAllocators &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty llvm_unreachable("Empty: uses_allocators"); } // Weak: empty // When: incomplete // Write: empty } // namespace clause Clause makeClause(const Fortran::parser::OmpClause &cls, semantics::SemanticsContext &semaCtx) { return std::visit( [&](auto &&s) { return makeClause(getClauseId(cls), 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); }); } } // namespace Fortran::lower::omp