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