4486 lines · cpp
1//===-- OpenMP.cpp -- Open MP directive lowering --------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/10//11//===----------------------------------------------------------------------===//12 13#include "flang/Lower/OpenMP.h"14 15#include "Atomic.h"16#include "ClauseProcessor.h"17#include "DataSharingProcessor.h"18#include "Decomposer.h"19#include "Utils.h"20#include "flang/Common/idioms.h"21#include "flang/Evaluate/type.h"22#include "flang/Lower/Bridge.h"23#include "flang/Lower/ConvertCall.h"24#include "flang/Lower/ConvertExpr.h"25#include "flang/Lower/ConvertExprToHLFIR.h"26#include "flang/Lower/ConvertVariable.h"27#include "flang/Lower/DirectivesCommon.h"28#include "flang/Lower/OpenMP/Clauses.h"29#include "flang/Lower/PFTBuilder.h"30#include "flang/Lower/StatementContext.h"31#include "flang/Lower/Support/ReductionProcessor.h"32#include "flang/Lower/SymbolMap.h"33#include "flang/Optimizer/Builder/BoxValue.h"34#include "flang/Optimizer/Builder/FIRBuilder.h"35#include "flang/Optimizer/Builder/Todo.h"36#include "flang/Optimizer/Dialect/FIRType.h"37#include "flang/Optimizer/HLFIR/HLFIROps.h"38#include "flang/Parser/characters.h"39#include "flang/Parser/openmp-utils.h"40#include "flang/Parser/parse-tree.h"41#include "flang/Parser/tools.h"42#include "flang/Semantics/openmp-directive-sets.h"43#include "flang/Semantics/openmp-utils.h"44#include "flang/Semantics/tools.h"45#include "flang/Support/Flags.h"46#include "flang/Support/OpenMP-utils.h"47#include "flang/Utils/OpenMP.h"48#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"49#include "mlir/Dialect/OpenMP/OpenMPDialect.h"50#include "mlir/Support/StateStack.h"51#include "mlir/Transforms/RegionUtils.h"52#include "llvm/ADT/STLExtras.h"53 54using namespace Fortran::lower::omp;55using namespace Fortran::common::openmp;56using namespace Fortran::utils::openmp;57 58//===----------------------------------------------------------------------===//59// Code generation helper functions60//===----------------------------------------------------------------------===//61 62static void genOMPDispatch(lower::AbstractConverter &converter,63 lower::SymMap &symTable,64 semantics::SemanticsContext &semaCtx,65 lower::pft::Evaluation &eval, mlir::Location loc,66 const ConstructQueue &queue,67 ConstructQueue::const_iterator item);68 69static void processHostEvalClauses(lower::AbstractConverter &converter,70 semantics::SemanticsContext &semaCtx,71 lower::StatementContext &stmtCtx,72 lower::pft::Evaluation &eval,73 mlir::Location loc);74 75namespace {76/// Structure holding information that is needed to pass host-evaluated77/// information to later lowering stages.78class HostEvalInfo {79public:80 // Allow this function access to private members in order to initialize them.81 friend void ::processHostEvalClauses(lower::AbstractConverter &,82 semantics::SemanticsContext &,83 lower::StatementContext &,84 lower::pft::Evaluation &,85 mlir::Location);86 87 /// Fill \c vars with values stored in \c ops.88 ///89 /// The order in which values are stored matches the one expected by \see90 /// bindOperands().91 void collectValues(llvm::SmallVectorImpl<mlir::Value> &vars) const {92 vars.append(ops.loopLowerBounds);93 vars.append(ops.loopUpperBounds);94 vars.append(ops.loopSteps);95 96 if (ops.numTeamsLower)97 vars.push_back(ops.numTeamsLower);98 99 if (ops.numTeamsUpper)100 vars.push_back(ops.numTeamsUpper);101 102 if (ops.numThreads)103 vars.push_back(ops.numThreads);104 105 if (ops.threadLimit)106 vars.push_back(ops.threadLimit);107 }108 109 /// Update \c ops, replacing all values with the corresponding block argument110 /// in \c args.111 ///112 /// The order in which values are stored in \c args is the same as the one113 /// used by \see collectValues().114 void bindOperands(llvm::ArrayRef<mlir::BlockArgument> args) {115 assert(args.size() ==116 ops.loopLowerBounds.size() + ops.loopUpperBounds.size() +117 ops.loopSteps.size() + (ops.numTeamsLower ? 1 : 0) +118 (ops.numTeamsUpper ? 1 : 0) + (ops.numThreads ? 1 : 0) +119 (ops.threadLimit ? 1 : 0) &&120 "invalid block argument list");121 int argIndex = 0;122 for (size_t i = 0; i < ops.loopLowerBounds.size(); ++i)123 ops.loopLowerBounds[i] = args[argIndex++];124 125 for (size_t i = 0; i < ops.loopUpperBounds.size(); ++i)126 ops.loopUpperBounds[i] = args[argIndex++];127 128 for (size_t i = 0; i < ops.loopSteps.size(); ++i)129 ops.loopSteps[i] = args[argIndex++];130 131 if (ops.numTeamsLower)132 ops.numTeamsLower = args[argIndex++];133 134 if (ops.numTeamsUpper)135 ops.numTeamsUpper = args[argIndex++];136 137 if (ops.numThreads)138 ops.numThreads = args[argIndex++];139 140 if (ops.threadLimit)141 ops.threadLimit = args[argIndex++];142 }143 144 /// Update \p clauseOps and \p ivOut with the corresponding host-evaluated145 /// values and Fortran symbols, respectively, if they have already been146 /// initialized but not yet applied.147 ///148 /// \returns whether an update was performed. If not, these clauses were not149 /// evaluated in the host device.150 bool apply(mlir::omp::LoopNestOperands &clauseOps,151 llvm::SmallVectorImpl<const semantics::Symbol *> &ivOut) {152 if (iv.empty() || loopNestApplied) {153 loopNestApplied = true;154 return false;155 }156 157 loopNestApplied = true;158 clauseOps.loopLowerBounds = ops.loopLowerBounds;159 clauseOps.loopUpperBounds = ops.loopUpperBounds;160 clauseOps.loopSteps = ops.loopSteps;161 clauseOps.collapseNumLoops = ops.collapseNumLoops;162 ivOut.append(iv);163 return true;164 }165 166 /// Update \p clauseOps with the corresponding host-evaluated values if they167 /// have already been initialized but not yet applied.168 ///169 /// \returns whether an update was performed. If not, these clauses were not170 /// evaluated in the host device.171 bool apply(mlir::omp::ParallelOperands &clauseOps) {172 if (!ops.numThreads || parallelApplied) {173 parallelApplied = true;174 return false;175 }176 177 parallelApplied = true;178 clauseOps.numThreads = ops.numThreads;179 return true;180 }181 182 /// Update \p clauseOps with the corresponding host-evaluated values if they183 /// have already been initialized.184 ///185 /// \returns whether an update was performed. If not, these clauses were not186 /// evaluated in the host device.187 bool apply(mlir::omp::TeamsOperands &clauseOps) {188 if (!ops.numTeamsLower && !ops.numTeamsUpper && !ops.threadLimit)189 return false;190 191 clauseOps.numTeamsLower = ops.numTeamsLower;192 clauseOps.numTeamsUpper = ops.numTeamsUpper;193 clauseOps.threadLimit = ops.threadLimit;194 return true;195 }196 197private:198 mlir::omp::HostEvaluatedOperands ops;199 llvm::SmallVector<const semantics::Symbol *> iv;200 bool loopNestApplied = false, parallelApplied = false;201};202} // namespace203 204/// Stack of \see HostEvalInfo to represent the current nest of \c omp.target205/// operations being created.206///207/// The current implementation prevents nested 'target' regions from breaking208/// the handling of the outer region by keeping a stack of information209/// structures, but it will probably still require some further work to support210/// reverse offloading.211class HostEvalInfoStackFrame212 : public mlir::StateStackFrameBase<HostEvalInfoStackFrame> {213public:214 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(HostEvalInfoStackFrame)215 216 HostEvalInfo info;217};218 219static HostEvalInfo *220getHostEvalInfoStackTop(lower::AbstractConverter &converter) {221 HostEvalInfoStackFrame *frame =222 converter.getStateStack().getStackTop<HostEvalInfoStackFrame>();223 return frame ? &frame->info : nullptr;224}225 226/// Stack frame for storing the OpenMPSectionsConstruct currently being227/// processed so that it can be referred to when lowering the construct.228class SectionsConstructStackFrame229 : public mlir::StateStackFrameBase<SectionsConstructStackFrame> {230public:231 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(SectionsConstructStackFrame)232 233 explicit SectionsConstructStackFrame(234 const parser::OpenMPSectionsConstruct §ionsConstruct)235 : sectionsConstruct{sectionsConstruct} {}236 237 const parser::OpenMPSectionsConstruct §ionsConstruct;238};239 240static const parser::OpenMPSectionsConstruct *241getSectionsConstructStackTop(lower::AbstractConverter &converter) {242 SectionsConstructStackFrame *frame =243 converter.getStateStack().getStackTop<SectionsConstructStackFrame>();244 return frame ? &frame->sectionsConstruct : nullptr;245}246 247/// Bind symbols to their corresponding entry block arguments.248///249/// The binding will be performed inside of the current block, which does not250/// necessarily have to be part of the operation for which the binding is done.251/// However, block arguments must be accessible. This enables controlling the252/// insertion point of any new MLIR operations related to the binding of253/// arguments of a loop wrapper operation.254///255/// \param [in] converter - PFT to MLIR conversion interface.256/// \param [in] op - owner operation of the block arguments to bind.257/// \param [in] args - entry block arguments information for the given258/// operation.259static void bindEntryBlockArgs(lower::AbstractConverter &converter,260 mlir::omp::BlockArgOpenMPOpInterface op,261 const EntryBlockArgs &args) {262 assert(op != nullptr && "invalid block argument-defining operation");263 assert(args.isValid() && "invalid args");264 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();265 266 auto bindSingleMapLike = [&converter](const semantics::Symbol &sym,267 const mlir::BlockArgument &arg) {268 fir::ExtendedValue extVal = converter.getSymbolExtendedValue(sym);269 auto refType = mlir::dyn_cast<fir::ReferenceType>(arg.getType());270 if (refType && fir::isa_builtin_cptr_type(refType.getElementType())) {271 converter.bindSymbol(sym, arg);272 } else {273 extVal.match(274 [&](const fir::BoxValue &v) {275 converter.bindSymbol(sym, fir::BoxValue(arg, v.getLBounds(),276 v.getExplicitParameters(),277 v.getExplicitExtents()));278 },279 [&](const fir::MutableBoxValue &v) {280 converter.bindSymbol(281 sym, fir::MutableBoxValue(arg, v.getLBounds(),282 v.getMutableProperties()));283 },284 [&](const fir::ArrayBoxValue &v) {285 converter.bindSymbol(sym, fir::ArrayBoxValue(arg, v.getExtents(),286 v.getLBounds(),287 v.getSourceBox()));288 },289 [&](const fir::CharArrayBoxValue &v) {290 converter.bindSymbol(sym, fir::CharArrayBoxValue(arg, v.getLen(),291 v.getExtents(),292 v.getLBounds()));293 },294 [&](const fir::CharBoxValue &v) {295 converter.bindSymbol(sym, fir::CharBoxValue(arg, v.getLen()));296 },297 [&](const fir::UnboxedValue &v) { converter.bindSymbol(sym, arg); },298 [&](const auto &) {299 TODO(converter.getCurrentLocation(),300 "target map clause operand unsupported type");301 });302 }303 };304 305 auto bindMapLike =306 [&bindSingleMapLike](llvm::ArrayRef<const semantics::Symbol *> syms,307 llvm::ArrayRef<mlir::BlockArgument> args) {308 // Structure component symbols don't have bindings, and can only be309 // explicitly mapped individually. If a member is captured implicitly310 // we map the entirety of the derived type when we find its symbol.311 llvm::SmallVector<const semantics::Symbol *> processedSyms;312 llvm::copy_if(syms, std::back_inserter(processedSyms),313 [](auto *sym) { return !sym->owner().IsDerivedType(); });314 315 for (auto [sym, arg] : llvm::zip_equal(processedSyms, args))316 bindSingleMapLike(*sym, arg);317 };318 319 auto bindPrivateLike = [&converter, &firOpBuilder](320 llvm::ArrayRef<const semantics::Symbol *> syms,321 llvm::ArrayRef<mlir::Value> vars,322 llvm::ArrayRef<mlir::BlockArgument> args) {323 llvm::SmallVector<const semantics::Symbol *> processedSyms;324 for (auto *sym : syms) {325 if (const auto *commonDet =326 sym->detailsIf<semantics::CommonBlockDetails>()) {327 llvm::transform(commonDet->objects(), std::back_inserter(processedSyms),328 [&](const auto &mem) { return &*mem; });329 } else {330 processedSyms.push_back(sym);331 }332 }333 334 for (auto [sym, var, arg] : llvm::zip_equal(processedSyms, vars, args))335 converter.bindSymbol(336 *sym,337 hlfir::translateToExtendedValue(338 var.getLoc(), firOpBuilder, hlfir::Entity{arg},339 /*contiguousHint=*/340 evaluate::IsSimplyContiguous(*sym, converter.getFoldingContext()))341 .first);342 };343 344 // Process in clause name alphabetical order to match block arguments order.345 // Do not bind host_eval variables because they cannot be used inside of the346 // corresponding region, except for very specific cases handled separately.347 bindMapLike(args.hasDeviceAddr.syms, op.getHasDeviceAddrBlockArgs());348 bindPrivateLike(args.inReduction.syms, args.inReduction.vars,349 op.getInReductionBlockArgs());350 bindMapLike(args.map.syms, op.getMapBlockArgs());351 bindPrivateLike(args.priv.syms, args.priv.vars, op.getPrivateBlockArgs());352 bindPrivateLike(args.reduction.syms, args.reduction.vars,353 op.getReductionBlockArgs());354 bindPrivateLike(args.taskReduction.syms, args.taskReduction.vars,355 op.getTaskReductionBlockArgs());356 bindMapLike(args.useDeviceAddr.syms, op.getUseDeviceAddrBlockArgs());357 bindMapLike(args.useDevicePtr.syms, op.getUseDevicePtrBlockArgs());358}359 360/// Get the list of base values that the specified map-like variables point to.361///362/// This function must be kept in sync with changes to the `createMapInfoOp`363/// utility function, since it must take into account the potential introduction364/// of levels of indirection (i.e. intermediate ops).365///366/// \param [in] vars - list of values passed to map-like clauses, returned367/// by an `omp.map.info` operation.368/// \param [out] baseOps - populated with the `var_ptr` values of the369/// corresponding defining operations.370static void371extractMappedBaseValues(llvm::ArrayRef<mlir::Value> vars,372 llvm::SmallVectorImpl<mlir::Value> &baseOps) {373 llvm::transform(vars, std::back_inserter(baseOps), [](mlir::Value map) {374 auto mapInfo = map.getDefiningOp<mlir::omp::MapInfoOp>();375 assert(mapInfo && "expected all map vars to be defined by omp.map.info");376 377 mlir::Value varPtr = mapInfo.getVarPtr();378 if (auto boxAddr = varPtr.getDefiningOp<fir::BoxAddrOp>())379 return boxAddr.getVal();380 381 return varPtr;382 });383}384 385/// Populate the global \see hostEvalInfo after processing clauses for the given386/// \p eval OpenMP target construct, or nested constructs, if these must be387/// evaluated outside of the target region per the spec.388///389/// In particular, this will ensure that in 'target teams' and equivalent nested390/// constructs, the \c thread_limit and \c num_teams clauses will be evaluated391/// in the host. Additionally, loop bounds, steps and the \c num_threads clause392/// will also be evaluated in the host if a target SPMD construct is detected393/// (i.e. 'target teams distribute parallel do [simd]' or equivalent nesting).394///395/// The result, stored as a global, is intended to be used to populate the \c396/// host_eval operands of the associated \c omp.target operation, and also to be397/// checked and used by later lowering steps to populate the corresponding398/// operands of the \c omp.teams, \c omp.parallel or \c omp.loop_nest399/// operations.400static void processHostEvalClauses(lower::AbstractConverter &converter,401 semantics::SemanticsContext &semaCtx,402 lower::StatementContext &stmtCtx,403 lower::pft::Evaluation &eval,404 mlir::Location loc) {405 // Obtain the list of clauses of the given OpenMP block or loop construct406 // evaluation. Other evaluations passed to this lambda keep `clauses`407 // unchanged.408 auto extractClauses = [&semaCtx](lower::pft::Evaluation &eval,409 List<Clause> &clauses) {410 const auto *ompEval = eval.getIf<parser::OpenMPConstruct>();411 if (!ompEval)412 return;413 414 const parser::OmpClauseList *beginClauseList = nullptr;415 const parser::OmpClauseList *endClauseList = nullptr;416 common::visit(417 [&](const auto &construct) {418 using Type = llvm::remove_cvref_t<decltype(construct)>;419 if constexpr (std::is_same_v<Type, parser::OmpBlockConstruct> ||420 std::is_same_v<Type, parser::OpenMPLoopConstruct>) {421 beginClauseList = &construct.BeginDir().Clauses();422 if (auto &endSpec = construct.EndDir())423 endClauseList = &endSpec->Clauses();424 }425 },426 ompEval->u);427 428 assert(beginClauseList && "expected begin directive");429 clauses.append(makeClauses(*beginClauseList, semaCtx));430 431 if (endClauseList)432 clauses.append(makeClauses(*endClauseList, semaCtx));433 };434 435 // Return the directive that is immediately nested inside of the given436 // `parent` evaluation, if it is its only non-end-statement nested evaluation437 // and it represents an OpenMP construct.438 auto extractOnlyOmpNestedDir = [](lower::pft::Evaluation &parent)439 -> std::optional<llvm::omp::Directive> {440 if (!parent.hasNestedEvaluations())441 return std::nullopt;442 443 llvm::omp::Directive dir;444 auto &nested = parent.getFirstNestedEvaluation();445 if (const auto *ompEval = nested.getIf<parser::OpenMPConstruct>())446 dir = parser::omp::GetOmpDirectiveName(*ompEval).v;447 else448 return std::nullopt;449 450 for (auto &sibling : parent.getNestedEvaluations())451 if (&sibling != &nested && !sibling.isEndStmt())452 return std::nullopt;453 454 return dir;455 };456 457 // Process the given evaluation assuming it's part of a 'target' construct or458 // captured by one, and store results in the global `hostEvalInfo`.459 std::function<void(lower::pft::Evaluation &, const List<Clause> &)>460 processEval;461 processEval = [&](lower::pft::Evaluation &eval, const List<Clause> &clauses) {462 using namespace llvm::omp;463 ClauseProcessor cp(converter, semaCtx, clauses);464 465 // Call `processEval` recursively with the immediately nested evaluation and466 // its corresponding clauses if there is a single nested evaluation467 // representing an OpenMP directive that passes the given test.468 auto processSingleNestedIf = [&](llvm::function_ref<bool(Directive)> test) {469 std::optional<Directive> nestedDir = extractOnlyOmpNestedDir(eval);470 if (!nestedDir || !test(*nestedDir))471 return;472 473 lower::pft::Evaluation &nestedEval = eval.getFirstNestedEvaluation();474 List<lower::omp::Clause> nestedClauses;475 extractClauses(nestedEval, nestedClauses);476 processEval(nestedEval, nestedClauses);477 };478 479 const auto *ompEval = eval.getIf<parser::OpenMPConstruct>();480 if (!ompEval)481 return;482 483 HostEvalInfo *hostInfo = getHostEvalInfoStackTop(converter);484 assert(hostInfo && "expected HOST_EVAL info structure");485 486 switch (parser::omp::GetOmpDirectiveName(*ompEval).v) {487 case OMPD_teams_distribute_parallel_do:488 case OMPD_teams_distribute_parallel_do_simd:489 cp.processThreadLimit(stmtCtx, hostInfo->ops);490 [[fallthrough]];491 case OMPD_target_teams_distribute_parallel_do:492 case OMPD_target_teams_distribute_parallel_do_simd:493 cp.processNumTeams(stmtCtx, hostInfo->ops);494 [[fallthrough]];495 case OMPD_distribute_parallel_do:496 case OMPD_distribute_parallel_do_simd:497 cp.processNumThreads(stmtCtx, hostInfo->ops);498 [[fallthrough]];499 case OMPD_distribute:500 case OMPD_distribute_simd:501 cp.processCollapse(loc, eval, hostInfo->ops, hostInfo->ops, hostInfo->iv);502 break;503 504 case OMPD_teams:505 cp.processThreadLimit(stmtCtx, hostInfo->ops);506 [[fallthrough]];507 case OMPD_target_teams:508 cp.processNumTeams(stmtCtx, hostInfo->ops);509 processSingleNestedIf([](Directive nestedDir) {510 return topDistributeSet.test(nestedDir) || topLoopSet.test(nestedDir);511 });512 break;513 514 case OMPD_teams_distribute:515 case OMPD_teams_distribute_simd:516 cp.processThreadLimit(stmtCtx, hostInfo->ops);517 [[fallthrough]];518 case OMPD_target_teams_distribute:519 case OMPD_target_teams_distribute_simd:520 cp.processCollapse(loc, eval, hostInfo->ops, hostInfo->ops, hostInfo->iv);521 cp.processNumTeams(stmtCtx, hostInfo->ops);522 break;523 524 case OMPD_teams_loop:525 cp.processThreadLimit(stmtCtx, hostInfo->ops);526 [[fallthrough]];527 case OMPD_target_teams_loop:528 cp.processNumTeams(stmtCtx, hostInfo->ops);529 [[fallthrough]];530 case OMPD_loop:531 cp.processCollapse(loc, eval, hostInfo->ops, hostInfo->ops, hostInfo->iv);532 break;533 534 case OMPD_teams_workdistribute:535 cp.processThreadLimit(stmtCtx, hostInfo->ops);536 [[fallthrough]];537 case OMPD_target_teams_workdistribute:538 cp.processNumTeams(stmtCtx, hostInfo->ops);539 break;540 541 // Standalone 'target' case.542 case OMPD_target: {543 processSingleNestedIf(544 [](Directive nestedDir) { return topTeamsSet.test(nestedDir); });545 break;546 }547 default:548 break;549 }550 };551 552 const auto *ompEval = eval.getIf<parser::OpenMPConstruct>();553 assert(ompEval &&554 llvm::omp::allTargetSet.test(555 parser::omp::GetOmpDirectiveName(*ompEval).v) &&556 "expected TARGET construct evaluation");557 (void)ompEval;558 559 // Use the whole list of clauses passed to the construct here, rather than the560 // ones only applied to omp.target.561 List<lower::omp::Clause> clauses;562 extractClauses(eval, clauses);563 processEval(eval, clauses);564}565 566static lower::pft::Evaluation *567getCollapsedLoopEval(lower::pft::Evaluation &eval, int collapseValue) {568 // Return the Evaluation of the innermost collapsed loop, or the current one569 // if there was no COLLAPSE.570 if (collapseValue == 0)571 return &eval;572 573 lower::pft::Evaluation *curEval = &eval;574 for (int i = 0; i < collapseValue; i++)575 curEval = getNestedDoConstruct(*curEval);576 return curEval;577}578 579static void genNestedEvaluations(lower::AbstractConverter &converter,580 lower::pft::Evaluation &eval,581 int collapseValue = 0) {582 lower::pft::Evaluation *curEval = getCollapsedLoopEval(eval, collapseValue);583 584 for (lower::pft::Evaluation &e : curEval->getNestedEvaluations())585 converter.genEval(e);586}587 588static fir::GlobalOp globalInitialization(lower::AbstractConverter &converter,589 fir::FirOpBuilder &firOpBuilder,590 const semantics::Symbol &sym,591 const lower::pft::Variable &var,592 mlir::Location currentLocation) {593 std::string globalName = converter.mangleName(sym);594 mlir::StringAttr linkage = firOpBuilder.createInternalLinkage();595 return Fortran::lower::defineGlobal(converter, var, globalName, linkage);596}597 598// Get the extended value for \p val by extracting additional variable599// information from \p base.600static fir::ExtendedValue getExtendedValue(fir::ExtendedValue base,601 mlir::Value val) {602 return base.match(603 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {604 return fir::MutableBoxValue(val, box.nonDeferredLenParams(), {});605 },606 [&](const auto &) -> fir::ExtendedValue {607 return fir::substBase(base, val);608 });609}610 611#ifndef NDEBUG612static bool isThreadPrivate(lower::SymbolRef sym) {613 if (const auto *details = sym->detailsIf<semantics::CommonBlockDetails>()) {614 for (const auto &obj : details->objects())615 if (!obj->test(semantics::Symbol::Flag::OmpThreadprivate))616 return false;617 return true;618 }619 return sym->test(semantics::Symbol::Flag::OmpThreadprivate);620}621#endif622 623static void threadPrivatizeVars(lower::AbstractConverter &converter,624 lower::pft::Evaluation &eval) {625 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();626 mlir::Location currentLocation = converter.getCurrentLocation();627 mlir::OpBuilder::InsertionGuard guard(firOpBuilder);628 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());629 630 // If the symbol corresponds to the original ThreadprivateOp, use the symbol631 // value from that operation to create one ThreadprivateOp copy operation632 // inside the parallel region.633 // In some cases, however, the symbol will correspond to the original,634 // non-threadprivate variable. This can happen, for instance, with a common635 // block, declared in a separate module, used by a parent procedure and636 // privatized in its child procedure.637 auto genThreadprivateOp = [&](lower::SymbolRef sym) -> mlir::Value {638 assert(isThreadPrivate(sym));639 mlir::Value symValue = converter.getSymbolAddress(sym);640 mlir::Operation *op = symValue.getDefiningOp();641 if (auto declOp = mlir::dyn_cast<hlfir::DeclareOp>(op))642 op = declOp.getMemref().getDefiningOp();643 if (mlir::isa<mlir::omp::ThreadprivateOp>(op))644 symValue = mlir::dyn_cast<mlir::omp::ThreadprivateOp>(op).getSymAddr();645 return mlir::omp::ThreadprivateOp::create(firOpBuilder, currentLocation,646 symValue.getType(), symValue);647 };648 649 llvm::SetVector<const semantics::Symbol *> threadprivateSyms;650 converter.collectSymbolSet(eval, threadprivateSyms,651 semantics::Symbol::Flag::OmpThreadprivate,652 /*collectSymbols=*/true,653 /*collectHostAssociatedSymbols=*/true);654 std::set<semantics::SourceName> threadprivateSymNames;655 656 // For a COMMON block, the ThreadprivateOp is generated for itself instead of657 // its members, so only bind the value of the new copied ThreadprivateOp658 // inside the parallel region to the common block symbol only once for659 // multiple members in one COMMON block.660 llvm::SetVector<const semantics::Symbol *> commonSyms;661 for (std::size_t i = 0; i < threadprivateSyms.size(); i++) {662 const semantics::Symbol *sym = threadprivateSyms[i];663 mlir::Value symThreadprivateValue;664 // The variable may be used more than once, and each reference has one665 // symbol with the same name. Only do once for references of one variable.666 if (threadprivateSymNames.find(sym->name()) != threadprivateSymNames.end())667 continue;668 threadprivateSymNames.insert(sym->name());669 if (const semantics::Symbol *common =670 semantics::FindCommonBlockContaining(sym->GetUltimate())) {671 mlir::Value commonThreadprivateValue;672 if (commonSyms.contains(common)) {673 commonThreadprivateValue = converter.getSymbolAddress(*common);674 } else {675 commonThreadprivateValue = genThreadprivateOp(*common);676 converter.bindSymbol(*common, commonThreadprivateValue);677 commonSyms.insert(common);678 }679 symThreadprivateValue = lower::genCommonBlockMember(680 converter, currentLocation, sym->GetUltimate(),681 commonThreadprivateValue, common->size());682 } else {683 symThreadprivateValue = genThreadprivateOp(*sym);684 }685 686 fir::ExtendedValue sexv = converter.getSymbolExtendedValue(*sym);687 fir::ExtendedValue symThreadprivateExv =688 getExtendedValue(sexv, symThreadprivateValue);689 converter.bindSymbol(*sym, symThreadprivateExv);690 }691}692 693static mlir::Operation *setLoopVar(lower::AbstractConverter &converter,694 mlir::Location loc, mlir::Value indexVal,695 const semantics::Symbol *sym) {696 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();697 698 mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();699 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());700 mlir::Type tempTy = converter.genType(*sym);701 firOpBuilder.restoreInsertionPoint(insPt);702 703 mlir::Value cvtVal = firOpBuilder.createConvert(loc, tempTy, indexVal);704 hlfir::Entity lhs{converter.getSymbolAddress(*sym)};705 706 lhs = hlfir::derefPointersAndAllocatables(loc, firOpBuilder, lhs);707 708 mlir::Operation *storeOp =709 hlfir::AssignOp::create(firOpBuilder, loc, cvtVal, lhs);710 return storeOp;711}712 713static mlir::Operation *714createAndSetPrivatizedLoopVar(lower::AbstractConverter &converter,715 mlir::Location loc, mlir::Value indexVal,716 const semantics::Symbol *sym) {717 assert(converter.isPresentShallowLookup(*sym) &&718 "Expected symbol to be in symbol table.");719 return setLoopVar(converter, loc, indexVal, sym);720}721 722// This helper function implements the functionality of "promoting" non-CPTR723// arguments of use_device_ptr to use_device_addr arguments (automagic724// conversion of use_device_ptr -> use_device_addr in these cases). The way we725// do so currently is through the shuffling of operands from the726// devicePtrOperands to deviceAddrOperands, as well as the types, locations and727// symbols.728//729// This effectively implements some deprecated OpenMP functionality that some730// legacy applications unfortunately depend on (deprecated in specification731// version 5.2):732//733// "If a list item in a use_device_ptr clause is not of type C_PTR, the behavior734// is as if the list item appeared in a use_device_addr clause. Support for735// such list items in a use_device_ptr clause is deprecated."736static void promoteNonCPtrUseDevicePtrArgsToUseDeviceAddr(737 llvm::SmallVectorImpl<mlir::Value> &useDeviceAddrVars,738 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceAddrSyms,739 llvm::SmallVectorImpl<mlir::Value> &useDevicePtrVars,740 llvm::SmallVectorImpl<const semantics::Symbol *> &useDevicePtrSyms) {741 // Iterate over our use_device_ptr list and shift all non-cptr arguments into742 // use_device_addr.743 auto *varIt = useDevicePtrVars.begin();744 auto *symIt = useDevicePtrSyms.begin();745 while (varIt != useDevicePtrVars.end()) {746 if (fir::isa_builtin_cptr_type(fir::unwrapRefType(varIt->getType()))) {747 ++varIt;748 ++symIt;749 continue;750 }751 752 useDeviceAddrVars.push_back(*varIt);753 useDeviceAddrSyms.push_back(*symIt);754 755 varIt = useDevicePtrVars.erase(varIt);756 symIt = useDevicePtrSyms.erase(symIt);757 }758}759 760/// Extract the list of function and variable symbols affected by the given761/// 'declare target' directive and return the intended device type for them.762static void getDeclareTargetInfo(763 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,764 lower::pft::Evaluation &eval,765 const parser::OpenMPDeclareTargetConstruct &construct,766 mlir::omp::DeclareTargetOperands &clauseOps,767 llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &symbolAndClause) {768 769 if (!construct.v.Arguments().v.empty()) {770 ObjectList objects{makeObjects(construct.v.Arguments(), semaCtx)};771 // Case: declare target(func, var1, var2)772 gatherFuncAndVarSyms(objects, mlir::omp::DeclareTargetCaptureClause::to,773 symbolAndClause, /*automap=*/false);774 } else {775 List<Clause> clauses = makeClauses(construct.v.Clauses(), semaCtx);776 if (clauses.empty()) {777 Fortran::lower::pft::FunctionLikeUnit *owningProc =778 eval.getOwningProcedure();779 if (owningProc && (!owningProc->isMainProgram() ||780 owningProc->getMainProgramSymbol())) {781 // Case: declare target, implicit capture of function782 symbolAndClause.emplace_back(mlir::omp::DeclareTargetCaptureClause::to,783 owningProc->getSubprogramSymbol());784 }785 }786 787 ClauseProcessor cp(converter, semaCtx, clauses);788 cp.processDeviceType(clauseOps);789 cp.processEnter(symbolAndClause);790 cp.processLink(symbolAndClause);791 cp.processTo(symbolAndClause);792 793 cp.processTODO<clause::Indirect>(converter.getCurrentLocation(),794 llvm::omp::Directive::OMPD_declare_target);795 }796}797 798static void collectDeferredDeclareTargets(799 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,800 lower::pft::Evaluation &eval,801 const parser::OpenMPDeclareTargetConstruct &declareTargetConstruct,802 llvm::SmallVectorImpl<lower::OMPDeferredDeclareTargetInfo>803 &deferredDeclareTarget) {804 mlir::omp::DeclareTargetOperands clauseOps;805 llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause;806 getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct,807 clauseOps, symbolAndClause);808 // Return the device type only if at least one of the targets for the809 // directive is a function or subroutine810 mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();811 812 for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) {813 mlir::Operation *op =814 mod.lookupSymbol(converter.mangleName(symClause.symbol));815 816 if (!op) {817 deferredDeclareTarget.push_back({symClause.clause, clauseOps.deviceType,818 symClause.automap, symClause.symbol});819 }820 }821}822 823static std::optional<mlir::omp::DeclareTargetDeviceType>824getDeclareTargetFunctionDevice(825 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,826 lower::pft::Evaluation &eval,827 const parser::OpenMPDeclareTargetConstruct &declareTargetConstruct) {828 mlir::omp::DeclareTargetOperands clauseOps;829 llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause;830 getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct,831 clauseOps, symbolAndClause);832 833 // Return the device type only if at least one of the targets for the834 // directive is a function or subroutine835 mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();836 for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) {837 mlir::Operation *op =838 mod.lookupSymbol(converter.mangleName(symClause.symbol));839 840 if (mlir::isa_and_nonnull<mlir::func::FuncOp>(op))841 return clauseOps.deviceType;842 }843 844 return std::nullopt;845}846 847/// Set up the entry block of the given `omp.loop_nest` operation, adding a848/// block argument for each loop induction variable and allocating and849/// initializing a private value to hold each of them.850///851/// This function can also bind the symbols of any variables that should match852/// block arguments on parent loop wrapper operations attached to the same853/// loop. This allows the introduction of any necessary `hlfir.declare`854/// operations inside of the entry block of the `omp.loop_nest` operation and855/// not directly under any of the wrappers, which would invalidate them.856///857/// \param [in] op - the loop nest operation.858/// \param [in] converter - PFT to MLIR conversion interface.859/// \param [in] loc - location.860/// \param [in] args - symbols of induction variables.861/// \param [in] wrapperArgs - list of parent loop wrappers and their associated862/// entry block arguments.863static void genLoopVars(864 mlir::Operation *op, lower::AbstractConverter &converter,865 mlir::Location &loc, llvm::ArrayRef<const semantics::Symbol *> args,866 llvm::ArrayRef<867 std::pair<mlir::omp::BlockArgOpenMPOpInterface, const EntryBlockArgs &>>868 wrapperArgs = {}) {869 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();870 auto ®ion = op->getRegion(0);871 872 std::size_t loopVarTypeSize = 0;873 for (const semantics::Symbol *arg : args)874 loopVarTypeSize = std::max(loopVarTypeSize, arg->GetUltimate().size());875 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);876 llvm::SmallVector<mlir::Type> tiv(args.size(), loopVarType);877 llvm::SmallVector<mlir::Location> locs(args.size(), loc);878 firOpBuilder.createBlock(®ion, {}, tiv, locs);879 880 // Update nested wrapper operands if parent wrappers have mapped these values881 // to block arguments.882 //883 // Binding these values earlier would take care of this, but we cannot rely on884 // that approach because binding in between the creation of a wrapper and the885 // next one would result in 'hlfir.declare' operations being introduced inside886 // of a wrapper, which is illegal.887 mlir::IRMapping mapper;888 for (auto [argGeneratingOp, blockArgs] : wrapperArgs) {889 for (mlir::OpOperand &operand : argGeneratingOp->getOpOperands())890 operand.set(mapper.lookupOrDefault(operand.get()));891 892 for (const auto [arg, var] : llvm::zip_equal(893 argGeneratingOp->getRegion(0).getArguments(), blockArgs.getVars()))894 mapper.map(var, arg);895 }896 897 // Bind the entry block arguments of parent wrappers to the corresponding898 // symbols.899 for (auto [argGeneratingOp, blockArgs] : wrapperArgs)900 bindEntryBlockArgs(converter, argGeneratingOp, blockArgs);901 902 // The argument is not currently in memory, so make a temporary for the903 // argument, and store it there, then bind that location to the argument.904 mlir::Operation *storeOp = nullptr;905 for (auto [argIndex, argSymbol] : llvm::enumerate(args)) {906 mlir::Value indexVal = fir::getBase(region.front().getArgument(argIndex));907 storeOp =908 createAndSetPrivatizedLoopVar(converter, loc, indexVal, argSymbol);909 }910 firOpBuilder.setInsertionPointAfter(storeOp);911}912 913static clause::Defaultmap::ImplicitBehavior914getDefaultmapIfPresent(const DefaultMapsTy &defaultMaps, mlir::Type varType) {915 using DefMap = clause::Defaultmap;916 917 if (defaultMaps.empty())918 return DefMap::ImplicitBehavior::Default;919 920 if (llvm::is_contained(defaultMaps, DefMap::VariableCategory::All))921 return defaultMaps.at(DefMap::VariableCategory::All);922 923 // NOTE: Unsure if complex and/or vector falls into a scalar type924 // or aggregate, but the current default implicit behaviour is to925 // treat them as such (c_ptr has its own behaviour, so perhaps926 // being lumped in as a scalar isn't the right thing).927 if ((fir::isa_trivial(varType) || fir::isa_char(varType) ||928 fir::isa_builtin_cptr_type(varType)) &&929 llvm::is_contained(defaultMaps, DefMap::VariableCategory::Scalar))930 return defaultMaps.at(DefMap::VariableCategory::Scalar);931 932 if (fir::isPointerType(varType) &&933 llvm::is_contained(defaultMaps, DefMap::VariableCategory::Pointer))934 return defaultMaps.at(DefMap::VariableCategory::Pointer);935 936 if (fir::isAllocatableType(varType) &&937 llvm::is_contained(defaultMaps, DefMap::VariableCategory::Allocatable))938 return defaultMaps.at(DefMap::VariableCategory::Allocatable);939 940 if (fir::isa_aggregate(varType) &&941 llvm::is_contained(defaultMaps, DefMap::VariableCategory::Aggregate))942 return defaultMaps.at(DefMap::VariableCategory::Aggregate);943 944 return DefMap::ImplicitBehavior::Default;945}946 947static std::pair<mlir::omp::ClauseMapFlags, mlir::omp::VariableCaptureKind>948getImplicitMapTypeAndKind(fir::FirOpBuilder &firOpBuilder,949 lower::AbstractConverter &converter,950 const DefaultMapsTy &defaultMaps, mlir::Type varType,951 mlir::Location loc, const semantics::Symbol &sym) {952 using DefMap = clause::Defaultmap;953 // Check if a value of type `type` can be passed to the kernel by value.954 // All kernel parameters are of pointer type, so if the value can be955 // represented inside of a pointer, then it can be passed by value.956 auto isLiteralType = [&](mlir::Type type) {957 const mlir::DataLayout &dl = firOpBuilder.getDataLayout();958 mlir::Type ptrTy =959 mlir::LLVM::LLVMPointerType::get(&converter.getMLIRContext());960 uint64_t ptrSize = dl.getTypeSize(ptrTy);961 uint64_t ptrAlign = dl.getTypePreferredAlignment(ptrTy);962 963 auto [size, align] = fir::getTypeSizeAndAlignmentOrCrash(964 loc, type, dl, converter.getKindMap());965 return size <= ptrSize && align <= ptrAlign;966 };967 968 mlir::omp::ClauseMapFlags mapFlag = mlir::omp::ClauseMapFlags::implicit;969 970 auto implicitBehaviour = getDefaultmapIfPresent(defaultMaps, varType);971 if (implicitBehaviour == DefMap::ImplicitBehavior::Default) {972 mlir::omp::VariableCaptureKind captureKind =973 mlir::omp::VariableCaptureKind::ByRef;974 975 // If a variable is specified in declare target link and if device976 // type is not specified as `nohost`, it needs to be mapped tofrom977 mlir::ModuleOp mod = firOpBuilder.getModule();978 mlir::Operation *op = mod.lookupSymbol(converter.mangleName(sym));979 auto declareTargetOp =980 llvm::dyn_cast_if_present<mlir::omp::DeclareTargetInterface>(op);981 if (declareTargetOp && declareTargetOp.isDeclareTarget()) {982 if (declareTargetOp.getDeclareTargetCaptureClause() ==983 mlir::omp::DeclareTargetCaptureClause::link &&984 declareTargetOp.getDeclareTargetDeviceType() !=985 mlir::omp::DeclareTargetDeviceType::nohost) {986 mapFlag |= mlir::omp::ClauseMapFlags::to;987 mapFlag |= mlir::omp::ClauseMapFlags::from;988 }989 } else if (fir::isa_trivial(varType) || fir::isa_char(varType)) {990 // Scalars behave as if they were "firstprivate".991 // TODO: Handle objects that are shared/lastprivate or were listed992 // in an in_reduction clause.993 if (isLiteralType(varType)) {994 captureKind = mlir::omp::VariableCaptureKind::ByCopy;995 } else {996 mapFlag |= mlir::omp::ClauseMapFlags::to;997 }998 } else if (!fir::isa_builtin_cptr_type(varType)) {999 mapFlag |= mlir::omp::ClauseMapFlags::to;1000 mapFlag |= mlir::omp::ClauseMapFlags::from;1001 }1002 return std::make_pair(mapFlag, captureKind);1003 }1004 1005 switch (implicitBehaviour) {1006 case DefMap::ImplicitBehavior::Alloc:1007 return std::make_pair(mlir::omp::ClauseMapFlags::storage,1008 mlir::omp::VariableCaptureKind::ByRef);1009 break;1010 case DefMap::ImplicitBehavior::Firstprivate:1011 TODO(loc, "Firstprivate is currently unsupported defaultmap behaviour");1012 break;1013 case DefMap::ImplicitBehavior::From:1014 return std::make_pair(mapFlag |= mlir::omp::ClauseMapFlags::from,1015 mlir::omp::VariableCaptureKind::ByRef);1016 break;1017 case DefMap::ImplicitBehavior::Present:1018 return std::make_pair(mapFlag |= mlir::omp::ClauseMapFlags::present,1019 mlir::omp::VariableCaptureKind::ByRef);1020 break;1021 case DefMap::ImplicitBehavior::To:1022 return std::make_pair(mapFlag |= mlir::omp::ClauseMapFlags::to,1023 (fir::isa_trivial(varType) || fir::isa_char(varType))1024 ? mlir::omp::VariableCaptureKind::ByCopy1025 : mlir::omp::VariableCaptureKind::ByRef);1026 break;1027 case DefMap::ImplicitBehavior::Tofrom:1028 return std::make_pair(mapFlag |= mlir::omp::ClauseMapFlags::from |1029 mlir::omp::ClauseMapFlags::to,1030 mlir::omp::VariableCaptureKind::ByRef);1031 break;1032 case DefMap::ImplicitBehavior::Default:1033 case DefMap::ImplicitBehavior::None:1034 llvm_unreachable(1035 "Implicit None and Default behaviour should have been handled earlier");1036 break;1037 }1038 1039 return std::make_pair(mapFlag |= mlir::omp::ClauseMapFlags::from |1040 mlir::omp::ClauseMapFlags::to,1041 mlir::omp::VariableCaptureKind::ByRef);1042}1043 1044static void1045markDeclareTarget(mlir::Operation *op, lower::AbstractConverter &converter,1046 mlir::omp::DeclareTargetCaptureClause captureClause,1047 mlir::omp::DeclareTargetDeviceType deviceType, bool automap) {1048 // TODO: Add support for program local variables with declare target applied1049 auto declareTargetOp = llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(op);1050 if (!declareTargetOp)1051 fir::emitFatalError(1052 converter.getCurrentLocation(),1053 "Attempt to apply declare target on unsupported operation");1054 1055 // The function or global already has a declare target applied to it, very1056 // likely through implicit capture (usage in another declare target1057 // function/subroutine). It should be marked as any if it has been assigned1058 // both host and nohost, else we skip, as there is no change1059 if (declareTargetOp.isDeclareTarget()) {1060 if (declareTargetOp.getDeclareTargetDeviceType() != deviceType)1061 declareTargetOp.setDeclareTarget(mlir::omp::DeclareTargetDeviceType::any,1062 captureClause, automap);1063 return;1064 }1065 1066 declareTargetOp.setDeclareTarget(deviceType, captureClause, automap);1067}1068 1069//===----------------------------------------------------------------------===//1070// Op body generation helper structures and functions1071//===----------------------------------------------------------------------===//1072 1073struct OpWithBodyGenInfo {1074 /// A type for a code-gen callback function. This takes as argument the op for1075 /// which the code is being generated and returns the arguments of the op's1076 /// region.1077 using GenOMPRegionEntryCBFn =1078 std::function<llvm::SmallVector<const semantics::Symbol *>(1079 mlir::Operation *)>;1080 1081 OpWithBodyGenInfo(lower::AbstractConverter &converter,1082 lower::SymMap &symTable,1083 semantics::SemanticsContext &semaCtx, mlir::Location loc,1084 lower::pft::Evaluation &eval, llvm::omp::Directive dir)1085 : converter(converter), symTable(symTable), semaCtx(semaCtx), loc(loc),1086 eval(eval), dir(dir) {}1087 1088 OpWithBodyGenInfo &setClauses(const List<Clause> *value) {1089 clauses = value;1090 return *this;1091 }1092 1093 OpWithBodyGenInfo &setDataSharingProcessor(DataSharingProcessor *value) {1094 dsp = value;1095 return *this;1096 }1097 1098 OpWithBodyGenInfo &setEntryBlockArgs(const EntryBlockArgs *value) {1099 blockArgs = value;1100 return *this;1101 }1102 1103 OpWithBodyGenInfo &setGenRegionEntryCb(GenOMPRegionEntryCBFn value) {1104 genRegionEntryCB = value;1105 return *this;1106 }1107 1108 OpWithBodyGenInfo &setGenSkeletonOnly(bool value) {1109 genSkeletonOnly = value;1110 return *this;1111 }1112 1113 OpWithBodyGenInfo &setPrivatize(bool value) {1114 privatize = value;1115 return *this;1116 }1117 1118 /// [inout] converter to use for the clauses.1119 lower::AbstractConverter &converter;1120 /// [in] Symbol table1121 lower::SymMap &symTable;1122 /// [in] Semantics context1123 semantics::SemanticsContext &semaCtx;1124 /// [in] location in source code.1125 mlir::Location loc;1126 /// [in] current PFT node/evaluation.1127 lower::pft::Evaluation &eval;1128 /// [in] leaf directive for which to generate the op body.1129 llvm::omp::Directive dir;1130 /// [in] list of clauses to process.1131 const List<Clause> *clauses = nullptr;1132 /// [in] if provided, processes the construct's data-sharing attributes.1133 DataSharingProcessor *dsp = nullptr;1134 /// [in] if provided, it is used to create the op's region entry block. It is1135 /// overriden when a \see genRegionEntryCB is provided. This is only valid for1136 /// operations implementing the \see mlir::omp::BlockArgOpenMPOpInterface.1137 const EntryBlockArgs *blockArgs = nullptr;1138 /// [in] if provided, it overrides the default op's region entry block1139 /// creation.1140 GenOMPRegionEntryCBFn genRegionEntryCB = nullptr;1141 /// [in] if set to `true`, skip generating nested evaluations and dispatching1142 /// any further leaf constructs.1143 bool genSkeletonOnly = false;1144 /// [in] enables handling of privatized variable unless set to `false`.1145 bool privatize = true;1146};1147 1148/// Create the body (block) for an OpenMP Operation.1149///1150/// \param [in] op - the operation the body belongs to.1151/// \param [in] info - options controlling code-gen for the construction.1152/// \param [in] queue - work queue with nested constructs.1153/// \param [in] item - item in the queue to generate body for.1154static void createBodyOfOp(mlir::Operation &op, const OpWithBodyGenInfo &info,1155 const ConstructQueue &queue,1156 ConstructQueue::const_iterator item) {1157 fir::FirOpBuilder &firOpBuilder = info.converter.getFirOpBuilder();1158 1159 auto insertMarker = [](fir::FirOpBuilder &builder) {1160 mlir::Value undef = fir::UndefOp::create(builder, builder.getUnknownLoc(),1161 builder.getIndexType());1162 return undef.getDefiningOp();1163 };1164 1165 // Create the entry block for the region and collect its arguments for use1166 // within the region. The entry block will be created as follows:1167 // - By default, it will be empty and have no arguments.1168 // - Operations implementing the omp::BlockArgOpenMPOpInterface can set the1169 // `info.blockArgs` pointer so that block arguments will be those1170 // corresponding to entry block argument-generating clauses. Binding of1171 // Fortran symbols to the new MLIR values is done automatically.1172 // - If the `info.genRegionEntryCB` callback is set, it takes precedence and1173 // allows callers to manually create the entry block with its intended1174 // list of arguments and to bind these arguments to their corresponding1175 // Fortran symbols. This is used for e.g. loop induction variables.1176 auto regionArgs = [&]() -> llvm::SmallVector<const semantics::Symbol *> {1177 if (info.genRegionEntryCB)1178 return info.genRegionEntryCB(&op);1179 1180 if (info.blockArgs) {1181 genEntryBlock(firOpBuilder, *info.blockArgs, op.getRegion(0));1182 bindEntryBlockArgs(info.converter,1183 llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op),1184 *info.blockArgs);1185 return llvm::to_vector(info.blockArgs->getSyms());1186 }1187 1188 firOpBuilder.createBlock(&op.getRegion(0));1189 return {};1190 }();1191 1192 // Mark the earliest insertion point.1193 mlir::Operation *marker = insertMarker(firOpBuilder);1194 1195 // If it is an unstructured region, create empty blocks for all evaluations.1196 if (lower::omp::isLastItemInQueue(item, queue) &&1197 info.eval.lowerAsUnstructured()) {1198 lower::createEmptyRegionBlocks<mlir::omp::TerminatorOp, mlir::omp::YieldOp>(1199 firOpBuilder, info.eval.getNestedEvaluations());1200 }1201 1202 // Start with privatization, so that the lowering of the nested1203 // code will use the right symbols.1204 bool isLoop = llvm::omp::getDirectiveAssociation(info.dir) ==1205 llvm::omp::Association::LoopNest;1206 bool privatize = info.clauses && info.privatize;1207 1208 firOpBuilder.setInsertionPoint(marker);1209 std::optional<DataSharingProcessor> tempDsp;1210 if (privatize && !info.dsp) {1211 tempDsp.emplace(info.converter, info.semaCtx, *info.clauses, info.eval,1212 Fortran::lower::omp::isLastItemInQueue(item, queue),1213 /*useDelayedPrivatization=*/false, info.symTable);1214 tempDsp->processStep1();1215 }1216 1217 if (info.dir == llvm::omp::Directive::OMPD_parallel) {1218 threadPrivatizeVars(info.converter, info.eval);1219 if (info.clauses) {1220 firOpBuilder.setInsertionPoint(marker);1221 ClauseProcessor(info.converter, info.semaCtx, *info.clauses)1222 .processCopyin();1223 }1224 }1225 1226 if (!info.genSkeletonOnly) {1227 if (ConstructQueue::const_iterator next = std::next(item);1228 next != queue.end()) {1229 genOMPDispatch(info.converter, info.symTable, info.semaCtx, info.eval,1230 info.loc, queue, next);1231 } else {1232 // genFIR(Evaluation&) tries to patch up unterminated blocks, causing1233 // a lot of complications for our approach if the terminator generation1234 // is delayed past this point. Insert a temporary terminator here, then1235 // delete it.1236 firOpBuilder.setInsertionPointToEnd(&op.getRegion(0).back());1237 auto *temp = lower::genOpenMPTerminator(firOpBuilder, &op, info.loc);1238 firOpBuilder.setInsertionPointAfter(marker);1239 genNestedEvaluations(info.converter, info.eval);1240 temp->erase();1241 }1242 }1243 1244 // Get or create a unique exiting block from the given region, or1245 // return nullptr if there is no exiting block.1246 auto getUniqueExit = [&](mlir::Region ®ion) -> mlir::Block * {1247 // Find the blocks where the OMP terminator should go. In simple cases1248 // it is the single block in the operation's region. When the region1249 // is more complicated, especially with unstructured control flow, there1250 // may be multiple blocks, and some of them may have non-OMP terminators1251 // resulting from lowering of the code contained within the operation.1252 // All the remaining blocks are potential exit points from the op's region.1253 //1254 // Explicit control flow cannot exit any OpenMP region (other than via1255 // STOP), and that is enforced by semantic checks prior to lowering. STOP1256 // statements are lowered to a function call.1257 1258 // Collect unterminated blocks.1259 llvm::SmallVector<mlir::Block *> exits;1260 for (mlir::Block &b : region) {1261 if (b.empty() || !b.back().hasTrait<mlir::OpTrait::IsTerminator>())1262 exits.push_back(&b);1263 }1264 1265 if (exits.empty())1266 return nullptr;1267 // If there already is a unique exiting block, do not create another one.1268 // Additionally, some ops (e.g. omp.sections) require only 1 block in1269 // its region.1270 if (exits.size() == 1)1271 return exits[0];1272 mlir::Block *exit = firOpBuilder.createBlock(®ion);1273 for (mlir::Block *b : exits) {1274 firOpBuilder.setInsertionPointToEnd(b);1275 mlir::cf::BranchOp::create(firOpBuilder, info.loc, exit);1276 }1277 return exit;1278 };1279 1280 if (auto *exitBlock = getUniqueExit(op.getRegion(0))) {1281 firOpBuilder.setInsertionPointToEnd(exitBlock);1282 auto *term = lower::genOpenMPTerminator(firOpBuilder, &op, info.loc);1283 // Only insert lastprivate code when there actually is an exit block.1284 // Such a block may not exist if the nested code produced an infinite1285 // loop (this may not make sense in production code, but a user could1286 // write that and we should handle it).1287 firOpBuilder.setInsertionPoint(term);1288 if (privatize) {1289 // DataSharingProcessor::processStep2() may create operations before/after1290 // the one passed as argument. We need to treat loop wrappers and their1291 // nested loop as a unit, so we need to pass the bottom level wrapper (if1292 // present). Otherwise, these operations will be inserted within a1293 // wrapper region.1294 mlir::Operation *privatizationBottomLevelOp = &op;1295 if (auto loopNest = llvm::dyn_cast<mlir::omp::LoopNestOp>(op)) {1296 llvm::SmallVector<mlir::omp::LoopWrapperInterface> wrappers;1297 loopNest.gatherWrappers(wrappers);1298 if (!wrappers.empty())1299 privatizationBottomLevelOp = &*wrappers.front();1300 }1301 1302 if (!info.dsp) {1303 assert(tempDsp.has_value());1304 tempDsp->processStep2(privatizationBottomLevelOp, isLoop);1305 } else {1306 if (isLoop && regionArgs.size() > 0) {1307 for (const auto ®ionArg : regionArgs) {1308 info.dsp->pushLoopIV(info.converter.getSymbolAddress(*regionArg));1309 }1310 }1311 info.dsp->processStep2(privatizationBottomLevelOp, isLoop);1312 }1313 }1314 }1315 1316 firOpBuilder.setInsertionPointAfter(marker);1317 marker->erase();1318}1319 1320static void genBodyOfTargetDataOp(1321 lower::AbstractConverter &converter, lower::SymMap &symTable,1322 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,1323 mlir::omp::TargetDataOp &dataOp, const EntryBlockArgs &args,1324 const mlir::Location ¤tLocation, const ConstructQueue &queue,1325 ConstructQueue::const_iterator item) {1326 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();1327 1328 genEntryBlock(firOpBuilder, args, dataOp.getRegion());1329 bindEntryBlockArgs(converter, dataOp, args);1330 1331 // Insert dummy instruction to remember the insertion position. The1332 // marker will be deleted by clean up passes since there are no uses.1333 // Remembering the position for further insertion is important since1334 // there are hlfir.declares inserted above while setting block arguments1335 // and new code from the body should be inserted after that.1336 mlir::Value undefMarker = fir::UndefOp::create(firOpBuilder, dataOp.getLoc(),1337 firOpBuilder.getIndexType());1338 1339 // Create blocks for unstructured regions. This has to be done since1340 // blocks are initially allocated with the function as the parent region.1341 if (eval.lowerAsUnstructured()) {1342 lower::createEmptyRegionBlocks<mlir::omp::TerminatorOp, mlir::omp::YieldOp>(1343 firOpBuilder, eval.getNestedEvaluations());1344 }1345 1346 mlir::omp::TerminatorOp::create(firOpBuilder, currentLocation);1347 1348 // Set the insertion point after the marker.1349 firOpBuilder.setInsertionPointAfter(undefMarker.getDefiningOp());1350 1351 if (ConstructQueue::const_iterator next = std::next(item);1352 next != queue.end()) {1353 genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,1354 next);1355 } else {1356 genNestedEvaluations(converter, eval);1357 }1358}1359 1360// This generates intermediate common block member accesses within a region1361// and then rebinds the members symbol to the intermediate accessors we have1362// generated so that subsequent code generation will utilise these instead.1363//1364// When the scope changes, the bindings to the intermediate accessors should1365// be dropped in place of the original symbol bindings.1366//1367// This is for utilisation with TargetOp.1368static void genIntermediateCommonBlockAccessors(1369 Fortran::lower::AbstractConverter &converter,1370 const mlir::Location ¤tLocation,1371 llvm::ArrayRef<mlir::BlockArgument> mapBlockArgs,1372 llvm::ArrayRef<const Fortran::semantics::Symbol *> mapSyms) {1373 // Iterate over the symbol list, which will be shorter than the list of1374 // arguments if new entry block arguments were introduced to implicitly map1375 // outside values used by the bounds cloned into the target region. In that1376 // case, the additional block arguments do not need processing here.1377 for (auto [mapSym, mapArg] : llvm::zip_first(mapSyms, mapBlockArgs)) {1378 auto *details = mapSym->detailsIf<Fortran::semantics::CommonBlockDetails>();1379 if (!details)1380 continue;1381 1382 for (auto obj : details->objects()) {1383 auto targetCBMemberBind = Fortran::lower::genCommonBlockMember(1384 converter, currentLocation, *obj, mapArg, mapSym->size());1385 fir::ExtendedValue sexv = converter.getSymbolExtendedValue(*obj);1386 fir::ExtendedValue targetCBExv =1387 getExtendedValue(sexv, targetCBMemberBind);1388 converter.bindSymbol(*obj, targetCBExv);1389 }1390 }1391}1392 1393// This functions creates a block for the body of the targetOp's region. It adds1394// all the symbols present in mapSymbols as block arguments to this block.1395static void genBodyOfTargetOp(1396 lower::AbstractConverter &converter, lower::SymMap &symTable,1397 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,1398 mlir::omp::TargetOp &targetOp, const EntryBlockArgs &args,1399 const mlir::Location ¤tLocation, const ConstructQueue &queue,1400 ConstructQueue::const_iterator item, DataSharingProcessor &dsp) {1401 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();1402 auto argIface = llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(*targetOp);1403 1404 mlir::Region ®ion = targetOp.getRegion();1405 genEntryBlock(firOpBuilder, args, region);1406 bindEntryBlockArgs(converter, targetOp, args);1407 if (HostEvalInfo *hostEvalInfo = getHostEvalInfoStackTop(converter))1408 hostEvalInfo->bindOperands(argIface.getHostEvalBlockArgs());1409 1410 // Check if cloning the bounds introduced any dependency on the outer region.1411 // If so, then either clone them as well if they are MemoryEffectFree, or else1412 // copy them to a new temporary and add them to the map and block_argument1413 // lists and replace their uses with the new temporary.1414 cloneOrMapRegionOutsiders(firOpBuilder, targetOp);1415 1416 // Insert dummy instruction to remember the insertion position. The1417 // marker will be deleted since there are not uses.1418 // In the HLFIR flow there are hlfir.declares inserted above while1419 // setting block arguments.1420 mlir::Value undefMarker = fir::UndefOp::create(1421 firOpBuilder, targetOp.getLoc(), firOpBuilder.getIndexType());1422 1423 // Create blocks for unstructured regions. This has to be done since1424 // blocks are initially allocated with the function as the parent region.1425 if (lower::omp::isLastItemInQueue(item, queue) &&1426 eval.lowerAsUnstructured()) {1427 lower::createEmptyRegionBlocks<mlir::omp::TerminatorOp, mlir::omp::YieldOp>(1428 firOpBuilder, eval.getNestedEvaluations());1429 }1430 1431 mlir::omp::TerminatorOp::create(firOpBuilder, currentLocation);1432 1433 // Create the insertion point after the marker.1434 firOpBuilder.setInsertionPointAfter(undefMarker.getDefiningOp());1435 1436 // If we map a common block using it's symbol e.g. map(tofrom: /common_block/)1437 // and accessing its members within the target region, there is a large1438 // chance we will end up with uses external to the region accessing the common1439 // resolve these, we do so by generating new common block member accesses1440 // within the region, binding them to the member symbol for the scope of the1441 // region so that subsequent code generation within the region will utilise1442 // our new member accesses we have created.1443 genIntermediateCommonBlockAccessors(1444 converter, currentLocation, argIface.getMapBlockArgs(), args.map.syms);1445 1446 if (ConstructQueue::const_iterator next = std::next(item);1447 next != queue.end()) {1448 genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,1449 next);1450 } else {1451 genNestedEvaluations(converter, eval);1452 }1453 1454 dsp.processStep2(targetOp, /*isLoop=*/false);1455}1456 1457template <typename OpTy, typename... Args>1458static OpTy genOpWithBody(const OpWithBodyGenInfo &info,1459 const ConstructQueue &queue,1460 ConstructQueue::const_iterator item, Args &&...args) {1461 auto op = OpTy::create(info.converter.getFirOpBuilder(), info.loc,1462 std::forward<Args>(args)...);1463 createBodyOfOp(*op, info, queue, item);1464 return op;1465}1466 1467template <typename OpTy, typename ClauseOpsTy>1468static OpTy genWrapperOp(lower::AbstractConverter &converter,1469 mlir::Location loc, const ClauseOpsTy &clauseOps,1470 const EntryBlockArgs &args) {1471 static_assert(1472 OpTy::template hasTrait<mlir::omp::LoopWrapperInterface::Trait>(),1473 "expected a loop wrapper");1474 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();1475 1476 // Create wrapper.1477 auto op = OpTy::create(firOpBuilder, loc, clauseOps);1478 1479 // Create entry block with arguments.1480 genEntryBlock(firOpBuilder, args, op.getRegion());1481 1482 return op;1483}1484 1485//===----------------------------------------------------------------------===//1486// Code generation functions for clauses1487//===----------------------------------------------------------------------===//1488 1489static void genCancelClauses(lower::AbstractConverter &converter,1490 semantics::SemanticsContext &semaCtx,1491 const List<Clause> &clauses, mlir::Location loc,1492 mlir::omp::CancelOperands &clauseOps) {1493 ClauseProcessor cp(converter, semaCtx, clauses);1494 cp.processCancelDirectiveName(clauseOps);1495 cp.processIf(llvm::omp::Directive::OMPD_cancel, clauseOps);1496}1497 1498static void1499genCancellationPointClauses(lower::AbstractConverter &converter,1500 semantics::SemanticsContext &semaCtx,1501 const List<Clause> &clauses, mlir::Location loc,1502 mlir::omp::CancellationPointOperands &clauseOps) {1503 ClauseProcessor cp(converter, semaCtx, clauses);1504 cp.processCancelDirectiveName(clauseOps);1505}1506 1507static void genCriticalDeclareClauses(1508 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1509 const List<Clause> &clauses, mlir::Location loc,1510 mlir::omp::CriticalDeclareOperands &clauseOps, llvm::StringRef name) {1511 ClauseProcessor cp(converter, semaCtx, clauses);1512 cp.processHint(clauseOps);1513 clauseOps.symName =1514 mlir::StringAttr::get(converter.getFirOpBuilder().getContext(), name);1515}1516 1517static void genDistributeClauses(lower::AbstractConverter &converter,1518 semantics::SemanticsContext &semaCtx,1519 lower::StatementContext &stmtCtx,1520 const List<Clause> &clauses,1521 mlir::Location loc,1522 mlir::omp::DistributeOperands &clauseOps) {1523 ClauseProcessor cp(converter, semaCtx, clauses);1524 cp.processAllocate(clauseOps);1525 cp.processDistSchedule(stmtCtx, clauseOps);1526 cp.processOrder(clauseOps);1527}1528 1529static void genFlushClauses(lower::AbstractConverter &converter,1530 semantics::SemanticsContext &semaCtx,1531 const ObjectList &objects,1532 const List<Clause> &clauses, mlir::Location loc,1533 llvm::SmallVectorImpl<mlir::Value> &operandRange) {1534 if (!objects.empty())1535 genObjectList(objects, converter, operandRange);1536 1537 ClauseProcessor cp(converter, semaCtx, clauses);1538 cp.processTODO<clause::AcqRel, clause::Acquire, clause::Release,1539 clause::SeqCst>(loc, llvm::omp::OMPD_flush);1540}1541 1542static void1543genLoopNestClauses(lower::AbstractConverter &converter,1544 semantics::SemanticsContext &semaCtx,1545 lower::pft::Evaluation &eval, const List<Clause> &clauses,1546 mlir::Location loc, mlir::omp::LoopNestOperands &clauseOps,1547 llvm::SmallVectorImpl<const semantics::Symbol *> &iv) {1548 ClauseProcessor cp(converter, semaCtx, clauses);1549 1550 HostEvalInfo *hostEvalInfo = getHostEvalInfoStackTop(converter);1551 if (!hostEvalInfo || !hostEvalInfo->apply(clauseOps, iv))1552 cp.processCollapse(loc, eval, clauseOps, clauseOps, iv);1553 1554 clauseOps.loopInclusive = converter.getFirOpBuilder().getUnitAttr();1555 cp.processTileSizes(eval, clauseOps);1556}1557 1558static void genLoopClauses(1559 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1560 const List<Clause> &clauses, mlir::Location loc,1561 mlir::omp::LoopOperands &clauseOps,1562 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {1563 ClauseProcessor cp(converter, semaCtx, clauses);1564 cp.processBind(clauseOps);1565 cp.processOrder(clauseOps);1566 cp.processReduction(loc, clauseOps, reductionSyms);1567 cp.processTODO<clause::Lastprivate>(loc, llvm::omp::Directive::OMPD_loop);1568}1569 1570static void genMaskedClauses(lower::AbstractConverter &converter,1571 semantics::SemanticsContext &semaCtx,1572 lower::StatementContext &stmtCtx,1573 const List<Clause> &clauses, mlir::Location loc,1574 mlir::omp::MaskedOperands &clauseOps) {1575 ClauseProcessor cp(converter, semaCtx, clauses);1576 cp.processFilter(stmtCtx, clauseOps);1577}1578 1579static void1580genOrderedRegionClauses(lower::AbstractConverter &converter,1581 semantics::SemanticsContext &semaCtx,1582 const List<Clause> &clauses, mlir::Location loc,1583 mlir::omp::OrderedRegionOperands &clauseOps) {1584 ClauseProcessor cp(converter, semaCtx, clauses);1585 cp.processTODO<clause::Simd>(loc, llvm::omp::Directive::OMPD_ordered);1586}1587 1588static void genParallelClauses(1589 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1590 lower::StatementContext &stmtCtx, const List<Clause> &clauses,1591 mlir::Location loc, mlir::omp::ParallelOperands &clauseOps,1592 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {1593 ClauseProcessor cp(converter, semaCtx, clauses);1594 cp.processAllocate(clauseOps);1595 cp.processIf(llvm::omp::Directive::OMPD_parallel, clauseOps);1596 1597 HostEvalInfo *hostEvalInfo = getHostEvalInfoStackTop(converter);1598 if (!hostEvalInfo || !hostEvalInfo->apply(clauseOps))1599 cp.processNumThreads(stmtCtx, clauseOps);1600 1601 cp.processProcBind(clauseOps);1602 cp.processReduction(loc, clauseOps, reductionSyms);1603}1604 1605static void genScanClauses(lower::AbstractConverter &converter,1606 semantics::SemanticsContext &semaCtx,1607 const List<Clause> &clauses, mlir::Location loc,1608 mlir::omp::ScanOperands &clauseOps) {1609 ClauseProcessor cp(converter, semaCtx, clauses);1610 cp.processInclusive(loc, clauseOps);1611 cp.processExclusive(loc, clauseOps);1612}1613 1614static void genSectionsClauses(1615 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1616 const List<Clause> &clauses, mlir::Location loc,1617 mlir::omp::SectionsOperands &clauseOps,1618 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {1619 ClauseProcessor cp(converter, semaCtx, clauses);1620 cp.processAllocate(clauseOps);1621 cp.processNowait(clauseOps);1622 cp.processReduction(loc, clauseOps, reductionSyms);1623 // TODO Support delayed privatization.1624}1625 1626static void genSimdClauses(1627 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1628 const List<Clause> &clauses, mlir::Location loc,1629 mlir::omp::SimdOperands &clauseOps,1630 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {1631 ClauseProcessor cp(converter, semaCtx, clauses);1632 cp.processAligned(clauseOps);1633 cp.processIf(llvm::omp::Directive::OMPD_simd, clauseOps);1634 cp.processNontemporal(clauseOps);1635 cp.processOrder(clauseOps);1636 cp.processReduction(loc, clauseOps, reductionSyms);1637 cp.processSafelen(clauseOps);1638 cp.processSimdlen(clauseOps);1639 1640 cp.processTODO<clause::Linear>(loc, llvm::omp::Directive::OMPD_simd);1641}1642 1643static void genSingleClauses(lower::AbstractConverter &converter,1644 semantics::SemanticsContext &semaCtx,1645 const List<Clause> &clauses, mlir::Location loc,1646 mlir::omp::SingleOperands &clauseOps) {1647 ClauseProcessor cp(converter, semaCtx, clauses);1648 cp.processAllocate(clauseOps);1649 cp.processCopyprivate(loc, clauseOps);1650 cp.processNowait(clauseOps);1651 // TODO Support delayed privatization.1652}1653 1654static void genTargetClauses(1655 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1656 lower::SymMap &symTable, lower::StatementContext &stmtCtx,1657 lower::pft::Evaluation &eval, const List<Clause> &clauses,1658 mlir::Location loc, mlir::omp::TargetOperands &clauseOps,1659 DefaultMapsTy &defaultMaps,1660 llvm::SmallVectorImpl<const semantics::Symbol *> &hasDeviceAddrSyms,1661 llvm::SmallVectorImpl<const semantics::Symbol *> &isDevicePtrSyms,1662 llvm::SmallVectorImpl<const semantics::Symbol *> &mapSyms) {1663 ClauseProcessor cp(converter, semaCtx, clauses);1664 cp.processBare(clauseOps);1665 cp.processDefaultMap(stmtCtx, defaultMaps);1666 cp.processDepend(symTable, stmtCtx, clauseOps);1667 cp.processDevice(stmtCtx, clauseOps);1668 cp.processHasDeviceAddr(stmtCtx, clauseOps, hasDeviceAddrSyms);1669 if (HostEvalInfo *hostEvalInfo = getHostEvalInfoStackTop(converter)) {1670 // Only process host_eval if compiling for the host device.1671 processHostEvalClauses(converter, semaCtx, stmtCtx, eval, loc);1672 hostEvalInfo->collectValues(clauseOps.hostEvalVars);1673 }1674 cp.processIf(llvm::omp::Directive::OMPD_target, clauseOps);1675 cp.processIsDevicePtr(clauseOps, isDevicePtrSyms);1676 cp.processMap(loc, stmtCtx, clauseOps, llvm::omp::Directive::OMPD_unknown,1677 &mapSyms);1678 cp.processNowait(clauseOps);1679 cp.processThreadLimit(stmtCtx, clauseOps);1680 1681 cp.processTODO<clause::Allocate, clause::InReduction, clause::UsesAllocators>(1682 loc, llvm::omp::Directive::OMPD_target);1683 1684 // `target private(..)` is only supported in delayed privatization mode.1685 if (!enableDelayedPrivatizationStaging)1686 cp.processTODO<clause::Firstprivate, clause::Private>(1687 loc, llvm::omp::Directive::OMPD_target);1688}1689 1690static void genTargetDataClauses(1691 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1692 lower::StatementContext &stmtCtx, const List<Clause> &clauses,1693 mlir::Location loc, mlir::omp::TargetDataOperands &clauseOps,1694 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceAddrSyms,1695 llvm::SmallVectorImpl<const semantics::Symbol *> &useDevicePtrSyms) {1696 ClauseProcessor cp(converter, semaCtx, clauses);1697 cp.processDevice(stmtCtx, clauseOps);1698 cp.processIf(llvm::omp::Directive::OMPD_target_data, clauseOps);1699 cp.processMap(loc, stmtCtx, clauseOps);1700 cp.processUseDeviceAddr(stmtCtx, clauseOps, useDeviceAddrSyms);1701 cp.processUseDevicePtr(stmtCtx, clauseOps, useDevicePtrSyms);1702 1703 // This function implements the deprecated functionality of use_device_ptr1704 // that allows users to provide non-CPTR arguments to it with the caveat1705 // that the compiler will treat them as use_device_addr. A lot of legacy1706 // code may still depend on this functionality, so we should support it1707 // in some manner. We do so currently by simply shifting non-cptr operands1708 // from the use_device_ptr lists into the use_device_addr lists.1709 // TODO: Perhaps create a user provideable compiler option that will1710 // re-introduce a hard-error rather than a warning in these cases.1711 promoteNonCPtrUseDevicePtrArgsToUseDeviceAddr(1712 clauseOps.useDeviceAddrVars, useDeviceAddrSyms,1713 clauseOps.useDevicePtrVars, useDevicePtrSyms);1714}1715 1716static void genTargetEnterExitUpdateDataClauses(1717 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1718 lower::SymMap &symTable, lower::StatementContext &stmtCtx,1719 const List<Clause> &clauses, mlir::Location loc,1720 llvm::omp::Directive directive,1721 mlir::omp::TargetEnterExitUpdateDataOperands &clauseOps) {1722 ClauseProcessor cp(converter, semaCtx, clauses);1723 cp.processDepend(symTable, stmtCtx, clauseOps);1724 cp.processDevice(stmtCtx, clauseOps);1725 cp.processIf(directive, clauseOps);1726 1727 if (directive == llvm::omp::Directive::OMPD_target_update)1728 cp.processMotionClauses(stmtCtx, clauseOps);1729 else1730 cp.processMap(loc, stmtCtx, clauseOps, directive);1731 1732 cp.processNowait(clauseOps);1733}1734 1735static void genTaskClauses(1736 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1737 lower::SymMap &symTable, lower::StatementContext &stmtCtx,1738 const List<Clause> &clauses, mlir::Location loc,1739 mlir::omp::TaskOperands &clauseOps,1740 llvm::SmallVectorImpl<const semantics::Symbol *> &inReductionSyms) {1741 ClauseProcessor cp(converter, semaCtx, clauses);1742 cp.processAllocate(clauseOps);1743 cp.processDepend(symTable, stmtCtx, clauseOps);1744 cp.processFinal(stmtCtx, clauseOps);1745 cp.processIf(llvm::omp::Directive::OMPD_task, clauseOps);1746 cp.processInReduction(loc, clauseOps, inReductionSyms);1747 cp.processMergeable(clauseOps);1748 cp.processPriority(stmtCtx, clauseOps);1749 cp.processUntied(clauseOps);1750 cp.processDetach(clauseOps);1751 1752 cp.processTODO<clause::Affinity>(loc, llvm::omp::Directive::OMPD_task);1753}1754 1755static void genTaskgroupClauses(1756 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1757 const List<Clause> &clauses, mlir::Location loc,1758 mlir::omp::TaskgroupOperands &clauseOps,1759 llvm::SmallVectorImpl<const semantics::Symbol *> &taskReductionSyms) {1760 ClauseProcessor cp(converter, semaCtx, clauses);1761 cp.processAllocate(clauseOps);1762 cp.processTaskReduction(loc, clauseOps, taskReductionSyms);1763}1764 1765static void genTaskloopClauses(1766 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1767 lower::StatementContext &stmtCtx, const List<Clause> &clauses,1768 mlir::Location loc, mlir::omp::TaskloopOperands &clauseOps,1769 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms,1770 llvm::SmallVectorImpl<const semantics::Symbol *> &inReductionSyms) {1771 1772 ClauseProcessor cp(converter, semaCtx, clauses);1773 cp.processAllocate(clauseOps);1774 cp.processFinal(stmtCtx, clauseOps);1775 cp.processGrainsize(stmtCtx, clauseOps);1776 cp.processIf(llvm::omp::Directive::OMPD_taskloop, clauseOps);1777 cp.processInReduction(loc, clauseOps, inReductionSyms);1778 cp.processMergeable(clauseOps);1779 cp.processNogroup(clauseOps);1780 cp.processNumTasks(stmtCtx, clauseOps);1781 cp.processPriority(stmtCtx, clauseOps);1782 cp.processReduction(loc, clauseOps, reductionSyms);1783 cp.processUntied(clauseOps);1784}1785 1786static void genTaskwaitClauses(lower::AbstractConverter &converter,1787 semantics::SemanticsContext &semaCtx,1788 const List<Clause> &clauses, mlir::Location loc,1789 mlir::omp::TaskwaitOperands &clauseOps) {1790 ClauseProcessor cp(converter, semaCtx, clauses);1791 cp.processTODO<clause::Depend, clause::Nowait>(1792 loc, llvm::omp::Directive::OMPD_taskwait);1793}1794 1795static void genWorkshareClauses(lower::AbstractConverter &converter,1796 semantics::SemanticsContext &semaCtx,1797 lower::StatementContext &stmtCtx,1798 const List<Clause> &clauses, mlir::Location loc,1799 mlir::omp::WorkshareOperands &clauseOps) {1800 ClauseProcessor cp(converter, semaCtx, clauses);1801 cp.processNowait(clauseOps);1802}1803 1804static void genTeamsClauses(1805 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1806 lower::StatementContext &stmtCtx, const List<Clause> &clauses,1807 mlir::Location loc, mlir::omp::TeamsOperands &clauseOps,1808 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {1809 ClauseProcessor cp(converter, semaCtx, clauses);1810 cp.processAllocate(clauseOps);1811 cp.processIf(llvm::omp::Directive::OMPD_teams, clauseOps);1812 1813 HostEvalInfo *hostEvalInfo = getHostEvalInfoStackTop(converter);1814 if (!hostEvalInfo || !hostEvalInfo->apply(clauseOps)) {1815 cp.processNumTeams(stmtCtx, clauseOps);1816 cp.processThreadLimit(stmtCtx, clauseOps);1817 }1818 1819 cp.processReduction(loc, clauseOps, reductionSyms);1820 // TODO Support delayed privatization.1821}1822 1823static void genWsloopClauses(1824 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1825 lower::StatementContext &stmtCtx, const List<Clause> &clauses,1826 mlir::Location loc, mlir::omp::WsloopOperands &clauseOps,1827 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {1828 ClauseProcessor cp(converter, semaCtx, clauses);1829 cp.processNowait(clauseOps);1830 cp.processOrder(clauseOps);1831 cp.processOrdered(clauseOps);1832 cp.processReduction(loc, clauseOps, reductionSyms);1833 cp.processSchedule(stmtCtx, clauseOps);1834 1835 cp.processTODO<clause::Allocate, clause::Linear>(1836 loc, llvm::omp::Directive::OMPD_do);1837}1838 1839//===----------------------------------------------------------------------===//1840// Code generation functions for leaf constructs1841//===----------------------------------------------------------------------===//1842 1843static mlir::omp::BarrierOp1844genBarrierOp(lower::AbstractConverter &converter, lower::SymMap &symTable,1845 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,1846 mlir::Location loc, const ConstructQueue &queue,1847 ConstructQueue::const_iterator item) {1848 return mlir::omp::BarrierOp::create(converter.getFirOpBuilder(), loc);1849}1850 1851static mlir::omp::CancelOp genCancelOp(lower::AbstractConverter &converter,1852 semantics::SemanticsContext &semaCtx,1853 lower::pft::Evaluation &eval,1854 mlir::Location loc,1855 const ConstructQueue &queue,1856 ConstructQueue::const_iterator item) {1857 mlir::omp::CancelOperands clauseOps;1858 genCancelClauses(converter, semaCtx, item->clauses, loc, clauseOps);1859 1860 return mlir::omp::CancelOp::create(converter.getFirOpBuilder(), loc,1861 clauseOps);1862}1863 1864static mlir::omp::CancellationPointOp genCancellationPointOp(1865 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,1866 lower::pft::Evaluation &eval, mlir::Location loc,1867 const ConstructQueue &queue, ConstructQueue::const_iterator item) {1868 mlir::omp::CancellationPointOperands clauseOps;1869 genCancellationPointClauses(converter, semaCtx, item->clauses, loc,1870 clauseOps);1871 1872 return mlir::omp::CancellationPointOp::create(converter.getFirOpBuilder(),1873 loc, clauseOps);1874}1875 1876static mlir::omp::CriticalOp1877genCriticalOp(lower::AbstractConverter &converter, lower::SymMap &symTable,1878 semantics::SemanticsContext &semaCtx,1879 lower::pft::Evaluation &eval, mlir::Location loc,1880 const ConstructQueue &queue, ConstructQueue::const_iterator item,1881 const std::optional<parser::Name> &name) {1882 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();1883 mlir::FlatSymbolRefAttr nameAttr;1884 1885 if (name) {1886 std::string nameStr = name->ToString();1887 mlir::ModuleOp mod = firOpBuilder.getModule();1888 auto global = mod.lookupSymbol<mlir::omp::CriticalDeclareOp>(nameStr);1889 if (!global) {1890 mlir::omp::CriticalDeclareOperands clauseOps;1891 genCriticalDeclareClauses(converter, semaCtx, item->clauses, loc,1892 clauseOps, nameStr);1893 1894 mlir::OpBuilder modBuilder(mod.getBodyRegion());1895 global = mlir::omp::CriticalDeclareOp::create(modBuilder, loc, clauseOps);1896 }1897 nameAttr = mlir::FlatSymbolRefAttr::get(firOpBuilder.getContext(),1898 global.getSymName());1899 }1900 1901 return genOpWithBody<mlir::omp::CriticalOp>(1902 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,1903 llvm::omp::Directive::OMPD_critical),1904 queue, item, nameAttr);1905}1906 1907static mlir::omp::FlushOp1908genFlushOp(lower::AbstractConverter &converter, lower::SymMap &symTable,1909 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,1910 mlir::Location loc, const ObjectList &objects,1911 const ConstructQueue &queue, ConstructQueue::const_iterator item) {1912 llvm::SmallVector<mlir::Value> operandRange;1913 genFlushClauses(converter, semaCtx, objects, item->clauses, loc,1914 operandRange);1915 1916 return mlir::omp::FlushOp::create(converter.getFirOpBuilder(),1917 converter.getCurrentLocation(),1918 operandRange);1919}1920 1921static mlir::omp::LoopNestOp genLoopNestOp(1922 lower::AbstractConverter &converter, lower::SymMap &symTable,1923 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,1924 mlir::Location loc, const ConstructQueue &queue,1925 ConstructQueue::const_iterator item, mlir::omp::LoopNestOperands &clauseOps,1926 llvm::ArrayRef<const semantics::Symbol *> iv,1927 llvm::ArrayRef<1928 std::pair<mlir::omp::BlockArgOpenMPOpInterface, const EntryBlockArgs &>>1929 wrapperArgs,1930 llvm::omp::Directive directive, DataSharingProcessor &dsp) {1931 auto ivCallback = [&](mlir::Operation *op) {1932 genLoopVars(op, converter, loc, iv, wrapperArgs);1933 return llvm::SmallVector<const semantics::Symbol *>(iv);1934 };1935 1936 uint64_t nestValue = getCollapseValue(item->clauses);1937 nestValue = nestValue < iv.size() ? iv.size() : nestValue;1938 auto *nestedEval = getCollapsedLoopEval(eval, nestValue);1939 return genOpWithBody<mlir::omp::LoopNestOp>(1940 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, *nestedEval,1941 directive)1942 .setClauses(&item->clauses)1943 .setDataSharingProcessor(&dsp)1944 .setGenRegionEntryCb(ivCallback),1945 queue, item, clauseOps);1946}1947 1948static mlir::omp::LoopOp1949genLoopOp(lower::AbstractConverter &converter, lower::SymMap &symTable,1950 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,1951 mlir::Location loc, const ConstructQueue &queue,1952 ConstructQueue::const_iterator item) {1953 mlir::omp::LoopOperands loopClauseOps;1954 llvm::SmallVector<const semantics::Symbol *> loopReductionSyms;1955 genLoopClauses(converter, semaCtx, item->clauses, loc, loopClauseOps,1956 loopReductionSyms);1957 1958 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,1959 /*shouldCollectPreDeterminedSymbols=*/true,1960 /*useDelayedPrivatization=*/true, symTable);1961 dsp.processStep1(&loopClauseOps);1962 1963 mlir::omp::LoopNestOperands loopNestClauseOps;1964 llvm::SmallVector<const semantics::Symbol *> iv;1965 genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,1966 loopNestClauseOps, iv);1967 1968 EntryBlockArgs loopArgs;1969 loopArgs.priv.syms = dsp.getDelayedPrivSymbols();1970 loopArgs.priv.vars = loopClauseOps.privateVars;1971 loopArgs.reduction.syms = loopReductionSyms;1972 loopArgs.reduction.vars = loopClauseOps.reductionVars;1973 1974 auto loopOp =1975 genWrapperOp<mlir::omp::LoopOp>(converter, loc, loopClauseOps, loopArgs);1976 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item,1977 loopNestClauseOps, iv, {{loopOp, loopArgs}},1978 llvm::omp::Directive::OMPD_loop, dsp);1979 return loopOp;1980}1981 1982static void genCanonicalLoopNest(1983 lower::AbstractConverter &converter, lower::SymMap &symTable,1984 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,1985 mlir::Location loc, const ConstructQueue &queue,1986 ConstructQueue::const_iterator item, size_t numLoops,1987 llvm::SmallVectorImpl<mlir::omp::CanonicalLoopOp> &loops) {1988 assert(loops.empty() && "Expecting empty list to fill");1989 assert(numLoops >= 1 && "Expecting at least one loop");1990 1991 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();1992 1993 mlir::omp::LoopRelatedClauseOps loopInfo;1994 llvm::SmallVector<const semantics::Symbol *, 3> ivs;1995 collectLoopRelatedInfo(converter, loc, eval, numLoops, loopInfo, ivs);1996 assert(ivs.size() == numLoops &&1997 "Expected to parse as many loop variables as there are loops");1998 1999 // Steps that follow:2000 // 1. Emit all of the loop's prologues (compute the tripcount)2001 // 2. Emit omp.canonical_loop nested inside each other (iteratively)2002 // 2.1. In the innermost omp.canonical_loop, emit the loop body prologue (in2003 // the body callback)2004 //2005 // Since emitting prologues and body code is split, remember prologue values2006 // for use when emitting the same loop's epilogues.2007 llvm::SmallVector<mlir::Value> tripcounts;2008 llvm::SmallVector<mlir::Value> clis;2009 llvm::SmallVector<lower::pft::Evaluation *> evals;2010 llvm::SmallVector<mlir::Type> loopVarTypes;2011 llvm::SmallVector<mlir::Value> loopStepVars;2012 llvm::SmallVector<mlir::Value> loopLBVars;2013 llvm::SmallVector<mlir::Value> blockArgs;2014 2015 // Step 1: Loop prologues2016 // Computing the trip count must happen before entering the outermost loop2017 lower::pft::Evaluation *innermostEval = &eval.getFirstNestedEvaluation();2018 for ([[maybe_unused]] auto iv : ivs) {2019 if (innermostEval->getIf<parser::DoConstruct>()->IsDoConcurrent()) {2020 // OpenMP specifies DO CONCURRENT only with the `!omp loop` construct.2021 // Will need to add special cases for this combination.2022 TODO(loc, "DO CONCURRENT as canonical loop not supported");2023 }2024 2025 auto &doLoopEval = innermostEval->getFirstNestedEvaluation();2026 evals.push_back(innermostEval);2027 2028 // Get the loop bounds (and increment)2029 // auto &doLoopEval = nestedEval.getFirstNestedEvaluation();2030 auto *doStmt = doLoopEval.getIf<parser::NonLabelDoStmt>();2031 assert(doStmt && "Expected do loop to be in the nested evaluation");2032 auto &loopControl = std::get<std::optional<parser::LoopControl>>(doStmt->t);2033 assert(loopControl.has_value());2034 auto *bounds = std::get_if<parser::LoopControl::Bounds>(&loopControl->u);2035 assert(bounds && "Expected bounds for canonical loop");2036 lower::StatementContext stmtCtx;2037 mlir::Value loopLBVar = fir::getBase(2038 converter.genExprValue(*semantics::GetExpr(bounds->lower), stmtCtx));2039 mlir::Value loopUBVar = fir::getBase(2040 converter.genExprValue(*semantics::GetExpr(bounds->upper), stmtCtx));2041 mlir::Value loopStepVar = [&]() {2042 if (bounds->step) {2043 return fir::getBase(2044 converter.genExprValue(*semantics::GetExpr(bounds->step), stmtCtx));2045 }2046 2047 // If `step` is not present, assume it is `1`.2048 auto intTy = firOpBuilder.getI32Type();2049 return firOpBuilder.createIntegerConstant(loc, intTy, 1);2050 }();2051 2052 // Get the integer kind for the loop variable and cast the loop bounds2053 size_t loopVarTypeSize = bounds->name.thing.symbol->GetUltimate().size();2054 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);2055 loopVarTypes.push_back(loopVarType);2056 loopLBVar = firOpBuilder.createConvert(loc, loopVarType, loopLBVar);2057 loopUBVar = firOpBuilder.createConvert(loc, loopVarType, loopUBVar);2058 loopStepVar = firOpBuilder.createConvert(loc, loopVarType, loopStepVar);2059 loopLBVars.push_back(loopLBVar);2060 loopStepVars.push_back(loopStepVar);2061 2062 // Start lowering2063 mlir::Value zero = firOpBuilder.createIntegerConstant(loc, loopVarType, 0);2064 mlir::Value one = firOpBuilder.createIntegerConstant(loc, loopVarType, 1);2065 mlir::Value isDownwards = mlir::arith::CmpIOp::create(2066 firOpBuilder, loc, mlir::arith::CmpIPredicate::slt, loopStepVar, zero);2067 2068 // Ensure we are counting upwards. If not, negate step and swap lb and ub.2069 mlir::Value negStep =2070 mlir::arith::SubIOp::create(firOpBuilder, loc, zero, loopStepVar);2071 mlir::Value incr = mlir::arith::SelectOp::create(2072 firOpBuilder, loc, isDownwards, negStep, loopStepVar);2073 mlir::Value lb = mlir::arith::SelectOp::create(2074 firOpBuilder, loc, isDownwards, loopUBVar, loopLBVar);2075 mlir::Value ub = mlir::arith::SelectOp::create(2076 firOpBuilder, loc, isDownwards, loopLBVar, loopUBVar);2077 2078 // Compute the trip count assuming lb <= ub. This guarantees that the result2079 // is non-negative and we can use unsigned arithmetic.2080 mlir::Value span = mlir::arith::SubIOp::create(2081 firOpBuilder, loc, ub, lb, ::mlir::arith::IntegerOverflowFlags::nuw);2082 mlir::Value tcMinusOne =2083 mlir::arith::DivUIOp::create(firOpBuilder, loc, span, incr);2084 mlir::Value tcIfLooping =2085 mlir::arith::AddIOp::create(firOpBuilder, loc, tcMinusOne, one,2086 ::mlir::arith::IntegerOverflowFlags::nuw);2087 2088 // Fall back to 0 if lb > ub2089 mlir::Value isZeroTC = mlir::arith::CmpIOp::create(2090 firOpBuilder, loc, mlir::arith::CmpIPredicate::slt, ub, lb);2091 mlir::Value tripcount = mlir::arith::SelectOp::create(2092 firOpBuilder, loc, isZeroTC, zero, tcIfLooping);2093 tripcounts.push_back(tripcount);2094 2095 // Create the CLI handle.2096 auto newcli = mlir::omp::NewCliOp::create(firOpBuilder, loc);2097 mlir::Value cli = newcli.getResult();2098 clis.push_back(cli);2099 2100 innermostEval = &*std::next(innermostEval->getNestedEvaluations().begin());2101 }2102 2103 // Step 2: Create nested canoncial loops2104 for (auto i : llvm::seq<size_t>(numLoops)) {2105 bool isInnermost = (i == numLoops - 1);2106 mlir::Type loopVarType = loopVarTypes[i];2107 mlir::Value tripcount = tripcounts[i];2108 mlir::Value cli = clis[i];2109 auto &&eval = evals[i];2110 2111 auto ivCallback = [&, i, isInnermost](mlir::Operation *op)2112 -> llvm::SmallVector<const Fortran::semantics::Symbol *> {2113 mlir::Region ®ion = op->getRegion(0);2114 2115 // Create the op's region skeleton (BB taking the iv as argument)2116 firOpBuilder.createBlock(®ion, {}, {loopVarType}, {loc});2117 blockArgs.push_back(region.front().getArgument(0));2118 2119 // Step 2.1: Emit body prologue code2120 // Compute the translation from logical iteration number to the value of2121 // the loop's iteration variable only in the innermost body. Currently,2122 // loop transformations do not allow any instruction between loops, but2123 // this will change with2124 if (isInnermost) {2125 assert(blockArgs.size() == numLoops &&2126 "Expecting all block args to have been collected by now");2127 for (auto j : llvm::seq<size_t>(numLoops)) {2128 mlir::Value natIterNum = fir::getBase(blockArgs[j]);2129 mlir::Value scaled = mlir::arith::MulIOp::create(2130 firOpBuilder, loc, natIterNum, loopStepVars[j]);2131 mlir::Value userVal = mlir::arith::AddIOp::create(2132 firOpBuilder, loc, loopLBVars[j], scaled);2133 2134 mlir::OpBuilder::InsertPoint insPt =2135 firOpBuilder.saveInsertionPoint();2136 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());2137 mlir::Type tempTy = converter.genType(*ivs[j]);2138 firOpBuilder.restoreInsertionPoint(insPt);2139 2140 // Write the loop value into loop variable2141 mlir::Value cvtVal = firOpBuilder.createConvert(loc, tempTy, userVal);2142 hlfir::Entity lhs{converter.getSymbolAddress(*ivs[j])};2143 lhs = hlfir::derefPointersAndAllocatables(loc, firOpBuilder, lhs);2144 mlir::Operation *storeOp =2145 hlfir::AssignOp::create(firOpBuilder, loc, cvtVal, lhs);2146 firOpBuilder.setInsertionPointAfter(storeOp);2147 }2148 }2149 2150 return {ivs[i]};2151 };2152 2153 // Create the omp.canonical_loop operation2154 auto opGenInfo = OpWithBodyGenInfo(converter, symTable, semaCtx, loc, *eval,2155 llvm::omp::Directive::OMPD_unknown)2156 .setGenSkeletonOnly(!isInnermost)2157 .setClauses(&item->clauses)2158 .setPrivatize(false)2159 .setGenRegionEntryCb(ivCallback);2160 auto canonLoop = genOpWithBody<mlir::omp::CanonicalLoopOp>(2161 std::move(opGenInfo), queue, item, tripcount, cli);2162 loops.push_back(canonLoop);2163 2164 // Insert next loop nested inside last loop2165 firOpBuilder.setInsertionPoint(2166 canonLoop.getRegion().back().getTerminator());2167 }2168 2169 firOpBuilder.setInsertionPointAfter(loops.front());2170}2171 2172static void genTileOp(Fortran::lower::AbstractConverter &converter,2173 Fortran::lower::SymMap &symTable,2174 lower::StatementContext &stmtCtx,2175 Fortran::semantics::SemanticsContext &semaCtx,2176 Fortran::lower::pft::Evaluation &eval, mlir::Location loc,2177 const ConstructQueue &queue,2178 ConstructQueue::const_iterator item) {2179 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();2180 2181 mlir::omp::SizesClauseOps sizesClause;2182 ClauseProcessor cp(converter, semaCtx, item->clauses);2183 cp.processSizes(stmtCtx, sizesClause);2184 2185 size_t numLoops = sizesClause.sizes.size();2186 llvm::SmallVector<mlir::omp::CanonicalLoopOp, 3> canonLoops;2187 canonLoops.reserve(numLoops);2188 2189 genCanonicalLoopNest(converter, symTable, semaCtx, eval, loc, queue, item,2190 numLoops, canonLoops);2191 assert((canonLoops.size() == numLoops) &&2192 "Expecting the predetermined number of loops");2193 2194 llvm::SmallVector<mlir::Value, 3> applyees;2195 applyees.reserve(numLoops);2196 for (mlir::omp::CanonicalLoopOp l : canonLoops)2197 applyees.push_back(l.getCli());2198 2199 // Emit the associated loops and create a CLI for each affected loop2200 llvm::SmallVector<mlir::Value, 3> gridGeneratees;2201 llvm::SmallVector<mlir::Value, 3> intratileGeneratees;2202 gridGeneratees.reserve(numLoops);2203 intratileGeneratees.reserve(numLoops);2204 for ([[maybe_unused]] auto i : llvm::seq<int>(0, sizesClause.sizes.size())) {2205 auto gridCLI = mlir::omp::NewCliOp::create(firOpBuilder, loc);2206 gridGeneratees.push_back(gridCLI.getResult());2207 auto intratileCLI = mlir::omp::NewCliOp::create(firOpBuilder, loc);2208 intratileGeneratees.push_back(intratileCLI.getResult());2209 }2210 2211 llvm::SmallVector<mlir::Value, 6> generatees;2212 generatees.reserve(2 * numLoops);2213 generatees.append(gridGeneratees);2214 generatees.append(intratileGeneratees);2215 2216 mlir::omp::TileOp::create(firOpBuilder, loc, generatees, applyees,2217 sizesClause.sizes);2218}2219 2220static void genUnrollOp(Fortran::lower::AbstractConverter &converter,2221 Fortran::lower::SymMap &symTable,2222 lower::StatementContext &stmtCtx,2223 Fortran::semantics::SemanticsContext &semaCtx,2224 Fortran::lower::pft::Evaluation &eval,2225 mlir::Location loc, const ConstructQueue &queue,2226 ConstructQueue::const_iterator item) {2227 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();2228 2229 // Clauses for unrolling not yet implemnted2230 ClauseProcessor cp(converter, semaCtx, item->clauses);2231 cp.processTODO<clause::Partial, clause::Full>(2232 loc, llvm::omp::Directive::OMPD_unroll);2233 2234 // Emit the associated loop2235 llvm::SmallVector<mlir::omp::CanonicalLoopOp, 1> canonLoops;2236 genCanonicalLoopNest(converter, symTable, semaCtx, eval, loc, queue, item, 1,2237 canonLoops);2238 2239 llvm::SmallVector<mlir::Value, 1> applyees;2240 for (auto &&canonLoop : canonLoops)2241 applyees.push_back(canonLoop.getCli());2242 2243 // Apply unrolling to it2244 auto cli = llvm::getSingleElement(canonLoops).getCli();2245 mlir::omp::UnrollHeuristicOp::create(firOpBuilder, loc, cli);2246}2247 2248static mlir::omp::MaskedOp2249genMaskedOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2250 lower::StatementContext &stmtCtx,2251 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2252 mlir::Location loc, const ConstructQueue &queue,2253 ConstructQueue::const_iterator item) {2254 mlir::omp::MaskedOperands clauseOps;2255 genMaskedClauses(converter, semaCtx, stmtCtx, item->clauses, loc, clauseOps);2256 2257 return genOpWithBody<mlir::omp::MaskedOp>(2258 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2259 llvm::omp::Directive::OMPD_masked),2260 queue, item, clauseOps);2261}2262 2263static mlir::omp::MasterOp2264genMasterOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2265 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2266 mlir::Location loc, const ConstructQueue &queue,2267 ConstructQueue::const_iterator item) {2268 return genOpWithBody<mlir::omp::MasterOp>(2269 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2270 llvm::omp::Directive::OMPD_master),2271 queue, item);2272}2273 2274static mlir::omp::OrderedOp2275genOrderedOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2276 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2277 mlir::Location loc, const ConstructQueue &queue,2278 ConstructQueue::const_iterator item) {2279 if (!semaCtx.langOptions().OpenMPSimd)2280 TODO(loc, "OMPD_ordered");2281 return nullptr;2282}2283 2284static mlir::omp::OrderedRegionOp2285genOrderedRegionOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2286 semantics::SemanticsContext &semaCtx,2287 lower::pft::Evaluation &eval, mlir::Location loc,2288 const ConstructQueue &queue,2289 ConstructQueue::const_iterator item) {2290 mlir::omp::OrderedRegionOperands clauseOps;2291 genOrderedRegionClauses(converter, semaCtx, item->clauses, loc, clauseOps);2292 2293 return genOpWithBody<mlir::omp::OrderedRegionOp>(2294 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2295 llvm::omp::Directive::OMPD_ordered),2296 queue, item, clauseOps);2297}2298 2299static mlir::omp::ParallelOp2300genParallelOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2301 semantics::SemanticsContext &semaCtx,2302 lower::pft::Evaluation &eval, mlir::Location loc,2303 const ConstructQueue &queue, ConstructQueue::const_iterator item,2304 mlir::omp::ParallelOperands &clauseOps,2305 const EntryBlockArgs &args, DataSharingProcessor *dsp,2306 bool isComposite = false) {2307 assert((!enableDelayedPrivatization || dsp) &&2308 "expected valid DataSharingProcessor");2309 2310 OpWithBodyGenInfo genInfo =2311 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2312 llvm::omp::Directive::OMPD_parallel)2313 .setClauses(&item->clauses)2314 .setEntryBlockArgs(&args)2315 .setGenSkeletonOnly(isComposite)2316 .setDataSharingProcessor(dsp);2317 2318 auto parallelOp =2319 genOpWithBody<mlir::omp::ParallelOp>(genInfo, queue, item, clauseOps);2320 parallelOp.setComposite(isComposite);2321 return parallelOp;2322}2323 2324static mlir::omp::ScanOp2325genScanOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2326 semantics::SemanticsContext &semaCtx, mlir::Location loc,2327 const ConstructQueue &queue, ConstructQueue::const_iterator item) {2328 mlir::omp::ScanOperands clauseOps;2329 genScanClauses(converter, semaCtx, item->clauses, loc, clauseOps);2330 return mlir::omp::ScanOp::create(converter.getFirOpBuilder(),2331 converter.getCurrentLocation(), clauseOps);2332}2333 2334static mlir::omp::SectionsOp2335genSectionsOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2336 semantics::SemanticsContext &semaCtx,2337 lower::pft::Evaluation &eval, mlir::Location loc,2338 const ConstructQueue &queue,2339 ConstructQueue::const_iterator item) {2340 const parser::OpenMPSectionsConstruct *sectionsConstruct =2341 getSectionsConstructStackTop(converter);2342 assert(sectionsConstruct && "Missing additional parsing information");2343 2344 const auto §ionBlocks =2345 std::get<std::list<parser::OpenMPConstruct>>(sectionsConstruct->t);2346 mlir::omp::SectionsOperands clauseOps;2347 llvm::SmallVector<const semantics::Symbol *> reductionSyms;2348 genSectionsClauses(converter, semaCtx, item->clauses, loc, clauseOps,2349 reductionSyms);2350 2351 auto &builder = converter.getFirOpBuilder();2352 2353 // Insert privatizations before SECTIONS2354 lower::SymMapScope scope(symTable);2355 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,2356 lower::omp::isLastItemInQueue(item, queue),2357 /*useDelayedPrivatization=*/false, symTable);2358 dsp.processStep1();2359 2360 List<Clause> nonDsaClauses;2361 List<const clause::Lastprivate *> lastprivates;2362 2363 for (const Clause &clause : item->clauses) {2364 if (clause.id == llvm::omp::Clause::OMPC_lastprivate) {2365 auto &lastp = std::get<clause::Lastprivate>(clause.u);2366 lastprivateModifierNotSupported(lastp, converter.getCurrentLocation());2367 lastprivates.push_back(&lastp);2368 } else {2369 switch (clause.id) {2370 case llvm::omp::Clause::OMPC_firstprivate:2371 case llvm::omp::Clause::OMPC_private:2372 case llvm::omp::Clause::OMPC_shared:2373 break;2374 default:2375 nonDsaClauses.push_back(clause);2376 }2377 }2378 }2379 2380 // SECTIONS construct.2381 auto sectionsOp = mlir::omp::SectionsOp::create(builder, loc, clauseOps);2382 2383 // Create entry block with reduction variables as arguments.2384 EntryBlockArgs args;2385 // TODO: Add private syms and vars.2386 args.reduction.syms = reductionSyms;2387 args.reduction.vars = clauseOps.reductionVars;2388 2389 genEntryBlock(builder, args, sectionsOp.getRegion());2390 mlir::Operation *terminator =2391 lower::genOpenMPTerminator(builder, sectionsOp, loc);2392 2393 // Generate nested SECTION constructs.2394 // This is done here rather than in genOMP([...], OpenMPSectionConstruct )2395 // because we need to run genReductionVars on each omp.section so that the2396 // reduction variable gets mapped to the private version2397 for (auto [construct, nestedEval] :2398 llvm::zip(sectionBlocks, eval.getNestedEvaluations())) {2399 const auto *sectionConstruct =2400 std::get_if<parser::OpenMPSectionConstruct>(&construct.u);2401 if (!sectionConstruct) {2402 assert(false &&2403 "unexpected construct nested inside of SECTIONS construct");2404 continue;2405 }2406 2407 ConstructQueue sectionQueue{buildConstructQueue(2408 converter.getFirOpBuilder().getModule(), semaCtx, nestedEval,2409 sectionConstruct->source, llvm::omp::Directive::OMPD_section, {})};2410 2411 builder.setInsertionPoint(terminator);2412 genOpWithBody<mlir::omp::SectionOp>(2413 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, nestedEval,2414 llvm::omp::Directive::OMPD_section)2415 .setClauses(§ionQueue.begin()->clauses)2416 .setDataSharingProcessor(&dsp)2417 .setEntryBlockArgs(&args),2418 sectionQueue, sectionQueue.begin());2419 }2420 2421 if (!lastprivates.empty()) {2422 mlir::Region §ionsBody = sectionsOp.getRegion();2423 assert(sectionsBody.hasOneBlock());2424 mlir::Block &body = sectionsBody.front();2425 2426 auto lastSectionOp = llvm::find_if(2427 llvm::reverse(body.getOperations()), [](const mlir::Operation &op) {2428 return llvm::isa<mlir::omp::SectionOp>(op);2429 });2430 assert(lastSectionOp != body.rend());2431 2432 for (const clause::Lastprivate *lastp : lastprivates) {2433 builder.setInsertionPoint(2434 lastSectionOp->getRegion(0).back().getTerminator());2435 mlir::OpBuilder::InsertPoint insp = builder.saveInsertionPoint();2436 const auto &objList = std::get<ObjectList>(lastp->t);2437 for (const Object &object : objList) {2438 semantics::Symbol *sym = object.sym();2439 if (const auto *common =2440 sym->detailsIf<semantics::CommonBlockDetails>()) {2441 for (const auto &obj : common->objects())2442 converter.copyHostAssociateVar(*obj, &insp, /*hostIsSource=*/false);2443 } else {2444 converter.copyHostAssociateVar(*sym, &insp, /*hostIsSource=*/false);2445 }2446 }2447 }2448 }2449 2450 // Perform DataSharingProcessor's step2 out of SECTIONS2451 builder.setInsertionPointAfter(sectionsOp.getOperation());2452 dsp.processStep2(sectionsOp, false);2453 // Emit implicit barrier to synchronize threads and avoid data2454 // races on post-update of lastprivate variables when `nowait`2455 // clause is present.2456 if (clauseOps.nowait && !lastprivates.empty())2457 mlir::omp::BarrierOp::create(builder, loc);2458 2459 return sectionsOp;2460}2461 2462static mlir::Operation *2463genScopeOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2464 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2465 mlir::Location loc, const ConstructQueue &queue,2466 ConstructQueue::const_iterator item) {2467 if (!semaCtx.langOptions().OpenMPSimd)2468 TODO(loc, "Scope construct");2469 return nullptr;2470}2471 2472static mlir::omp::SingleOp2473genSingleOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2474 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2475 mlir::Location loc, const ConstructQueue &queue,2476 ConstructQueue::const_iterator item) {2477 mlir::omp::SingleOperands clauseOps;2478 genSingleClauses(converter, semaCtx, item->clauses, loc, clauseOps);2479 2480 return genOpWithBody<mlir::omp::SingleOp>(2481 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2482 llvm::omp::Directive::OMPD_single)2483 .setClauses(&item->clauses),2484 queue, item, clauseOps);2485}2486 2487static bool isDuplicateMappedSymbol(2488 const semantics::Symbol &sym,2489 const llvm::SetVector<const semantics::Symbol *> &privatizedSyms,2490 const llvm::SmallVectorImpl<const semantics::Symbol *> &hasDevSyms,2491 const llvm::SmallVectorImpl<const semantics::Symbol *> &mappedSyms) {2492 llvm::SmallVector<const semantics::Symbol *> concatSyms;2493 concatSyms.reserve(privatizedSyms.size() + hasDevSyms.size() +2494 mappedSyms.size());2495 concatSyms.append(privatizedSyms.begin(), privatizedSyms.end());2496 concatSyms.append(hasDevSyms.begin(), hasDevSyms.end());2497 concatSyms.append(mappedSyms.begin(), mappedSyms.end());2498 2499 auto checkSymbol = [&](const semantics::Symbol &checkSym) {2500 return std::any_of(concatSyms.begin(), concatSyms.end(),2501 [&](auto v) { return v->GetUltimate() == checkSym; });2502 };2503 2504 if (checkSymbol(sym))2505 return true;2506 2507 const auto *hostAssoc{sym.detailsIf<semantics::HostAssocDetails>()};2508 if (hostAssoc && checkSymbol(hostAssoc->symbol()))2509 return true;2510 2511 return checkSymbol(sym.GetUltimate());2512}2513 2514static mlir::omp::TargetOp2515genTargetOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2516 lower::StatementContext &stmtCtx,2517 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2518 mlir::Location loc, const ConstructQueue &queue,2519 ConstructQueue::const_iterator item) {2520 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();2521 bool isTargetDevice =2522 llvm::cast<mlir::omp::OffloadModuleInterface>(*converter.getModuleOp())2523 .getIsTargetDevice();2524 2525 // Introduce a new host_eval information structure for this target region.2526 if (!isTargetDevice)2527 converter.getStateStack().stackPush<HostEvalInfoStackFrame>();2528 2529 mlir::omp::TargetOperands clauseOps;2530 DefaultMapsTy defaultMaps;2531 llvm::SmallVector<const semantics::Symbol *> mapSyms, isDevicePtrSyms,2532 hasDeviceAddrSyms;2533 genTargetClauses(converter, semaCtx, symTable, stmtCtx, eval, item->clauses,2534 loc, clauseOps, defaultMaps, hasDeviceAddrSyms,2535 isDevicePtrSyms, mapSyms);2536 2537 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,2538 /*shouldCollectPreDeterminedSymbols=*/2539 lower::omp::isLastItemInQueue(item, queue),2540 /*useDelayedPrivatization=*/true, symTable,2541 /*isTargetPrivitization=*/true);2542 dsp.processStep1(&clauseOps);2543 2544 // 5.8.1 Implicit Data-Mapping Attribute Rules2545 // The following code follows the implicit data-mapping rules to map all the2546 // symbols used inside the region that do not have explicit data-environment2547 // attribute clauses (neither data-sharing; e.g. `private`, nor `map`2548 // clauses).2549 auto captureImplicitMap = [&](const semantics::Symbol &sym) {2550 // Structure component symbols don't have bindings, and can only be2551 // explicitly mapped individually. If a member is captured implicitly2552 // we map the entirety of the derived type when we find its symbol.2553 if (sym.owner().IsDerivedType())2554 return;2555 2556 // if the symbol is part of an already mapped common block, do not make a2557 // map for it.2558 if (const Fortran::semantics::Symbol *common =2559 Fortran::semantics::FindCommonBlockContaining(sym.GetUltimate()))2560 if (llvm::is_contained(mapSyms, common))2561 return;2562 2563 // If we come across a symbol without a symbol address, we2564 // return as we cannot process it, this is intended as a2565 // catch all early exit for symbols that do not have a2566 // corresponding extended value. Such as subroutines,2567 // interfaces and named blocks.2568 if (!converter.getSymbolAddress(sym))2569 return;2570 2571 // Skip parameters/constants as they do not need to be mapped.2572 if (semantics::IsNamedConstant(sym))2573 return;2574 2575 if (!isDuplicateMappedSymbol(sym, dsp.getAllSymbolsToPrivatize(),2576 hasDeviceAddrSyms, mapSyms)) {2577 if (const auto *details =2578 sym.template detailsIf<semantics::HostAssocDetails>())2579 converter.copySymbolBinding(details->symbol(), sym);2580 std::stringstream name;2581 fir::ExtendedValue dataExv = converter.getSymbolExtendedValue(sym);2582 name << sym.name().ToString();2583 2584 fir::factory::AddrAndBoundsInfo info =2585 Fortran::lower::getDataOperandBaseAddr(2586 converter, firOpBuilder, sym.GetUltimate(),2587 converter.getCurrentLocation());2588 llvm::SmallVector<mlir::Value> bounds =2589 fir::factory::genImplicitBoundsOps<mlir::omp::MapBoundsOp,2590 mlir::omp::MapBoundsType>(2591 firOpBuilder, info, dataExv,2592 semantics::IsAssumedSizeArray(sym.GetUltimate()),2593 converter.getCurrentLocation());2594 mlir::Value baseOp = info.rawInput;2595 mlir::Type eleType = baseOp.getType();2596 if (auto refType = mlir::dyn_cast<fir::ReferenceType>(baseOp.getType()))2597 eleType = refType.getElementType();2598 2599 std::pair<mlir::omp::ClauseMapFlags, mlir::omp::VariableCaptureKind>2600 mapFlagAndKind = getImplicitMapTypeAndKind(2601 firOpBuilder, converter, defaultMaps, eleType, loc, sym);2602 2603 mlir::FlatSymbolRefAttr mapperId;2604 if (defaultMaps.empty()) {2605 // TODO: Honor user-provided defaultmap clauses (aggregates/pointers)2606 // instead of blanket-disabling implicit mapper generation whenever any2607 // explicit default map is present.2608 const semantics::DerivedTypeSpec *typeSpec =2609 sym.GetType() ? sym.GetType()->AsDerived() : nullptr;2610 if (typeSpec) {2611 std::string mapperIdName =2612 typeSpec->name().ToString() + llvm::omp::OmpDefaultMapperName;2613 if (auto *mapperSym =2614 converter.getCurrentScope().FindSymbol(mapperIdName))2615 mapperIdName = converter.mangleName(2616 mapperIdName, mapperSym->GetUltimate().owner());2617 else2618 mapperIdName =2619 converter.mangleName(mapperIdName, *typeSpec->GetScope());2620 2621 if (!mapperIdName.empty()) {2622 bool allowImplicitMapper =2623 semantics::IsAllocatableOrObjectPointer(&sym);2624 bool hasDefaultMapper =2625 converter.getModuleOp().lookupSymbol(mapperIdName);2626 if (hasDefaultMapper || allowImplicitMapper) {2627 if (!hasDefaultMapper) {2628 if (auto recordType = mlir::dyn_cast_or_null<fir::RecordType>(2629 converter.genType(*typeSpec)))2630 mapperId = getOrGenImplicitDefaultDeclareMapper(2631 converter, loc, recordType, mapperIdName);2632 } else {2633 mapperId = mlir::FlatSymbolRefAttr::get(2634 &converter.getMLIRContext(), mapperIdName);2635 }2636 }2637 }2638 }2639 }2640 2641 mlir::Value mapOp = createMapInfoOp(2642 firOpBuilder, converter.getCurrentLocation(), baseOp,2643 /*varPtrPtr=*/mlir::Value{}, name.str(), bounds, /*members=*/{},2644 /*membersIndex=*/mlir::ArrayAttr{}, std::get<0>(mapFlagAndKind),2645 std::get<1>(mapFlagAndKind), baseOp.getType(),2646 /*partialMap=*/false, mapperId);2647 2648 clauseOps.mapVars.push_back(mapOp);2649 mapSyms.push_back(&sym);2650 }2651 };2652 lower::pft::visitAllSymbols(eval, captureImplicitMap);2653 2654 auto targetOp = mlir::omp::TargetOp::create(firOpBuilder, loc, clauseOps);2655 2656 llvm::SmallVector<mlir::Value> hasDeviceAddrBaseValues, mapBaseValues;2657 extractMappedBaseValues(clauseOps.hasDeviceAddrVars, hasDeviceAddrBaseValues);2658 extractMappedBaseValues(clauseOps.mapVars, mapBaseValues);2659 2660 EntryBlockArgs args;2661 args.hasDeviceAddr.syms = hasDeviceAddrSyms;2662 args.hasDeviceAddr.vars = hasDeviceAddrBaseValues;2663 args.hostEvalVars = clauseOps.hostEvalVars;2664 // TODO: Add in_reduction syms and vars.2665 args.map.syms = mapSyms;2666 args.map.vars = mapBaseValues;2667 args.priv.syms = dsp.getDelayedPrivSymbols();2668 args.priv.vars = clauseOps.privateVars;2669 2670 genBodyOfTargetOp(converter, symTable, semaCtx, eval, targetOp, args, loc,2671 queue, item, dsp);2672 2673 // Remove the host_eval information structure created for this target region.2674 if (!isTargetDevice)2675 converter.getStateStack().stackPop();2676 return targetOp;2677}2678 2679static mlir::omp::TargetDataOp genTargetDataOp(2680 lower::AbstractConverter &converter, lower::SymMap &symTable,2681 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,2682 lower::pft::Evaluation &eval, mlir::Location loc,2683 const ConstructQueue &queue, ConstructQueue::const_iterator item) {2684 mlir::omp::TargetDataOperands clauseOps;2685 llvm::SmallVector<const semantics::Symbol *> useDeviceAddrSyms,2686 useDevicePtrSyms;2687 genTargetDataClauses(converter, semaCtx, stmtCtx, item->clauses, loc,2688 clauseOps, useDeviceAddrSyms, useDevicePtrSyms);2689 2690 auto targetDataOp = mlir::omp::TargetDataOp::create(2691 converter.getFirOpBuilder(), loc, clauseOps);2692 2693 llvm::SmallVector<mlir::Value> useDeviceAddrBaseValues,2694 useDevicePtrBaseValues;2695 extractMappedBaseValues(clauseOps.useDeviceAddrVars, useDeviceAddrBaseValues);2696 extractMappedBaseValues(clauseOps.useDevicePtrVars, useDevicePtrBaseValues);2697 2698 EntryBlockArgs args;2699 args.useDeviceAddr.syms = useDeviceAddrSyms;2700 args.useDeviceAddr.vars = useDeviceAddrBaseValues;2701 args.useDevicePtr.syms = useDevicePtrSyms;2702 args.useDevicePtr.vars = useDevicePtrBaseValues;2703 2704 genBodyOfTargetDataOp(converter, symTable, semaCtx, eval, targetDataOp, args,2705 loc, queue, item);2706 return targetDataOp;2707}2708 2709template <typename OpTy>2710static OpTy genTargetEnterExitUpdateDataOp(2711 lower::AbstractConverter &converter, lower::SymMap &symTable,2712 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,2713 mlir::Location loc, const ConstructQueue &queue,2714 ConstructQueue::const_iterator item) {2715 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();2716 2717 // GCC 9.3.0 emits a (probably) bogus warning about an unused variable.2718 [[maybe_unused]] llvm::omp::Directive directive;2719 if constexpr (std::is_same_v<OpTy, mlir::omp::TargetEnterDataOp>) {2720 directive = llvm::omp::Directive::OMPD_target_enter_data;2721 } else if constexpr (std::is_same_v<OpTy, mlir::omp::TargetExitDataOp>) {2722 directive = llvm::omp::Directive::OMPD_target_exit_data;2723 } else if constexpr (std::is_same_v<OpTy, mlir::omp::TargetUpdateOp>) {2724 directive = llvm::omp::Directive::OMPD_target_update;2725 } else {2726 llvm_unreachable("Unexpected TARGET DATA construct");2727 }2728 2729 mlir::omp::TargetEnterExitUpdateDataOperands clauseOps;2730 genTargetEnterExitUpdateDataClauses(converter, semaCtx, symTable, stmtCtx,2731 item->clauses, loc, directive, clauseOps);2732 2733 return OpTy::create(firOpBuilder, loc, clauseOps);2734}2735 2736static mlir::omp::TaskOp2737genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2738 lower::StatementContext &stmtCtx,2739 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2740 mlir::Location loc, const ConstructQueue &queue,2741 ConstructQueue::const_iterator item) {2742 mlir::omp::TaskOperands clauseOps;2743 llvm::SmallVector<const semantics::Symbol *> inReductionSyms;2744 genTaskClauses(converter, semaCtx, symTable, stmtCtx, item->clauses, loc,2745 clauseOps, inReductionSyms);2746 2747 if (!enableDelayedPrivatization)2748 return genOpWithBody<mlir::omp::TaskOp>(2749 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2750 llvm::omp::Directive::OMPD_task)2751 .setClauses(&item->clauses),2752 queue, item, clauseOps);2753 2754 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,2755 lower::omp::isLastItemInQueue(item, queue),2756 /*useDelayedPrivatization=*/true, symTable);2757 dsp.processStep1(&clauseOps);2758 2759 EntryBlockArgs taskArgs;2760 taskArgs.priv.syms = dsp.getDelayedPrivSymbols();2761 taskArgs.priv.vars = clauseOps.privateVars;2762 taskArgs.inReduction.syms = inReductionSyms;2763 taskArgs.inReduction.vars = clauseOps.inReductionVars;2764 2765 return genOpWithBody<mlir::omp::TaskOp>(2766 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2767 llvm::omp::Directive::OMPD_task)2768 .setClauses(&item->clauses)2769 .setDataSharingProcessor(&dsp)2770 .setEntryBlockArgs(&taskArgs),2771 queue, item, clauseOps);2772}2773 2774static mlir::omp::TaskgroupOp2775genTaskgroupOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2776 semantics::SemanticsContext &semaCtx,2777 lower::pft::Evaluation &eval, mlir::Location loc,2778 const ConstructQueue &queue,2779 ConstructQueue::const_iterator item) {2780 mlir::omp::TaskgroupOperands clauseOps;2781 llvm::SmallVector<const semantics::Symbol *> taskReductionSyms;2782 genTaskgroupClauses(converter, semaCtx, item->clauses, loc, clauseOps,2783 taskReductionSyms);2784 2785 EntryBlockArgs taskgroupArgs;2786 taskgroupArgs.taskReduction.syms = taskReductionSyms;2787 taskgroupArgs.taskReduction.vars = clauseOps.taskReductionVars;2788 2789 return genOpWithBody<mlir::omp::TaskgroupOp>(2790 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2791 llvm::omp::Directive::OMPD_taskgroup)2792 .setClauses(&item->clauses)2793 .setEntryBlockArgs(&taskgroupArgs),2794 queue, item, clauseOps);2795}2796 2797static mlir::omp::TaskwaitOp2798genTaskwaitOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2799 semantics::SemanticsContext &semaCtx,2800 lower::pft::Evaluation &eval, mlir::Location loc,2801 const ConstructQueue &queue,2802 ConstructQueue::const_iterator item) {2803 mlir::omp::TaskwaitOperands clauseOps;2804 genTaskwaitClauses(converter, semaCtx, item->clauses, loc, clauseOps);2805 return mlir::omp::TaskwaitOp::create(converter.getFirOpBuilder(), loc,2806 clauseOps);2807}2808 2809static mlir::omp::TaskyieldOp2810genTaskyieldOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2811 semantics::SemanticsContext &semaCtx,2812 lower::pft::Evaluation &eval, mlir::Location loc,2813 const ConstructQueue &queue,2814 ConstructQueue::const_iterator item) {2815 return mlir::omp::TaskyieldOp::create(converter.getFirOpBuilder(), loc);2816}2817 2818static mlir::omp::WorkshareOp genWorkshareOp(2819 lower::AbstractConverter &converter, lower::SymMap &symTable,2820 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,2821 lower::pft::Evaluation &eval, mlir::Location loc,2822 const ConstructQueue &queue, ConstructQueue::const_iterator item) {2823 mlir::omp::WorkshareOperands clauseOps;2824 genWorkshareClauses(converter, semaCtx, stmtCtx, item->clauses, loc,2825 clauseOps);2826 2827 return genOpWithBody<mlir::omp::WorkshareOp>(2828 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2829 llvm::omp::Directive::OMPD_workshare)2830 .setClauses(&item->clauses),2831 queue, item, clauseOps);2832}2833 2834static mlir::omp::TeamsOp2835genTeamsOp(lower::AbstractConverter &converter, lower::SymMap &symTable,2836 lower::StatementContext &stmtCtx,2837 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2838 mlir::Location loc, const ConstructQueue &queue,2839 ConstructQueue::const_iterator item) {2840 lower::SymMapScope scope(symTable);2841 mlir::omp::TeamsOperands clauseOps;2842 llvm::SmallVector<const semantics::Symbol *> reductionSyms;2843 genTeamsClauses(converter, semaCtx, stmtCtx, item->clauses, loc, clauseOps,2844 reductionSyms);2845 2846 EntryBlockArgs args;2847 // TODO: Add private syms and vars.2848 args.reduction.syms = reductionSyms;2849 args.reduction.vars = clauseOps.reductionVars;2850 return genOpWithBody<mlir::omp::TeamsOp>(2851 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2852 llvm::omp::Directive::OMPD_teams)2853 .setClauses(&item->clauses)2854 .setEntryBlockArgs(&args),2855 queue, item, clauseOps);2856}2857 2858static mlir::omp::WorkdistributeOp genWorkdistributeOp(2859 lower::AbstractConverter &converter, lower::SymMap &symTable,2860 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,2861 mlir::Location loc, const ConstructQueue &queue,2862 ConstructQueue::const_iterator item) {2863 return genOpWithBody<mlir::omp::WorkdistributeOp>(2864 OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval,2865 llvm::omp::Directive::OMPD_workdistribute),2866 queue, item);2867}2868 2869//===----------------------------------------------------------------------===//2870// Code generation functions for the standalone version of constructs that can2871// also be a leaf of a composite construct2872//===----------------------------------------------------------------------===//2873 2874static mlir::omp::DistributeOp genStandaloneDistribute(2875 lower::AbstractConverter &converter, lower::SymMap &symTable,2876 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,2877 lower::pft::Evaluation &eval, mlir::Location loc,2878 const ConstructQueue &queue, ConstructQueue::const_iterator item) {2879 mlir::omp::DistributeOperands distributeClauseOps;2880 genDistributeClauses(converter, semaCtx, stmtCtx, item->clauses, loc,2881 distributeClauseOps);2882 2883 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,2884 /*shouldCollectPreDeterminedSymbols=*/true,2885 enableDelayedPrivatization, symTable);2886 dsp.processStep1(&distributeClauseOps);2887 2888 mlir::omp::LoopNestOperands loopNestClauseOps;2889 llvm::SmallVector<const semantics::Symbol *> iv;2890 genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,2891 loopNestClauseOps, iv);2892 2893 EntryBlockArgs distributeArgs;2894 distributeArgs.priv.syms = dsp.getDelayedPrivSymbols();2895 distributeArgs.priv.vars = distributeClauseOps.privateVars;2896 auto distributeOp = genWrapperOp<mlir::omp::DistributeOp>(2897 converter, loc, distributeClauseOps, distributeArgs);2898 2899 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item,2900 loopNestClauseOps, iv, {{distributeOp, distributeArgs}},2901 llvm::omp::Directive::OMPD_distribute, dsp);2902 return distributeOp;2903}2904 2905static mlir::omp::WsloopOp genStandaloneDo(2906 lower::AbstractConverter &converter, lower::SymMap &symTable,2907 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,2908 lower::pft::Evaluation &eval, mlir::Location loc,2909 const ConstructQueue &queue, ConstructQueue::const_iterator item) {2910 mlir::omp::WsloopOperands wsloopClauseOps;2911 llvm::SmallVector<const semantics::Symbol *> wsloopReductionSyms;2912 genWsloopClauses(converter, semaCtx, stmtCtx, item->clauses, loc,2913 wsloopClauseOps, wsloopReductionSyms);2914 2915 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,2916 /*shouldCollectPreDeterminedSymbols=*/true,2917 enableDelayedPrivatization, symTable);2918 dsp.processStep1(&wsloopClauseOps);2919 2920 mlir::omp::LoopNestOperands loopNestClauseOps;2921 llvm::SmallVector<const semantics::Symbol *> iv;2922 genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,2923 loopNestClauseOps, iv);2924 2925 EntryBlockArgs wsloopArgs;2926 wsloopArgs.priv.syms = dsp.getDelayedPrivSymbols();2927 wsloopArgs.priv.vars = wsloopClauseOps.privateVars;2928 wsloopArgs.reduction.syms = wsloopReductionSyms;2929 wsloopArgs.reduction.vars = wsloopClauseOps.reductionVars;2930 auto wsloopOp = genWrapperOp<mlir::omp::WsloopOp>(2931 converter, loc, wsloopClauseOps, wsloopArgs);2932 2933 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item,2934 loopNestClauseOps, iv, {{wsloopOp, wsloopArgs}},2935 llvm::omp::Directive::OMPD_do, dsp);2936 return wsloopOp;2937}2938 2939static mlir::omp::ParallelOp genStandaloneParallel(2940 lower::AbstractConverter &converter, lower::SymMap &symTable,2941 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,2942 lower::pft::Evaluation &eval, mlir::Location loc,2943 const ConstructQueue &queue, ConstructQueue::const_iterator item) {2944 lower::SymMapScope scope(symTable);2945 mlir::omp::ParallelOperands parallelClauseOps;2946 llvm::SmallVector<const semantics::Symbol *> parallelReductionSyms;2947 genParallelClauses(converter, semaCtx, stmtCtx, item->clauses, loc,2948 parallelClauseOps, parallelReductionSyms);2949 2950 std::optional<DataSharingProcessor> dsp;2951 if (enableDelayedPrivatization) {2952 dsp.emplace(converter, semaCtx, item->clauses, eval,2953 lower::omp::isLastItemInQueue(item, queue),2954 /*useDelayedPrivatization=*/true, symTable);2955 dsp->processStep1(¶llelClauseOps);2956 }2957 2958 EntryBlockArgs parallelArgs;2959 if (dsp)2960 parallelArgs.priv.syms = dsp->getDelayedPrivSymbols();2961 parallelArgs.priv.vars = parallelClauseOps.privateVars;2962 parallelArgs.reduction.syms = parallelReductionSyms;2963 parallelArgs.reduction.vars = parallelClauseOps.reductionVars;2964 return genParallelOp(converter, symTable, semaCtx, eval, loc, queue, item,2965 parallelClauseOps, parallelArgs,2966 enableDelayedPrivatization ? &dsp.value() : nullptr);2967}2968 2969static mlir::omp::SimdOp2970genStandaloneSimd(lower::AbstractConverter &converter, lower::SymMap &symTable,2971 semantics::SemanticsContext &semaCtx,2972 lower::pft::Evaluation &eval, mlir::Location loc,2973 const ConstructQueue &queue,2974 ConstructQueue::const_iterator item) {2975 mlir::omp::SimdOperands simdClauseOps;2976 llvm::SmallVector<const semantics::Symbol *> simdReductionSyms;2977 genSimdClauses(converter, semaCtx, item->clauses, loc, simdClauseOps,2978 simdReductionSyms);2979 2980 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,2981 /*shouldCollectPreDeterminedSymbols=*/true,2982 enableDelayedPrivatization, symTable);2983 dsp.processStep1(&simdClauseOps);2984 2985 mlir::omp::LoopNestOperands loopNestClauseOps;2986 llvm::SmallVector<const semantics::Symbol *> iv;2987 genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,2988 loopNestClauseOps, iv);2989 2990 EntryBlockArgs simdArgs;2991 simdArgs.priv.syms = dsp.getDelayedPrivSymbols();2992 simdArgs.priv.vars = simdClauseOps.privateVars;2993 simdArgs.reduction.syms = simdReductionSyms;2994 simdArgs.reduction.vars = simdClauseOps.reductionVars;2995 auto simdOp =2996 genWrapperOp<mlir::omp::SimdOp>(converter, loc, simdClauseOps, simdArgs);2997 2998 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item,2999 loopNestClauseOps, iv, {{simdOp, simdArgs}},3000 llvm::omp::Directive::OMPD_simd, dsp);3001 return simdOp;3002}3003 3004static mlir::omp::TaskloopOp genStandaloneTaskloop(3005 lower::AbstractConverter &converter, lower::SymMap &symTable,3006 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,3007 lower::pft::Evaluation &eval, mlir::Location loc,3008 const ConstructQueue &queue, ConstructQueue::const_iterator item) {3009 mlir::omp::TaskloopOperands taskloopClauseOps;3010 llvm::SmallVector<const semantics::Symbol *> reductionSyms;3011 llvm::SmallVector<const semantics::Symbol *> inReductionSyms;3012 3013 genTaskloopClauses(converter, semaCtx, stmtCtx, item->clauses, loc,3014 taskloopClauseOps, reductionSyms, inReductionSyms);3015 DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,3016 /*shouldCollectPreDeterminedSymbols=*/true,3017 enableDelayedPrivatization, symTable);3018 dsp.processStep1(&taskloopClauseOps);3019 3020 mlir::omp::LoopNestOperands loopNestClauseOps;3021 llvm::SmallVector<const semantics::Symbol *> iv;3022 genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,3023 loopNestClauseOps, iv);3024 3025 EntryBlockArgs taskloopArgs;3026 taskloopArgs.priv.syms = dsp.getDelayedPrivSymbols();3027 taskloopArgs.priv.vars = taskloopClauseOps.privateVars;3028 taskloopArgs.reduction.syms = reductionSyms;3029 taskloopArgs.reduction.vars = taskloopClauseOps.reductionVars;3030 taskloopArgs.inReduction.syms = inReductionSyms;3031 taskloopArgs.inReduction.vars = taskloopClauseOps.inReductionVars;3032 3033 auto taskLoopOp = genWrapperOp<mlir::omp::TaskloopOp>(3034 converter, loc, taskloopClauseOps, taskloopArgs);3035 3036 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item,3037 loopNestClauseOps, iv, {{taskLoopOp, taskloopArgs}},3038 llvm::omp::Directive::OMPD_taskloop, dsp);3039 return taskLoopOp;3040}3041 3042//===----------------------------------------------------------------------===//3043// Code generation functions for composite constructs3044//===----------------------------------------------------------------------===//3045 3046static mlir::omp::DistributeOp genCompositeDistributeParallelDo(3047 lower::AbstractConverter &converter, lower::SymMap &symTable,3048 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,3049 lower::pft::Evaluation &eval, mlir::Location loc,3050 const ConstructQueue &queue, ConstructQueue::const_iterator item) {3051 assert(std::distance(item, queue.end()) == 3 && "Invalid leaf constructs");3052 ConstructQueue::const_iterator distributeItem = item;3053 ConstructQueue::const_iterator parallelItem = std::next(distributeItem);3054 ConstructQueue::const_iterator doItem = std::next(parallelItem);3055 3056 // Create parent omp.parallel first.3057 mlir::omp::ParallelOperands parallelClauseOps;3058 llvm::SmallVector<const semantics::Symbol *> parallelReductionSyms;3059 genParallelClauses(converter, semaCtx, stmtCtx, parallelItem->clauses, loc,3060 parallelClauseOps, parallelReductionSyms);3061 3062 DataSharingProcessor dsp(converter, semaCtx, doItem->clauses, eval,3063 /*shouldCollectPreDeterminedSymbols=*/true,3064 /*useDelayedPrivatization=*/true, symTable);3065 dsp.processStep1(¶llelClauseOps);3066 3067 EntryBlockArgs parallelArgs;3068 parallelArgs.priv.syms = dsp.getDelayedPrivSymbols();3069 parallelArgs.priv.vars = parallelClauseOps.privateVars;3070 parallelArgs.reduction.syms = parallelReductionSyms;3071 parallelArgs.reduction.vars = parallelClauseOps.reductionVars;3072 genParallelOp(converter, symTable, semaCtx, eval, loc, queue, parallelItem,3073 parallelClauseOps, parallelArgs, &dsp, /*isComposite=*/true);3074 3075 // Clause processing.3076 mlir::omp::DistributeOperands distributeClauseOps;3077 genDistributeClauses(converter, semaCtx, stmtCtx, distributeItem->clauses,3078 loc, distributeClauseOps);3079 3080 mlir::omp::WsloopOperands wsloopClauseOps;3081 llvm::SmallVector<const semantics::Symbol *> wsloopReductionSyms;3082 genWsloopClauses(converter, semaCtx, stmtCtx, doItem->clauses, loc,3083 wsloopClauseOps, wsloopReductionSyms);3084 3085 mlir::omp::LoopNestOperands loopNestClauseOps;3086 llvm::SmallVector<const semantics::Symbol *> iv;3087 genLoopNestClauses(converter, semaCtx, eval, doItem->clauses, loc,3088 loopNestClauseOps, iv);3089 3090 // Operation creation.3091 EntryBlockArgs distributeArgs;3092 // TODO: Add private syms and vars.3093 auto distributeOp = genWrapperOp<mlir::omp::DistributeOp>(3094 converter, loc, distributeClauseOps, distributeArgs);3095 distributeOp.setComposite(/*val=*/true);3096 3097 EntryBlockArgs wsloopArgs;3098 // TODO: Add private syms and vars.3099 wsloopArgs.reduction.syms = wsloopReductionSyms;3100 wsloopArgs.reduction.vars = wsloopClauseOps.reductionVars;3101 auto wsloopOp = genWrapperOp<mlir::omp::WsloopOp>(3102 converter, loc, wsloopClauseOps, wsloopArgs);3103 wsloopOp.setComposite(/*val=*/true);3104 3105 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, doItem,3106 loopNestClauseOps, iv,3107 {{distributeOp, distributeArgs}, {wsloopOp, wsloopArgs}},3108 llvm::omp::Directive::OMPD_distribute_parallel_do, dsp);3109 return distributeOp;3110}3111 3112static mlir::omp::DistributeOp genCompositeDistributeParallelDoSimd(3113 lower::AbstractConverter &converter, lower::SymMap &symTable,3114 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,3115 lower::pft::Evaluation &eval, mlir::Location loc,3116 const ConstructQueue &queue, ConstructQueue::const_iterator item) {3117 assert(std::distance(item, queue.end()) == 4 && "Invalid leaf constructs");3118 ConstructQueue::const_iterator distributeItem = item;3119 ConstructQueue::const_iterator parallelItem = std::next(distributeItem);3120 ConstructQueue::const_iterator doItem = std::next(parallelItem);3121 ConstructQueue::const_iterator simdItem = std::next(doItem);3122 3123 // Create parent omp.parallel first.3124 mlir::omp::ParallelOperands parallelClauseOps;3125 llvm::SmallVector<const semantics::Symbol *> parallelReductionSyms;3126 genParallelClauses(converter, semaCtx, stmtCtx, parallelItem->clauses, loc,3127 parallelClauseOps, parallelReductionSyms);3128 3129 DataSharingProcessor parallelItemDSP(3130 converter, semaCtx, parallelItem->clauses, eval,3131 /*shouldCollectPreDeterminedSymbols=*/false,3132 /*useDelayedPrivatization=*/true, symTable);3133 parallelItemDSP.processStep1(¶llelClauseOps);3134 3135 EntryBlockArgs parallelArgs;3136 parallelArgs.priv.syms = parallelItemDSP.getDelayedPrivSymbols();3137 parallelArgs.priv.vars = parallelClauseOps.privateVars;3138 parallelArgs.reduction.syms = parallelReductionSyms;3139 parallelArgs.reduction.vars = parallelClauseOps.reductionVars;3140 genParallelOp(converter, symTable, semaCtx, eval, loc, queue, parallelItem,3141 parallelClauseOps, parallelArgs, ¶llelItemDSP,3142 /*isComposite=*/true);3143 3144 // Clause processing.3145 mlir::omp::DistributeOperands distributeClauseOps;3146 genDistributeClauses(converter, semaCtx, stmtCtx, distributeItem->clauses,3147 loc, distributeClauseOps);3148 3149 mlir::omp::WsloopOperands wsloopClauseOps;3150 llvm::SmallVector<const semantics::Symbol *> wsloopReductionSyms;3151 genWsloopClauses(converter, semaCtx, stmtCtx, doItem->clauses, loc,3152 wsloopClauseOps, wsloopReductionSyms);3153 3154 mlir::omp::SimdOperands simdClauseOps;3155 llvm::SmallVector<const semantics::Symbol *> simdReductionSyms;3156 genSimdClauses(converter, semaCtx, simdItem->clauses, loc, simdClauseOps,3157 simdReductionSyms);3158 3159 DataSharingProcessor simdItemDSP(converter, semaCtx, simdItem->clauses, eval,3160 /*shouldCollectPreDeterminedSymbols=*/true,3161 /*useDelayedPrivatization=*/true, symTable);3162 simdItemDSP.processStep1(&simdClauseOps);3163 3164 mlir::omp::LoopNestOperands loopNestClauseOps;3165 llvm::SmallVector<const semantics::Symbol *> iv;3166 genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc,3167 loopNestClauseOps, iv);3168 3169 // Operation creation.3170 EntryBlockArgs distributeArgs;3171 // TODO: Add private syms and vars.3172 auto distributeOp = genWrapperOp<mlir::omp::DistributeOp>(3173 converter, loc, distributeClauseOps, distributeArgs);3174 distributeOp.setComposite(/*val=*/true);3175 3176 EntryBlockArgs wsloopArgs;3177 // TODO: Add private syms and vars.3178 wsloopArgs.reduction.syms = wsloopReductionSyms;3179 wsloopArgs.reduction.vars = wsloopClauseOps.reductionVars;3180 auto wsloopOp = genWrapperOp<mlir::omp::WsloopOp>(3181 converter, loc, wsloopClauseOps, wsloopArgs);3182 wsloopOp.setComposite(/*val=*/true);3183 3184 EntryBlockArgs simdArgs;3185 simdArgs.priv.syms = simdItemDSP.getDelayedPrivSymbols();3186 simdArgs.priv.vars = simdClauseOps.privateVars;3187 simdArgs.reduction.syms = simdReductionSyms;3188 simdArgs.reduction.vars = simdClauseOps.reductionVars;3189 auto simdOp =3190 genWrapperOp<mlir::omp::SimdOp>(converter, loc, simdClauseOps, simdArgs);3191 simdOp.setComposite(/*val=*/true);3192 3193 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, simdItem,3194 loopNestClauseOps, iv,3195 {{distributeOp, distributeArgs},3196 {wsloopOp, wsloopArgs},3197 {simdOp, simdArgs}},3198 llvm::omp::Directive::OMPD_distribute_parallel_do_simd,3199 simdItemDSP);3200 return distributeOp;3201}3202 3203static mlir::omp::DistributeOp genCompositeDistributeSimd(3204 lower::AbstractConverter &converter, lower::SymMap &symTable,3205 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,3206 lower::pft::Evaluation &eval, mlir::Location loc,3207 const ConstructQueue &queue, ConstructQueue::const_iterator item) {3208 assert(std::distance(item, queue.end()) == 2 && "Invalid leaf constructs");3209 ConstructQueue::const_iterator distributeItem = item;3210 ConstructQueue::const_iterator simdItem = std::next(distributeItem);3211 3212 // Clause processing.3213 mlir::omp::DistributeOperands distributeClauseOps;3214 genDistributeClauses(converter, semaCtx, stmtCtx, distributeItem->clauses,3215 loc, distributeClauseOps);3216 3217 mlir::omp::SimdOperands simdClauseOps;3218 llvm::SmallVector<const semantics::Symbol *> simdReductionSyms;3219 genSimdClauses(converter, semaCtx, simdItem->clauses, loc, simdClauseOps,3220 simdReductionSyms);3221 3222 DataSharingProcessor distributeItemDSP(3223 converter, semaCtx, distributeItem->clauses, eval,3224 /*shouldCollectPreDeterminedSymbols=*/false,3225 /*useDelayedPrivatization=*/true, symTable);3226 distributeItemDSP.processStep1(&distributeClauseOps);3227 3228 DataSharingProcessor simdItemDSP(converter, semaCtx, simdItem->clauses, eval,3229 /*shouldCollectPreDeterminedSymbols=*/true,3230 /*useDelayedPrivatization=*/true, symTable);3231 simdItemDSP.processStep1(&simdClauseOps);3232 3233 // Pass the innermost leaf construct's clauses because that's where COLLAPSE3234 // is placed by construct decomposition.3235 mlir::omp::LoopNestOperands loopNestClauseOps;3236 llvm::SmallVector<const semantics::Symbol *> iv;3237 genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc,3238 loopNestClauseOps, iv);3239 3240 // Operation creation.3241 EntryBlockArgs distributeArgs;3242 distributeArgs.priv.syms = distributeItemDSP.getDelayedPrivSymbols();3243 distributeArgs.priv.vars = distributeClauseOps.privateVars;3244 auto distributeOp = genWrapperOp<mlir::omp::DistributeOp>(3245 converter, loc, distributeClauseOps, distributeArgs);3246 distributeOp.setComposite(/*val=*/true);3247 3248 EntryBlockArgs simdArgs;3249 simdArgs.priv.syms = simdItemDSP.getDelayedPrivSymbols();3250 simdArgs.priv.vars = simdClauseOps.privateVars;3251 simdArgs.reduction.syms = simdReductionSyms;3252 simdArgs.reduction.vars = simdClauseOps.reductionVars;3253 auto simdOp =3254 genWrapperOp<mlir::omp::SimdOp>(converter, loc, simdClauseOps, simdArgs);3255 simdOp.setComposite(/*val=*/true);3256 3257 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, simdItem,3258 loopNestClauseOps, iv,3259 {{distributeOp, distributeArgs}, {simdOp, simdArgs}},3260 llvm::omp::Directive::OMPD_distribute_simd, simdItemDSP);3261 return distributeOp;3262}3263 3264static mlir::omp::WsloopOp genCompositeDoSimd(3265 lower::AbstractConverter &converter, lower::SymMap &symTable,3266 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,3267 lower::pft::Evaluation &eval, mlir::Location loc,3268 const ConstructQueue &queue, ConstructQueue::const_iterator item) {3269 assert(std::distance(item, queue.end()) == 2 && "Invalid leaf constructs");3270 ConstructQueue::const_iterator doItem = item;3271 ConstructQueue::const_iterator simdItem = std::next(doItem);3272 3273 // Clause processing.3274 mlir::omp::WsloopOperands wsloopClauseOps;3275 llvm::SmallVector<const semantics::Symbol *> wsloopReductionSyms;3276 genWsloopClauses(converter, semaCtx, stmtCtx, doItem->clauses, loc,3277 wsloopClauseOps, wsloopReductionSyms);3278 3279 mlir::omp::SimdOperands simdClauseOps;3280 llvm::SmallVector<const semantics::Symbol *> simdReductionSyms;3281 genSimdClauses(converter, semaCtx, simdItem->clauses, loc, simdClauseOps,3282 simdReductionSyms);3283 3284 DataSharingProcessor wsloopItemDSP(3285 converter, semaCtx, doItem->clauses, eval,3286 /*shouldCollectPreDeterminedSymbols=*/false,3287 /*useDelayedPrivatization=*/true, symTable);3288 wsloopItemDSP.processStep1(&wsloopClauseOps);3289 3290 DataSharingProcessor simdItemDSP(converter, semaCtx, simdItem->clauses, eval,3291 /*shouldCollectPreDeterminedSymbols=*/true,3292 /*useDelayedPrivatization=*/true, symTable);3293 simdItemDSP.processStep1(&simdClauseOps, simdItem->id);3294 3295 // Pass the innermost leaf construct's clauses because that's where COLLAPSE3296 // is placed by construct decomposition.3297 mlir::omp::LoopNestOperands loopNestClauseOps;3298 llvm::SmallVector<const semantics::Symbol *> iv;3299 genLoopNestClauses(converter, semaCtx, eval, simdItem->clauses, loc,3300 loopNestClauseOps, iv);3301 3302 // Operation creation.3303 EntryBlockArgs wsloopArgs;3304 wsloopArgs.priv.syms = wsloopItemDSP.getDelayedPrivSymbols();3305 wsloopArgs.priv.vars = wsloopClauseOps.privateVars;3306 wsloopArgs.reduction.syms = wsloopReductionSyms;3307 wsloopArgs.reduction.vars = wsloopClauseOps.reductionVars;3308 auto wsloopOp = genWrapperOp<mlir::omp::WsloopOp>(3309 converter, loc, wsloopClauseOps, wsloopArgs);3310 wsloopOp.setComposite(/*val=*/true);3311 3312 EntryBlockArgs simdArgs;3313 simdArgs.priv.syms = simdItemDSP.getDelayedPrivSymbols();3314 simdArgs.priv.vars = simdClauseOps.privateVars;3315 simdArgs.reduction.syms = simdReductionSyms;3316 simdArgs.reduction.vars = simdClauseOps.reductionVars;3317 auto simdOp =3318 genWrapperOp<mlir::omp::SimdOp>(converter, loc, simdClauseOps, simdArgs);3319 simdOp.setComposite(/*val=*/true);3320 3321 genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, simdItem,3322 loopNestClauseOps, iv,3323 {{wsloopOp, wsloopArgs}, {simdOp, simdArgs}},3324 llvm::omp::Directive::OMPD_do_simd, simdItemDSP);3325 return wsloopOp;3326}3327 3328static mlir::omp::TaskloopOp genCompositeTaskloopSimd(3329 lower::AbstractConverter &converter, lower::SymMap &symTable,3330 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,3331 lower::pft::Evaluation &eval, mlir::Location loc,3332 const ConstructQueue &queue, ConstructQueue::const_iterator item) {3333 assert(std::distance(item, queue.end()) == 2 && "Invalid leaf constructs");3334 if (!semaCtx.langOptions().OpenMPSimd)3335 TODO(loc, "Composite TASKLOOP SIMD");3336 return nullptr;3337}3338 3339//===----------------------------------------------------------------------===//3340// Dispatch3341//===----------------------------------------------------------------------===//3342 3343static bool genOMPCompositeDispatch(3344 lower::AbstractConverter &converter, lower::SymMap &symTable,3345 lower::StatementContext &stmtCtx, semantics::SemanticsContext &semaCtx,3346 lower::pft::Evaluation &eval, mlir::Location loc,3347 const ConstructQueue &queue, ConstructQueue::const_iterator item,3348 mlir::Operation *&newOp) {3349 using llvm::omp::Directive;3350 using lower::omp::matchLeafSequence;3351 3352 // TODO: Privatization for composite constructs is currently only done based3353 // on the clauses for their last leaf construct, which may not always be3354 // correct. Consider per-leaf privatization of composite constructs once3355 // delayed privatization is supported by all participating ops.3356 if (matchLeafSequence(item, queue, Directive::OMPD_distribute_parallel_do))3357 newOp = genCompositeDistributeParallelDo(converter, symTable, stmtCtx,3358 semaCtx, eval, loc, queue, item);3359 else if (matchLeafSequence(item, queue,3360 Directive::OMPD_distribute_parallel_do_simd))3361 newOp = genCompositeDistributeParallelDoSimd(3362 converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item);3363 else if (matchLeafSequence(item, queue, Directive::OMPD_distribute_simd))3364 newOp = genCompositeDistributeSimd(converter, symTable, stmtCtx, semaCtx,3365 eval, loc, queue, item);3366 else if (matchLeafSequence(item, queue, Directive::OMPD_do_simd))3367 newOp = genCompositeDoSimd(converter, symTable, stmtCtx, semaCtx, eval, loc,3368 queue, item);3369 else if (matchLeafSequence(item, queue, Directive::OMPD_taskloop_simd))3370 newOp = genCompositeTaskloopSimd(converter, symTable, stmtCtx, semaCtx,3371 eval, loc, queue, item);3372 else3373 return false;3374 3375 return true;3376}3377 3378static void genOMPDispatch(lower::AbstractConverter &converter,3379 lower::SymMap &symTable,3380 semantics::SemanticsContext &semaCtx,3381 lower::pft::Evaluation &eval, mlir::Location loc,3382 const ConstructQueue &queue,3383 ConstructQueue::const_iterator item) {3384 assert(item != queue.end());3385 3386 lower::StatementContext stmtCtx;3387 mlir::Operation *newOp = nullptr;3388 3389 // Generate cleanup code for the stmtCtx after newOp3390 auto finalizeStmtCtx = [&]() {3391 if (newOp) {3392 fir::FirOpBuilder &builder = converter.getFirOpBuilder();3393 fir::FirOpBuilder::InsertionGuard guard(builder);3394 builder.setInsertionPointAfter(newOp);3395 stmtCtx.finalizeAndPop();3396 }3397 };3398 3399 bool loopLeaf = llvm::omp::getDirectiveAssociation(item->id) ==3400 llvm::omp::Association::LoopNest;3401 if (loopLeaf) {3402 symTable.pushScope();3403 if (genOMPCompositeDispatch(converter, symTable, stmtCtx, semaCtx, eval,3404 loc, queue, item, newOp)) {3405 symTable.popScope();3406 finalizeStmtCtx();3407 return;3408 }3409 }3410 3411 llvm::omp::Directive dir = item->id;3412 switch (dir) {3413 case llvm::omp::Directive::OMPD_barrier:3414 newOp = genBarrierOp(converter, symTable, semaCtx, eval, loc, queue, item);3415 break;3416 case llvm::omp::Directive::OMPD_distribute:3417 newOp = genStandaloneDistribute(converter, symTable, stmtCtx, semaCtx, eval,3418 loc, queue, item);3419 break;3420 case llvm::omp::Directive::OMPD_do:3421 newOp = genStandaloneDo(converter, symTable, stmtCtx, semaCtx, eval, loc,3422 queue, item);3423 break;3424 case llvm::omp::Directive::OMPD_loop:3425 newOp = genLoopOp(converter, symTable, semaCtx, eval, loc, queue, item);3426 break;3427 case llvm::omp::Directive::OMPD_masked:3428 newOp = genMaskedOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue,3429 item);3430 break;3431 case llvm::omp::Directive::OMPD_master:3432 newOp = genMasterOp(converter, symTable, semaCtx, eval, loc, queue, item);3433 break;3434 case llvm::omp::Directive::OMPD_ordered:3435 // Block-associated "ordered" construct.3436 newOp = genOrderedRegionOp(converter, symTable, semaCtx, eval, loc, queue,3437 item);3438 break;3439 case llvm::omp::Directive::OMPD_parallel:3440 newOp = genStandaloneParallel(converter, symTable, stmtCtx, semaCtx, eval,3441 loc, queue, item);3442 break;3443 case llvm::omp::Directive::OMPD_scan:3444 newOp = genScanOp(converter, symTable, semaCtx, loc, queue, item);3445 break;3446 case llvm::omp::Directive::OMPD_section:3447 llvm_unreachable("genOMPDispatch: OMPD_section");3448 // Lowered in the enclosing genSectionsOp.3449 break;3450 case llvm::omp::Directive::OMPD_sections:3451 genSectionsOp(converter, symTable, semaCtx, eval, loc, queue, item);3452 break;3453 case llvm::omp::Directive::OMPD_simd:3454 newOp =3455 genStandaloneSimd(converter, symTable, semaCtx, eval, loc, queue, item);3456 break;3457 case llvm::omp::Directive::OMPD_scope:3458 newOp = genScopeOp(converter, symTable, semaCtx, eval, loc, queue, item);3459 break;3460 case llvm::omp::Directive::OMPD_single:3461 newOp = genSingleOp(converter, symTable, semaCtx, eval, loc, queue, item);3462 break;3463 case llvm::omp::Directive::OMPD_target:3464 newOp = genTargetOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue,3465 item);3466 break;3467 case llvm::omp::Directive::OMPD_target_data:3468 newOp = genTargetDataOp(converter, symTable, stmtCtx, semaCtx, eval, loc,3469 queue, item);3470 break;3471 case llvm::omp::Directive::OMPD_target_enter_data:3472 newOp = genTargetEnterExitUpdateDataOp<mlir::omp::TargetEnterDataOp>(3473 converter, symTable, stmtCtx, semaCtx, loc, queue, item);3474 break;3475 case llvm::omp::Directive::OMPD_target_exit_data:3476 newOp = genTargetEnterExitUpdateDataOp<mlir::omp::TargetExitDataOp>(3477 converter, symTable, stmtCtx, semaCtx, loc, queue, item);3478 break;3479 case llvm::omp::Directive::OMPD_target_update:3480 newOp = genTargetEnterExitUpdateDataOp<mlir::omp::TargetUpdateOp>(3481 converter, symTable, stmtCtx, semaCtx, loc, queue, item);3482 break;3483 case llvm::omp::Directive::OMPD_task:3484 newOp = genTaskOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue,3485 item);3486 break;3487 case llvm::omp::Directive::OMPD_taskgroup:3488 newOp =3489 genTaskgroupOp(converter, symTable, semaCtx, eval, loc, queue, item);3490 break;3491 case llvm::omp::Directive::OMPD_taskloop:3492 newOp = genStandaloneTaskloop(converter, symTable, stmtCtx, semaCtx, eval,3493 loc, queue, item);3494 break;3495 case llvm::omp::Directive::OMPD_taskwait:3496 newOp = genTaskwaitOp(converter, symTable, semaCtx, eval, loc, queue, item);3497 break;3498 case llvm::omp::Directive::OMPD_taskyield:3499 newOp =3500 genTaskyieldOp(converter, symTable, semaCtx, eval, loc, queue, item);3501 break;3502 case llvm::omp::Directive::OMPD_teams:3503 newOp = genTeamsOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue,3504 item);3505 break;3506 case llvm::omp::Directive::OMPD_tile:3507 genTileOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item);3508 break;3509 case llvm::omp::Directive::OMPD_fuse: {3510 unsigned version = semaCtx.langOptions().OpenMPVersion;3511 if (!semaCtx.langOptions().OpenMPSimd)3512 TODO(loc, "Unhandled loop directive (" +3513 llvm::omp::getOpenMPDirectiveName(dir, version) + ")");3514 break;3515 }3516 case llvm::omp::Directive::OMPD_unroll:3517 genUnrollOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item);3518 break;3519 case llvm::omp::Directive::OMPD_workdistribute:3520 newOp = genWorkdistributeOp(converter, symTable, semaCtx, eval, loc, queue,3521 item);3522 break;3523 case llvm::omp::Directive::OMPD_workshare:3524 newOp = genWorkshareOp(converter, symTable, stmtCtx, semaCtx, eval, loc,3525 queue, item);3526 break;3527 default:3528 // Combined and composite constructs should have been split into a sequence3529 // of leaf constructs when building the construct queue.3530 assert(!llvm::omp::isLeafConstruct(dir) &&3531 "Unexpected compound construct.");3532 break;3533 }3534 3535 finalizeStmtCtx();3536 if (loopLeaf)3537 symTable.popScope();3538}3539 3540//===----------------------------------------------------------------------===//3541// OpenMPDeclarativeConstruct visitors3542//===----------------------------------------------------------------------===//3543static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3544 semantics::SemanticsContext &semaCtx,3545 lower::pft::Evaluation &eval,3546 const parser::OpenMPUtilityConstruct &);3547 3548static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3549 semantics::SemanticsContext &semaCtx,3550 lower::pft::Evaluation &eval,3551 const parser::OmpAllocateDirective &allocate) {3552 if (!semaCtx.langOptions().OpenMPSimd)3553 TODO(converter.getCurrentLocation(), "OmpAllocateDirective");3554}3555 3556static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3557 semantics::SemanticsContext &semaCtx,3558 lower::pft::Evaluation &eval,3559 const parser::OpenMPDeclarativeAssumes &assumesConstruct) {3560 if (!semaCtx.langOptions().OpenMPSimd)3561 TODO(converter.getCurrentLocation(), "OpenMP ASSUMES declaration");3562}3563 3564static void3565genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3566 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,3567 const parser::OmpDeclareVariantDirective &declareVariantDirective) {3568 if (!semaCtx.langOptions().OpenMPSimd)3569 TODO(converter.getCurrentLocation(), "OmpDeclareVariantDirective");3570}3571 3572static ReductionProcessor::GenCombinerCBTy3573processReductionCombiner(lower::AbstractConverter &converter,3574 lower::SymMap &symTable,3575 semantics::SemanticsContext &semaCtx,3576 const parser::OmpReductionSpecifier &specifier) {3577 ReductionProcessor::GenCombinerCBTy genCombinerCB;3578 const auto &combinerExpression =3579 std::get<std::optional<parser::OmpCombinerExpression>>(specifier.t)3580 .value();3581 const parser::OmpStylizedInstance &combinerInstance =3582 combinerExpression.v.front();3583 const parser::OmpStylizedInstance::Instance &instance =3584 std::get<parser::OmpStylizedInstance::Instance>(combinerInstance.t);3585 3586 std::optional<semantics::SomeExpr> evalExprOpt;3587 if (const auto *as = std::get_if<parser::AssignmentStmt>(&instance.u)) {3588 auto &expr = std::get<parser::Expr>(as->t);3589 evalExprOpt = makeExpr(expr, semaCtx);3590 } else if (const auto *call = std::get_if<parser::CallStmt>(&instance.u)) {3591 if (call->typedCall) {3592 const auto &procRef = *call->typedCall;3593 evalExprOpt = semantics::SomeExpr{procRef};3594 } else {3595 TODO(converter.getCurrentLocation(),3596 "CallStmt without typedCall is not yet supported");3597 }3598 } else {3599 TODO(converter.getCurrentLocation(), "Unsupported combiner instance type");3600 }3601 3602 assert(evalExprOpt.has_value() && "evalExpr must be initialized");3603 semantics::SomeExpr evalExpr = *evalExprOpt;3604 3605 genCombinerCB = [&, evalExpr](fir::FirOpBuilder &builder, mlir::Location loc,3606 mlir::Type type, mlir::Value lhs,3607 mlir::Value rhs, bool isByRef) {3608 lower::SymMapScope scope(symTable);3609 const std::list<parser::OmpStylizedDeclaration> &declList =3610 std::get<std::list<parser::OmpStylizedDeclaration>>(combinerInstance.t);3611 mlir::Value ompOutVar;3612 for (const parser::OmpStylizedDeclaration &decl : declList) {3613 auto &name = std::get<parser::ObjectName>(decl.var.t);3614 mlir::Value addr = lhs;3615 mlir::Type type = lhs.getType();3616 bool isRhs = name.ToString() == std::string("omp_in");3617 if (isRhs) {3618 addr = rhs;3619 type = rhs.getType();3620 }3621 3622 assert(name.symbol && "Reduction object name does not have a symbol");3623 if (!fir::conformsWithPassByRef(type)) {3624 addr = builder.createTemporary(loc, type);3625 fir::StoreOp::create(builder, loc, isRhs ? rhs : lhs, addr);3626 }3627 fir::FortranVariableFlagsEnum extraFlags = {};3628 fir::FortranVariableFlagsAttr attributes =3629 Fortran::lower::translateSymbolAttributes(builder.getContext(),3630 *name.symbol, extraFlags);3631 auto declareOp =3632 hlfir::DeclareOp::create(builder, loc, addr, name.ToString(), nullptr,3633 {}, nullptr, nullptr, 0, attributes);3634 if (name.ToString() == "omp_out")3635 ompOutVar = declareOp.getResult(0);3636 symTable.addVariableDefinition(*name.symbol, declareOp);3637 }3638 3639 lower::StatementContext stmtCtx;3640 mlir::Value result = common::visit(3641 common::visitors{3642 [&](const evaluate::ProcedureRef &procRef) -> mlir::Value {3643 convertCallToHLFIR(loc, converter, procRef, std::nullopt,3644 symTable, stmtCtx);3645 auto outVal = fir::LoadOp::create(builder, loc, ompOutVar);3646 return outVal;3647 },3648 [&](const auto &expr) -> mlir::Value {3649 mlir::Value exprResult = fir::getBase(convertExprToValue(3650 loc, converter, evalExpr, symTable, stmtCtx));3651 // Optional load may be generated if we get a reference to the3652 // reduction type.3653 if (auto refType =3654 llvm::dyn_cast<fir::ReferenceType>(exprResult.getType()))3655 if (lhs.getType() == refType.getElementType())3656 exprResult = fir::LoadOp::create(builder, loc, exprResult);3657 return exprResult;3658 }},3659 evalExpr.u);3660 stmtCtx.finalizeAndPop();3661 if (isByRef) {3662 fir::StoreOp::create(builder, loc, result, lhs);3663 mlir::omp::YieldOp::create(builder, loc, lhs);3664 } else {3665 mlir::omp::YieldOp::create(builder, loc, result);3666 }3667 };3668 return genCombinerCB;3669}3670 3671// Checks that the reduction type is either a trivial type or a derived type of3672// trivial types.3673static bool isSimpleReductionType(mlir::Type reductionType) {3674 if (fir::isa_trivial(reductionType))3675 return true;3676 if (auto recordTy = mlir::dyn_cast<fir::RecordType>(reductionType)) {3677 for (auto [_, fieldType] : recordTy.getTypeList()) {3678 if (!fir::isa_trivial(fieldType))3679 return false;3680 }3681 }3682 return true;3683}3684 3685// Getting the type from a symbol compared to a DeclSpec is simpler since we do3686// not need to consider derived vs intrinsic types. Semantics is guaranteed to3687// generate these symbols.3688static mlir::Type3689getReductionType(lower::AbstractConverter &converter,3690 const parser::OmpReductionSpecifier &specifier) {3691 const auto &combinerExpression =3692 std::get<std::optional<parser::OmpCombinerExpression>>(specifier.t)3693 .value();3694 const parser::OmpStylizedInstance &combinerInstance =3695 combinerExpression.v.front();3696 const std::list<parser::OmpStylizedDeclaration> &declList =3697 std::get<std::list<parser::OmpStylizedDeclaration>>(combinerInstance.t);3698 const parser::OmpStylizedDeclaration &decl = declList.front();3699 const auto &name = std::get<parser::ObjectName>(decl.var.t);3700 const auto &symbol = semantics::SymbolRef(*name.symbol);3701 mlir::Type reductionType = converter.genType(symbol);3702 3703 if (!isSimpleReductionType(reductionType))3704 TODO(converter.getCurrentLocation(),3705 "declare reduction currently only supports trival types or derived "3706 "types containing trivial types");3707 return reductionType;3708}3709 3710static void genOMP(3711 lower::AbstractConverter &converter, lower::SymMap &symTable,3712 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,3713 const parser::OpenMPDeclareReductionConstruct &declareReductionConstruct) {3714 if (semaCtx.langOptions().OpenMPSimd)3715 return;3716 3717 const parser::OmpArgumentList &args{declareReductionConstruct.v.Arguments()};3718 const parser::OmpArgument &arg{args.v.front()};3719 const auto &specifier = std::get<parser::OmpReductionSpecifier>(arg.u);3720 3721 if (std::get<parser::OmpTypeNameList>(specifier.t).v.size() > 1)3722 TODO(converter.getCurrentLocation(),3723 "multiple types in declare reduction is not yet supported");3724 3725 mlir::Type reductionType = getReductionType(converter, specifier);3726 ReductionProcessor::GenCombinerCBTy genCombinerCB =3727 processReductionCombiner(converter, symTable, semaCtx, specifier);3728 const parser::OmpClauseList &initializer =3729 declareReductionConstruct.v.Clauses();3730 if (initializer.v.size() > 0) {3731 List<Clause> clauses = makeClauses(initializer, semaCtx);3732 ReductionProcessor::GenInitValueCBTy genInitValueCB;3733 ClauseProcessor cp(converter, semaCtx, clauses);3734 const parser::OmpClause::Initializer &iclause{3735 std::get<parser::OmpClause::Initializer>(initializer.v.front().u)};3736 cp.processInitializer(symTable, iclause, genInitValueCB);3737 const auto &identifier =3738 std::get<parser::OmpReductionIdentifier>(specifier.t);3739 const auto &designator =3740 std::get<parser::ProcedureDesignator>(identifier.u);3741 const auto &reductionName = std::get<parser::Name>(designator.u);3742 bool isByRef = ReductionProcessor::doReductionByRef(reductionType);3743 ReductionProcessor::createDeclareReductionHelper<3744 mlir::omp::DeclareReductionOp>(3745 converter, reductionName.ToString(), reductionType,3746 converter.getCurrentLocation(), isByRef, genCombinerCB, genInitValueCB);3747 } else {3748 TODO(converter.getCurrentLocation(),3749 "declare reduction without an initializer clause is not yet "3750 "supported");3751 }3752}3753 3754static void3755genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3756 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,3757 const parser::OpenMPDeclareSimdConstruct &declareSimdConstruct) {3758 if (!semaCtx.langOptions().OpenMPSimd)3759 TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");3760}3761 3762static void genOpenMPDeclareMapperImpl(3763 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,3764 const parser::OpenMPDeclareMapperConstruct &construct,3765 const semantics::Symbol *mapperSymOpt = nullptr) {3766 mlir::Location loc = converter.genLocation(construct.source);3767 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();3768 const parser::OmpArgumentList &args = construct.v.Arguments();3769 assert(args.v.size() == 1 && "Expecting single argument");3770 lower::StatementContext stmtCtx;3771 const auto *spec = std::get_if<parser::OmpMapperSpecifier>(&args.v.front().u);3772 assert(spec && "Expecting mapper specifier");3773 const auto &mapperName{std::get<std::string>(spec->t)};3774 const auto &varType{std::get<parser::TypeSpec>(spec->t)};3775 const auto &varName{std::get<parser::Name>(spec->t)};3776 assert(varType.declTypeSpec->category() ==3777 semantics::DeclTypeSpec::Category::TypeDerived &&3778 "Expected derived type");3779 3780 std::string mapperNameStr = mapperName;3781 if (mapperSymOpt && mapperNameStr != "default") {3782 mapperNameStr = converter.mangleName(mapperNameStr, mapperSymOpt->owner());3783 } else if (auto *sym =3784 converter.getCurrentScope().FindSymbol(mapperNameStr)) {3785 mapperNameStr = converter.mangleName(mapperNameStr, sym->owner());3786 }3787 3788 // If the mapper op already exists (e.g., created by regular lowering or by3789 // materialization of imported mappers), do not recreate it.3790 if (converter.getModuleOp().lookupSymbol(mapperNameStr))3791 return;3792 3793 // Save current insertion point before moving to the module scope to create3794 // the DeclareMapperOp3795 mlir::OpBuilder::InsertionGuard guard(firOpBuilder);3796 3797 firOpBuilder.setInsertionPointToStart(converter.getModuleOp().getBody());3798 auto mlirType = converter.genType(varType.declTypeSpec->derivedTypeSpec());3799 auto declMapperOp = mlir::omp::DeclareMapperOp::create(3800 firOpBuilder, loc, mapperNameStr, mlirType);3801 auto ®ion = declMapperOp.getRegion();3802 firOpBuilder.createBlock(®ion);3803 auto varVal = region.addArgument(firOpBuilder.getRefType(mlirType), loc);3804 converter.bindSymbol(*varName.symbol, varVal);3805 3806 // Populate the declareMapper region with the map information.3807 mlir::omp::DeclareMapperInfoOperands clauseOps;3808 List<Clause> clauses = makeClauses(construct.v.Clauses(), semaCtx);3809 ClauseProcessor cp(converter, semaCtx, clauses);3810 cp.processMap(loc, stmtCtx, clauseOps);3811 mlir::omp::DeclareMapperInfoOp::create(firOpBuilder, loc, clauseOps.mapVars);3812}3813 3814static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3815 semantics::SemanticsContext &semaCtx,3816 lower::pft::Evaluation &eval,3817 const parser::OpenMPDeclareMapperConstruct &construct) {3818 genOpenMPDeclareMapperImpl(converter, semaCtx, construct);3819}3820 3821static void3822genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3823 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,3824 const parser::OpenMPDeclareTargetConstruct &declareTargetConstruct) {3825 mlir::omp::DeclareTargetOperands clauseOps;3826 llvm::SmallVector<DeclareTargetCaptureInfo> symbolAndClause;3827 mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();3828 getDeclareTargetInfo(converter, semaCtx, eval, declareTargetConstruct,3829 clauseOps, symbolAndClause);3830 3831 for (const DeclareTargetCaptureInfo &symClause : symbolAndClause) {3832 mlir::Operation *op =3833 mod.lookupSymbol(converter.mangleName(symClause.symbol));3834 3835 // Some symbols are deferred until later in the module, these are handled3836 // upon finalization of the module for OpenMP inside of Bridge, so we simply3837 // skip for now.3838 if (!op)3839 continue;3840 3841 markDeclareTarget(op, converter, symClause.clause, clauseOps.deviceType,3842 symClause.automap);3843 }3844}3845 3846static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3847 semantics::SemanticsContext &semaCtx,3848 lower::pft::Evaluation &eval,3849 const parser::OpenMPGroupprivate &directive) {3850 TODO(converter.getCurrentLocation(), "GROUPPRIVATE");3851}3852 3853static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3854 semantics::SemanticsContext &semaCtx,3855 lower::pft::Evaluation &eval,3856 const parser::OpenMPRequiresConstruct &requiresConstruct) {3857 // Requires directives are gathered and processed in semantics and3858 // then combined in the lowering bridge before triggering codegen3859 // just once. Hence, there is no need to lower each individual3860 // occurrence here.3861}3862 3863static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3864 semantics::SemanticsContext &semaCtx,3865 lower::pft::Evaluation &eval,3866 const parser::OpenMPThreadprivate &threadprivate) {3867 // The directive is lowered when instantiating the variable to3868 // support the case of threadprivate variable declared in module.3869}3870 3871static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3872 semantics::SemanticsContext &semaCtx,3873 lower::pft::Evaluation &eval,3874 const parser::OmpMetadirectiveDirective &meta) {3875 TODO(converter.getCurrentLocation(), "METADIRECTIVE");3876}3877 3878static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3879 semantics::SemanticsContext &semaCtx,3880 lower::pft::Evaluation &eval,3881 const parser::OpenMPDeclarativeConstruct &ompDeclConstruct) {3882 Fortran::common::visit(3883 [&](auto &&s) { return genOMP(converter, symTable, semaCtx, eval, s); },3884 ompDeclConstruct.u);3885}3886 3887//===----------------------------------------------------------------------===//3888// OpenMPStandaloneConstruct visitors3889//===----------------------------------------------------------------------===//3890 3891static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3892 semantics::SemanticsContext &semaCtx,3893 lower::pft::Evaluation &eval,3894 const parser::OpenMPSimpleStandaloneConstruct &construct) {3895 const auto &directive = std::get<parser::OmpDirectiveName>(construct.v.t);3896 List<Clause> clauses = makeClauses(construct.v.Clauses(), semaCtx);3897 mlir::Location currentLocation = converter.genLocation(directive.source);3898 3899 ConstructQueue queue{3900 buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,3901 eval, directive.source, directive.v, clauses)};3902 if (directive.v == llvm::omp::Directive::OMPD_ordered) {3903 // Standalone "ordered" directive.3904 genOrderedOp(converter, symTable, semaCtx, eval, currentLocation, queue,3905 queue.begin());3906 } else {3907 // Dispatch handles the "block-associated" variant of "ordered".3908 genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,3909 queue.begin());3910 }3911}3912 3913static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3914 semantics::SemanticsContext &semaCtx,3915 lower::pft::Evaluation &eval,3916 const parser::OpenMPFlushConstruct &construct) {3917 const auto &argumentList = construct.v.Arguments();3918 const auto &clauseList = construct.v.Clauses();3919 ObjectList objects = makeObjects(argumentList, semaCtx);3920 List<Clause> clauses =3921 makeList(clauseList.v, [&](auto &&s) { return makeClause(s, semaCtx); });3922 mlir::Location currentLocation = converter.genLocation(construct.source);3923 3924 ConstructQueue queue{buildConstructQueue(3925 converter.getFirOpBuilder().getModule(), semaCtx, eval, construct.source,3926 llvm::omp::Directive::OMPD_flush, clauses)};3927 genFlushOp(converter, symTable, semaCtx, eval, currentLocation, objects,3928 queue, queue.begin());3929}3930 3931static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3932 semantics::SemanticsContext &semaCtx,3933 lower::pft::Evaluation &eval,3934 const parser::OpenMPCancelConstruct &cancelConstruct) {3935 List<Clause> clauses = makeList(cancelConstruct.v.Clauses().v, [&](auto &&s) {3936 return makeClause(s, semaCtx);3937 });3938 mlir::Location loc = converter.genLocation(cancelConstruct.source);3939 3940 ConstructQueue queue{buildConstructQueue(3941 converter.getFirOpBuilder().getModule(), semaCtx, eval,3942 cancelConstruct.source, llvm::omp::Directive::OMPD_cancel, clauses)};3943 genCancelOp(converter, semaCtx, eval, loc, queue, queue.begin());3944}3945 3946static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3947 semantics::SemanticsContext &semaCtx,3948 lower::pft::Evaluation &eval,3949 const parser::OpenMPCancellationPointConstruct3950 &cancellationPointConstruct) {3951 List<Clause> clauses =3952 makeList(cancellationPointConstruct.v.Clauses().v,3953 [&](auto &&s) { return makeClause(s, semaCtx); });3954 mlir::Location loc = converter.genLocation(cancellationPointConstruct.source);3955 3956 ConstructQueue queue{3957 buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,3958 eval, cancellationPointConstruct.source,3959 llvm::omp::Directive::OMPD_cancel, clauses)};3960 genCancellationPointOp(converter, semaCtx, eval, loc, queue, queue.begin());3961}3962 3963static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3964 semantics::SemanticsContext &semaCtx,3965 lower::pft::Evaluation &eval,3966 const parser::OpenMPDepobjConstruct &construct) {3967 // These values will be ignored until the construct itself is implemented,3968 // but run them anyway for the sake of testing (via a Todo test).3969 ObjectList objects = makeObjects(construct.v.Arguments(), semaCtx);3970 assert(objects.size() == 1);3971 List<Clause> clauses = makeClauses(construct.v.Clauses(), semaCtx);3972 assert(clauses.size() == 1);3973 (void)objects;3974 (void)clauses;3975 3976 if (!semaCtx.langOptions().OpenMPSimd)3977 TODO(converter.getCurrentLocation(), "OpenMPDepobjConstruct");3978}3979 3980static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3981 semantics::SemanticsContext &semaCtx,3982 lower::pft::Evaluation &eval,3983 const parser::OpenMPInteropConstruct &interopConstruct) {3984 if (!semaCtx.langOptions().OpenMPSimd)3985 TODO(converter.getCurrentLocation(), "OpenMPInteropConstruct");3986}3987 3988static void3989genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3990 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,3991 const parser::OpenMPStandaloneConstruct &standaloneConstruct) {3992 Fortran::common::visit(3993 [&](auto &&s) { return genOMP(converter, symTable, semaCtx, eval, s); },3994 standaloneConstruct.u);3995}3996 3997static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,3998 semantics::SemanticsContext &semaCtx,3999 lower::pft::Evaluation &eval,4000 const parser::OpenMPAllocatorsConstruct &allocsConstruct) {4001 if (!semaCtx.langOptions().OpenMPSimd)4002 TODO(converter.getCurrentLocation(), "OpenMPAllocatorsConstruct");4003}4004 4005//===----------------------------------------------------------------------===//4006// OpenMPConstruct visitors4007//===----------------------------------------------------------------------===//4008 4009static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4010 semantics::SemanticsContext &semaCtx,4011 lower::pft::Evaluation &eval,4012 const parser::OpenMPAtomicConstruct &construct) {4013 lowerAtomic(converter, symTable, semaCtx, eval, construct);4014}4015 4016static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4017 semantics::SemanticsContext &semaCtx,4018 lower::pft::Evaluation &eval,4019 const parser::OmpBlockConstruct &blockConstruct) {4020 const parser::OmpDirectiveSpecification &beginSpec =4021 blockConstruct.BeginDir();4022 List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx);4023 if (auto &endSpec = blockConstruct.EndDir())4024 clauses.append(makeClauses(endSpec->Clauses(), semaCtx));4025 4026 llvm::omp::Directive directive = beginSpec.DirId();4027 assert(llvm::omp::blockConstructSet.test(directive) &&4028 "Expected block construct");4029 mlir::Location currentLocation = converter.genLocation(beginSpec.source);4030 4031 for (const Clause &clause : clauses) {4032 mlir::Location clauseLocation = converter.genLocation(clause.source);4033 if (!std::holds_alternative<clause::Affinity>(clause.u) &&4034 !std::holds_alternative<clause::Allocate>(clause.u) &&4035 !std::holds_alternative<clause::Copyin>(clause.u) &&4036 !std::holds_alternative<clause::Copyprivate>(clause.u) &&4037 !std::holds_alternative<clause::Default>(clause.u) &&4038 !std::holds_alternative<clause::Defaultmap>(clause.u) &&4039 !std::holds_alternative<clause::Depend>(clause.u) &&4040 !std::holds_alternative<clause::Filter>(clause.u) &&4041 !std::holds_alternative<clause::Final>(clause.u) &&4042 !std::holds_alternative<clause::Firstprivate>(clause.u) &&4043 !std::holds_alternative<clause::HasDeviceAddr>(clause.u) &&4044 !std::holds_alternative<clause::If>(clause.u) &&4045 !std::holds_alternative<clause::IsDevicePtr>(clause.u) &&4046 !std::holds_alternative<clause::Map>(clause.u) &&4047 !std::holds_alternative<clause::Nowait>(clause.u) &&4048 !std::holds_alternative<clause::NumTeams>(clause.u) &&4049 !std::holds_alternative<clause::NumThreads>(clause.u) &&4050 !std::holds_alternative<clause::OmpxBare>(clause.u) &&4051 !std::holds_alternative<clause::Priority>(clause.u) &&4052 !std::holds_alternative<clause::Private>(clause.u) &&4053 !std::holds_alternative<clause::ProcBind>(clause.u) &&4054 !std::holds_alternative<clause::Reduction>(clause.u) &&4055 !std::holds_alternative<clause::Shared>(clause.u) &&4056 !std::holds_alternative<clause::Simd>(clause.u) &&4057 !std::holds_alternative<clause::ThreadLimit>(clause.u) &&4058 !std::holds_alternative<clause::Threads>(clause.u) &&4059 !std::holds_alternative<clause::UseDeviceAddr>(clause.u) &&4060 !std::holds_alternative<clause::UseDevicePtr>(clause.u) &&4061 !std::holds_alternative<clause::InReduction>(clause.u) &&4062 !std::holds_alternative<clause::Mergeable>(clause.u) &&4063 !std::holds_alternative<clause::Untied>(clause.u) &&4064 !std::holds_alternative<clause::TaskReduction>(clause.u) &&4065 !std::holds_alternative<clause::Detach>(clause.u)) {4066 std::string name =4067 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(clause.id));4068 if (!semaCtx.langOptions().OpenMPSimd)4069 TODO(clauseLocation, name + " clause is not implemented yet");4070 }4071 }4072 4073 ConstructQueue queue{4074 buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,4075 eval, beginSpec.source, directive, clauses)};4076 genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,4077 queue.begin());4078}4079 4080static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4081 semantics::SemanticsContext &semaCtx,4082 lower::pft::Evaluation &eval,4083 const parser::OpenMPAssumeConstruct &assumeConstruct) {4084 mlir::Location clauseLocation = converter.genLocation(assumeConstruct.source);4085 if (!semaCtx.langOptions().OpenMPSimd)4086 TODO(clauseLocation, "OpenMP ASSUME construct");4087}4088 4089static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4090 semantics::SemanticsContext &semaCtx,4091 lower::pft::Evaluation &eval,4092 const parser::OpenMPCriticalConstruct &criticalConstruct) {4093 const parser::OmpDirectiveSpecification &beginSpec =4094 criticalConstruct.BeginDir();4095 List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx);4096 4097 ConstructQueue queue{buildConstructQueue(4098 converter.getFirOpBuilder().getModule(), semaCtx, eval, beginSpec.source,4099 llvm::omp::Directive::OMPD_critical, clauses)};4100 4101 std::optional<parser::Name> critName;4102 const parser::OmpArgumentList &args = beginSpec.Arguments();4103 if (!args.v.empty()) {4104 // All of these things should be guaranteed to exist after semantic checks.4105 auto *object = parser::Unwrap<parser::OmpObject>(args.v.front());4106 assert(object && "Expecting object as argument");4107 auto *designator = semantics::omp::GetDesignatorFromObj(*object);4108 assert(designator && "Expecting desginator in argument");4109 auto *name = parser::GetDesignatorNameIfDataRef(*designator);4110 assert(name && "Expecting dataref in designator");4111 critName = *name;4112 }4113 mlir::Location currentLocation = converter.getCurrentLocation();4114 genCriticalOp(converter, symTable, semaCtx, eval, currentLocation, queue,4115 queue.begin(), critName);4116}4117 4118static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4119 semantics::SemanticsContext &semaCtx,4120 lower::pft::Evaluation &eval,4121 const parser::OpenMPUtilityConstruct &) {4122 if (!semaCtx.langOptions().OpenMPSimd)4123 TODO(converter.getCurrentLocation(), "OpenMPUtilityConstruct");4124}4125 4126static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4127 semantics::SemanticsContext &semaCtx,4128 lower::pft::Evaluation &eval,4129 const parser::OpenMPDispatchConstruct &) {4130 if (!semaCtx.langOptions().OpenMPSimd)4131 TODO(converter.getCurrentLocation(), "OpenMPDispatchConstruct");4132}4133 4134static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4135 semantics::SemanticsContext &semaCtx,4136 lower::pft::Evaluation &eval,4137 const parser::OpenMPLoopConstruct &loopConstruct) {4138 const parser::OmpDirectiveSpecification &beginSpec = loopConstruct.BeginDir();4139 List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx);4140 if (auto &endSpec = loopConstruct.EndDir())4141 clauses.append(makeClauses(endSpec->Clauses(), semaCtx));4142 4143 mlir::Location currentLocation = converter.genLocation(beginSpec.source);4144 4145 for (auto &construct : std::get<parser::Block>(loopConstruct.t)) {4146 if (const parser::OpenMPLoopConstruct *ompNestedLoopCons =4147 parser::omp::GetOmpLoop(construct)) {4148 llvm::omp::Directive nestedDirective =4149 parser::omp::GetOmpDirectiveName(*ompNestedLoopCons).v;4150 switch (nestedDirective) {4151 case llvm::omp::Directive::OMPD_tile:4152 // Skip OMPD_tile since the tile sizes will be retrieved when4153 // generating the omp.loop_nest op.4154 break;4155 default: {4156 unsigned version = semaCtx.langOptions().OpenMPVersion;4157 TODO(currentLocation,4158 "Applying a loop-associated on the loop generated by the " +4159 llvm::omp::getOpenMPDirectiveName(nestedDirective, version) +4160 " construct");4161 }4162 }4163 }4164 }4165 4166 const parser::OmpDirectiveName &beginName = beginSpec.DirName();4167 ConstructQueue queue{4168 buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,4169 eval, beginName.source, beginName.v, clauses)};4170 genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,4171 queue.begin());4172}4173 4174static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4175 semantics::SemanticsContext &semaCtx,4176 lower::pft::Evaluation &eval,4177 const parser::OpenMPSectionConstruct §ionConstruct) {4178 // Do nothing here. SECTION is lowered inside of the lowering for Sections4179}4180 4181static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4182 semantics::SemanticsContext &semaCtx,4183 lower::pft::Evaluation &eval,4184 const parser::OpenMPSectionsConstruct &construct) {4185 const parser::OmpDirectiveSpecification &beginSpec{construct.BeginDir()};4186 List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx);4187 const auto &endSpec{construct.EndDir()};4188 assert(endSpec &&4189 "Missing end section directive should have been handled in semantics");4190 clauses.append(makeClauses(endSpec->Clauses(), semaCtx));4191 mlir::Location currentLocation = converter.getCurrentLocation();4192 4193 const parser::OmpDirectiveName &beginName{beginSpec.DirName()};4194 ConstructQueue queue{4195 buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,4196 eval, beginName.source, beginName.v, clauses)};4197 4198 mlir::SaveStateStack<SectionsConstructStackFrame> saveStateStack{4199 converter.getStateStack(), construct};4200 genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue,4201 queue.begin());4202}4203 4204static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,4205 semantics::SemanticsContext &semaCtx,4206 lower::pft::Evaluation &eval,4207 const parser::OpenMPConstruct &ompConstruct) {4208 Fortran::common::visit(4209 [&](auto &&s) { return genOMP(converter, symTable, semaCtx, eval, s); },4210 ompConstruct.u);4211}4212 4213//===----------------------------------------------------------------------===//4214// Public functions4215//===----------------------------------------------------------------------===//4216 4217mlir::Operation *Fortran::lower::genOpenMPTerminator(fir::FirOpBuilder &builder,4218 mlir::Operation *op,4219 mlir::Location loc) {4220 if (mlir::isa<mlir::omp::AtomicUpdateOp, mlir::omp::DeclareReductionOp,4221 mlir::omp::LoopNestOp>(op))4222 return mlir::omp::YieldOp::create(builder, loc);4223 return mlir::omp::TerminatorOp::create(builder, loc);4224}4225 4226void Fortran::lower::genOpenMPConstruct(lower::AbstractConverter &converter,4227 lower::SymMap &symTable,4228 semantics::SemanticsContext &semaCtx,4229 lower::pft::Evaluation &eval,4230 const parser::OpenMPConstruct &omp) {4231 lower::SymMapScope scope(symTable);4232 genOMP(converter, symTable, semaCtx, eval, omp);4233}4234 4235void Fortran::lower::genOpenMPDeclarativeConstruct(4236 lower::AbstractConverter &converter, lower::SymMap &symTable,4237 semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,4238 const parser::OpenMPDeclarativeConstruct &omp) {4239 genOMP(converter, symTable, semaCtx, eval, omp);4240 genNestedEvaluations(converter, eval);4241}4242 4243void Fortran::lower::genOpenMPSymbolProperties(4244 lower::AbstractConverter &converter, const lower::pft::Variable &var) {4245 assert(var.hasSymbol() && "Expecting Symbol");4246 const semantics::Symbol &sym = var.getSymbol();4247 4248 if (sym.test(semantics::Symbol::Flag::OmpThreadprivate))4249 lower::genThreadprivateOp(converter, var);4250 4251 if (sym.test(semantics::Symbol::Flag::OmpDeclareTarget))4252 lower::genDeclareTargetIntGlobal(converter, var);4253}4254 4255void Fortran::lower::genThreadprivateOp(lower::AbstractConverter &converter,4256 const lower::pft::Variable &var) {4257 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();4258 mlir::Location currentLocation = converter.getCurrentLocation();4259 4260 const semantics::Symbol &sym = var.getSymbol();4261 mlir::Value symThreadprivateValue;4262 if (const semantics::Symbol *common =4263 semantics::FindCommonBlockContaining(sym.GetUltimate())) {4264 mlir::Value commonValue = converter.getSymbolAddress(*common);4265 if (mlir::isa<mlir::omp::ThreadprivateOp>(commonValue.getDefiningOp())) {4266 // Generate ThreadprivateOp for a common block instead of its members and4267 // only do it once for a common block.4268 return;4269 }4270 // Generate ThreadprivateOp and rebind the common block.4271 mlir::Value commonThreadprivateValue = mlir::omp::ThreadprivateOp::create(4272 firOpBuilder, currentLocation, commonValue.getType(), commonValue);4273 converter.bindSymbol(*common, commonThreadprivateValue);4274 // Generate the threadprivate value for the common block member.4275 symThreadprivateValue =4276 genCommonBlockMember(converter, currentLocation, sym,4277 commonThreadprivateValue, common->size());4278 } else if (!var.isGlobal()) {4279 // Non-global variable which can be in threadprivate directive must be one4280 // variable in main program, and it has implicit SAVE attribute. Take it as4281 // with SAVE attribute, so to create GlobalOp for it to simplify the4282 // translation to LLVM IR.4283 // Avoids performing multiple globalInitializations.4284 fir::GlobalOp global;4285 auto module = converter.getModuleOp();4286 std::string globalName = converter.mangleName(sym);4287 if (module.lookupSymbol<fir::GlobalOp>(globalName))4288 global = module.lookupSymbol<fir::GlobalOp>(globalName);4289 else4290 global = globalInitialization(converter, firOpBuilder, sym, var,4291 currentLocation);4292 4293 mlir::Value symValue = fir::AddrOfOp::create(4294 firOpBuilder, currentLocation, global.resultType(), global.getSymbol());4295 symThreadprivateValue = mlir::omp::ThreadprivateOp::create(4296 firOpBuilder, currentLocation, symValue.getType(), symValue);4297 } else {4298 mlir::Value symValue = converter.getSymbolAddress(sym);4299 4300 // The symbol may be use-associated multiple times, and nothing needs to be4301 // done after the original symbol is mapped to the threadprivatized value4302 // for the first time. Use the threadprivatized value directly.4303 mlir::Operation *op;4304 if (auto declOp = symValue.getDefiningOp<hlfir::DeclareOp>())4305 op = declOp.getMemref().getDefiningOp();4306 else4307 op = symValue.getDefiningOp();4308 if (mlir::isa<mlir::omp::ThreadprivateOp>(op))4309 return;4310 4311 symThreadprivateValue = mlir::omp::ThreadprivateOp::create(4312 firOpBuilder, currentLocation, symValue.getType(), symValue);4313 }4314 4315 fir::ExtendedValue sexv = converter.getSymbolExtendedValue(sym);4316 fir::ExtendedValue symThreadprivateExv =4317 getExtendedValue(sexv, symThreadprivateValue);4318 converter.bindSymbol(sym, symThreadprivateExv);4319}4320 4321// This function replicates threadprivate's behaviour of generating4322// an internal fir.GlobalOp for non-global variables in the main program4323// that have the implicit SAVE attribute, to simplifiy LLVM-IR and MLIR4324// generation.4325void Fortran::lower::genDeclareTargetIntGlobal(4326 lower::AbstractConverter &converter, const lower::pft::Variable &var) {4327 if (!var.isGlobal()) {4328 // A non-global variable which can be in a declare target directive must4329 // be a variable in the main program, and it has the implicit SAVE4330 // attribute. We create a GlobalOp for it to simplify the translation to4331 // LLVM IR.4332 globalInitialization(converter, converter.getFirOpBuilder(),4333 var.getSymbol(), var, converter.getCurrentLocation());4334 }4335}4336 4337bool Fortran::lower::isOpenMPTargetConstruct(4338 const parser::OpenMPConstruct &omp) {4339 llvm::omp::Directive dir = llvm::omp::Directive::OMPD_unknown;4340 if (const auto *block = std::get_if<parser::OmpBlockConstruct>(&omp.u)) {4341 dir = block->BeginDir().DirId();4342 } else if (const auto *loop =4343 std::get_if<parser::OpenMPLoopConstruct>(&omp.u)) {4344 dir = loop->BeginDir().DirId();4345 }4346 return llvm::omp::allTargetSet.test(dir);4347}4348 4349void Fortran::lower::gatherOpenMPDeferredDeclareTargets(4350 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,4351 lower::pft::Evaluation &eval,4352 const parser::OpenMPDeclarativeConstruct &ompDecl,4353 llvm::SmallVectorImpl<OMPDeferredDeclareTargetInfo>4354 &deferredDeclareTarget) {4355 Fortran::common::visit(4356 common::visitors{4357 [&](const parser::OpenMPDeclareTargetConstruct &ompReq) {4358 collectDeferredDeclareTargets(converter, semaCtx, eval, ompReq,4359 deferredDeclareTarget);4360 },4361 [&](const auto &) {},4362 },4363 ompDecl.u);4364}4365 4366bool Fortran::lower::isOpenMPDeviceDeclareTarget(4367 lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,4368 lower::pft::Evaluation &eval,4369 const parser::OpenMPDeclarativeConstruct &ompDecl) {4370 return Fortran::common::visit(4371 common::visitors{4372 [&](const parser::OpenMPDeclareTargetConstruct &ompReq) {4373 mlir::omp::DeclareTargetDeviceType targetType =4374 getDeclareTargetFunctionDevice(converter, semaCtx, eval, ompReq)4375 .value_or(mlir::omp::DeclareTargetDeviceType::host);4376 return targetType != mlir::omp::DeclareTargetDeviceType::host;4377 },4378 [&](const auto &) { return false; },4379 },4380 ompDecl.u);4381}4382 4383// In certain cases such as subroutine or function interfaces which declare4384// but do not define or directly call the subroutine or function in the same4385// module, their lowering is delayed until after the declare target construct4386// itself is processed, so there symbol is not within the table.4387//4388// This function will also return true if we encounter any device declare4389// target cases, to satisfy checking if we require the requires attributes4390// on the module.4391bool Fortran::lower::markOpenMPDeferredDeclareTargetFunctions(4392 mlir::Operation *mod,4393 llvm::SmallVectorImpl<OMPDeferredDeclareTargetInfo> &deferredDeclareTargets,4394 AbstractConverter &converter) {4395 bool deviceCodeFound = false;4396 auto modOp = llvm::cast<mlir::ModuleOp>(mod);4397 for (auto declTar : deferredDeclareTargets) {4398 mlir::Operation *op = modOp.lookupSymbol(converter.mangleName(declTar.sym));4399 4400 // Due to interfaces being optionally emitted on usage in a module,4401 // not finding an operation at this point cannot be a hard error, we4402 // simply ignore it for now.4403 // TODO: Add semantic checks for detecting cases where an erronous4404 // (undefined) symbol has been supplied to a declare target clause4405 if (!op)4406 continue;4407 4408 auto devType = declTar.declareTargetDeviceType;4409 if (!deviceCodeFound && devType != mlir::omp::DeclareTargetDeviceType::host)4410 deviceCodeFound = true;4411 4412 markDeclareTarget(op, converter, declTar.declareTargetCaptureClause,4413 devType, declTar.automap);4414 }4415 4416 return deviceCodeFound;4417}4418 4419void Fortran::lower::genOpenMPRequires(mlir::Operation *mod,4420 const semantics::Symbol *symbol) {4421 using MlirRequires = mlir::omp::ClauseRequires;4422 4423 if (auto offloadMod =4424 llvm::dyn_cast<mlir::omp::OffloadModuleInterface>(mod)) {4425 semantics::WithOmpDeclarative::RequiresClauses reqs;4426 if (symbol) {4427 common::visit(4428 [&](const auto &details) {4429 if constexpr (std::is_base_of_v<semantics::WithOmpDeclarative,4430 std::decay_t<decltype(details)>>) {4431 if (details.has_ompRequires())4432 reqs = *details.ompRequires();4433 }4434 },4435 symbol->details());4436 }4437 4438 // Use pre-populated omp.requires module attribute if it was set, so that4439 // the "-fopenmp-force-usm" compiler option is honored.4440 MlirRequires mlirFlags = offloadMod.getRequires();4441 if (reqs.test(llvm::omp::Clause::OMPC_dynamic_allocators))4442 mlirFlags = mlirFlags | MlirRequires::dynamic_allocators;4443 if (reqs.test(llvm::omp::Clause::OMPC_reverse_offload))4444 mlirFlags = mlirFlags | MlirRequires::reverse_offload;4445 if (reqs.test(llvm::omp::Clause::OMPC_unified_address))4446 mlirFlags = mlirFlags | MlirRequires::unified_address;4447 if (reqs.test(llvm::omp::Clause::OMPC_unified_shared_memory))4448 mlirFlags = mlirFlags | MlirRequires::unified_shared_memory;4449 4450 offloadMod.setRequires(mlirFlags);4451 }4452}4453 4454// Walk scopes and materialize omp.declare_mapper ops for mapper declarations4455// found in imported modules. If \p scope is null, start from the global scope.4456void Fortran::lower::materializeOpenMPDeclareMappers(4457 Fortran::lower::AbstractConverter &converter,4458 semantics::SemanticsContext &semaCtx, const semantics::Scope *scope) {4459 const semantics::Scope &root = scope ? *scope : semaCtx.globalScope();4460 4461 // Recurse into child scopes first (modules, submodules, etc.).4462 for (const semantics::Scope &child : root.children())4463 materializeOpenMPDeclareMappers(converter, semaCtx, &child);4464 4465 // Only consider module scopes to avoid duplicating local constructs.4466 if (!root.IsModule())4467 return;4468 4469 // Only materialize for modules coming from mod files to avoid duplicates.4470 if (!root.symbol() || !root.symbol()->test(semantics::Symbol::Flag::ModFile))4471 return;4472 4473 // Scan symbols in this module scope for MapperDetails.4474 for (auto &it : root) {4475 const semantics::Symbol &sym = *it.second;4476 if (auto *md = sym.detailsIf<semantics::MapperDetails>()) {4477 for (const auto *decl : md->GetDeclList()) {4478 if (const auto *mapperDecl =4479 std::get_if<parser::OpenMPDeclareMapperConstruct>(&decl->u)) {4480 genOpenMPDeclareMapperImpl(converter, semaCtx, *mapperDecl, &sym);4481 }4482 }4483 }4484 }4485}4486