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.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 auto &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 auto &block = op.getRegion().back(); 209 firOpBuilder.setInsertionPointToStart(&block); 210 211 if (eval.lowerAsUnstructured()) 212 createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations()); 213 214 // Ensure the block is well-formed by inserting terminators. 215 if constexpr (std::is_same_v<Op, omp::WsLoopOp>) { 216 mlir::ValueRange results; 217 firOpBuilder.create<mlir::omp::YieldOp>(loc, results); 218 } else { 219 firOpBuilder.create<mlir::omp::TerminatorOp>(loc); 220 } 221 222 // Reset the insertion point to the start of the first block. 223 firOpBuilder.setInsertionPointToStart(&block); 224 // Handle privatization. Do not privatize if this is the outer operation. 225 if (clauses && !outerCombined) 226 privatizeVars(converter, *clauses); 227 } 228 229 static void genOMP(Fortran::lower::AbstractConverter &converter, 230 Fortran::lower::pft::Evaluation &eval, 231 const Fortran::parser::OpenMPSimpleStandaloneConstruct 232 &simpleStandaloneConstruct) { 233 const auto &directive = 234 std::get<Fortran::parser::OmpSimpleStandaloneDirective>( 235 simpleStandaloneConstruct.t); 236 switch (directive.v) { 237 default: 238 break; 239 case llvm::omp::Directive::OMPD_barrier: 240 converter.getFirOpBuilder().create<mlir::omp::BarrierOp>( 241 converter.getCurrentLocation()); 242 break; 243 case llvm::omp::Directive::OMPD_taskwait: 244 converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>( 245 converter.getCurrentLocation()); 246 break; 247 case llvm::omp::Directive::OMPD_taskyield: 248 converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>( 249 converter.getCurrentLocation()); 250 break; 251 case llvm::omp::Directive::OMPD_target_enter_data: 252 TODO(converter.getCurrentLocation(), "OMPD_target_enter_data"); 253 case llvm::omp::Directive::OMPD_target_exit_data: 254 TODO(converter.getCurrentLocation(), "OMPD_target_exit_data"); 255 case llvm::omp::Directive::OMPD_target_update: 256 TODO(converter.getCurrentLocation(), "OMPD_target_update"); 257 case llvm::omp::Directive::OMPD_ordered: 258 TODO(converter.getCurrentLocation(), "OMPD_ordered"); 259 } 260 } 261 262 static void 263 genAllocateClause(Fortran::lower::AbstractConverter &converter, 264 const Fortran::parser::OmpAllocateClause &ompAllocateClause, 265 SmallVector<Value> &allocatorOperands, 266 SmallVector<Value> &allocateOperands) { 267 auto &firOpBuilder = converter.getFirOpBuilder(); 268 auto currentLocation = converter.getCurrentLocation(); 269 Fortran::lower::StatementContext stmtCtx; 270 271 mlir::Value allocatorOperand; 272 const Fortran::parser::OmpObjectList &ompObjectList = 273 std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t); 274 const auto &allocatorValue = 275 std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>( 276 ompAllocateClause.t); 277 // Check if allocate clause has allocator specified. If so, add it 278 // to list of allocators, otherwise, add default allocator to 279 // list of allocators. 280 if (allocatorValue) { 281 allocatorOperand = fir::getBase(converter.genExprValue( 282 *Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx)); 283 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 284 allocatorOperand); 285 } else { 286 allocatorOperand = firOpBuilder.createIntegerConstant( 287 currentLocation, firOpBuilder.getI32Type(), 1); 288 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 289 allocatorOperand); 290 } 291 genObjectList(ompObjectList, converter, allocateOperands); 292 } 293 294 static void 295 genOMP(Fortran::lower::AbstractConverter &converter, 296 Fortran::lower::pft::Evaluation &eval, 297 const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) { 298 std::visit( 299 Fortran::common::visitors{ 300 [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct 301 &simpleStandaloneConstruct) { 302 genOMP(converter, eval, simpleStandaloneConstruct); 303 }, 304 [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) { 305 SmallVector<Value, 4> operandRange; 306 if (const auto &ompObjectList = 307 std::get<std::optional<Fortran::parser::OmpObjectList>>( 308 flushConstruct.t)) 309 genObjectList(*ompObjectList, converter, operandRange); 310 const auto &memOrderClause = std::get<std::optional< 311 std::list<Fortran::parser::OmpMemoryOrderClause>>>( 312 flushConstruct.t); 313 if (memOrderClause.has_value() && memOrderClause->size() > 0) 314 TODO(converter.getCurrentLocation(), 315 "Handle OmpMemoryOrderClause"); 316 converter.getFirOpBuilder().create<mlir::omp::FlushOp>( 317 converter.getCurrentLocation(), operandRange); 318 }, 319 [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) { 320 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 321 }, 322 [&](const Fortran::parser::OpenMPCancellationPointConstruct 323 &cancellationPointConstruct) { 324 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 325 }, 326 }, 327 standaloneConstruct.u); 328 } 329 330 static omp::ClauseProcBindKindAttr genProcBindKindAttr( 331 fir::FirOpBuilder &firOpBuilder, 332 const Fortran::parser::OmpClause::ProcBind *procBindClause) { 333 omp::ClauseProcBindKind pbKind; 334 switch (procBindClause->v.v) { 335 case Fortran::parser::OmpProcBindClause::Type::Master: 336 pbKind = omp::ClauseProcBindKind::Master; 337 break; 338 case Fortran::parser::OmpProcBindClause::Type::Close: 339 pbKind = omp::ClauseProcBindKind::Close; 340 break; 341 case Fortran::parser::OmpProcBindClause::Type::Spread: 342 pbKind = omp::ClauseProcBindKind::Spread; 343 break; 344 case Fortran::parser::OmpProcBindClause::Type::Primary: 345 pbKind = omp::ClauseProcBindKind::Primary; 346 break; 347 } 348 return omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind); 349 } 350 351 /* When parallel is used in a combined construct, then use this function to 352 * create the parallel operation. It handles the parallel specific clauses 353 * and leaves the rest for handling at the inner operations. 354 * TODO: Refactor clause handling 355 */ 356 template <typename Directive> 357 static void 358 createCombinedParallelOp(Fortran::lower::AbstractConverter &converter, 359 Fortran::lower::pft::Evaluation &eval, 360 const Directive &directive) { 361 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 362 mlir::Location currentLocation = converter.getCurrentLocation(); 363 Fortran::lower::StatementContext stmtCtx; 364 llvm::ArrayRef<mlir::Type> argTy; 365 mlir::Value ifClauseOperand, numThreadsClauseOperand; 366 SmallVector<Value> allocatorOperands, allocateOperands; 367 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 368 const auto &opClauseList = 369 std::get<Fortran::parser::OmpClauseList>(directive.t); 370 // TODO: Handle the following clauses 371 // 1. default 372 // 2. copyin 373 // Note: rest of the clauses are handled when the inner operation is created 374 for (const Fortran::parser::OmpClause &clause : opClauseList.v) { 375 if (const auto &ifClause = 376 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 377 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 378 mlir::Value ifVal = fir::getBase( 379 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 380 ifClauseOperand = firOpBuilder.createConvert( 381 currentLocation, firOpBuilder.getI1Type(), ifVal); 382 } else if (const auto &numThreadsClause = 383 std::get_if<Fortran::parser::OmpClause::NumThreads>( 384 &clause.u)) { 385 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 386 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 387 } else if (const auto &procBindClause = 388 std::get_if<Fortran::parser::OmpClause::ProcBind>( 389 &clause.u)) { 390 procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause); 391 } 392 } 393 // Create and insert the operation. 394 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 395 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 396 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 397 /*reductions=*/nullptr, procBindKindAttr); 398 399 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, eval, 400 &opClauseList, /*iv=*/{}, 401 /*isCombined=*/true); 402 } 403 404 static void 405 genOMP(Fortran::lower::AbstractConverter &converter, 406 Fortran::lower::pft::Evaluation &eval, 407 const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 408 const auto &beginBlockDirective = 409 std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t); 410 const auto &blockDirective = 411 std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t); 412 const auto &endBlockDirective = 413 std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t); 414 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 415 mlir::Location currentLocation = converter.getCurrentLocation(); 416 417 Fortran::lower::StatementContext stmtCtx; 418 llvm::ArrayRef<mlir::Type> argTy; 419 mlir::Value ifClauseOperand, numThreadsClauseOperand, finalClauseOperand, 420 priorityClauseOperand; 421 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 422 SmallVector<Value> allocateOperands, allocatorOperands; 423 mlir::UnitAttr nowaitAttr, untiedAttr, mergeableAttr; 424 425 const auto &opClauseList = 426 std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t); 427 for (const auto &clause : opClauseList.v) { 428 if (const auto &ifClause = 429 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 430 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 431 mlir::Value ifVal = fir::getBase( 432 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 433 ifClauseOperand = firOpBuilder.createConvert( 434 currentLocation, firOpBuilder.getI1Type(), ifVal); 435 } else if (const auto &numThreadsClause = 436 std::get_if<Fortran::parser::OmpClause::NumThreads>( 437 &clause.u)) { 438 // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`. 439 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 440 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 441 } else if (const auto &procBindClause = 442 std::get_if<Fortran::parser::OmpClause::ProcBind>( 443 &clause.u)) { 444 procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause); 445 } else if (const auto &allocateClause = 446 std::get_if<Fortran::parser::OmpClause::Allocate>( 447 &clause.u)) { 448 genAllocateClause(converter, allocateClause->v, allocatorOperands, 449 allocateOperands); 450 } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) || 451 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 452 &clause.u)) { 453 // Privatisation clauses are handled elsewhere. 454 continue; 455 } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) { 456 // Nothing needs to be done for threads clause. 457 continue; 458 } else if (const auto &finalClause = 459 std::get_if<Fortran::parser::OmpClause::Final>(&clause.u)) { 460 mlir::Value finalVal = fir::getBase(converter.genExprValue( 461 *Fortran::semantics::GetExpr(finalClause->v), stmtCtx)); 462 finalClauseOperand = firOpBuilder.createConvert( 463 currentLocation, firOpBuilder.getI1Type(), finalVal); 464 } else if (std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u)) { 465 untiedAttr = firOpBuilder.getUnitAttr(); 466 } else if (std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u)) { 467 mergeableAttr = firOpBuilder.getUnitAttr(); 468 } else if (const auto &priorityClause = 469 std::get_if<Fortran::parser::OmpClause::Priority>( 470 &clause.u)) { 471 priorityClauseOperand = fir::getBase(converter.genExprValue( 472 *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx)); 473 } else { 474 TODO(currentLocation, "OpenMP Block construct clauses"); 475 } 476 } 477 478 for (const auto &clause : 479 std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) { 480 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 481 nowaitAttr = firOpBuilder.getUnitAttr(); 482 } 483 484 if (blockDirective.v == llvm::omp::OMPD_parallel) { 485 // Create and insert the operation. 486 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 487 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 488 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 489 /*reductions=*/nullptr, procBindKindAttr); 490 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, 491 eval, &opClauseList); 492 } else if (blockDirective.v == llvm::omp::OMPD_master) { 493 auto masterOp = 494 firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy); 495 createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation, eval); 496 } else if (blockDirective.v == llvm::omp::OMPD_single) { 497 auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>( 498 currentLocation, allocateOperands, allocatorOperands, nowaitAttr); 499 createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval); 500 } else if (blockDirective.v == llvm::omp::OMPD_ordered) { 501 auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>( 502 currentLocation, /*simd=*/nullptr); 503 createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation, 504 eval); 505 } else if (blockDirective.v == llvm::omp::OMPD_task) { 506 auto taskOp = firOpBuilder.create<mlir::omp::TaskOp>( 507 currentLocation, ifClauseOperand, finalClauseOperand, untiedAttr, 508 mergeableAttr, /*in_reduction_vars=*/ValueRange(), 509 /*in_reductions=*/nullptr, priorityClauseOperand, allocateOperands, 510 allocatorOperands); 511 createBodyOfOp(taskOp, converter, currentLocation, eval, &opClauseList); 512 } else { 513 TODO(converter.getCurrentLocation(), "Unhandled block directive"); 514 } 515 } 516 517 static void genOMP(Fortran::lower::AbstractConverter &converter, 518 Fortran::lower::pft::Evaluation &eval, 519 const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 520 521 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 522 mlir::Location currentLocation = converter.getCurrentLocation(); 523 llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars, 524 linearStepVars, reductionVars; 525 mlir::Value scheduleChunkClauseOperand; 526 mlir::Attribute scheduleClauseOperand, collapseClauseOperand, 527 noWaitClauseOperand, orderedClauseOperand, orderClauseOperand; 528 const auto &wsLoopOpClauseList = std::get<Fortran::parser::OmpClauseList>( 529 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t); 530 531 const auto ompDirective = 532 std::get<Fortran::parser::OmpLoopDirective>( 533 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t) 534 .v; 535 if (llvm::omp::OMPD_parallel_do == ompDirective) { 536 createCombinedParallelOp<Fortran::parser::OmpBeginLoopDirective>( 537 converter, eval, 538 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t)); 539 } else if (llvm::omp::OMPD_do != ompDirective) { 540 TODO(converter.getCurrentLocation(), "Construct enclosing do loop"); 541 } 542 543 // Collect the loops to collapse. 544 auto *doConstructEval = &eval.getFirstNestedEvaluation(); 545 546 std::int64_t collapseValue = 547 Fortran::lower::getCollapseValue(wsLoopOpClauseList); 548 std::size_t loopVarTypeSize = 0; 549 SmallVector<const Fortran::semantics::Symbol *> iv; 550 do { 551 auto *doLoop = &doConstructEval->getFirstNestedEvaluation(); 552 auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>(); 553 assert(doStmt && "Expected do loop to be in the nested evaluation"); 554 const auto &loopControl = 555 std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t); 556 const Fortran::parser::LoopControl::Bounds *bounds = 557 std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u); 558 assert(bounds && "Expected bounds for worksharing do loop"); 559 Fortran::lower::StatementContext stmtCtx; 560 lowerBound.push_back(fir::getBase(converter.genExprValue( 561 *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); 562 upperBound.push_back(fir::getBase(converter.genExprValue( 563 *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); 564 if (bounds->step) { 565 step.push_back(fir::getBase(converter.genExprValue( 566 *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); 567 } else { // If `step` is not present, assume it as `1`. 568 step.push_back(firOpBuilder.createIntegerConstant( 569 currentLocation, firOpBuilder.getIntegerType(32), 1)); 570 } 571 iv.push_back(bounds->name.thing.symbol); 572 loopVarTypeSize = std::max(loopVarTypeSize, 573 bounds->name.thing.symbol->GetUltimate().size()); 574 575 collapseValue--; 576 doConstructEval = 577 &*std::next(doConstructEval->getNestedEvaluations().begin()); 578 } while (collapseValue > 0); 579 580 // The types of lower bound, upper bound, and step are converted into the 581 // type of the loop variable if necessary. 582 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize); 583 for (unsigned it = 0; it < (unsigned)lowerBound.size(); it++) { 584 lowerBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType, 585 lowerBound[it]); 586 upperBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType, 587 upperBound[it]); 588 step[it] = 589 firOpBuilder.createConvert(currentLocation, loopVarType, step[it]); 590 } 591 592 // FIXME: Add support for following clauses: 593 // 1. linear 594 // 2. order 595 // 3. schedule (with chunk) 596 auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>( 597 currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars, 598 reductionVars, /*reductions=*/nullptr, 599 scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(), 600 scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr, 601 /*simd_modifier=*/nullptr, 602 collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(), 603 noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(), 604 orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(), 605 orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(), 606 /*inclusive=*/firOpBuilder.getUnitAttr()); 607 608 // Handle attribute based clauses. 609 for (const Fortran::parser::OmpClause &clause : wsLoopOpClauseList.v) { 610 if (const auto &orderedClause = 611 std::get_if<Fortran::parser::OmpClause::Ordered>(&clause.u)) { 612 if (orderedClause->v.has_value()) { 613 const auto *expr = Fortran::semantics::GetExpr(orderedClause->v); 614 const std::optional<std::int64_t> orderedClauseValue = 615 Fortran::evaluate::ToInt64(*expr); 616 wsLoopOp.ordered_valAttr( 617 firOpBuilder.getI64IntegerAttr(*orderedClauseValue)); 618 } else { 619 wsLoopOp.ordered_valAttr(firOpBuilder.getI64IntegerAttr(0)); 620 } 621 } else if (const auto &collapseClause = 622 std::get_if<Fortran::parser::OmpClause::Collapse>( 623 &clause.u)) { 624 const auto *expr = Fortran::semantics::GetExpr(collapseClause->v); 625 const std::optional<std::int64_t> collapseValue = 626 Fortran::evaluate::ToInt64(*expr); 627 wsLoopOp.collapse_valAttr(firOpBuilder.getI64IntegerAttr(*collapseValue)); 628 } else if (const auto &scheduleClause = 629 std::get_if<Fortran::parser::OmpClause::Schedule>( 630 &clause.u)) { 631 mlir::MLIRContext *context = firOpBuilder.getContext(); 632 const auto &scheduleType = scheduleClause->v; 633 const auto &scheduleKind = 634 std::get<Fortran::parser::OmpScheduleClause::ScheduleType>( 635 scheduleType.t); 636 switch (scheduleKind) { 637 case Fortran::parser::OmpScheduleClause::ScheduleType::Static: 638 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 639 context, omp::ClauseScheduleKind::Static)); 640 break; 641 case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic: 642 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 643 context, omp::ClauseScheduleKind::Dynamic)); 644 break; 645 case Fortran::parser::OmpScheduleClause::ScheduleType::Guided: 646 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 647 context, omp::ClauseScheduleKind::Guided)); 648 break; 649 case Fortran::parser::OmpScheduleClause::ScheduleType::Auto: 650 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 651 context, omp::ClauseScheduleKind::Auto)); 652 break; 653 case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime: 654 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 655 context, omp::ClauseScheduleKind::Runtime)); 656 break; 657 } 658 } 659 } 660 // In FORTRAN `nowait` clause occur at the end of `omp do` directive. 661 // i.e 662 // !$omp do 663 // <...> 664 // !$omp end do nowait 665 if (const auto &endClauseList = 666 std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>( 667 loopConstruct.t)) { 668 const auto &clauseList = 669 std::get<Fortran::parser::OmpClauseList>((*endClauseList).t); 670 for (const Fortran::parser::OmpClause &clause : clauseList.v) 671 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 672 wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr()); 673 } 674 675 createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, eval, 676 &wsLoopOpClauseList, iv); 677 } 678 679 static void 680 genOMP(Fortran::lower::AbstractConverter &converter, 681 Fortran::lower::pft::Evaluation &eval, 682 const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) { 683 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 684 mlir::Location currentLocation = converter.getCurrentLocation(); 685 std::string name; 686 const Fortran::parser::OmpCriticalDirective &cd = 687 std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t); 688 if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) { 689 name = 690 std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString(); 691 } 692 693 uint64_t hint = 0; 694 const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t); 695 for (const Fortran::parser::OmpClause &clause : clauseList.v) 696 if (auto hintClause = 697 std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) { 698 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 699 hint = *Fortran::evaluate::ToInt64(*expr); 700 break; 701 } 702 703 mlir::omp::CriticalOp criticalOp = [&]() { 704 if (name.empty()) { 705 return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation, 706 FlatSymbolRefAttr()); 707 } else { 708 mlir::ModuleOp module = firOpBuilder.getModule(); 709 mlir::OpBuilder modBuilder(module.getBodyRegion()); 710 auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name); 711 if (!global) 712 global = modBuilder.create<mlir::omp::CriticalDeclareOp>( 713 currentLocation, name, hint); 714 return firOpBuilder.create<mlir::omp::CriticalOp>( 715 currentLocation, mlir::FlatSymbolRefAttr::get( 716 firOpBuilder.getContext(), global.sym_name())); 717 } 718 }(); 719 createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation, eval); 720 } 721 722 static void 723 genOMP(Fortran::lower::AbstractConverter &converter, 724 Fortran::lower::pft::Evaluation &eval, 725 const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 726 727 auto &firOpBuilder = converter.getFirOpBuilder(); 728 auto currentLocation = converter.getCurrentLocation(); 729 mlir::omp::SectionOp sectionOp = 730 firOpBuilder.create<mlir::omp::SectionOp>(currentLocation); 731 createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation, eval); 732 } 733 734 // TODO: Add support for reduction 735 static void 736 genOMP(Fortran::lower::AbstractConverter &converter, 737 Fortran::lower::pft::Evaluation &eval, 738 const Fortran::parser::OpenMPSectionsConstruct §ionsConstruct) { 739 auto &firOpBuilder = converter.getFirOpBuilder(); 740 auto currentLocation = converter.getCurrentLocation(); 741 SmallVector<Value> reductionVars, allocateOperands, allocatorOperands; 742 mlir::UnitAttr noWaitClauseOperand; 743 const auto §ionsClauseList = std::get<Fortran::parser::OmpClauseList>( 744 std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t) 745 .t); 746 for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) { 747 748 // Reduction Clause 749 if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) { 750 TODO(currentLocation, "OMPC_Reduction"); 751 752 // Allocate clause 753 } else if (const auto &allocateClause = 754 std::get_if<Fortran::parser::OmpClause::Allocate>( 755 &clause.u)) { 756 genAllocateClause(converter, allocateClause->v, allocatorOperands, 757 allocateOperands); 758 } 759 } 760 const auto &endSectionsClauseList = 761 std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t); 762 const auto &clauseList = 763 std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t); 764 for (const auto &clause : clauseList.v) { 765 // Nowait clause 766 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) { 767 noWaitClauseOperand = firOpBuilder.getUnitAttr(); 768 } 769 } 770 771 llvm::omp::Directive dir = 772 std::get<Fortran::parser::OmpSectionsDirective>( 773 std::get<Fortran::parser::OmpBeginSectionsDirective>( 774 sectionsConstruct.t) 775 .t) 776 .v; 777 778 // Parallel Sections Construct 779 if (dir == llvm::omp::Directive::OMPD_parallel_sections) { 780 createCombinedParallelOp<Fortran::parser::OmpBeginSectionsDirective>( 781 converter, eval, 782 std::get<Fortran::parser::OmpBeginSectionsDirective>( 783 sectionsConstruct.t)); 784 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 785 currentLocation, /*reduction_vars*/ ValueRange(), 786 /*reductions=*/nullptr, allocateOperands, allocatorOperands, 787 /*nowait=*/nullptr); 788 createBodyOfOp(sectionsOp, converter, currentLocation, eval); 789 790 // Sections Construct 791 } else if (dir == llvm::omp::Directive::OMPD_sections) { 792 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 793 currentLocation, reductionVars, /*reductions = */ nullptr, 794 allocateOperands, allocatorOperands, noWaitClauseOperand); 795 createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation, 796 eval); 797 } 798 } 799 800 static void genOmpAtomicHintAndMemoryOrderClauses( 801 Fortran::lower::AbstractConverter &converter, 802 const Fortran::parser::OmpAtomicClauseList &clauseList, 803 mlir::IntegerAttr &hint, 804 mlir::omp::ClauseMemoryOrderKindAttr &memory_order) { 805 auto &firOpBuilder = converter.getFirOpBuilder(); 806 for (const auto &clause : clauseList.v) { 807 if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) { 808 if (auto hintClause = 809 std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) { 810 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 811 uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr); 812 hint = firOpBuilder.getI64IntegerAttr(hintExprValue); 813 } 814 } else if (auto ompMemoryOrderClause = 815 std::get_if<Fortran::parser::OmpMemoryOrderClause>( 816 &clause.u)) { 817 if (std::get_if<Fortran::parser::OmpClause::Acquire>( 818 &ompMemoryOrderClause->v.u)) { 819 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 820 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire); 821 } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>( 822 &ompMemoryOrderClause->v.u)) { 823 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 824 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed); 825 } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>( 826 &ompMemoryOrderClause->v.u)) { 827 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 828 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst); 829 } else if (std::get_if<Fortran::parser::OmpClause::Release>( 830 &ompMemoryOrderClause->v.u)) { 831 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 832 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release); 833 } 834 } 835 } 836 } 837 838 static void 839 genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter, 840 Fortran::lower::pft::Evaluation &eval, 841 const Fortran::parser::OmpAtomicWrite &atomicWrite) { 842 auto &firOpBuilder = converter.getFirOpBuilder(); 843 auto currentLocation = converter.getCurrentLocation(); 844 mlir::Value address; 845 // If no hint clause is specified, the effect is as if 846 // hint(omp_sync_hint_none) had been specified. 847 mlir::IntegerAttr hint = nullptr; 848 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 849 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 850 std::get<2>(atomicWrite.t); 851 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 852 std::get<0>(atomicWrite.t); 853 const auto &assignmentStmtExpr = 854 std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t); 855 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 856 std::get<3>(atomicWrite.t).statement.t); 857 Fortran::lower::StatementContext stmtCtx; 858 auto value = fir::getBase(converter.genExprValue( 859 *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); 860 if (auto varDesignator = std::get_if< 861 Fortran::common::Indirection<Fortran::parser::Designator>>( 862 &assignmentStmtVariable.u)) { 863 if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) { 864 address = converter.getSymbolAddress(*name->symbol); 865 } 866 } 867 868 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 869 memory_order); 870 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 871 memory_order); 872 firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value, 873 hint, memory_order); 874 } 875 876 static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter, 877 Fortran::lower::pft::Evaluation &eval, 878 const Fortran::parser::OmpAtomicRead &atomicRead) { 879 auto &firOpBuilder = converter.getFirOpBuilder(); 880 auto currentLocation = converter.getCurrentLocation(); 881 mlir::Value to_address; 882 mlir::Value from_address; 883 // If no hint clause is specified, the effect is as if 884 // hint(omp_sync_hint_none) had been specified. 885 mlir::IntegerAttr hint = nullptr; 886 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 887 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 888 std::get<2>(atomicRead.t); 889 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 890 std::get<0>(atomicRead.t); 891 const auto &assignmentStmtExpr = 892 std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t); 893 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 894 std::get<3>(atomicRead.t).statement.t); 895 if (auto exprDesignator = std::get_if< 896 Fortran::common::Indirection<Fortran::parser::Designator>>( 897 &assignmentStmtExpr.u)) { 898 if (const auto *name = 899 getDesignatorNameIfDataRef(exprDesignator->value())) { 900 from_address = converter.getSymbolAddress(*name->symbol); 901 } 902 } 903 904 if (auto varDesignator = std::get_if< 905 Fortran::common::Indirection<Fortran::parser::Designator>>( 906 &assignmentStmtVariable.u)) { 907 if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) { 908 to_address = converter.getSymbolAddress(*name->symbol); 909 } 910 } 911 912 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 913 memory_order); 914 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 915 memory_order); 916 firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address, 917 to_address, hint, memory_order); 918 } 919 920 static void 921 genOMP(Fortran::lower::AbstractConverter &converter, 922 Fortran::lower::pft::Evaluation &eval, 923 const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 924 std::visit(Fortran::common::visitors{ 925 [&](const Fortran::parser::OmpAtomicRead &atomicRead) { 926 genOmpAtomicRead(converter, eval, atomicRead); 927 }, 928 [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) { 929 genOmpAtomicWrite(converter, eval, atomicWrite); 930 }, 931 [&](const auto &) { 932 TODO(converter.getCurrentLocation(), 933 "Atomic update & capture"); 934 }, 935 }, 936 atomicConstruct.u); 937 } 938 939 void Fortran::lower::genOpenMPConstruct( 940 Fortran::lower::AbstractConverter &converter, 941 Fortran::lower::pft::Evaluation &eval, 942 const Fortran::parser::OpenMPConstruct &ompConstruct) { 943 944 std::visit( 945 common::visitors{ 946 [&](const Fortran::parser::OpenMPStandaloneConstruct 947 &standaloneConstruct) { 948 genOMP(converter, eval, standaloneConstruct); 949 }, 950 [&](const Fortran::parser::OpenMPSectionsConstruct 951 §ionsConstruct) { 952 genOMP(converter, eval, sectionsConstruct); 953 }, 954 [&](const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 955 genOMP(converter, eval, sectionConstruct); 956 }, 957 [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 958 genOMP(converter, eval, loopConstruct); 959 }, 960 [&](const Fortran::parser::OpenMPDeclarativeAllocate 961 &execAllocConstruct) { 962 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 963 }, 964 [&](const Fortran::parser::OpenMPExecutableAllocate 965 &execAllocConstruct) { 966 TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate"); 967 }, 968 [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 969 genOMP(converter, eval, blockConstruct); 970 }, 971 [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 972 genOMP(converter, eval, atomicConstruct); 973 }, 974 [&](const Fortran::parser::OpenMPCriticalConstruct 975 &criticalConstruct) { 976 genOMP(converter, eval, criticalConstruct); 977 }, 978 }, 979 ompConstruct.u); 980 } 981 982 void Fortran::lower::genOpenMPDeclarativeConstruct( 983 Fortran::lower::AbstractConverter &converter, 984 Fortran::lower::pft::Evaluation &eval, 985 const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) { 986 987 std::visit( 988 common::visitors{ 989 [&](const Fortran::parser::OpenMPDeclarativeAllocate 990 &declarativeAllocate) { 991 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 992 }, 993 [&](const Fortran::parser::OpenMPDeclareReductionConstruct 994 &declareReductionConstruct) { 995 TODO(converter.getCurrentLocation(), 996 "OpenMPDeclareReductionConstruct"); 997 }, 998 [&](const Fortran::parser::OpenMPDeclareSimdConstruct 999 &declareSimdConstruct) { 1000 TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct"); 1001 }, 1002 [&](const Fortran::parser::OpenMPDeclareTargetConstruct 1003 &declareTargetConstruct) { 1004 TODO(converter.getCurrentLocation(), 1005 "OpenMPDeclareTargetConstruct"); 1006 }, 1007 [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) { 1008 TODO(converter.getCurrentLocation(), "OpenMPThreadprivate"); 1009 }, 1010 }, 1011 ompDeclConstruct.u); 1012 } 1013