| //===-- ClauseProcessor.cpp -------------------------------------*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ClauseProcessor.h" |
| #include "Utils.h" |
| |
| #include "flang/Lower/ConvertCall.h" |
| #include "flang/Lower/ConvertExprToHLFIR.h" |
| #include "flang/Lower/OpenMP/Clauses.h" |
| #include "flang/Lower/PFTBuilder.h" |
| #include "flang/Lower/Support/ReductionProcessor.h" |
| #include "flang/Optimizer/Dialect/FIRType.h" |
| #include "flang/Semantics/tools.h" |
| #include "flang/Utils/OpenMP.h" |
| #include "llvm/Frontend/OpenMP/OMP.h.inc" |
| #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" |
| |
| namespace Fortran { |
| namespace lower { |
| namespace omp { |
| |
| static void TodoLocators(mlir::Location loc, const omp::ObjectList &objects) { |
| for (const omp::Object &object : objects) { |
| if (auto &ref = object.ref()) { |
| auto op = GetTopLevelOperation(*ref).first; |
| if (op == evaluate::operation::Operator::Call) |
| TODO(loc, "Function call locators are not supported yet"); |
| } |
| semantics::Symbol *symbol = object.sym(); |
| if (symbol->test(semantics::Symbol::Flag::OmpReserved)) { |
| std::string name = |
| parser::ToLowerCaseLetters(object.sym()->name().ToString()); |
| if (llvm::is_contained(llvm::omp::getReservedLocatorNames(), name)) { |
| TODO(loc, "Reserved locators are not supported yet"); |
| } |
| } |
| } |
| } |
| |
| using ReductionModifier = |
| Fortran::lower::omp::clause::Reduction::ReductionModifier; |
| |
| mlir::omp::ReductionModifier translateReductionModifier(ReductionModifier mod) { |
| switch (mod) { |
| case ReductionModifier::Default: |
| return mlir::omp::ReductionModifier::defaultmod; |
| case ReductionModifier::Inscan: |
| return mlir::omp::ReductionModifier::inscan; |
| case ReductionModifier::Task: |
| return mlir::omp::ReductionModifier::task; |
| } |
| return mlir::omp::ReductionModifier::defaultmod; |
| } |
| |
| static mlir::omp::ScheduleModifier |
| translateScheduleModifier(const omp::clause::Schedule::OrderingModifier &m) { |
| switch (m) { |
| case omp::clause::Schedule::OrderingModifier::Monotonic: |
| return mlir::omp::ScheduleModifier::monotonic; |
| case omp::clause::Schedule::OrderingModifier::Nonmonotonic: |
| return mlir::omp::ScheduleModifier::nonmonotonic; |
| } |
| return mlir::omp::ScheduleModifier::none; |
| } |
| |
| static mlir::omp::ScheduleModifier |
| getScheduleModifier(const omp::clause::Schedule &clause) { |
| using Schedule = omp::clause::Schedule; |
| const auto &modifier = |
| std::get<std::optional<Schedule::OrderingModifier>>(clause.t); |
| if (modifier) |
| return translateScheduleModifier(*modifier); |
| return mlir::omp::ScheduleModifier::none; |
| } |
| |
| static mlir::omp::ScheduleModifier |
| getSimdModifier(const omp::clause::Schedule &clause) { |
| using Schedule = omp::clause::Schedule; |
| const auto &modifier = |
| std::get<std::optional<Schedule::ChunkModifier>>(clause.t); |
| if (modifier && *modifier == Schedule::ChunkModifier::Simd) |
| return mlir::omp::ScheduleModifier::simd; |
| return mlir::omp::ScheduleModifier::none; |
| } |
| |
| static void |
| genAllocateClause(lower::AbstractConverter &converter, |
| const omp::clause::Allocate &clause, |
| llvm::SmallVectorImpl<mlir::Value> &allocatorOperands, |
| llvm::SmallVectorImpl<mlir::Value> &allocateOperands) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::Location currentLocation = converter.getCurrentLocation(); |
| lower::StatementContext stmtCtx; |
| |
| auto &objects = std::get<omp::ObjectList>(clause.t); |
| |
| using Allocate = omp::clause::Allocate; |
| // ALIGN in this context is unimplemented |
| if (std::get<std::optional<Allocate::AlignModifier>>(clause.t)) |
| TODO(currentLocation, "OmpAllocateClause ALIGN modifier"); |
| |
| // Check if allocate clause has allocator specified. If so, add it |
| // to list of allocators, otherwise, add default allocator to |
| // list of allocators. |
| using ComplexModifier = Allocate::AllocatorComplexModifier; |
| if (auto &mod = std::get<std::optional<ComplexModifier>>(clause.t)) { |
| mlir::Value operand = fir::getBase(converter.genExprValue(mod->v, stmtCtx)); |
| allocatorOperands.append(objects.size(), operand); |
| } else { |
| mlir::Value operand = firOpBuilder.createIntegerConstant( |
| currentLocation, firOpBuilder.getI32Type(), 1); |
| allocatorOperands.append(objects.size(), operand); |
| } |
| |
| genObjectList(objects, converter, allocateOperands); |
| } |
| |
| static mlir::omp::ClauseBindKindAttr |
| genBindKindAttr(fir::FirOpBuilder &firOpBuilder, |
| const omp::clause::Bind &clause) { |
| mlir::omp::ClauseBindKind bindKind; |
| switch (clause.v) { |
| case omp::clause::Bind::Binding::Teams: |
| bindKind = mlir::omp::ClauseBindKind::Teams; |
| break; |
| case omp::clause::Bind::Binding::Parallel: |
| bindKind = mlir::omp::ClauseBindKind::Parallel; |
| break; |
| case omp::clause::Bind::Binding::Thread: |
| bindKind = mlir::omp::ClauseBindKind::Thread; |
| break; |
| } |
| return mlir::omp::ClauseBindKindAttr::get(firOpBuilder.getContext(), |
| bindKind); |
| } |
| |
| static mlir::omp::ClauseProcBindKindAttr |
| genProcBindKindAttr(fir::FirOpBuilder &firOpBuilder, |
| const omp::clause::ProcBind &clause) { |
| mlir::omp::ClauseProcBindKind procBindKind; |
| switch (clause.v) { |
| case omp::clause::ProcBind::AffinityPolicy::Master: |
| procBindKind = mlir::omp::ClauseProcBindKind::Master; |
| break; |
| case omp::clause::ProcBind::AffinityPolicy::Close: |
| procBindKind = mlir::omp::ClauseProcBindKind::Close; |
| break; |
| case omp::clause::ProcBind::AffinityPolicy::Spread: |
| procBindKind = mlir::omp::ClauseProcBindKind::Spread; |
| break; |
| case omp::clause::ProcBind::AffinityPolicy::Primary: |
| procBindKind = mlir::omp::ClauseProcBindKind::Primary; |
| break; |
| } |
| return mlir::omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), |
| procBindKind); |
| } |
| |
| static mlir::omp::ClauseTaskDependAttr |
| genDependKindAttr(lower::AbstractConverter &converter, |
| const omp::clause::DependenceType kind) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::Location currentLocation = converter.getCurrentLocation(); |
| |
| mlir::omp::ClauseTaskDepend pbKind; |
| switch (kind) { |
| case omp::clause::DependenceType::In: |
| pbKind = mlir::omp::ClauseTaskDepend::taskdependin; |
| break; |
| case omp::clause::DependenceType::Out: |
| pbKind = mlir::omp::ClauseTaskDepend::taskdependout; |
| break; |
| case omp::clause::DependenceType::Inout: |
| pbKind = mlir::omp::ClauseTaskDepend::taskdependinout; |
| break; |
| case omp::clause::DependenceType::Mutexinoutset: |
| pbKind = mlir::omp::ClauseTaskDepend::taskdependmutexinoutset; |
| break; |
| case omp::clause::DependenceType::Inoutset: |
| pbKind = mlir::omp::ClauseTaskDepend::taskdependinoutset; |
| break; |
| case omp::clause::DependenceType::Depobj: |
| TODO(currentLocation, "DEPOBJ dependence-type"); |
| break; |
| case omp::clause::DependenceType::Sink: |
| case omp::clause::DependenceType::Source: |
| llvm_unreachable("unhandled parser task dependence type"); |
| break; |
| } |
| return mlir::omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(), |
| pbKind); |
| } |
| |
| static mlir::Value |
| getIfClauseOperand(lower::AbstractConverter &converter, |
| const omp::clause::If &clause, |
| omp::clause::If::DirectiveNameModifier directiveName, |
| mlir::Location clauseLocation) { |
| // Only consider the clause if it's intended for the given directive. |
| auto &directive = |
| std::get<std::optional<omp::clause::If::DirectiveNameModifier>>(clause.t); |
| if (directive && directive.value() != directiveName) |
| return nullptr; |
| |
| lower::StatementContext stmtCtx; |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::Value ifVal = fir::getBase( |
| converter.genExprValue(std::get<omp::SomeExpr>(clause.t), stmtCtx)); |
| return firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(), |
| ifVal); |
| } |
| |
| template <typename SomeType, typename IteratorSpecT> |
| static IteratorRange lowerIteratorRange( |
| Fortran::lower::AbstractConverter &converter, const IteratorSpecT &itSpec, |
| Fortran::lower::StatementContext &stmtCtx, mlir::Location loc) { |
| auto &builder = converter.getFirOpBuilder(); |
| |
| using IdTy = |
| Fortran::lower::omp::IdTyTemplate<Fortran::evaluate::Expr<SomeType>>; |
| using ExprTy = Fortran::evaluate::Expr<SomeType>; |
| |
| using ObjTy = tomp::type::ObjectT<IdTy, ExprTy>; |
| using RangeTy = tomp::type::RangeT<ExprTy>; |
| |
| const ObjTy &ivObj = std::get<1>(itSpec.t); |
| const RangeTy &range = std::get<2>(itSpec.t); |
| |
| IteratorRange r; |
| r.ivSym = ivObj.sym(); |
| assert(r.ivSym && "expected iterator induction symbol"); |
| |
| const auto &lbExpr = std::get<0>(range.t); |
| const auto &ubExpr = std::get<1>(range.t); |
| const auto &stExpr = std::get<2>(range.t); |
| |
| mlir::Value lbVal = |
| fir::getBase(converter.genExprValue(toEvExpr(lbExpr), stmtCtx)); |
| mlir::Value ubVal = |
| fir::getBase(converter.genExprValue(toEvExpr(ubExpr), stmtCtx)); |
| |
| auto toIndex = [](fir::FirOpBuilder &builder, mlir::Location loc, |
| mlir::Value v) -> mlir::Value { |
| if (v.getType().isIndex()) |
| return v; |
| return fir::ConvertOp::create(builder, loc, builder.getIndexType(), v); |
| }; |
| |
| r.lb = toIndex(builder, loc, lbVal); |
| r.ub = toIndex(builder, loc, ubVal); |
| |
| if (stExpr) { |
| mlir::Value stVal = |
| fir::getBase(converter.genExprValue(toEvExpr(*stExpr), stmtCtx)); |
| r.step = toIndex(builder, loc, stVal); |
| } else { |
| r.step = mlir::arith::ConstantIndexOp::create(builder, loc, 1); |
| } |
| |
| return r; |
| } |
| |
| template <typename BodyFn> |
| static mlir::Value buildIteratorOp(Fortran::lower::AbstractConverter &converter, |
| mlir::Location loc, mlir::Type iterTy, |
| llvm::ArrayRef<IteratorRange> ranges, |
| BodyFn &&bodyGen) { |
| |
| auto &builder = converter.getFirOpBuilder(); |
| |
| llvm::SmallVector<mlir::Value> lbs, ubs, steps; |
| lbs.reserve(ranges.size()); |
| ubs.reserve(ranges.size()); |
| steps.reserve(ranges.size()); |
| for (auto &r : ranges) { |
| lbs.push_back(r.lb); |
| ubs.push_back(r.ub); |
| steps.push_back(r.step); |
| } |
| |
| auto itOp = mlir::omp::IteratorOp::create( |
| builder, loc, iterTy, mlir::ValueRange{lbs}, mlir::ValueRange{ubs}, |
| mlir::ValueRange{steps}); |
| |
| mlir::OpBuilder::InsertionGuard guard(builder); |
| |
| mlir::Region ® = itOp.getRegion(); |
| mlir::Block *body = builder.createBlock(®); |
| |
| llvm::SmallVector<mlir::Value> ivs; |
| ivs.reserve(ranges.size()); |
| for (size_t i = 0; i < ranges.size(); ++i) |
| ivs.push_back(body->addArgument(builder.getIndexType(), loc)); |
| |
| Fortran::lower::SymMap &symMap = converter.getSymbolMap(); |
| Fortran::lower::SymMapScope scope(symMap); |
| for (size_t i = 0; i < ranges.size(); ++i) { |
| mlir::Value ivVal = ivs[i]; |
| mlir::Type ivTy = converter.genType(*ranges[i].ivSym); |
| if (ivVal.getType() != ivTy) |
| ivVal = fir::ConvertOp::create(builder, loc, ivTy, ivVal); |
| symMap.addSymbol(*ranges[i].ivSym, ivVal, /*force=*/true); |
| } |
| |
| mlir::omp::YieldOp::create(builder, loc, bodyGen(builder, loc, ivs)); |
| |
| return itOp.getResult(); |
| } |
| |
| template <typename ClauseTuple> |
| static void collectIteratorIVs( |
| const ClauseTuple &clause, Fortran::lower::AbstractConverter &converter, |
| Fortran::lower::StatementContext &stmtCtx, |
| llvm::SmallVectorImpl<IteratorRange> &iteratorRanges, |
| llvm::SmallPtrSetImpl<const Fortran::semantics::Symbol *> &ivSyms) { |
| auto &iteratorModifier = |
| std::get<std::optional<omp::clause::Iterator>>(clause.t); |
| if (!iteratorModifier.has_value()) |
| return; |
| |
| mlir::Location clauseLocation = converter.getCurrentLocation(); |
| const auto &iteratorModifierSpecs = *iteratorModifier; |
| iteratorRanges.reserve(iteratorModifierSpecs.size()); |
| for (const auto &itSpec : iteratorModifierSpecs) |
| iteratorRanges.push_back(lowerIteratorRange<Fortran::evaluate::SomeType>( |
| converter, itSpec, stmtCtx, clauseLocation)); |
| |
| for (const IteratorRange &r : iteratorRanges) |
| ivSyms.insert(&r.ivSym->GetUltimate()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ClauseProcessor unique clauses |
| //===----------------------------------------------------------------------===// |
| |
| bool ClauseProcessor::processAlign(mlir::omp::AlignClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Align>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| const std::optional<std::int64_t> align = evaluate::ToInt64(clause->v); |
| result.align = firOpBuilder.getI64IntegerAttr(*align); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processAllocator( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::AllocatorClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Allocator>()) { |
| result.allocator = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processBind(mlir::omp::BindClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Bind>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| result.bindKind = genBindKindAttr(firOpBuilder, *clause); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processCancelDirectiveName( |
| mlir::omp::CancelDirectiveNameClauseOps &result) const { |
| using ConstructType = mlir::omp::ClauseCancellationConstructType; |
| mlir::MLIRContext *context = &converter.getMLIRContext(); |
| |
| ConstructType directive; |
| if (auto *clause = findUniqueClause<omp::CancellationConstructType>()) { |
| switch (clause->v) { |
| case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Parallel: |
| directive = mlir::omp::ClauseCancellationConstructType::Parallel; |
| break; |
| case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Loop: |
| directive = mlir::omp::ClauseCancellationConstructType::Loop; |
| break; |
| case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Sections: |
| directive = mlir::omp::ClauseCancellationConstructType::Sections; |
| break; |
| case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Taskgroup: |
| directive = mlir::omp::ClauseCancellationConstructType::Taskgroup; |
| break; |
| case llvm::omp::OMP_CANCELLATION_CONSTRUCT_None: |
| llvm_unreachable("OMP_CANCELLATION_CONSTRUCT_None"); |
| break; |
| } |
| } else { |
| llvm_unreachable("cancel construct missing cancellation construct type"); |
| } |
| |
| result.cancelDirective = |
| mlir::omp::ClauseCancellationConstructTypeAttr::get(context, directive); |
| return true; |
| } |
| |
| bool ClauseProcessor::processCollapse( |
| mlir::Location currentLocation, lower::pft::Evaluation &eval, |
| mlir::omp::LoopRelatedClauseOps &loopResult, |
| mlir::omp::CollapseClauseOps &collapseResult, |
| llvm::SmallVectorImpl<const semantics::Symbol *> &iv) const { |
| |
| int64_t numCollapse = collectLoopRelatedInfo(converter, currentLocation, eval, |
| getNestedDoConstruct(eval), |
| clauses, loopResult, iv); |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| collapseResult.collapseNumLoops = firOpBuilder.getI64IntegerAttr(numCollapse); |
| return numCollapse > 1; |
| } |
| |
| bool ClauseProcessor::processDevice(lower::StatementContext &stmtCtx, |
| mlir::omp::DeviceClauseOps &result) const { |
| const parser::CharBlock *source = nullptr; |
| if (auto *clause = findUniqueClause<omp::clause::Device>(&source)) { |
| mlir::Location clauseLocation = converter.genLocation(*source); |
| if (auto deviceModifier = |
| std::get<std::optional<omp::clause::Device::DeviceModifier>>( |
| clause->t)) { |
| if (deviceModifier == omp::clause::Device::DeviceModifier::Ancestor) { |
| TODO(clauseLocation, "OMPD_target Device Modifier Ancestor"); |
| } |
| } |
| const auto &deviceExpr = std::get<omp::SomeExpr>(clause->t); |
| result.device = fir::getBase(converter.genExprValue(deviceExpr, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processDeviceType( |
| mlir::omp::DeviceTypeClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::DeviceType>()) { |
| // Case: declare target ... device_type(any | host | nohost) |
| switch (clause->v) { |
| case omp::clause::DeviceType::DeviceTypeDescription::Nohost: |
| result.deviceType = mlir::omp::DeclareTargetDeviceType::nohost; |
| break; |
| case omp::clause::DeviceType::DeviceTypeDescription::Host: |
| result.deviceType = mlir::omp::DeclareTargetDeviceType::host; |
| break; |
| case omp::clause::DeviceType::DeviceTypeDescription::Any: |
| result.deviceType = mlir::omp::DeclareTargetDeviceType::any; |
| break; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processDistSchedule( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::DistScheduleClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::DistSchedule>()) { |
| result.distScheduleStatic = converter.getFirOpBuilder().getUnitAttr(); |
| const auto &chunkSize = std::get<std::optional<ExprTy>>(clause->t); |
| if (chunkSize) |
| result.distScheduleChunkSize = |
| fir::getBase(converter.genExprValue(*chunkSize, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processExclusive( |
| mlir::Location currentLocation, |
| mlir::omp::ExclusiveClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Exclusive>()) { |
| for (const Object &object : clause->v) { |
| const semantics::Symbol *symbol = object.sym(); |
| mlir::Value symVal = converter.getSymbolAddress(*symbol); |
| result.exclusiveVars.push_back(symVal); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processFilter(lower::StatementContext &stmtCtx, |
| mlir::omp::FilterClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Filter>()) { |
| result.filteredThreadId = |
| fir::getBase(converter.genExprValue(clause->v, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processFinal(lower::StatementContext &stmtCtx, |
| mlir::omp::FinalClauseOps &result) const { |
| const parser::CharBlock *source = nullptr; |
| if (auto *clause = findUniqueClause<omp::clause::Final>(&source)) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::Location clauseLocation = converter.genLocation(*source); |
| |
| mlir::Value finalVal = |
| fir::getBase(converter.genExprValue(clause->v, stmtCtx)); |
| result.final = firOpBuilder.createConvert( |
| clauseLocation, firOpBuilder.getI1Type(), finalVal); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processHint(mlir::omp::HintClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Hint>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| int64_t hintValue = *evaluate::ToInt64(clause->v); |
| result.hint = firOpBuilder.getI64IntegerAttr(hintValue); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processInclusive( |
| mlir::Location currentLocation, |
| mlir::omp::InclusiveClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Inclusive>()) { |
| for (const Object &object : clause->v) { |
| const semantics::Symbol *symbol = object.sym(); |
| mlir::Value symVal = converter.getSymbolAddress(*symbol); |
| result.inclusiveVars.push_back(symVal); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processInitializer( |
| lower::SymMap &symMap, ReductionProcessor::GenInitValueCBTy &genInitValueCB, |
| const parser::OmpStylizedInstance *parserInitInstance) const { |
| if (auto *clause = findUniqueClause<omp::clause::Initializer>()) { |
| // Extract the typed assignment from the parser-level instance, if |
| // the initializer is an assignment statement (as opposed to a call). |
| const evaluate::Assignment *assign = nullptr; |
| if (parserInitInstance) { |
| const auto &instance = std::get<parser::OmpStylizedInstance::Instance>( |
| parserInitInstance->t); |
| if (const auto *assignStmt = |
| std::get_if<parser::AssignmentStmt>(&instance.u)) { |
| if (auto *wrapper = assignStmt->typedAssignment.get()) |
| if (wrapper->v) |
| assign = &*wrapper->v; |
| } |
| } |
| genInitValueCB = [&, clause, assign](fir::FirOpBuilder &builder, |
| mlir::Location loc, mlir::Type type, |
| mlir::Value moldArg, |
| mlir::Value privArg) { |
| lower::SymMapScope scope(symMap); |
| mlir::Value ompPrivVar; |
| const StylizedInstance &inst = clause->v.front(); |
| |
| for (const Object &object : |
| std::get<StylizedInstance::Variables>(inst.t)) { |
| mlir::Value addr; |
| std::string name = object.sym()->name().ToString(); |
| mlir::Type moldArgType = moldArg.getType(); |
| // Check for unsupported dynamic-length character reductions |
| mlir::Type unwrappedType = fir::unwrapRefType(moldArgType); |
| if (mlir::isa<fir::BoxCharType>(unwrappedType)) { |
| TODO(loc, "OpenMP reduction allocation for dynamic length character"); |
| } |
| if (auto charTy = mlir::dyn_cast<fir::CharacterType>(unwrappedType)) { |
| if (!charTy.hasConstantLen()) { |
| TODO(loc, |
| "OpenMP reduction allocation for dynamic length character"); |
| } |
| } |
| // For by-ref reductions, omp_priv maps to privArg (the private |
| // allocation) and omp_orig maps to moldArg (the original). |
| if (name == "omp_priv" && privArg) { |
| addr = privArg; |
| } else if (fir::isa_ref_type(moldArgType)) { |
| addr = moldArg; |
| } else { |
| addr = builder.createTemporary(loc, moldArgType); |
| fir::StoreOp::create(builder, loc, moldArg, addr); |
| } |
| fir::FortranVariableFlagsEnum extraFlags = {}; |
| fir::FortranVariableFlagsAttr attributes = |
| Fortran::lower::translateSymbolAttributes( |
| builder.getContext(), *object.sym(), extraFlags); |
| // Get length parameters for types that need them (e.g., characters). |
| // Note: DeclareOp requires exactly one type parameter for non-boxed |
| // characters, unlike EmboxOp which doesn't allow them for constant-len. |
| llvm::SmallVector<mlir::Value> typeParams; |
| if (hlfir::isFortranEntity(addr)) { |
| hlfir::genLengthParameters(loc, builder, hlfir::Entity{addr}, |
| typeParams); |
| } |
| auto declareOp = hlfir::DeclareOp::create(builder, loc, addr, name, |
| nullptr, typeParams, nullptr, |
| nullptr, 0, attributes); |
| if (name == "omp_priv") |
| ompPrivVar = declareOp.getResult(0); |
| symMap.addVariableDefinition(*object.sym(), declareOp); |
| } |
| |
| // Lower the expression/function call |
| lower::StatementContext stmtCtx; |
| const semantics::SomeExpr &initExpr = |
| std::get<StylizedInstance::Instance>(inst.t); |
| mlir::Value result = common::visit( |
| common::visitors{ |
| [&](const evaluate::ProcedureRef &procRef) -> mlir::Value { |
| convertCallToHLFIR(loc, converter, procRef, std::nullopt, |
| symMap, stmtCtx); |
| auto privVal = fir::LoadOp::create(builder, loc, ompPrivVar); |
| return privVal; |
| }, |
| [&](const auto &expr) -> mlir::Value { |
| // For by-ref reductions with a typed assignment, lower |
| // the full assignment (both LHS and RHS) directly. |
| // This handles both whole-variable (omp_priv = val) and |
| // component-level (omp_priv%member = val) initializers. |
| // Mirror the combiner pattern: dispatch on assign->u to |
| // handle both intrinsic and user-defined assignment. |
| if (privArg && assign) { |
| lower::StatementContext assignCtx; |
| hlfir::Entity rhs = lower::convertExprToHLFIR( |
| loc, converter, assign->rhs, symMap, assignCtx); |
| rhs = hlfir::loadTrivialScalar(loc, builder, rhs); |
| hlfir::Entity lhs = lower::convertExprToHLFIR( |
| loc, converter, assign->lhs, symMap, assignCtx); |
| common::visit( |
| common::visitors{ |
| [&](const evaluate::Assignment::Intrinsic &) { |
| hlfir::AssignOp::create(builder, loc, rhs, lhs); |
| }, |
| [&](const evaluate::ProcedureRef &procRef) { |
| lower::convertUserDefinedAssignmentToHLFIR( |
| loc, converter, procRef, lhs, rhs, symMap); |
| }, |
| [&](const auto &) { |
| llvm_unreachable("Unexpected assignment type in " |
| "reduction initializer"); |
| }, |
| }, |
| assign->u); |
| assignCtx.finalizeAndPop(); |
| return mlir::Value{}; |
| } |
| mlir::Value exprResult = fir::getBase(convertExprToValue( |
| loc, converter, initExpr, symMap, stmtCtx)); |
| if (auto refType = llvm::dyn_cast<fir::ReferenceType>( |
| exprResult.getType())) |
| if (ompPrivVar.getType() == refType) |
| exprResult = fir::LoadOp::create(builder, loc, exprResult); |
| // The initializer expression may have a different but |
| // convertible scalar type than the reduction. For example a |
| // LOGICAL initializer (e.g. omp_priv = .false.) lowers to an |
| // i1 while the reduction type is !fir.logical<4>. Convert so |
| // the init region yields the reduction type, as the |
| // omp.declare_reduction verifier requires. |
| if (exprResult.getType() != type && |
| fir::isa_trivial(exprResult.getType()) && |
| fir::isa_trivial(type)) |
| exprResult = builder.createConvert(loc, type, exprResult); |
| return exprResult; |
| }}, |
| initExpr.u); |
| stmtCtx.finalizeAndPop(); |
| return result; |
| }; |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processMergeable( |
| mlir::omp::MergeableClauseOps &result) const { |
| return markClauseOccurrence<omp::clause::Mergeable>(result.mergeable); |
| } |
| |
| bool ClauseProcessor::processNogroup( |
| mlir::omp::NogroupClauseOps &result) const { |
| return markClauseOccurrence<omp::clause::Nogroup>(result.nogroup); |
| } |
| |
| bool ClauseProcessor::processNowait(mlir::omp::NowaitClauseOps &result) const { |
| return markClauseOccurrence<omp::clause::Nowait>(result.nowait); |
| } |
| |
| bool ClauseProcessor::processNumTasks( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::NumTasksClauseOps &result) const { |
| using NumTasks = omp::clause::NumTasks; |
| if (auto *clause = findUniqueClause<NumTasks>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::MLIRContext *context = firOpBuilder.getContext(); |
| const auto &modifier = |
| std::get<std::optional<NumTasks::Prescriptiveness>>(clause->t); |
| if (modifier && *modifier == NumTasks::Prescriptiveness::Strict) { |
| result.numTasksMod = mlir::omp::ClauseNumTasksTypeAttr::get( |
| context, mlir::omp::ClauseNumTasksType::Strict); |
| } |
| const auto &numtasksExpr = std::get<omp::SomeExpr>(clause->t); |
| result.numTasks = |
| fir::getBase(converter.genExprValue(numtasksExpr, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processSizes(StatementContext &stmtCtx, |
| mlir::omp::SizesClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Sizes>()) { |
| result.sizes.reserve(clause->v.size()); |
| for (const ExprTy &vv : clause->v) |
| result.sizes.push_back(fir::getBase(converter.genExprValue(vv, stmtCtx))); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ClauseProcessor::processLooprange(StatementContext &stmtCtx, |
| mlir::omp::LooprangeClauseOps &result, |
| int64_t &count) const { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| if (auto *clause = findUniqueClause<omp::clause::Looprange>()) { |
| int64_t first = evaluate::ToInt64(std::get<0>(clause->t)).value(); |
| count = evaluate::ToInt64(std::get<1>(clause->t)).value(); |
| result.first = firOpBuilder.getI64IntegerAttr(first); |
| result.count = firOpBuilder.getI64IntegerAttr(count); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool ClauseProcessor::processNumTeams( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::NumTeamsClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::NumTeams>()) { |
| // Structure: {LB?, [UB]} - single optional lower bound, list of upper |
| // bounds |
| auto &lowerBound = std::get<std::optional<ExprTy>>(clause->t); |
| auto &upperBounds = |
| std::get<omp::clause::NumTeams::UpperBoundList>(clause->t); |
| assert(!upperBounds.empty()); |
| |
| // Extract optional lower bound |
| if (lowerBound) { |
| result.numTeamsLower = |
| fir::getBase(converter.genExprValue(*lowerBound, stmtCtx)); |
| } |
| |
| // Extract all upper bounds |
| result.numTeamsUpperVars.reserve(upperBounds.size()); |
| for (const auto &ub : upperBounds) { |
| result.numTeamsUpperVars.push_back( |
| fir::getBase(converter.genExprValue(ub, stmtCtx))); |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processNumThreads( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::NumThreadsClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::NumThreads>()) { |
| // OMPIRBuilder expects `NUM_THREADS` clause as a list of Values. |
| for (const ExprTy &expr : clause->v) { |
| result.numThreadsVars.push_back( |
| fir::getBase(converter.genExprValue(expr, stmtCtx))); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processOrder(mlir::omp::OrderClauseOps &result) const { |
| using Order = omp::clause::Order; |
| if (auto *clause = findUniqueClause<Order>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| result.order = mlir::omp::ClauseOrderKindAttr::get( |
| firOpBuilder.getContext(), mlir::omp::ClauseOrderKind::Concurrent); |
| const auto &modifier = |
| std::get<std::optional<Order::OrderModifier>>(clause->t); |
| if (modifier && *modifier == Order::OrderModifier::Unconstrained) { |
| result.orderMod = mlir::omp::OrderModifierAttr::get( |
| firOpBuilder.getContext(), mlir::omp::OrderModifier::unconstrained); |
| } else { |
| // "If order-modifier is not unconstrained, the behavior is as if the |
| // reproducible modifier is present." |
| result.orderMod = mlir::omp::OrderModifierAttr::get( |
| firOpBuilder.getContext(), mlir::omp::OrderModifier::reproducible); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processOrdered( |
| mlir::omp::OrderedClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Ordered>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| int64_t orderedClauseValue = 0l; |
| if (clause->v.has_value()) |
| orderedClauseValue = *evaluate::ToInt64(*clause->v); |
| result.ordered = firOpBuilder.getI64IntegerAttr(orderedClauseValue); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processPartial(std::optional<int64_t> &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Partial>()) { |
| if (clause->v.has_value()) |
| result = evaluate::ToInt64(*clause->v); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processPriority( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::PriorityClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Priority>()) { |
| result.priority = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processDetach(mlir::omp::DetachClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Detach>()) { |
| semantics::Symbol *sym = clause->v.sym(); |
| mlir::Value symVal = converter.getSymbolAddress(*sym); |
| result.eventHandle = symVal; |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processProcBind( |
| mlir::omp::ProcBindClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::ProcBind>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| result.procBindKind = genProcBindKindAttr(firOpBuilder, *clause); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processTileSizes( |
| lower::pft::Evaluation &eval, mlir::omp::LoopNestOperands &result) const { |
| auto *ompCons{eval.getIf<parser::OpenMPConstruct>()}; |
| collectTileSizesFromOpenMPConstruct(ompCons, result.tileSizes, semaCtx); |
| return !result.tileSizes.empty(); |
| } |
| |
| bool ClauseProcessor::processSafelen( |
| mlir::omp::SafelenClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Safelen>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| const std::optional<std::int64_t> safelenVal = evaluate::ToInt64(clause->v); |
| result.safelen = firOpBuilder.getI64IntegerAttr(*safelenVal); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processSchedule( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::ScheduleClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Schedule>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::MLIRContext *context = firOpBuilder.getContext(); |
| const auto &scheduleType = std::get<omp::clause::Schedule::Kind>(clause->t); |
| |
| mlir::omp::ClauseScheduleKind scheduleKind; |
| switch (scheduleType) { |
| case omp::clause::Schedule::Kind::Static: |
| scheduleKind = mlir::omp::ClauseScheduleKind::Static; |
| break; |
| case omp::clause::Schedule::Kind::Dynamic: |
| scheduleKind = mlir::omp::ClauseScheduleKind::Dynamic; |
| break; |
| case omp::clause::Schedule::Kind::Guided: |
| scheduleKind = mlir::omp::ClauseScheduleKind::Guided; |
| break; |
| case omp::clause::Schedule::Kind::Auto: |
| scheduleKind = mlir::omp::ClauseScheduleKind::Auto; |
| break; |
| case omp::clause::Schedule::Kind::Runtime: |
| scheduleKind = mlir::omp::ClauseScheduleKind::Runtime; |
| break; |
| } |
| |
| result.scheduleKind = |
| mlir::omp::ClauseScheduleKindAttr::get(context, scheduleKind); |
| |
| mlir::omp::ScheduleModifier scheduleMod = getScheduleModifier(*clause); |
| if (scheduleMod != mlir::omp::ScheduleModifier::none) |
| result.scheduleMod = |
| mlir::omp::ScheduleModifierAttr::get(context, scheduleMod); |
| |
| if (getSimdModifier(*clause) != mlir::omp::ScheduleModifier::none) |
| result.scheduleSimd = firOpBuilder.getUnitAttr(); |
| |
| if (const auto &chunkExpr = std::get<omp::MaybeExpr>(clause->t)) |
| result.scheduleChunk = |
| fir::getBase(converter.genExprValue(*chunkExpr, stmtCtx)); |
| |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processSimdlen( |
| mlir::omp::SimdlenClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::Simdlen>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| const std::optional<std::int64_t> simdlenVal = evaluate::ToInt64(clause->v); |
| result.simdlen = firOpBuilder.getI64IntegerAttr(*simdlenVal); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processSimd( |
| mlir::omp::OrderedRegionOperands &result) const { |
| return markClauseOccurrence<omp::clause::Simd>(result.parLevelSimd); |
| } |
| |
| bool ClauseProcessor::processThreadLimit( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::ThreadLimitClauseOps &result) const { |
| if (auto *clause = findUniqueClause<omp::clause::ThreadLimit>()) { |
| result.threadLimitVars.reserve(clause->v.size()); |
| for (const ExprTy &vv : clause->v) |
| result.threadLimitVars.push_back( |
| fir::getBase(converter.genExprValue(vv, stmtCtx))); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processUntied(mlir::omp::UntiedClauseOps &result) const { |
| return markClauseOccurrence<omp::clause::Untied>(result.untied); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // ClauseProcessor repeatable clauses |
| //===----------------------------------------------------------------------===// |
| static llvm::StringMap<bool> getTargetFeatures(mlir::ModuleOp module) { |
| llvm::StringMap<bool> featuresMap; |
| llvm::SmallVector<llvm::StringRef> targetFeaturesVec; |
| if (mlir::LLVM::TargetFeaturesAttr features = |
| fir::getTargetFeatures(module)) { |
| llvm::ArrayRef<mlir::StringAttr> featureAttrs = features.getFeatures(); |
| for (auto &featureAttr : featureAttrs) { |
| llvm::StringRef featureKeyString = featureAttr.strref(); |
| featuresMap[featureKeyString.substr(1)] = (featureKeyString[0] == '+'); |
| } |
| } |
| return featuresMap; |
| } |
| |
| bool ClauseProcessor::processAffinity( |
| mlir::omp::AffinityClauseOps &result) const { |
| return findRepeatableClause<omp::clause::Affinity>( |
| [&](const omp::clause::Affinity &clause, const parser::CharBlock &) { |
| const auto &objects = std::get<omp::ObjectList>(clause.t); |
| lower::StatementContext stmtCtx; |
| auto &builder = converter.getFirOpBuilder(); |
| auto &context = converter.getMLIRContext(); |
| mlir::Location clauseLocation = converter.getCurrentLocation(); |
| |
| mlir::Type refI8Ty = fir::ReferenceType::get(builder.getIntegerType(8)); |
| mlir::Type entryTy = mlir::omp::AffinityEntryType::get( |
| &context, refI8Ty, builder.getI64Type()); |
| mlir::Type iterTy = |
| mlir::omp::IteratedType::get(&converter.getMLIRContext(), entryTy); |
| |
| auto makeAffinityEntry = [&](fir::FirOpBuilder &b, mlir::Location l, |
| mlir::Type entryTy, mlir::Value addr, |
| mlir::Value len) -> mlir::Value { |
| mlir::Value addrI8 = fir::ConvertOp::create(b, l, refI8Ty, addr); |
| return mlir::omp::AffinityEntryOp::create(b, l, entryTy, addrI8, len) |
| .getResult(); |
| }; |
| |
| llvm::SmallVector<IteratorRange> iteratorRanges; |
| llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4> ivSyms; |
| |
| auto &iteratorModifier = |
| std::get<std::optional<omp::clause::Iterator>>(clause.t); |
| collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms); |
| |
| TodoLocators(clauseLocation, objects); |
| |
| for (const omp::Object &object : objects) { |
| llvm::SmallVector<mlir::Value> bounds; |
| std::stringstream asFortran; |
| if (iteratorModifier.has_value() && |
| hasIteratorIVReference(object, ivSyms)) { |
| mlir::Value iterHandle = buildIteratorOp( |
| converter, clauseLocation, iterTy, iteratorRanges, |
| [&](fir::FirOpBuilder &builder, mlir::Location loc, |
| llvm::ArrayRef<mlir::Value> /*ivs*/) -> mlir::Value { |
| lower::StatementContext iterStmtCtx; |
| |
| if (std::optional<llvm::SmallVector<mlir::Value>> |
| loweredIndices = getIteratorElementIndices( |
| converter, object, iterStmtCtx, loc)) { |
| const Fortran::semantics::Symbol *sym = object.sym(); |
| assert(sym && "expected symbol for iterator object"); |
| fir::factory::AddrAndBoundsInfo info = |
| Fortran::lower::getDataOperandBaseAddr( |
| converter, builder, *sym, loc, |
| /*unwrapFirBox=*/false); |
| hlfir::Entity entity{info.addr}; |
| mlir::Value iteratedAddr = genIteratorCoordinate( |
| converter, entity, *loweredIndices, loc); |
| mlir::Value len = genElementSizeInBytes( |
| builder, loc, builder.getDataLayout(), entity); |
| return makeAffinityEntry(builder, loc, entryTy, |
| iteratedAddr, len); |
| } |
| |
| TODO(loc, "object type not supported by iterator modifier"); |
| }); |
| result.iterated.push_back(iterHandle); |
| } else { |
| mlir::Value addr = |
| genAffinityAddr(converter, object, stmtCtx, clauseLocation); |
| // get hlfir.declare for length calculation |
| fir::factory::AddrAndBoundsInfo info = |
| lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp, |
| mlir::omp::MapBoundsType>( |
| converter, builder, semaCtx, stmtCtx, *object.sym(), |
| object.ref(), clauseLocation, asFortran, bounds, |
| treatIndexAsSection); |
| mlir::Value len = |
| genAffinityLen(builder, clauseLocation, builder.getDataLayout(), |
| hlfir::Entity{info.addr}, bounds); |
| result.affinityVars.push_back( |
| makeAffinityEntry(builder, clauseLocation, entryTy, addr, len)); |
| } |
| } |
| |
| return true; |
| }); |
| } |
| |
| static void |
| addAlignedClause(lower::AbstractConverter &converter, |
| const omp::clause::Aligned &clause, |
| llvm::SmallVectorImpl<mlir::Value> &alignedVars, |
| llvm::SmallVectorImpl<mlir::Attribute> &alignments) { |
| using Aligned = omp::clause::Aligned; |
| lower::StatementContext stmtCtx; |
| mlir::IntegerAttr alignmentValueAttr; |
| int64_t alignment = 0; |
| fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
| |
| if (auto &alignmentValueParserExpr = |
| std::get<std::optional<Aligned::Alignment>>(clause.t)) { |
| mlir::Value operand = fir::getBase( |
| converter.genExprValue(*alignmentValueParserExpr, stmtCtx)); |
| alignment = *fir::getIntIfConstant(operand); |
| } else { |
| llvm::StringMap<bool> featuresMap = getTargetFeatures(builder.getModule()); |
| llvm::Triple triple = fir::getTargetTriple(builder.getModule()); |
| alignment = |
| llvm::OpenMPIRBuilder::getOpenMPDefaultSimdAlign(triple, featuresMap); |
| } |
| |
| // The default alignment for some targets is equal to 0. |
| // Do not generate alignment assumption if alignment is less than or equal to |
| // 0 or not a power of two |
| if (alignment > 0 && ((alignment & (alignment - 1)) == 0)) { |
| auto &objects = std::get<omp::ObjectList>(clause.t); |
| if (!objects.empty()) |
| genObjectList(objects, converter, alignedVars); |
| alignmentValueAttr = builder.getI64IntegerAttr(alignment); |
| // All the list items in a aligned clause will have same alignment |
| for (std::size_t i = 0; i < objects.size(); i++) |
| alignments.push_back(alignmentValueAttr); |
| } |
| } |
| |
| bool ClauseProcessor::processAligned( |
| mlir::omp::AlignedClauseOps &result) const { |
| return findRepeatableClause<omp::clause::Aligned>( |
| [&](const omp::clause::Aligned &clause, const parser::CharBlock &) { |
| addAlignedClause(converter, clause, result.alignedVars, |
| result.alignments); |
| }); |
| } |
| |
| bool ClauseProcessor::processAllocate( |
| mlir::omp::AllocateClauseOps &result) const { |
| return findRepeatableClause<omp::clause::Allocate>( |
| [&](const omp::clause::Allocate &clause, const parser::CharBlock &) { |
| genAllocateClause(converter, clause, result.allocatorVars, |
| result.allocateVars); |
| }); |
| } |
| |
| bool ClauseProcessor::processCopyin() const { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint(); |
| firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); |
| auto checkAndCopyHostAssociateVar = |
| [&](semantics::Symbol *sym, |
| mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) { |
| assert(sym->has<semantics::HostAssocDetails>() && |
| "No host-association found"); |
| if (converter.isPresentShallowLookup(*sym)) |
| converter.copyHostAssociateVar(*sym, copyAssignIP); |
| }; |
| bool hasCopyin = findRepeatableClause<omp::clause::Copyin>( |
| [&](const omp::clause::Copyin &clause, const parser::CharBlock &) { |
| for (const omp::Object &object : clause.v) { |
| semantics::Symbol *sym = object.sym(); |
| assert(sym && "Expecting symbol"); |
| if (const auto *commonDetails = |
| sym->detailsIf<semantics::CommonBlockDetails>()) { |
| for (const auto &mem : commonDetails->objects()) |
| checkAndCopyHostAssociateVar(&*mem, &insPt); |
| break; |
| } |
| |
| assert(sym->has<semantics::HostAssocDetails>() && |
| "No host-association found"); |
| checkAndCopyHostAssociateVar(sym); |
| } |
| }); |
| |
| // [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to |
| // the execution of the associated structured block. Emit implicit barrier to |
| // synchronize threads and avoid data races on propagation master's thread |
| // values of threadprivate variables to local instances of that variables of |
| // all other implicit threads. |
| |
| // All copies are inserted at either "insPt" (i.e. immediately before it), |
| // or at some earlier point (as determined by "copyHostAssociateVar"). |
| // Unless the insertion point is given to "copyHostAssociateVar" explicitly, |
| // it will not restore the builder's insertion point. Since the copies may be |
| // inserted in any order (not following the execution order), make sure the |
| // barrier is inserted following all of them. |
| firOpBuilder.restoreInsertionPoint(insPt); |
| if (hasCopyin) |
| mlir::omp::BarrierOp::create(firOpBuilder, converter.getCurrentLocation()); |
| return hasCopyin; |
| } |
| |
| /// Class that extracts information from the specified type. |
| class TypeInfo { |
| public: |
| TypeInfo(mlir::Type ty) { typeScan(ty); } |
| |
| // Returns the length of character types. |
| std::optional<fir::CharacterType::LenType> getCharLength() const { |
| return charLen; |
| } |
| |
| // Returns the shape of array types. |
| llvm::ArrayRef<int64_t> getShape() const { return shape; } |
| |
| // Is the type inside a box? |
| bool isBox() const { return inBox; } |
| |
| bool isBoxChar() const { return inBoxChar; } |
| |
| private: |
| void typeScan(mlir::Type type); |
| |
| std::optional<fir::CharacterType::LenType> charLen; |
| llvm::SmallVector<int64_t> shape; |
| bool inBox = false; |
| bool inBoxChar = false; |
| }; |
| |
| void TypeInfo::typeScan(mlir::Type ty) { |
| if (auto sty = mlir::dyn_cast<fir::SequenceType>(ty)) { |
| assert(shape.empty() && !sty.getShape().empty()); |
| shape = llvm::SmallVector<int64_t>(sty.getShape()); |
| typeScan(sty.getEleTy()); |
| } else if (auto bty = mlir::dyn_cast<fir::BoxType>(ty)) { |
| inBox = true; |
| typeScan(bty.getEleTy()); |
| } else if (auto cty = mlir::dyn_cast<fir::ClassType>(ty)) { |
| inBox = true; |
| typeScan(cty.getEleTy()); |
| } else if (auto cty = mlir::dyn_cast<fir::CharacterType>(ty)) { |
| charLen = cty.getLen(); |
| } else if (auto cty = mlir::dyn_cast<fir::BoxCharType>(ty)) { |
| inBoxChar = true; |
| typeScan(cty.getEleTy()); |
| } else if (auto hty = mlir::dyn_cast<fir::HeapType>(ty)) { |
| typeScan(hty.getEleTy()); |
| } else if (auto pty = mlir::dyn_cast<fir::PointerType>(ty)) { |
| typeScan(pty.getEleTy()); |
| } else { |
| // The scan ends when reaching any built-in, record or boxproc type. |
| // A `none` element type is reached for unlimited polymorphic entities |
| // (e.g. `class(*)`), which are always inside a box; the copy is then |
| // performed through the descriptor, so no scalar type info is needed. |
| assert(ty.isIntOrIndexOrFloat() || mlir::isa<mlir::ComplexType>(ty) || |
| mlir::isa<fir::LogicalType>(ty) || mlir::isa<fir::RecordType>(ty) || |
| mlir::isa<fir::BoxProcType>(ty) || |
| (inBox && mlir::isa<mlir::NoneType>(ty))); |
| } |
| } |
| |
| // Create a function that performs a copy between two variables, compatible |
| // with their types and attributes. |
| static mlir::func::FuncOp |
| createCopyFunc(mlir::Location loc, lower::AbstractConverter &converter, |
| mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) { |
| fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
| mlir::ModuleOp module = builder.getModule(); |
| mlir::Type eleTy = fir::unwrapRefType(varType); |
| TypeInfo typeInfo(eleTy); |
| std::string copyFuncName = |
| fir::getTypeAsString(varType, builder.getKindMap(), "_copy"); |
| |
| if (auto decl = module.lookupSymbol<mlir::func::FuncOp>(copyFuncName)) |
| return decl; |
| |
| // create function |
| mlir::OpBuilder::InsertionGuard guard(builder); |
| mlir::OpBuilder modBuilder(module.getBodyRegion()); |
| llvm::SmallVector<mlir::Type> argsTy = {varType, varType}; |
| auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {}); |
| mlir::func::FuncOp funcOp = |
| mlir::func::FuncOp::create(modBuilder, loc, copyFuncName, funcType); |
| funcOp.setVisibility(mlir::SymbolTable::Visibility::Private); |
| fir::factory::setInternalLinkage(funcOp); |
| builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy, |
| {loc, loc}); |
| builder.setInsertionPointToStart(&funcOp.getRegion().back()); |
| // generate body |
| fir::FortranVariableFlagsAttr attrs; |
| if (varAttrs != fir::FortranVariableFlagsEnum::None) |
| attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs); |
| mlir::Value shape; |
| if (!typeInfo.isBox() && !typeInfo.getShape().empty()) { |
| llvm::SmallVector<mlir::Value> extents; |
| for (auto extent : typeInfo.getShape()) |
| extents.push_back( |
| builder.createIntegerConstant(loc, builder.getIndexType(), extent)); |
| shape = fir::ShapeOp::create(builder, loc, extents); |
| } |
| mlir::Value dst = funcOp.getArgument(0); |
| mlir::Value src = funcOp.getArgument(1); |
| llvm::SmallVector<mlir::Value> typeparams; |
| if (typeInfo.isBoxChar()) { |
| // fir.boxchar will be passed here as fir.ref<fir.boxchar> |
| auto loadDst = fir::LoadOp::create(builder, loc, dst); |
| auto loadSrc = fir::LoadOp::create(builder, loc, src); |
| // get the actual fir.ref<fir.char> type |
| mlir::Type refType = |
| fir::ReferenceType::get(mlir::cast<fir::BoxCharType>(eleTy).getEleTy()); |
| auto unboxedDst = fir::UnboxCharOp::create(builder, loc, refType, |
| builder.getIndexType(), loadDst); |
| auto unboxedSrc = fir::UnboxCharOp::create(builder, loc, refType, |
| builder.getIndexType(), loadSrc); |
| // Add length to type parameters |
| typeparams.push_back(unboxedDst.getResult(1)); |
| dst = unboxedDst.getResult(0); |
| src = unboxedSrc.getResult(0); |
| } else if (typeInfo.getCharLength().has_value()) { |
| mlir::Value charLen = builder.createIntegerConstant( |
| loc, builder.getCharacterLengthType(), *typeInfo.getCharLength()); |
| typeparams.push_back(charLen); |
| } |
| auto declDst = hlfir::DeclareOp::create( |
| builder, loc, dst, copyFuncName + "_dst", shape, typeparams, |
| /*dummy_scope=*/nullptr, /*storage=*/nullptr, |
| /*storage_offset=*/0, attrs); |
| auto declSrc = hlfir::DeclareOp::create( |
| builder, loc, src, copyFuncName + "_src", shape, typeparams, |
| /*dummy_scope=*/nullptr, /*storage=*/nullptr, |
| /*storage_offset=*/0, attrs); |
| converter.copyVar(loc, declDst.getBase(), declSrc.getBase(), varAttrs); |
| mlir::func::ReturnOp::create(builder, loc); |
| return funcOp; |
| } |
| |
| bool ClauseProcessor::processCopyprivate( |
| mlir::Location currentLocation, |
| mlir::omp::CopyprivateClauseOps &result) const { |
| auto addCopyPrivateVar = [&](semantics::Symbol *sym) { |
| mlir::Value symVal = converter.getSymbolAddress(*sym); |
| auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>(); |
| if (!declOp) |
| fir::emitFatalError(currentLocation, |
| "COPYPRIVATE is supported only in HLFIR mode"); |
| symVal = declOp.getBase(); |
| mlir::Type symType = symVal.getType(); |
| fir::FortranVariableFlagsEnum attrs = |
| declOp.getFortranAttrs().has_value() |
| ? *declOp.getFortranAttrs() |
| : fir::FortranVariableFlagsEnum::None; |
| mlir::Value cpVar = symVal; |
| |
| // CopyPrivate variables must be passed by reference. However, in the case |
| // of assumed shapes/vla the type is not a !fir.ref, but a !fir.box. |
| // In the case of character types, the passed in type can also be |
| // !fir.boxchar. In these cases to retrieve the appropriate |
| // !fir.ref<!fir.box<...>> or !fir.ref<!fir.boxchar<..>> to access the data |
| // we need we must perform an alloca and then store to it and retrieve the |
| // data from the new alloca. |
| if (mlir::isa<fir::BaseBoxType>(symType) || |
| mlir::isa<fir::BoxCharType>(symType)) { |
| fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
| auto alloca = fir::AllocaOp::create(builder, currentLocation, symType); |
| fir::StoreOp::create(builder, currentLocation, symVal, alloca); |
| cpVar = alloca; |
| } |
| |
| result.copyprivateVars.push_back(cpVar); |
| mlir::func::FuncOp funcOp = |
| createCopyFunc(currentLocation, converter, cpVar.getType(), attrs); |
| result.copyprivateSyms.push_back(mlir::SymbolRefAttr::get(funcOp)); |
| }; |
| |
| bool hasCopyPrivate = findRepeatableClause<clause::Copyprivate>( |
| [&](const clause::Copyprivate &clause, const parser::CharBlock &) { |
| for (const Object &object : clause.v) { |
| semantics::Symbol *sym = object.sym(); |
| if (const auto *commonDetails = |
| sym->detailsIf<semantics::CommonBlockDetails>()) { |
| for (const auto &mem : commonDetails->objects()) |
| addCopyPrivateVar(&*mem); |
| break; |
| } |
| addCopyPrivateVar(sym); |
| } |
| }); |
| |
| return hasCopyPrivate; |
| } |
| |
| template <typename T> |
| static bool isVectorSubscript(const evaluate::Expr<T> &expr) { |
| if (std::optional<evaluate::DataRef> dataRef{evaluate::ExtractDataRef(expr)}) |
| if (const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u)) |
| for (const evaluate::Subscript &subscript : arrayRef->subscript()) |
| if (std::holds_alternative<evaluate::IndirectSubscriptIntegerExpr>( |
| subscript.u)) |
| if (subscript.Rank() > 0) |
| return true; |
| return false; |
| } |
| |
| bool ClauseProcessor::processDefaultMap(lower::StatementContext &stmtCtx, |
| DefaultMapsTy &result) const { |
| auto process = [&](const omp::clause::Defaultmap &clause, |
| const parser::CharBlock &) { |
| using Defmap = omp::clause::Defaultmap; |
| clause::Defaultmap::VariableCategory variableCategory = |
| Defmap::VariableCategory::All; |
| // Variable Category is optional, if not specified defaults to all. |
| // Multiples of the same category are illegal as are any other |
| // defaultmaps being specified when a user specified all is in place, |
| // however, this should be handled earlier during semantics. |
| if (auto varCat = |
| std::get<std::optional<Defmap::VariableCategory>>(clause.t)) |
| variableCategory = varCat.value(); |
| auto behaviour = std::get<Defmap::ImplicitBehavior>(clause.t); |
| result[variableCategory] = behaviour; |
| }; |
| return findRepeatableClause<omp::clause::Defaultmap>(process); |
| } |
| |
| bool ClauseProcessor::processDepend(lower::SymMap &symMap, |
| lower::StatementContext &stmtCtx, |
| mlir::omp::DependClauseOps &result) const { |
| auto process = [&](const omp::clause::Depend &clause, |
| const parser::CharBlock &) { |
| auto depType = std::get<clause::DependenceType>(clause.t); |
| auto &objects = std::get<omp::ObjectList>(clause.t); |
| fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
| mlir::Location clauseLocation = converter.getCurrentLocation(); |
| |
| mlir::omp::ClauseTaskDependAttr dependTypeOperand = |
| genDependKindAttr(converter, depType); |
| |
| auto genDependVar = |
| [&](const omp::Object &object, lower::SymMap &localSymMap, |
| lower::StatementContext &localStmtCtx) -> mlir::Value { |
| assert(object.ref() && "Expecting designator"); |
| mlir::Value dependVar; |
| SomeExpr expr = *object.ref(); |
| |
| if (evaluate::IsArrayElement(expr) || evaluate::ExtractSubstring(expr)) { |
| // Array Section or character (sub)string |
| if (isVectorSubscript(expr)) { |
| // OpenMP needs the address of the first indexed element (required by |
| // the standard to be the lowest index) to identify the dependency. We |
| // don't need an accurate length for the array section because the |
| // OpenMP standard forbids overlapping array sections. |
| dependVar = genVectorSubscriptedDesignatorFirstElementAddress( |
| clauseLocation, converter, expr, localSymMap, localStmtCtx); |
| } else { |
| // Ordinary array section e.g. A(1:512:2) |
| hlfir::EntityWithAttributes entity = convertExprToHLFIR( |
| clauseLocation, converter, expr, localSymMap, localStmtCtx); |
| dependVar = entity.getBase(); |
| } |
| } else if (evaluate::isStructureComponent(expr) || |
| evaluate::ExtractComplexPart(expr)) { |
| SomeExpr expr = *object.ref(); |
| hlfir::EntityWithAttributes entity = convertExprToHLFIR( |
| clauseLocation, converter, expr, localSymMap, localStmtCtx); |
| dependVar = entity.getBase(); |
| } else { |
| semantics::Symbol *sym = object.sym(); |
| dependVar = converter.getSymbolAddress(*sym); |
| } |
| |
| // If we pass a mutable box e.g. !fir.ref<!fir.box<!fir.heap<...>>> then |
| // the runtime will use the address of the box not the address of the |
| // data. Flang generates a lot of memcpys between different box |
| // allocations so this is not a reliable way to identify the dependency. |
| if (auto ref = mlir::dyn_cast<fir::ReferenceType>(dependVar.getType())) |
| if (fir::isa_box_type(ref.getElementType())) |
| dependVar = fir::LoadOp::create(builder, clauseLocation, dependVar); |
| |
| // The openmp dialect doesn't know what to do with boxes (and it would |
| // break layering to teach it about them). The dependency variable can be |
| // a box because it was an array section or because the original symbol |
| // was mapped to a box. |
| // Getting the address of the box data is okay because all the runtime |
| // ultimately cares about is the base address of the array. |
| if (fir::isa_box_type(dependVar.getType())) |
| dependVar = fir::BoxAddrOp::create(builder, clauseLocation, dependVar); |
| |
| return dependVar; |
| }; |
| |
| auto &iteratorModifier = |
| std::get<std::optional<omp::clause::Iterator>>(clause.t); |
| |
| llvm::SmallVector<IteratorRange> iteratorRanges; |
| llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4> ivSyms; |
| collectIteratorIVs(clause, converter, stmtCtx, iteratorRanges, ivSyms); |
| |
| mlir::Type ptrTy = |
| mlir::LLVM::LLVMPointerType::get(&converter.getMLIRContext()); |
| mlir::Type iterTy = |
| mlir::omp::IteratedType::get(&converter.getMLIRContext(), ptrTy); |
| |
| for (const omp::Object &object : objects) { |
| if (iteratorModifier.has_value() && |
| hasIteratorIVReference(object, ivSyms)) { |
| mlir::Value iterHandle = buildIteratorOp( |
| converter, clauseLocation, iterTy, iteratorRanges, |
| [&](fir::FirOpBuilder &builder, mlir::Location loc, |
| llvm::ArrayRef<mlir::Value> /*ivs*/) -> mlir::Value { |
| lower::StatementContext iterStmtCtx; |
| if (std::optional<llvm::SmallVector<mlir::Value>> loweredIndices = |
| getIteratorElementIndices(converter, object, iterStmtCtx, |
| loc)) { |
| const Fortran::semantics::Symbol *sym = object.sym(); |
| assert(sym && "expected symbol for iterator object"); |
| // We currently cannot reuse genDependVar here because |
| // buildIteratorOp maps iterator IV symbols to bare scalar |
| // values (e.g. i32), but genDependVar uses convertExprToHLFIR |
| // which expects memory-backed references. Instead, manually get |
| // the base address and compute the element coordinate from the |
| // FIR-level lowered indices. |
| fir::factory::AddrAndBoundsInfo info = |
| Fortran::lower::getDataOperandBaseAddr( |
| converter, builder, *sym, loc, |
| /*unwrapFirBox=*/false); |
| hlfir::Entity entity{info.addr}; |
| mlir::Value iteratedAddr = genIteratorCoordinate( |
| converter, entity, *loweredIndices, loc); |
| // Convert to !llvm.ptr for the omp.yield |
| return fir::ConvertOp::create(builder, loc, ptrTy, |
| iteratedAddr); |
| } |
| |
| TODO(loc, "object type not supported by iterator modifier"); |
| }); |
| result.dependIterated.push_back(iterHandle); |
| result.dependIteratedKinds.push_back(dependTypeOperand); |
| } else { |
| result.dependVars.push_back(genDependVar(object, symMap, stmtCtx)); |
| result.dependKinds.push_back(dependTypeOperand); |
| } |
| } |
| }; |
| |
| return findRepeatableClause<omp::clause::Depend>(process); |
| } |
| |
| bool ClauseProcessor::processDynGroupprivate( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::DynGroupprivateClauseOps &result) const { |
| using DynGroupprivate = omp::clause::DynGroupprivate; |
| |
| // OpenMP 6.1 allows the `dyn_groupprivate` clause to appear more than once |
| // on the same construct (with distinct access-group modifiers). Semantics |
| // already rejects two clauses sharing the same access-group, but multiple |
| // clauses with different access-groups are spec-legal. The current MLIR |
| // representation (`mlir::omp::DynGroupprivateClauseOps`) and the OMPIRBuilder |
| // only support a single set of modifiers + size, so reject the multi-clause |
| // form up-front. |
| unsigned count = 0; |
| parser::CharBlock duplicateSource; |
| findRepeatableClause<DynGroupprivate>( |
| [&](const DynGroupprivate &, const parser::CharBlock &source) { |
| if (++count == 2) |
| duplicateSource = source; |
| }); |
| if (count > 1) { |
| TODO(converter.genLocation(duplicateSource), |
| "multiple dyn_groupprivate clauses on the same construct"); |
| } |
| |
| if (auto *clause = findUniqueClause<DynGroupprivate>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::MLIRContext *context = firOpBuilder.getContext(); |
| |
| // Process AccessGroup modifier (cgroup). |
| if (auto accessGroup = |
| std::get<std::optional<DynGroupprivate::AccessGroup>>(clause->t)) { |
| switch (*accessGroup) { |
| case DynGroupprivate::AccessGroup::Cgroup: |
| result.dynGroupprivateAccessGroup = |
| mlir::omp::AccessGroupModifierAttr::get( |
| context, mlir::omp::AccessGroupModifier::cgroup); |
| break; |
| } |
| } |
| |
| // Process Fallback modifier (abort, default_mem, null). |
| if (auto fallback = |
| std::get<std::optional<DynGroupprivate::Fallback>>(clause->t)) { |
| switch (*fallback) { |
| case DynGroupprivate::Fallback::Abort: |
| result.dynGroupprivateFallback = mlir::omp::FallbackModifierAttr::get( |
| context, mlir::omp::FallbackModifier::abort); |
| break; |
| case DynGroupprivate::Fallback::Default_Mem: |
| result.dynGroupprivateFallback = mlir::omp::FallbackModifierAttr::get( |
| context, mlir::omp::FallbackModifier::default_mem); |
| break; |
| case DynGroupprivate::Fallback::Null: |
| result.dynGroupprivateFallback = mlir::omp::FallbackModifierAttr::get( |
| context, mlir::omp::FallbackModifier::null); |
| break; |
| } |
| } |
| |
| // Process size expression. |
| const auto &sizeExpr = std::get<SomeExpr>(clause->t); |
| result.dynGroupprivateSize = |
| fir::getBase(converter.genExprValue(sizeExpr, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processGrainsize( |
| lower::StatementContext &stmtCtx, |
| mlir::omp::GrainsizeClauseOps &result) const { |
| using Grainsize = omp::clause::Grainsize; |
| if (auto *clause = findUniqueClause<Grainsize>()) { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::MLIRContext *context = firOpBuilder.getContext(); |
| const auto &modifier = |
| std::get<std::optional<Grainsize::Prescriptiveness>>(clause->t); |
| if (modifier && *modifier == Grainsize::Prescriptiveness::Strict) { |
| result.grainsizeMod = mlir::omp::ClauseGrainsizeTypeAttr::get( |
| context, mlir::omp::ClauseGrainsizeType::Strict); |
| } |
| const auto &grainsizeExpr = std::get<omp::SomeExpr>(clause->t); |
| result.grainsize = |
| fir::getBase(converter.genExprValue(grainsizeExpr, stmtCtx)); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processHasDeviceAddr( |
| lower::StatementContext &stmtCtx, mlir::omp::HasDeviceAddrClauseOps &result, |
| llvm::SmallVectorImpl<Object> &hasDeviceObjects) const { |
| // For HAS_DEVICE_ADDR objects, implicitly map the top-level entities. |
| // Their address (or the whole descriptor, if the entity had one) will be |
| // passed to the target region. |
| std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; |
| bool clauseFound = findRepeatableClause<omp::clause::HasDeviceAddr>( |
| [&](const omp::clause::HasDeviceAddr &clause, |
| const parser::CharBlock &source) { |
| mlir::Location location = converter.genLocation(source); |
| mlir::omp::ClauseMapFlags mapTypeBits = |
| mlir::omp::ClauseMapFlags::to | mlir::omp::ClauseMapFlags::implicit; |
| omp::ObjectList baseObjects; |
| llvm::transform(clause.v, std::back_inserter(baseObjects), |
| [&](const omp::Object &object) { |
| if (auto maybeBase = getBaseObject(object, semaCtx)) |
| return *maybeBase; |
| return object; |
| }); |
| processMapObjects(stmtCtx, location, baseObjects, mapTypeBits, |
| parentMemberIndices, result.hasDeviceAddrVars, |
| hasDeviceObjects); |
| }); |
| |
| insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, |
| result.hasDeviceAddrVars, hasDeviceObjects); |
| return clauseFound; |
| } |
| |
| bool ClauseProcessor::processIf( |
| omp::clause::If::DirectiveNameModifier directiveName, |
| mlir::omp::IfClauseOps &result) const { |
| bool found = false; |
| findRepeatableClause<omp::clause::If>([&](const omp::clause::If &clause, |
| const parser::CharBlock &source) { |
| mlir::Location clauseLocation = converter.genLocation(source); |
| mlir::Value operand = |
| getIfClauseOperand(converter, clause, directiveName, clauseLocation); |
| // Assume that, at most, a single 'if' clause will be applicable to the |
| // given directive. |
| if (operand) { |
| result.ifExpr = operand; |
| found = true; |
| } |
| }); |
| return found; |
| } |
| |
| template <typename T> |
| void collectReductionObjects(const T &reduction, |
| llvm::SmallVectorImpl<Object> &reductionObjects) { |
| const omp::ObjectList &objectList{std::get<omp::ObjectList>(reduction.t)}; |
| reductionObjects.reserve(objectList.size()); |
| llvm::copy(objectList, std::back_inserter(reductionObjects)); |
| } |
| |
| static llvm::SmallVector<const semantics::Symbol *> |
| getObjectsSyms(llvm::ArrayRef<Object> objects) { |
| llvm::SmallVector<const semantics::Symbol *> syms; |
| syms.reserve(objects.size()); |
| llvm::transform( |
| objects, std::back_inserter(syms), |
| [](const Fortran::lower::omp::Object &object) { return object.sym(); }); |
| return syms; |
| } |
| |
| bool ClauseProcessor::processInReduction( |
| mlir::Location currentLocation, mlir::omp::InReductionClauseOps &result, |
| llvm::SmallVectorImpl<Object> &outReductionObjects) const { |
| return findRepeatableClause<omp::clause::InReduction>( |
| [&](const omp::clause::InReduction &clause, const parser::CharBlock &) { |
| llvm::SmallVector<mlir::Value> inReductionVars; |
| llvm::SmallVector<bool> inReduceVarByRef; |
| llvm::SmallVector<mlir::Attribute> inReductionDeclSymbols; |
| llvm::SmallVector<Object> inReductionObjects; |
| collectReductionObjects(clause, inReductionObjects); |
| llvm::SmallVector<const semantics::Symbol *> inReductionSyms = |
| getObjectsSyms(inReductionObjects); |
| |
| ReductionProcessor rp; |
| if (!rp.processReductionArguments<mlir::omp::DeclareReductionOp>( |
| currentLocation, converter, |
| std::get<typename omp::clause::ReductionOperatorList>(clause.t), |
| inReductionVars, inReduceVarByRef, inReductionDeclSymbols, |
| inReductionSyms, inReductionObjects, converter.getSymbolMap())) |
| TODO(currentLocation, "Lowering unrecognised reduction type"); |
| |
| // Copy local lists into the output. |
| llvm::copy(inReductionVars, std::back_inserter(result.inReductionVars)); |
| llvm::copy(inReduceVarByRef, |
| std::back_inserter(result.inReductionByref)); |
| llvm::copy(inReductionDeclSymbols, |
| std::back_inserter(result.inReductionSyms)); |
| llvm::copy(inReductionObjects, std::back_inserter(outReductionObjects)); |
| }); |
| } |
| |
| bool ClauseProcessor::processIsDevicePtr( |
| lower::StatementContext &stmtCtx, mlir::omp::IsDevicePtrClauseOps &result, |
| llvm::SmallVectorImpl<Object> &isDeviceObjects) const { |
| std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; |
| bool clauseFound = findRepeatableClause<omp::clause::IsDevicePtr>( |
| [&](const omp::clause::IsDevicePtr &clause, |
| const parser::CharBlock &source) { |
| mlir::Location location = converter.genLocation(source); |
| // Force a map so the descriptor is materialized on the device with the |
| // device address inside. |
| mlir::omp::ClauseMapFlags mapTypeBits = |
| mlir::omp::ClauseMapFlags::is_device_ptr | |
| mlir::omp::ClauseMapFlags::to; |
| processMapObjects(stmtCtx, location, clause.v, mapTypeBits, |
| parentMemberIndices, result.isDevicePtrVars, |
| isDeviceObjects); |
| }); |
| |
| insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, |
| result.isDevicePtrVars, isDeviceObjects); |
| return clauseFound; |
| } |
| |
| bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result, |
| bool isDeclareSimd) const { |
| lower::StatementContext stmtCtx; |
| std::vector<mlir::Attribute> typeAttrs; |
| std::vector<mlir::Attribute> linearModAttrs; |
| return findRepeatableClause< |
| omp::clause::Linear>([&](const omp::clause::Linear &clause, |
| const parser::CharBlock &) { |
| auto &objects = std::get<omp::ObjectList>(clause.t); |
| |
| std::optional<mlir::omp::LinearModifier> explicitLinearMod; |
| if (auto &linearModifier = |
| std::get<std::optional<omp::clause::Linear::LinearModifier>>( |
| clause.t)) { |
| switch (*linearModifier) { |
| case omp::clause::Linear::LinearModifier::Val: |
| explicitLinearMod = mlir::omp::LinearModifier::val; |
| break; |
| case omp::clause::Linear::LinearModifier::Ref: |
| explicitLinearMod = mlir::omp::LinearModifier::ref; |
| break; |
| case omp::clause::Linear::LinearModifier::Uval: |
| explicitLinearMod = mlir::omp::LinearModifier::uval; |
| break; |
| } |
| } |
| |
| for (const omp::Object &object : objects) { |
| semantics::Symbol *sym = object.sym(); |
| const mlir::Value variable = converter.getSymbolAddress(*sym); |
| result.linearVars.push_back(variable); |
| mlir::Type ty = converter.genType(*sym); |
| typeAttrs.push_back(mlir::TypeAttr::get(ty)); |
| |
| if (auto &mod = |
| std::get<std::optional<omp::clause::Linear::StepComplexModifier>>( |
| clause.t)) { |
| mlir::Value operand = |
| fir::getBase(converter.genExprValue(toEvExpr(*mod), stmtCtx)); |
| result.linearStepVars.append(objects.size(), operand); |
| } else { |
| // If nothing is present, add the default step of 1. |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| mlir::Location currentLocation = converter.getCurrentLocation(); |
| mlir::Type integerTy = ty.isInteger() ? ty : firOpBuilder.getI32Type(); |
| mlir::Value operand = |
| firOpBuilder.createIntegerConstant(currentLocation, integerTy, 1); |
| result.linearStepVars.append(objects.size(), operand); |
| } |
| |
| // Determine the linear modifier: |
| // 1. Use explicit modifier if provided. |
| // 2. For OpenMP >= 5.2 (Section 5.4.6: "the default linear-modifier |
| // is val"): |
| // - declare simd: "ref" for POINTER or non-VALUE dummy args, |
| // "val" otherwise. |
| // - do/simd: always "val". |
| // 3. Otherwise, leave unset (UnitAttr placeholder). |
| auto getDeclareSimdDefaultMod = [](const semantics::Symbol &sym) { |
| const auto &ultimate = sym.GetUltimate(); |
| if (semantics::IsPointer(ultimate)) |
| return mlir::omp::LinearModifier::ref; |
| if (const auto *obj = |
| ultimate.detailsIf<semantics::ObjectEntityDetails>()) |
| if (obj->isDummy() && !semantics::IsValue(ultimate)) |
| return mlir::omp::LinearModifier::ref; |
| return mlir::omp::LinearModifier::val; |
| }; |
| |
| std::optional<mlir::omp::LinearModifier> linearMod; |
| if (explicitLinearMod) |
| linearMod = *explicitLinearMod; |
| else if (semaCtx.langOptions().OpenMPVersion >= 52) |
| linearMod = isDeclareSimd ? getDeclareSimdDefaultMod(*sym) |
| : mlir::omp::LinearModifier::val; |
| |
| if (linearMod) |
| linearModAttrs.push_back(mlir::omp::LinearModifierAttr::get( |
| &converter.getMLIRContext(), *linearMod)); |
| else |
| linearModAttrs.push_back( |
| mlir::UnitAttr::get(&converter.getMLIRContext())); |
| } |
| result.linearVarTypes = |
| mlir::ArrayAttr::get(&converter.getMLIRContext(), typeAttrs); |
| result.linearModifiers = |
| mlir::ArrayAttr::get(&converter.getMLIRContext(), linearModAttrs); |
| }); |
| } |
| |
| bool ClauseProcessor::processLink( |
| llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const { |
| return findRepeatableClause<omp::clause::Link>( |
| [&](const omp::clause::Link &clause, const parser::CharBlock &) { |
| // Case: declare target link(var1, var2)... |
| gatherFuncAndVarSyms( |
| clause.v, mlir::omp::DeclareTargetCaptureClause::link, result, |
| /*automap=*/false); |
| }); |
| } |
| |
| void ClauseProcessor::processMapObjects( |
| lower::StatementContext &stmtCtx, mlir::Location clauseLocation, |
| const omp::ObjectList &objects, mlir::omp::ClauseMapFlags mapTypeBits, |
| std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices, |
| llvm::SmallVectorImpl<mlir::Value> &mapVars, |
| llvm::SmallVectorImpl<Object> &mapObjects, llvm::StringRef mapperIdNameRef, |
| bool isMotionModifier, llvm::omp::Directive directive) const { |
| fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); |
| |
| for (const omp::Object &object : objects) { |
| llvm::SmallVector<mlir::Value> bounds; |
| std::stringstream asFortran; |
| std::optional<omp::Object> parentObj; |
| |
| fir::factory::AddrAndBoundsInfo info = |
| lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp, |
| mlir::omp::MapBoundsType>( |
| converter, firOpBuilder, semaCtx, stmtCtx, *object.sym(), |
| object.ref(), clauseLocation, asFortran, bounds, |
| treatIndexAsSection); |
| |
| mlir::Value baseOp = info.rawInput; |
| if (object.sym()->owner().IsDerivedType() && !isMotionModifier) { |
| omp::ObjectList objectList = gatherObjectsOf(object, semaCtx); |
| assert(!objectList.empty() && |
| "could not find parent objects of derived type member"); |
| parentObj = objectList[0]; |
| parentMemberIndices.emplace(parentObj.value(), |
| OmpMapParentAndMemberData{}); |
| |
| if (isMemberOrParentAllocatableOrPointer(object, semaCtx)) { |
| llvm::SmallVector<int64_t> indices; |
| generateMemberPlacementIndices(object, indices, semaCtx); |
| baseOp = createParentSymAndGenIntermediateMaps( |
| clauseLocation, converter, semaCtx, stmtCtx, objectList, indices, |
| parentMemberIndices[parentObj.value()], asFortran.str(), |
| mapTypeBits); |
| } |
| } |
| |
| mlir::FlatSymbolRefAttr mapperId = |
| resolveMapperId(converter, clauseLocation, object, mapperIdNameRef, |
| mapTypeBits, directive, parentObj.has_value()); |
| |
| // Explicit map captures are captured ByRef by default, |
| // optimisation passes may alter this to ByCopy or other capture |
| // types to optimise |
| auto location = mlir::NameLoc::get( |
| mlir::StringAttr::get(firOpBuilder.getContext(), asFortran.str()), |
| baseOp.getLoc()); |
| mlir::omp::MapInfoOp mapOp = utils::openmp::createMapInfoOp( |
| firOpBuilder, location, baseOp, |
| /*varPtrPtr=*/mlir::Value{}, asFortran.str(), bounds, |
| /*members=*/{}, /*membersIndex=*/mlir::ArrayAttr{}, mapTypeBits, |
| mlir::omp::VariableCaptureKind::ByRef, baseOp.getType(), |
| /*partialMap=*/false, mapperId); |
| |
| if (parentObj.has_value()) { |
| parentMemberIndices[parentObj.value()].addChildIndexAndMapToParent( |
| object, mapOp, semaCtx); |
| } else { |
| mapVars.push_back(mapOp); |
| mapObjects.push_back(object); |
| } |
| } |
| } |
| |
| /// Extract and mangle the mapper identifier name from a mapper clause. |
| /// Returns "__implicit_mapper" if no mapper is specified, or "default" if |
| /// the default mapper is specified, otherwise returns the mangled mapper name. |
| /// This handles both the Map clause (which uses a vector of mappers) and |
| /// To/From clauses (which use a DefinedOperator). |
| template <typename MapperType> |
| static std::string |
| getMapperIdentifier(lower::AbstractConverter &converter, |
| const std::optional<MapperType> &mapper) { |
| if (!mapper) |
| return "__implicit_mapper"; |
| |
| // Handle mapper types (both have the same structure) |
| assert(mapper->size() == 1 && "more than one mapper"); |
| const semantics::Symbol *mapperSym = mapper->front().v.id().symbol; |
| |
| std::string mapperIdName = mapperSym->name().ToString(); |
| if (mapperIdName != "default") { |
| // Mangle with the ultimate owner so that use-associated mapper |
| // identifiers resolve to the same symbol as their defining scope. |
| const semantics::Symbol &ultimate = mapperSym->GetUltimate(); |
| mapperIdName = converter.mangleName(mapperIdName, ultimate.owner()); |
| } |
| return mapperIdName; |
| } |
| |
| bool ClauseProcessor::processMap( |
| mlir::Location currentLocation, lower::StatementContext &stmtCtx, |
| mlir::omp::MapClauseOps &result, llvm::omp::Directive directive, |
| llvm::SmallVectorImpl<Object> *mapObjects) const { |
| // We always require tracking of objects, even if the caller does not, |
| // so we create an optionally used local set of objects when the mapObjects |
| // argument is not present. |
| llvm::SmallVector<Object> localMapObjects; |
| llvm::SmallVectorImpl<Object> *ptrMapObjects = |
| mapObjects ? mapObjects : &localMapObjects; |
| std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; |
| |
| auto process = [&](const omp::clause::Map &clause, |
| const parser::CharBlock &source) { |
| using Map = omp::clause::Map; |
| mlir::Location clauseLocation = converter.genLocation(source); |
| const auto &[mapType, typeMods, attachMod, refMod, mappers, iterator, |
| objects] = clause.t; |
| mlir::omp::ClauseMapFlags mapTypeBits = mlir::omp::ClauseMapFlags::none; |
| |
| std::string mapperIdName = getMapperIdentifier(converter, mappers); |
| |
| // If the map type is specified, then process it else set the appropriate |
| // default value |
| Map::MapType type; |
| if (directive == llvm::omp::Directive::OMPD_target_enter_data && |
| semaCtx.langOptions().OpenMPVersion >= 52) |
| type = mapType.value_or(Map::MapType::To); |
| else if (directive == llvm::omp::Directive::OMPD_target_exit_data && |
| semaCtx.langOptions().OpenMPVersion >= 52) |
| type = mapType.value_or(Map::MapType::From); |
| else |
| type = mapType.value_or(Map::MapType::Tofrom); |
| |
| switch (type) { |
| case Map::MapType::To: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::to; |
| break; |
| case Map::MapType::From: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::from; |
| break; |
| case Map::MapType::Tofrom: |
| mapTypeBits |= |
| mlir::omp::ClauseMapFlags::to | mlir::omp::ClauseMapFlags::from; |
| break; |
| case Map::MapType::Storage: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::storage; |
| break; |
| } |
| |
| if (typeMods) { |
| // TODO: Still requires "self" modifier, an OpenMP 6.0+ feature |
| if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Always)) |
| mapTypeBits |= mlir::omp::ClauseMapFlags::always; |
| if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Present)) |
| mapTypeBits |= mlir::omp::ClauseMapFlags::present; |
| if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Close)) |
| mapTypeBits |= mlir::omp::ClauseMapFlags::close; |
| if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Delete)) |
| mapTypeBits |= mlir::omp::ClauseMapFlags::del; |
| if (llvm::is_contained(*typeMods, Map::MapTypeModifier::OmpxHold)) |
| mapTypeBits |= mlir::omp::ClauseMapFlags::ompx_hold; |
| } |
| |
| if (refMod) { |
| switch (*refMod) { |
| case Map::RefModifier::RefPtee: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::ref_ptee; |
| break; |
| case Map::RefModifier::RefPtr: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::ref_ptr; |
| break; |
| case Map::RefModifier::RefPtrPtee: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::ref_ptr | |
| mlir::omp::ClauseMapFlags::ref_ptee; |
| break; |
| } |
| } |
| |
| if (attachMod) { |
| switch (*attachMod) { |
| case Map::AttachModifier::Always: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::attach_always; |
| break; |
| case Map::AttachModifier::Never: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::attach_never; |
| break; |
| case Map::AttachModifier::Auto: |
| mapTypeBits |= mlir::omp::ClauseMapFlags::attach_auto; |
| break; |
| } |
| } |
| |
| if (iterator) |
| TODO(currentLocation, |
| "Support for iterator modifiers is not implemented yet"); |
| TodoLocators(currentLocation, objects); |
| |
| processMapObjects(stmtCtx, clauseLocation, |
| std::get<omp::ObjectList>(clause.t), mapTypeBits, |
| parentMemberIndices, result.mapVars, *ptrMapObjects, |
| mapperIdName, /*isMotionModifier=*/false, directive); |
| }; |
| |
| bool clauseFound = findRepeatableClause<omp::clause::Map>(process); |
| insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, |
| result.mapVars, *ptrMapObjects); |
| |
| return clauseFound; |
| } |
| |
| bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx, |
| mlir::omp::MapClauseOps &result) { |
| std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; |
| llvm::SmallVector<Object> mapObjects; |
| |
| auto callbackFn = [&](const auto &clause, const parser::CharBlock &source) { |
| mlir::Location clauseLocation = converter.genLocation(source); |
| const auto &[expectation, mapper, iterator, objects] = clause.t; |
| |
| mlir::omp::ClauseMapFlags mapTypeBits = |
| std::is_same_v<llvm::remove_cvref_t<decltype(clause)>, omp::clause::To> |
| ? mlir::omp::ClauseMapFlags::to |
| : mlir::omp::ClauseMapFlags::from; |
| if (expectation && *expectation == omp::clause::To::Expectation::Present) |
| mapTypeBits |= mlir::omp::ClauseMapFlags::present; |
| |
| // Support motion modifiers: iterator. |
| std::string mapperIdName = getMapperIdentifier(converter, mapper); |
| |
| if (iterator) |
| TODO(clauseLocation, "Iterator modifier is not supported yet"); |
| TodoLocators(clauseLocation, objects); |
| |
| processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits, |
| parentMemberIndices, result.mapVars, mapObjects, |
| mapperIdName, /*isMotionModifier=*/true); |
| }; |
| |
| bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn); |
| clauseFound = |
| findRepeatableClause<omp::clause::From>(callbackFn) || clauseFound; |
| |
| insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, |
| result.mapVars, mapObjects); |
| |
| return clauseFound; |
| } |
| |
| bool ClauseProcessor::processNontemporal( |
| mlir::omp::NontemporalClauseOps &result) const { |
| return findRepeatableClause<omp::clause::Nontemporal>( |
| [&](const omp::clause::Nontemporal &clause, const parser::CharBlock &) { |
| for (const Object &object : clause.v) { |
| semantics::Symbol *sym = object.sym(); |
| mlir::Value symVal = converter.getSymbolAddress(*sym); |
| result.nontemporalVars.push_back(symVal); |
| } |
| }); |
| } |
| |
| bool ClauseProcessor::processReduction( |
| mlir::Location currentLocation, mlir::omp::ReductionClauseOps &result, |
| llvm::SmallVectorImpl<Object> &outReductionObjects, |
| llvm::DenseMap<const semantics::Symbol *, mlir::Value> *reductionVarCache) |
| const { |
| return findRepeatableClause<omp::clause::Reduction>( |
| [&](const omp::clause::Reduction &clause, const parser::CharBlock &) { |
| llvm::SmallVector<mlir::Value> reductionVars; |
| llvm::SmallVector<bool> reduceVarByRef; |
| llvm::SmallVector<mlir::Attribute> reductionDeclSymbols; |
| llvm::SmallVector<Object> reductionObjects; |
| collectReductionObjects(clause, reductionObjects); |
| llvm::SmallVector<const semantics::Symbol *> reductionSyms = |
| getObjectsSyms(reductionObjects); |
| |
| auto mod = std::get<std::optional<ReductionModifier>>(clause.t); |
| if (mod.has_value()) { |
| result.reductionMod = mlir::omp::ReductionModifierAttr::get( |
| converter.getFirOpBuilder().getContext(), |
| translateReductionModifier(mod.value())); |
| } |
| |
| ReductionProcessor rp; |
| if (!rp.processReductionArguments<mlir::omp::DeclareReductionOp>( |
| currentLocation, converter, |
| std::get<typename omp::clause::ReductionOperatorList>(clause.t), |
| reductionVars, reduceVarByRef, reductionDeclSymbols, |
| reductionSyms, reductionObjects, converter.getSymbolMap(), |
| reductionVarCache)) |
| TODO(currentLocation, "Lowering unrecognised reduction type"); |
| // Copy local lists into the output. |
| llvm::copy(reductionVars, std::back_inserter(result.reductionVars)); |
| llvm::copy(reduceVarByRef, std::back_inserter(result.reductionByref)); |
| llvm::copy(reductionDeclSymbols, |
| std::back_inserter(result.reductionSyms)); |
| llvm::copy(reductionObjects, std::back_inserter(outReductionObjects)); |
| }); |
| } |
| |
| bool ClauseProcessor::processTaskReduction( |
| mlir::Location currentLocation, mlir::omp::TaskReductionClauseOps &result, |
| llvm::SmallVectorImpl<Object> &outReductionObjects) const { |
| return findRepeatableClause<omp::clause::TaskReduction>( |
| [&](const omp::clause::TaskReduction &clause, const parser::CharBlock &) { |
| llvm::SmallVector<mlir::Value> taskReductionVars; |
| llvm::SmallVector<bool> taskReduceVarByRef; |
| llvm::SmallVector<mlir::Attribute> taskReductionDeclSymbols; |
| llvm::SmallVector<Object> taskReductionObjects; |
| collectReductionObjects(clause, taskReductionObjects); |
| llvm::SmallVector<const semantics::Symbol *> taskReductionSyms = |
| getObjectsSyms(taskReductionObjects); |
| |
| ReductionProcessor rp; |
| if (!rp.processReductionArguments<mlir::omp::DeclareReductionOp>( |
| currentLocation, converter, |
| std::get<typename omp::clause::ReductionOperatorList>(clause.t), |
| taskReductionVars, taskReduceVarByRef, taskReductionDeclSymbols, |
| taskReductionSyms, taskReductionObjects, |
| converter.getSymbolMap())) |
| TODO(currentLocation, "Lowering unrecognised reduction type"); |
| // Copy local lists into the output. |
| llvm::copy(taskReductionVars, |
| std::back_inserter(result.taskReductionVars)); |
| llvm::copy(taskReduceVarByRef, |
| std::back_inserter(result.taskReductionByref)); |
| llvm::copy(taskReductionDeclSymbols, |
| std::back_inserter(result.taskReductionSyms)); |
| llvm::copy(taskReductionObjects, |
| std::back_inserter(outReductionObjects)); |
| }); |
| } |
| |
| bool ClauseProcessor::processTo( |
| llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const { |
| return findRepeatableClause<omp::clause::To>( |
| [&](const omp::clause::To &clause, const parser::CharBlock &) { |
| // Case: declare target to(func, var1, var2)... |
| gatherFuncAndVarSyms(std::get<ObjectList>(clause.t), |
| mlir::omp::DeclareTargetCaptureClause::to, result, |
| /*automap=*/false); |
| }); |
| } |
| |
| bool ClauseProcessor::processEnter( |
| llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const { |
| return findRepeatableClause<omp::clause::Enter>( |
| [&](const omp::clause::Enter &clause, const parser::CharBlock &source) { |
| bool automap = |
| std::get<std::optional<omp::clause::Enter::Modifier>>(clause.t) |
| .has_value(); |
| // Case: declare target enter(func, var1, var2)... |
| gatherFuncAndVarSyms(std::get<ObjectList>(clause.t), |
| mlir::omp::DeclareTargetCaptureClause::enter, |
| result, automap); |
| }); |
| } |
| |
| bool ClauseProcessor::processUseDeviceAddr( |
| lower::StatementContext &stmtCtx, mlir::omp::UseDeviceAddrClauseOps &result, |
| llvm::SmallVectorImpl<Object> &useDeviceObjects) const { |
| std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; |
| bool clauseFound = findRepeatableClause<omp::clause::UseDeviceAddr>( |
| [&](const omp::clause::UseDeviceAddr &clause, |
| const parser::CharBlock &source) { |
| mlir::Location location = converter.genLocation(source); |
| mlir::omp::ClauseMapFlags mapTypeBits = |
| mlir::omp::ClauseMapFlags::return_param; |
| processMapObjects(stmtCtx, location, clause.v, mapTypeBits, |
| parentMemberIndices, result.useDeviceAddrVars, |
| useDeviceObjects); |
| }); |
| |
| insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, |
| result.useDeviceAddrVars, useDeviceObjects); |
| return clauseFound; |
| } |
| |
| bool ClauseProcessor::processUseDevicePtr( |
| lower::StatementContext &stmtCtx, mlir::omp::UseDevicePtrClauseOps &result, |
| llvm::SmallVectorImpl<Object> &useDeviceObjects) const { |
| std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; |
| |
| bool clauseFound = findRepeatableClause<omp::clause::UseDevicePtr>( |
| [&](const omp::clause::UseDevicePtr &clause, |
| const parser::CharBlock &source) { |
| mlir::Location location = converter.genLocation(source); |
| mlir::omp::ClauseMapFlags mapTypeBits = |
| mlir::omp::ClauseMapFlags::return_param; |
| processMapObjects(stmtCtx, location, clause.v, mapTypeBits, |
| parentMemberIndices, result.useDevicePtrVars, |
| useDeviceObjects); |
| }); |
| |
| insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, |
| result.useDevicePtrVars, useDeviceObjects); |
| return clauseFound; |
| } |
| |
| bool ClauseProcessor::processUniform( |
| mlir::omp::UniformClauseOps &result) const { |
| return findRepeatableClause<omp::clause::Uniform>( |
| [&](const omp::clause::Uniform &clause, const parser::CharBlock &) { |
| const auto &objects = clause.v; |
| if (!objects.empty()) |
| genObjectList(objects, converter, result.uniformVars); |
| }); |
| } |
| |
| bool ClauseProcessor::processInbranch( |
| mlir::omp::InbranchClauseOps &result) const { |
| if (findUniqueClause<omp::clause::Inbranch>()) { |
| result.inbranch = converter.getFirOpBuilder().getUnitAttr(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClauseProcessor::processNotinbranch( |
| mlir::omp::NotinbranchClauseOps &result) const { |
| if (findUniqueClause<omp::clause::Notinbranch>()) { |
| result.notinbranch = converter.getFirOpBuilder().getUnitAttr(); |
| return true; |
| } |
| return false; |
| } |
| |
| } // namespace omp |
| } // namespace lower |
| } // namespace Fortran |