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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Lower/OpenMP.h" 14 #include "flang/Common/idioms.h" 15 #include "flang/Lower/Bridge.h" 16 #include "flang/Lower/PFTBuilder.h" 17 #include "flang/Lower/StatementContext.h" 18 #include "flang/Lower/Todo.h" 19 #include "flang/Optimizer/Builder/BoxValue.h" 20 #include "flang/Optimizer/Builder/FIRBuilder.h" 21 #include "flang/Parser/parse-tree.h" 22 #include "flang/Semantics/tools.h" 23 #include "mlir/Dialect/OpenMP/OpenMPDialect.h" 24 #include "llvm/Frontend/OpenMP/OMPConstants.h" 25 26 using namespace mlir; 27 28 int64_t Fortran::lower::getCollapseValue( 29 const Fortran::parser::OmpClauseList &clauseList) { 30 for (const auto &clause : clauseList.v) { 31 if (const auto &collapseClause = 32 std::get_if<Fortran::parser::OmpClause::Collapse>(&clause.u)) { 33 const auto *expr = Fortran::semantics::GetExpr(collapseClause->v); 34 return Fortran::evaluate::ToInt64(*expr).value(); 35 } 36 } 37 return 1; 38 } 39 40 static const Fortran::parser::Name * 41 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) { 42 const auto *dataRef = std::get_if<Fortran::parser::DataRef>(&designator.u); 43 return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr; 44 } 45 46 template <typename T> 47 static void createPrivateVarSyms(Fortran::lower::AbstractConverter &converter, 48 const T *clause) { 49 Fortran::semantics::Symbol *sym = nullptr; 50 const Fortran::parser::OmpObjectList &ompObjectList = clause->v; 51 for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) { 52 std::visit( 53 Fortran::common::visitors{ 54 [&](const Fortran::parser::Designator &designator) { 55 if (const Fortran::parser::Name *name = 56 getDesignatorNameIfDataRef(designator)) { 57 sym = name->symbol; 58 } 59 }, 60 [&](const Fortran::parser::Name &name) { sym = name.symbol; }}, 61 ompObject.u); 62 63 // Privatization for symbols which are pre-determined (like loop index 64 // variables) happen separately, for everything else privatize here 65 if constexpr (std::is_same_v<T, Fortran::parser::OmpClause::Firstprivate>) { 66 converter.copyHostAssociateVar(*sym); 67 } else { 68 bool success = converter.createHostAssociateVarClone(*sym); 69 (void)success; 70 assert(success && "Privatization failed due to existing binding"); 71 } 72 } 73 } 74 75 static void privatizeVars(Fortran::lower::AbstractConverter &converter, 76 const Fortran::parser::OmpClauseList &opClauseList) { 77 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 78 auto insPt = firOpBuilder.saveInsertionPoint(); 79 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); 80 for (const Fortran::parser::OmpClause &clause : opClauseList.v) { 81 if (const auto &privateClause = 82 std::get_if<Fortran::parser::OmpClause::Private>(&clause.u)) { 83 createPrivateVarSyms(converter, privateClause); 84 } else if (const auto &firstPrivateClause = 85 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 86 &clause.u)) { 87 createPrivateVarSyms(converter, firstPrivateClause); 88 } 89 } 90 firOpBuilder.restoreInsertionPoint(insPt); 91 } 92 93 static void genObjectList(const Fortran::parser::OmpObjectList &objectList, 94 Fortran::lower::AbstractConverter &converter, 95 llvm::SmallVectorImpl<Value> &operands) { 96 auto addOperands = [&](Fortran::lower::SymbolRef sym) { 97 const mlir::Value variable = converter.getSymbolAddress(sym); 98 if (variable) { 99 operands.push_back(variable); 100 } else { 101 if (const auto *details = 102 sym->detailsIf<Fortran::semantics::HostAssocDetails>()) { 103 operands.push_back(converter.getSymbolAddress(details->symbol())); 104 converter.copySymbolBinding(details->symbol(), sym); 105 } 106 } 107 }; 108 for (const Fortran::parser::OmpObject &ompObject : objectList.v) { 109 std::visit(Fortran::common::visitors{ 110 [&](const Fortran::parser::Designator &designator) { 111 if (const Fortran::parser::Name *name = 112 getDesignatorNameIfDataRef(designator)) { 113 addOperands(*name->symbol); 114 } 115 }, 116 [&](const Fortran::parser::Name &name) { 117 addOperands(*name.symbol); 118 }}, 119 ompObject.u); 120 } 121 } 122 123 static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter, 124 std::size_t loopVarTypeSize) { 125 // OpenMP runtime requires 32-bit or 64-bit loop variables. 126 loopVarTypeSize = loopVarTypeSize * 8; 127 if (loopVarTypeSize < 32) { 128 loopVarTypeSize = 32; 129 } else if (loopVarTypeSize > 64) { 130 loopVarTypeSize = 64; 131 mlir::emitWarning(converter.getCurrentLocation(), 132 "OpenMP loop iteration variable cannot have more than 64 " 133 "bits size and will be narrowed into 64 bits."); 134 } 135 assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) && 136 "OpenMP loop iteration variable size must be transformed into 32-bit " 137 "or 64-bit"); 138 return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize); 139 } 140 141 /// Create empty blocks for the current region. 142 /// These blocks replace blocks parented to an enclosing region. 143 void createEmptyRegionBlocks( 144 fir::FirOpBuilder &firOpBuilder, 145 std::list<Fortran::lower::pft::Evaluation> &evaluationList) { 146 auto *region = &firOpBuilder.getRegion(); 147 for (auto &eval : evaluationList) { 148 if (eval.block) { 149 if (eval.block->empty()) { 150 eval.block->erase(); 151 eval.block = firOpBuilder.createBlock(region); 152 } else { 153 [[maybe_unused]] auto &terminatorOp = eval.block->back(); 154 assert((mlir::isa<mlir::omp::TerminatorOp>(terminatorOp) || 155 mlir::isa<mlir::omp::YieldOp>(terminatorOp)) && 156 "expected terminator op"); 157 } 158 } 159 if (!eval.isDirective() && eval.hasNestedEvaluations()) 160 createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations()); 161 } 162 } 163 164 /// Create the body (block) for an OpenMP Operation. 165 /// 166 /// \param [in] op - the operation the body belongs to. 167 /// \param [inout] converter - converter to use for the clauses. 168 /// \param [in] loc - location in source code. 169 /// \param [in] eval - current PFT node/evaluation. 170 /// \oaran [in] clauses - list of clauses to process. 171 /// \param [in] args - block arguments (induction variable[s]) for the 172 //// region. 173 /// \param [in] outerCombined - is this an outer operation - prevents 174 /// privatization. 175 template <typename Op> 176 static void 177 createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter, 178 mlir::Location &loc, Fortran::lower::pft::Evaluation &eval, 179 const Fortran::parser::OmpClauseList *clauses = nullptr, 180 const SmallVector<const Fortran::semantics::Symbol *> &args = {}, 181 bool outerCombined = false) { 182 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 183 // If an argument for the region is provided then create the block with that 184 // argument. Also update the symbol's address with the mlir argument value. 185 // e.g. For loops the argument is the induction variable. And all further 186 // uses of the induction variable should use this mlir value. 187 if (args.size()) { 188 std::size_t loopVarTypeSize = 0; 189 for (const Fortran::semantics::Symbol *arg : args) 190 loopVarTypeSize = std::max(loopVarTypeSize, arg->GetUltimate().size()); 191 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize); 192 SmallVector<Type> tiv; 193 SmallVector<Location> locs; 194 for (int i = 0; i < (int)args.size(); i++) { 195 tiv.push_back(loopVarType); 196 locs.push_back(loc); 197 } 198 firOpBuilder.createBlock(&op.getRegion(), {}, tiv, locs); 199 int argIndex = 0; 200 for (const Fortran::semantics::Symbol *arg : args) { 201 fir::ExtendedValue exval = op.getRegion().front().getArgument(argIndex); 202 converter.bindSymbol(*arg, exval); 203 argIndex++; 204 } 205 } else { 206 firOpBuilder.createBlock(&op.getRegion()); 207 } 208 mlir::Block &block = op.getRegion().back(); 209 firOpBuilder.setInsertionPointToEnd(&block); 210 211 // If it is an unstructured region and is not the outer region of a combined 212 // construct, create empty blocks for all evaluations. 213 if (eval.lowerAsUnstructured() && !outerCombined) 214 createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations()); 215 216 // Insert the terminator. 217 if constexpr (std::is_same_v<Op, omp::WsLoopOp>) { 218 mlir::ValueRange results; 219 firOpBuilder.create<mlir::omp::YieldOp>(loc, results); 220 } else { 221 firOpBuilder.create<mlir::omp::TerminatorOp>(loc); 222 } 223 224 // Reset the insertion point to the start of the first block. 225 firOpBuilder.setInsertionPointToStart(&block); 226 227 // Handle privatization. Do not privatize if this is the outer operation. 228 if (clauses && !outerCombined) 229 privatizeVars(converter, *clauses); 230 } 231 232 static void genOMP(Fortran::lower::AbstractConverter &converter, 233 Fortran::lower::pft::Evaluation &eval, 234 const Fortran::parser::OpenMPSimpleStandaloneConstruct 235 &simpleStandaloneConstruct) { 236 const auto &directive = 237 std::get<Fortran::parser::OmpSimpleStandaloneDirective>( 238 simpleStandaloneConstruct.t); 239 switch (directive.v) { 240 default: 241 break; 242 case llvm::omp::Directive::OMPD_barrier: 243 converter.getFirOpBuilder().create<mlir::omp::BarrierOp>( 244 converter.getCurrentLocation()); 245 break; 246 case llvm::omp::Directive::OMPD_taskwait: 247 converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>( 248 converter.getCurrentLocation()); 249 break; 250 case llvm::omp::Directive::OMPD_taskyield: 251 converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>( 252 converter.getCurrentLocation()); 253 break; 254 case llvm::omp::Directive::OMPD_target_enter_data: 255 TODO(converter.getCurrentLocation(), "OMPD_target_enter_data"); 256 case llvm::omp::Directive::OMPD_target_exit_data: 257 TODO(converter.getCurrentLocation(), "OMPD_target_exit_data"); 258 case llvm::omp::Directive::OMPD_target_update: 259 TODO(converter.getCurrentLocation(), "OMPD_target_update"); 260 case llvm::omp::Directive::OMPD_ordered: 261 TODO(converter.getCurrentLocation(), "OMPD_ordered"); 262 } 263 } 264 265 static void 266 genAllocateClause(Fortran::lower::AbstractConverter &converter, 267 const Fortran::parser::OmpAllocateClause &ompAllocateClause, 268 SmallVector<Value> &allocatorOperands, 269 SmallVector<Value> &allocateOperands) { 270 auto &firOpBuilder = converter.getFirOpBuilder(); 271 auto currentLocation = converter.getCurrentLocation(); 272 Fortran::lower::StatementContext stmtCtx; 273 274 mlir::Value allocatorOperand; 275 const Fortran::parser::OmpObjectList &ompObjectList = 276 std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t); 277 const auto &allocatorValue = 278 std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>( 279 ompAllocateClause.t); 280 // Check if allocate clause has allocator specified. If so, add it 281 // to list of allocators, otherwise, add default allocator to 282 // list of allocators. 283 if (allocatorValue) { 284 allocatorOperand = fir::getBase(converter.genExprValue( 285 *Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx)); 286 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 287 allocatorOperand); 288 } else { 289 allocatorOperand = firOpBuilder.createIntegerConstant( 290 currentLocation, firOpBuilder.getI32Type(), 1); 291 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 292 allocatorOperand); 293 } 294 genObjectList(ompObjectList, converter, allocateOperands); 295 } 296 297 static void 298 genOMP(Fortran::lower::AbstractConverter &converter, 299 Fortran::lower::pft::Evaluation &eval, 300 const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) { 301 std::visit( 302 Fortran::common::visitors{ 303 [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct 304 &simpleStandaloneConstruct) { 305 genOMP(converter, eval, simpleStandaloneConstruct); 306 }, 307 [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) { 308 SmallVector<Value, 4> operandRange; 309 if (const auto &ompObjectList = 310 std::get<std::optional<Fortran::parser::OmpObjectList>>( 311 flushConstruct.t)) 312 genObjectList(*ompObjectList, converter, operandRange); 313 const auto &memOrderClause = std::get<std::optional< 314 std::list<Fortran::parser::OmpMemoryOrderClause>>>( 315 flushConstruct.t); 316 if (memOrderClause.has_value() && memOrderClause->size() > 0) 317 TODO(converter.getCurrentLocation(), 318 "Handle OmpMemoryOrderClause"); 319 converter.getFirOpBuilder().create<mlir::omp::FlushOp>( 320 converter.getCurrentLocation(), operandRange); 321 }, 322 [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) { 323 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 324 }, 325 [&](const Fortran::parser::OpenMPCancellationPointConstruct 326 &cancellationPointConstruct) { 327 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 328 }, 329 }, 330 standaloneConstruct.u); 331 } 332 333 static omp::ClauseProcBindKindAttr genProcBindKindAttr( 334 fir::FirOpBuilder &firOpBuilder, 335 const Fortran::parser::OmpClause::ProcBind *procBindClause) { 336 omp::ClauseProcBindKind pbKind; 337 switch (procBindClause->v.v) { 338 case Fortran::parser::OmpProcBindClause::Type::Master: 339 pbKind = omp::ClauseProcBindKind::Master; 340 break; 341 case Fortran::parser::OmpProcBindClause::Type::Close: 342 pbKind = omp::ClauseProcBindKind::Close; 343 break; 344 case Fortran::parser::OmpProcBindClause::Type::Spread: 345 pbKind = omp::ClauseProcBindKind::Spread; 346 break; 347 case Fortran::parser::OmpProcBindClause::Type::Primary: 348 pbKind = omp::ClauseProcBindKind::Primary; 349 break; 350 } 351 return omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind); 352 } 353 354 /* When parallel is used in a combined construct, then use this function to 355 * create the parallel operation. It handles the parallel specific clauses 356 * and leaves the rest for handling at the inner operations. 357 * TODO: Refactor clause handling 358 */ 359 template <typename Directive> 360 static void 361 createCombinedParallelOp(Fortran::lower::AbstractConverter &converter, 362 Fortran::lower::pft::Evaluation &eval, 363 const Directive &directive) { 364 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 365 mlir::Location currentLocation = converter.getCurrentLocation(); 366 Fortran::lower::StatementContext stmtCtx; 367 llvm::ArrayRef<mlir::Type> argTy; 368 mlir::Value ifClauseOperand, numThreadsClauseOperand; 369 SmallVector<Value> allocatorOperands, allocateOperands; 370 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 371 const auto &opClauseList = 372 std::get<Fortran::parser::OmpClauseList>(directive.t); 373 // TODO: Handle the following clauses 374 // 1. default 375 // 2. copyin 376 // Note: rest of the clauses are handled when the inner operation is created 377 for (const Fortran::parser::OmpClause &clause : opClauseList.v) { 378 if (const auto &ifClause = 379 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 380 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 381 mlir::Value ifVal = fir::getBase( 382 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 383 ifClauseOperand = firOpBuilder.createConvert( 384 currentLocation, firOpBuilder.getI1Type(), ifVal); 385 } else if (const auto &numThreadsClause = 386 std::get_if<Fortran::parser::OmpClause::NumThreads>( 387 &clause.u)) { 388 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 389 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 390 } else if (const auto &procBindClause = 391 std::get_if<Fortran::parser::OmpClause::ProcBind>( 392 &clause.u)) { 393 procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause); 394 } 395 } 396 // Create and insert the operation. 397 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 398 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 399 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 400 /*reductions=*/nullptr, procBindKindAttr); 401 402 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, eval, 403 &opClauseList, /*iv=*/{}, 404 /*isCombined=*/true); 405 } 406 407 static void 408 genOMP(Fortran::lower::AbstractConverter &converter, 409 Fortran::lower::pft::Evaluation &eval, 410 const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 411 const auto &beginBlockDirective = 412 std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t); 413 const auto &blockDirective = 414 std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t); 415 const auto &endBlockDirective = 416 std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t); 417 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 418 mlir::Location currentLocation = converter.getCurrentLocation(); 419 420 Fortran::lower::StatementContext stmtCtx; 421 llvm::ArrayRef<mlir::Type> argTy; 422 mlir::Value ifClauseOperand, numThreadsClauseOperand, finalClauseOperand, 423 priorityClauseOperand; 424 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 425 SmallVector<Value> allocateOperands, allocatorOperands; 426 mlir::UnitAttr nowaitAttr, untiedAttr, mergeableAttr; 427 428 const auto &opClauseList = 429 std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t); 430 for (const auto &clause : opClauseList.v) { 431 if (const auto &ifClause = 432 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 433 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 434 mlir::Value ifVal = fir::getBase( 435 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 436 ifClauseOperand = firOpBuilder.createConvert( 437 currentLocation, firOpBuilder.getI1Type(), ifVal); 438 } else if (const auto &numThreadsClause = 439 std::get_if<Fortran::parser::OmpClause::NumThreads>( 440 &clause.u)) { 441 // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`. 442 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 443 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 444 } else if (const auto &procBindClause = 445 std::get_if<Fortran::parser::OmpClause::ProcBind>( 446 &clause.u)) { 447 procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause); 448 } else if (const auto &allocateClause = 449 std::get_if<Fortran::parser::OmpClause::Allocate>( 450 &clause.u)) { 451 genAllocateClause(converter, allocateClause->v, allocatorOperands, 452 allocateOperands); 453 } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) || 454 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 455 &clause.u)) { 456 // Privatisation clauses are handled elsewhere. 457 continue; 458 } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) { 459 // Nothing needs to be done for threads clause. 460 continue; 461 } else if (const auto &finalClause = 462 std::get_if<Fortran::parser::OmpClause::Final>(&clause.u)) { 463 mlir::Value finalVal = fir::getBase(converter.genExprValue( 464 *Fortran::semantics::GetExpr(finalClause->v), stmtCtx)); 465 finalClauseOperand = firOpBuilder.createConvert( 466 currentLocation, firOpBuilder.getI1Type(), finalVal); 467 } else if (std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u)) { 468 untiedAttr = firOpBuilder.getUnitAttr(); 469 } else if (std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u)) { 470 mergeableAttr = firOpBuilder.getUnitAttr(); 471 } else if (const auto &priorityClause = 472 std::get_if<Fortran::parser::OmpClause::Priority>( 473 &clause.u)) { 474 priorityClauseOperand = fir::getBase(converter.genExprValue( 475 *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx)); 476 } else { 477 TODO(currentLocation, "OpenMP Block construct clauses"); 478 } 479 } 480 481 for (const auto &clause : 482 std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) { 483 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 484 nowaitAttr = firOpBuilder.getUnitAttr(); 485 } 486 487 if (blockDirective.v == llvm::omp::OMPD_parallel) { 488 // Create and insert the operation. 489 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 490 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 491 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 492 /*reductions=*/nullptr, procBindKindAttr); 493 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, 494 eval, &opClauseList); 495 } else if (blockDirective.v == llvm::omp::OMPD_master) { 496 auto masterOp = 497 firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy); 498 createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation, eval); 499 } else if (blockDirective.v == llvm::omp::OMPD_single) { 500 auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>( 501 currentLocation, allocateOperands, allocatorOperands, nowaitAttr); 502 createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval); 503 } else if (blockDirective.v == llvm::omp::OMPD_ordered) { 504 auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>( 505 currentLocation, /*simd=*/nullptr); 506 createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation, 507 eval); 508 } else if (blockDirective.v == llvm::omp::OMPD_task) { 509 auto taskOp = firOpBuilder.create<mlir::omp::TaskOp>( 510 currentLocation, ifClauseOperand, finalClauseOperand, untiedAttr, 511 mergeableAttr, /*in_reduction_vars=*/ValueRange(), 512 /*in_reductions=*/nullptr, priorityClauseOperand, allocateOperands, 513 allocatorOperands); 514 createBodyOfOp(taskOp, converter, currentLocation, eval, &opClauseList); 515 } else { 516 TODO(converter.getCurrentLocation(), "Unhandled block directive"); 517 } 518 } 519 520 static void genOMP(Fortran::lower::AbstractConverter &converter, 521 Fortran::lower::pft::Evaluation &eval, 522 const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 523 524 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 525 mlir::Location currentLocation = converter.getCurrentLocation(); 526 llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars, 527 linearStepVars, reductionVars; 528 mlir::Value scheduleChunkClauseOperand; 529 mlir::Attribute scheduleClauseOperand, collapseClauseOperand, 530 noWaitClauseOperand, orderedClauseOperand, orderClauseOperand; 531 const auto &wsLoopOpClauseList = std::get<Fortran::parser::OmpClauseList>( 532 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t); 533 534 const auto ompDirective = 535 std::get<Fortran::parser::OmpLoopDirective>( 536 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t) 537 .v; 538 if (llvm::omp::OMPD_parallel_do == ompDirective) { 539 createCombinedParallelOp<Fortran::parser::OmpBeginLoopDirective>( 540 converter, eval, 541 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t)); 542 } else if (llvm::omp::OMPD_do != ompDirective) { 543 TODO(converter.getCurrentLocation(), "Construct enclosing do loop"); 544 } 545 546 // Collect the loops to collapse. 547 auto *doConstructEval = &eval.getFirstNestedEvaluation(); 548 549 std::int64_t collapseValue = 550 Fortran::lower::getCollapseValue(wsLoopOpClauseList); 551 std::size_t loopVarTypeSize = 0; 552 SmallVector<const Fortran::semantics::Symbol *> iv; 553 do { 554 auto *doLoop = &doConstructEval->getFirstNestedEvaluation(); 555 auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>(); 556 assert(doStmt && "Expected do loop to be in the nested evaluation"); 557 const auto &loopControl = 558 std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t); 559 const Fortran::parser::LoopControl::Bounds *bounds = 560 std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u); 561 assert(bounds && "Expected bounds for worksharing do loop"); 562 Fortran::lower::StatementContext stmtCtx; 563 lowerBound.push_back(fir::getBase(converter.genExprValue( 564 *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); 565 upperBound.push_back(fir::getBase(converter.genExprValue( 566 *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); 567 if (bounds->step) { 568 step.push_back(fir::getBase(converter.genExprValue( 569 *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); 570 } else { // If `step` is not present, assume it as `1`. 571 step.push_back(firOpBuilder.createIntegerConstant( 572 currentLocation, firOpBuilder.getIntegerType(32), 1)); 573 } 574 iv.push_back(bounds->name.thing.symbol); 575 loopVarTypeSize = std::max(loopVarTypeSize, 576 bounds->name.thing.symbol->GetUltimate().size()); 577 578 collapseValue--; 579 doConstructEval = 580 &*std::next(doConstructEval->getNestedEvaluations().begin()); 581 } while (collapseValue > 0); 582 583 for (const auto &clause : wsLoopOpClauseList.v) { 584 if (const auto &scheduleClause = 585 std::get_if<Fortran::parser::OmpClause::Schedule>(&clause.u)) { 586 if (const auto &chunkExpr = 587 std::get<std::optional<Fortran::parser::ScalarIntExpr>>( 588 scheduleClause->v.t)) { 589 if (const auto *expr = Fortran::semantics::GetExpr(*chunkExpr)) { 590 Fortran::lower::StatementContext stmtCtx; 591 scheduleChunkClauseOperand = 592 fir::getBase(converter.genExprValue(*expr, stmtCtx)); 593 } 594 } 595 } 596 } 597 598 // The types of lower bound, upper bound, and step are converted into the 599 // type of the loop variable if necessary. 600 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize); 601 for (unsigned it = 0; it < (unsigned)lowerBound.size(); it++) { 602 lowerBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType, 603 lowerBound[it]); 604 upperBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType, 605 upperBound[it]); 606 step[it] = 607 firOpBuilder.createConvert(currentLocation, loopVarType, step[it]); 608 } 609 610 // FIXME: Add support for following clauses: 611 // 1. linear 612 // 2. order 613 auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>( 614 currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars, 615 reductionVars, /*reductions=*/nullptr, 616 scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(), 617 scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr, 618 /*simd_modifier=*/nullptr, 619 collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(), 620 noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(), 621 orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(), 622 orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(), 623 /*inclusive=*/firOpBuilder.getUnitAttr()); 624 625 // Handle attribute based clauses. 626 for (const Fortran::parser::OmpClause &clause : wsLoopOpClauseList.v) { 627 if (const auto &orderedClause = 628 std::get_if<Fortran::parser::OmpClause::Ordered>(&clause.u)) { 629 if (orderedClause->v.has_value()) { 630 const auto *expr = Fortran::semantics::GetExpr(orderedClause->v); 631 const std::optional<std::int64_t> orderedClauseValue = 632 Fortran::evaluate::ToInt64(*expr); 633 wsLoopOp.ordered_valAttr( 634 firOpBuilder.getI64IntegerAttr(*orderedClauseValue)); 635 } else { 636 wsLoopOp.ordered_valAttr(firOpBuilder.getI64IntegerAttr(0)); 637 } 638 } else if (const auto &collapseClause = 639 std::get_if<Fortran::parser::OmpClause::Collapse>( 640 &clause.u)) { 641 const auto *expr = Fortran::semantics::GetExpr(collapseClause->v); 642 const std::optional<std::int64_t> collapseValue = 643 Fortran::evaluate::ToInt64(*expr); 644 wsLoopOp.collapse_valAttr(firOpBuilder.getI64IntegerAttr(*collapseValue)); 645 } else if (const auto &scheduleClause = 646 std::get_if<Fortran::parser::OmpClause::Schedule>( 647 &clause.u)) { 648 mlir::MLIRContext *context = firOpBuilder.getContext(); 649 const auto &scheduleType = scheduleClause->v; 650 const auto &scheduleKind = 651 std::get<Fortran::parser::OmpScheduleClause::ScheduleType>( 652 scheduleType.t); 653 switch (scheduleKind) { 654 case Fortran::parser::OmpScheduleClause::ScheduleType::Static: 655 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 656 context, omp::ClauseScheduleKind::Static)); 657 break; 658 case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic: 659 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 660 context, omp::ClauseScheduleKind::Dynamic)); 661 break; 662 case Fortran::parser::OmpScheduleClause::ScheduleType::Guided: 663 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 664 context, omp::ClauseScheduleKind::Guided)); 665 break; 666 case Fortran::parser::OmpScheduleClause::ScheduleType::Auto: 667 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 668 context, omp::ClauseScheduleKind::Auto)); 669 break; 670 case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime: 671 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 672 context, omp::ClauseScheduleKind::Runtime)); 673 break; 674 } 675 } 676 } 677 // In FORTRAN `nowait` clause occur at the end of `omp do` directive. 678 // i.e 679 // !$omp do 680 // <...> 681 // !$omp end do nowait 682 if (const auto &endClauseList = 683 std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>( 684 loopConstruct.t)) { 685 const auto &clauseList = 686 std::get<Fortran::parser::OmpClauseList>((*endClauseList).t); 687 for (const Fortran::parser::OmpClause &clause : clauseList.v) 688 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 689 wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr()); 690 } 691 692 createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, eval, 693 &wsLoopOpClauseList, iv); 694 } 695 696 static void 697 genOMP(Fortran::lower::AbstractConverter &converter, 698 Fortran::lower::pft::Evaluation &eval, 699 const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) { 700 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 701 mlir::Location currentLocation = converter.getCurrentLocation(); 702 std::string name; 703 const Fortran::parser::OmpCriticalDirective &cd = 704 std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t); 705 if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) { 706 name = 707 std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString(); 708 } 709 710 uint64_t hint = 0; 711 const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t); 712 for (const Fortran::parser::OmpClause &clause : clauseList.v) 713 if (auto hintClause = 714 std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) { 715 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 716 hint = *Fortran::evaluate::ToInt64(*expr); 717 break; 718 } 719 720 mlir::omp::CriticalOp criticalOp = [&]() { 721 if (name.empty()) { 722 return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation, 723 FlatSymbolRefAttr()); 724 } else { 725 mlir::ModuleOp module = firOpBuilder.getModule(); 726 mlir::OpBuilder modBuilder(module.getBodyRegion()); 727 auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name); 728 if (!global) 729 global = modBuilder.create<mlir::omp::CriticalDeclareOp>( 730 currentLocation, name, hint); 731 return firOpBuilder.create<mlir::omp::CriticalOp>( 732 currentLocation, mlir::FlatSymbolRefAttr::get( 733 firOpBuilder.getContext(), global.sym_name())); 734 } 735 }(); 736 createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation, eval); 737 } 738 739 static void 740 genOMP(Fortran::lower::AbstractConverter &converter, 741 Fortran::lower::pft::Evaluation &eval, 742 const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 743 744 auto &firOpBuilder = converter.getFirOpBuilder(); 745 auto currentLocation = converter.getCurrentLocation(); 746 mlir::omp::SectionOp sectionOp = 747 firOpBuilder.create<mlir::omp::SectionOp>(currentLocation); 748 createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation, eval); 749 } 750 751 // TODO: Add support for reduction 752 static void 753 genOMP(Fortran::lower::AbstractConverter &converter, 754 Fortran::lower::pft::Evaluation &eval, 755 const Fortran::parser::OpenMPSectionsConstruct §ionsConstruct) { 756 auto &firOpBuilder = converter.getFirOpBuilder(); 757 auto currentLocation = converter.getCurrentLocation(); 758 SmallVector<Value> reductionVars, allocateOperands, allocatorOperands; 759 mlir::UnitAttr noWaitClauseOperand; 760 const auto §ionsClauseList = std::get<Fortran::parser::OmpClauseList>( 761 std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t) 762 .t); 763 for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) { 764 765 // Reduction Clause 766 if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) { 767 TODO(currentLocation, "OMPC_Reduction"); 768 769 // Allocate clause 770 } else if (const auto &allocateClause = 771 std::get_if<Fortran::parser::OmpClause::Allocate>( 772 &clause.u)) { 773 genAllocateClause(converter, allocateClause->v, allocatorOperands, 774 allocateOperands); 775 } 776 } 777 const auto &endSectionsClauseList = 778 std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t); 779 const auto &clauseList = 780 std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t); 781 for (const auto &clause : clauseList.v) { 782 // Nowait clause 783 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) { 784 noWaitClauseOperand = firOpBuilder.getUnitAttr(); 785 } 786 } 787 788 llvm::omp::Directive dir = 789 std::get<Fortran::parser::OmpSectionsDirective>( 790 std::get<Fortran::parser::OmpBeginSectionsDirective>( 791 sectionsConstruct.t) 792 .t) 793 .v; 794 795 // Parallel Sections Construct 796 if (dir == llvm::omp::Directive::OMPD_parallel_sections) { 797 createCombinedParallelOp<Fortran::parser::OmpBeginSectionsDirective>( 798 converter, eval, 799 std::get<Fortran::parser::OmpBeginSectionsDirective>( 800 sectionsConstruct.t)); 801 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 802 currentLocation, /*reduction_vars*/ ValueRange(), 803 /*reductions=*/nullptr, allocateOperands, allocatorOperands, 804 /*nowait=*/nullptr); 805 createBodyOfOp(sectionsOp, converter, currentLocation, eval); 806 807 // Sections Construct 808 } else if (dir == llvm::omp::Directive::OMPD_sections) { 809 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 810 currentLocation, reductionVars, /*reductions = */ nullptr, 811 allocateOperands, allocatorOperands, noWaitClauseOperand); 812 createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation, 813 eval); 814 } 815 } 816 817 static void genOmpAtomicHintAndMemoryOrderClauses( 818 Fortran::lower::AbstractConverter &converter, 819 const Fortran::parser::OmpAtomicClauseList &clauseList, 820 mlir::IntegerAttr &hint, 821 mlir::omp::ClauseMemoryOrderKindAttr &memory_order) { 822 auto &firOpBuilder = converter.getFirOpBuilder(); 823 for (const auto &clause : clauseList.v) { 824 if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) { 825 if (auto hintClause = 826 std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) { 827 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 828 uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr); 829 hint = firOpBuilder.getI64IntegerAttr(hintExprValue); 830 } 831 } else if (auto ompMemoryOrderClause = 832 std::get_if<Fortran::parser::OmpMemoryOrderClause>( 833 &clause.u)) { 834 if (std::get_if<Fortran::parser::OmpClause::Acquire>( 835 &ompMemoryOrderClause->v.u)) { 836 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 837 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire); 838 } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>( 839 &ompMemoryOrderClause->v.u)) { 840 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 841 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed); 842 } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>( 843 &ompMemoryOrderClause->v.u)) { 844 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 845 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst); 846 } else if (std::get_if<Fortran::parser::OmpClause::Release>( 847 &ompMemoryOrderClause->v.u)) { 848 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 849 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release); 850 } 851 } 852 } 853 } 854 855 static void 856 genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter, 857 Fortran::lower::pft::Evaluation &eval, 858 const Fortran::parser::OmpAtomicWrite &atomicWrite) { 859 auto &firOpBuilder = converter.getFirOpBuilder(); 860 auto currentLocation = converter.getCurrentLocation(); 861 // Get the value and address of atomic write operands. 862 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 863 std::get<2>(atomicWrite.t); 864 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 865 std::get<0>(atomicWrite.t); 866 const auto &assignmentStmtExpr = 867 std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t); 868 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 869 std::get<3>(atomicWrite.t).statement.t); 870 Fortran::lower::StatementContext stmtCtx; 871 mlir::Value value = fir::getBase(converter.genExprValue( 872 *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); 873 mlir::Value address = fir::getBase(converter.genExprAddr( 874 *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); 875 // If no hint clause is specified, the effect is as if 876 // hint(omp_sync_hint_none) had been specified. 877 mlir::IntegerAttr hint = nullptr; 878 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 879 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 880 memory_order); 881 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 882 memory_order); 883 firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value, 884 hint, memory_order); 885 } 886 887 static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter, 888 Fortran::lower::pft::Evaluation &eval, 889 const Fortran::parser::OmpAtomicRead &atomicRead) { 890 auto &firOpBuilder = converter.getFirOpBuilder(); 891 auto currentLocation = converter.getCurrentLocation(); 892 // Get the address of atomic read operands. 893 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 894 std::get<2>(atomicRead.t); 895 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 896 std::get<0>(atomicRead.t); 897 const auto &assignmentStmtExpr = 898 std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t); 899 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 900 std::get<3>(atomicRead.t).statement.t); 901 Fortran::lower::StatementContext stmtCtx; 902 mlir::Value from_address = fir::getBase(converter.genExprAddr( 903 *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); 904 mlir::Value to_address = fir::getBase(converter.genExprAddr( 905 *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); 906 // If no hint clause is specified, the effect is as if 907 // hint(omp_sync_hint_none) had been specified. 908 mlir::IntegerAttr hint = nullptr; 909 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 910 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 911 memory_order); 912 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 913 memory_order); 914 firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address, 915 to_address, hint, memory_order); 916 } 917 918 static void 919 genOMP(Fortran::lower::AbstractConverter &converter, 920 Fortran::lower::pft::Evaluation &eval, 921 const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 922 std::visit(Fortran::common::visitors{ 923 [&](const Fortran::parser::OmpAtomicRead &atomicRead) { 924 genOmpAtomicRead(converter, eval, atomicRead); 925 }, 926 [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) { 927 genOmpAtomicWrite(converter, eval, atomicWrite); 928 }, 929 [&](const auto &) { 930 TODO(converter.getCurrentLocation(), 931 "Atomic update & capture"); 932 }, 933 }, 934 atomicConstruct.u); 935 } 936 937 void Fortran::lower::genOpenMPConstruct( 938 Fortran::lower::AbstractConverter &converter, 939 Fortran::lower::pft::Evaluation &eval, 940 const Fortran::parser::OpenMPConstruct &ompConstruct) { 941 942 std::visit( 943 common::visitors{ 944 [&](const Fortran::parser::OpenMPStandaloneConstruct 945 &standaloneConstruct) { 946 genOMP(converter, eval, standaloneConstruct); 947 }, 948 [&](const Fortran::parser::OpenMPSectionsConstruct 949 §ionsConstruct) { 950 genOMP(converter, eval, sectionsConstruct); 951 }, 952 [&](const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 953 genOMP(converter, eval, sectionConstruct); 954 }, 955 [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 956 genOMP(converter, eval, loopConstruct); 957 }, 958 [&](const Fortran::parser::OpenMPDeclarativeAllocate 959 &execAllocConstruct) { 960 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 961 }, 962 [&](const Fortran::parser::OpenMPExecutableAllocate 963 &execAllocConstruct) { 964 TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate"); 965 }, 966 [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 967 genOMP(converter, eval, blockConstruct); 968 }, 969 [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 970 genOMP(converter, eval, atomicConstruct); 971 }, 972 [&](const Fortran::parser::OpenMPCriticalConstruct 973 &criticalConstruct) { 974 genOMP(converter, eval, criticalConstruct); 975 }, 976 }, 977 ompConstruct.u); 978 } 979 980 void Fortran::lower::genOpenMPDeclarativeConstruct( 981 Fortran::lower::AbstractConverter &converter, 982 Fortran::lower::pft::Evaluation &eval, 983 const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) { 984 985 std::visit( 986 common::visitors{ 987 [&](const Fortran::parser::OpenMPDeclarativeAllocate 988 &declarativeAllocate) { 989 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 990 }, 991 [&](const Fortran::parser::OpenMPDeclareReductionConstruct 992 &declareReductionConstruct) { 993 TODO(converter.getCurrentLocation(), 994 "OpenMPDeclareReductionConstruct"); 995 }, 996 [&](const Fortran::parser::OpenMPDeclareSimdConstruct 997 &declareSimdConstruct) { 998 TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct"); 999 }, 1000 [&](const Fortran::parser::OpenMPDeclareTargetConstruct 1001 &declareTargetConstruct) { 1002 TODO(converter.getCurrentLocation(), 1003 "OpenMPDeclareTargetConstruct"); 1004 }, 1005 [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) { 1006 TODO(converter.getCurrentLocation(), "OpenMPThreadprivate"); 1007 }, 1008 }, 1009 ompDeclConstruct.u); 1010 } 1011