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 static const Fortran::parser::Name * 29 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) { 30 const auto *dataRef = std::get_if<Fortran::parser::DataRef>(&designator.u); 31 return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr; 32 } 33 34 template <typename T> 35 static void createPrivateVarSyms(Fortran::lower::AbstractConverter &converter, 36 const T *clause) { 37 Fortran::semantics::Symbol *sym = nullptr; 38 const Fortran::parser::OmpObjectList &ompObjectList = clause->v; 39 for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) { 40 std::visit( 41 Fortran::common::visitors{ 42 [&](const Fortran::parser::Designator &designator) { 43 if (const Fortran::parser::Name *name = 44 getDesignatorNameIfDataRef(designator)) { 45 sym = name->symbol; 46 } 47 }, 48 [&](const Fortran::parser::Name &name) { sym = name.symbol; }}, 49 ompObject.u); 50 51 // Privatization for symbols which are pre-determined (like loop index 52 // variables) happen separately, for everything else privatize here 53 if constexpr (std::is_same_v<T, Fortran::parser::OmpClause::Firstprivate>) { 54 converter.copyHostAssociateVar(*sym); 55 } else { 56 bool success = converter.createHostAssociateVarClone(*sym); 57 (void)success; 58 assert(success && "Privatization failed due to existing binding"); 59 } 60 } 61 } 62 63 static void privatizeVars(Fortran::lower::AbstractConverter &converter, 64 const Fortran::parser::OmpClauseList &opClauseList) { 65 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 66 auto insPt = firOpBuilder.saveInsertionPoint(); 67 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); 68 for (const Fortran::parser::OmpClause &clause : opClauseList.v) { 69 if (const auto &privateClause = 70 std::get_if<Fortran::parser::OmpClause::Private>(&clause.u)) { 71 createPrivateVarSyms(converter, privateClause); 72 } else if (const auto &firstPrivateClause = 73 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 74 &clause.u)) { 75 createPrivateVarSyms(converter, firstPrivateClause); 76 } 77 } 78 firOpBuilder.restoreInsertionPoint(insPt); 79 } 80 81 static void genObjectList(const Fortran::parser::OmpObjectList &objectList, 82 Fortran::lower::AbstractConverter &converter, 83 llvm::SmallVectorImpl<Value> &operands) { 84 auto addOperands = [&](Fortran::lower::SymbolRef sym) { 85 const mlir::Value variable = converter.getSymbolAddress(sym); 86 if (variable) { 87 operands.push_back(variable); 88 } else { 89 if (const auto *details = 90 sym->detailsIf<Fortran::semantics::HostAssocDetails>()) { 91 operands.push_back(converter.getSymbolAddress(details->symbol())); 92 converter.copySymbolBinding(details->symbol(), sym); 93 } 94 } 95 }; 96 for (const Fortran::parser::OmpObject &ompObject : objectList.v) { 97 std::visit(Fortran::common::visitors{ 98 [&](const Fortran::parser::Designator &designator) { 99 if (const Fortran::parser::Name *name = 100 getDesignatorNameIfDataRef(designator)) { 101 addOperands(*name->symbol); 102 } 103 }, 104 [&](const Fortran::parser::Name &name) { 105 addOperands(*name.symbol); 106 }}, 107 ompObject.u); 108 } 109 } 110 111 template <typename Op> 112 static void 113 createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter, 114 mlir::Location &loc, 115 const Fortran::parser::OmpClauseList *clauses = nullptr, 116 const Fortran::semantics::Symbol *arg = nullptr, 117 bool outerCombined = false) { 118 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 119 // If an argument for the region is provided then create the block with that 120 // argument. Also update the symbol's address with the mlir argument value. 121 // e.g. For loops the argument is the induction variable. And all further 122 // uses of the induction variable should use this mlir value. 123 if (arg) { 124 firOpBuilder.createBlock(&op.getRegion(), {}, {converter.genType(*arg)}, 125 {loc}); 126 converter.bindSymbol(*arg, op.getRegion().front().getArgument(0)); 127 } else { 128 firOpBuilder.createBlock(&op.getRegion()); 129 } 130 auto &block = op.getRegion().back(); 131 firOpBuilder.setInsertionPointToStart(&block); 132 133 // Insert the terminator. 134 if constexpr (std::is_same_v<Op, omp::WsLoopOp>) { 135 mlir::ValueRange results; 136 firOpBuilder.create<mlir::omp::YieldOp>(loc, results); 137 } else { 138 firOpBuilder.create<mlir::omp::TerminatorOp>(loc); 139 } 140 141 // Reset the insertion point to the start of the first block. 142 firOpBuilder.setInsertionPointToStart(&block); 143 // Handle privatization. Do not privatize if this is the outer operation. 144 if (clauses && !outerCombined) 145 privatizeVars(converter, *clauses); 146 } 147 148 static void genOMP(Fortran::lower::AbstractConverter &converter, 149 Fortran::lower::pft::Evaluation &eval, 150 const Fortran::parser::OpenMPSimpleStandaloneConstruct 151 &simpleStandaloneConstruct) { 152 const auto &directive = 153 std::get<Fortran::parser::OmpSimpleStandaloneDirective>( 154 simpleStandaloneConstruct.t); 155 switch (directive.v) { 156 default: 157 break; 158 case llvm::omp::Directive::OMPD_barrier: 159 converter.getFirOpBuilder().create<mlir::omp::BarrierOp>( 160 converter.getCurrentLocation()); 161 break; 162 case llvm::omp::Directive::OMPD_taskwait: 163 converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>( 164 converter.getCurrentLocation()); 165 break; 166 case llvm::omp::Directive::OMPD_taskyield: 167 converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>( 168 converter.getCurrentLocation()); 169 break; 170 case llvm::omp::Directive::OMPD_target_enter_data: 171 TODO(converter.getCurrentLocation(), "OMPD_target_enter_data"); 172 case llvm::omp::Directive::OMPD_target_exit_data: 173 TODO(converter.getCurrentLocation(), "OMPD_target_exit_data"); 174 case llvm::omp::Directive::OMPD_target_update: 175 TODO(converter.getCurrentLocation(), "OMPD_target_update"); 176 case llvm::omp::Directive::OMPD_ordered: 177 TODO(converter.getCurrentLocation(), "OMPD_ordered"); 178 } 179 } 180 181 static void 182 genAllocateClause(Fortran::lower::AbstractConverter &converter, 183 const Fortran::parser::OmpAllocateClause &ompAllocateClause, 184 SmallVector<Value> &allocatorOperands, 185 SmallVector<Value> &allocateOperands) { 186 auto &firOpBuilder = converter.getFirOpBuilder(); 187 auto currentLocation = converter.getCurrentLocation(); 188 Fortran::lower::StatementContext stmtCtx; 189 190 mlir::Value allocatorOperand; 191 const Fortran::parser::OmpObjectList &ompObjectList = 192 std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t); 193 const auto &allocatorValue = 194 std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>( 195 ompAllocateClause.t); 196 // Check if allocate clause has allocator specified. If so, add it 197 // to list of allocators, otherwise, add default allocator to 198 // list of allocators. 199 if (allocatorValue) { 200 allocatorOperand = fir::getBase(converter.genExprValue( 201 *Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx)); 202 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 203 allocatorOperand); 204 } else { 205 allocatorOperand = firOpBuilder.createIntegerConstant( 206 currentLocation, firOpBuilder.getI32Type(), 1); 207 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 208 allocatorOperand); 209 } 210 genObjectList(ompObjectList, converter, allocateOperands); 211 } 212 213 static void 214 genOMP(Fortran::lower::AbstractConverter &converter, 215 Fortran::lower::pft::Evaluation &eval, 216 const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) { 217 std::visit( 218 Fortran::common::visitors{ 219 [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct 220 &simpleStandaloneConstruct) { 221 genOMP(converter, eval, simpleStandaloneConstruct); 222 }, 223 [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) { 224 SmallVector<Value, 4> operandRange; 225 if (const auto &ompObjectList = 226 std::get<std::optional<Fortran::parser::OmpObjectList>>( 227 flushConstruct.t)) 228 genObjectList(*ompObjectList, converter, operandRange); 229 const auto &memOrderClause = std::get<std::optional< 230 std::list<Fortran::parser::OmpMemoryOrderClause>>>( 231 flushConstruct.t); 232 if (memOrderClause.has_value() && memOrderClause->size() > 0) 233 TODO(converter.getCurrentLocation(), 234 "Handle OmpMemoryOrderClause"); 235 converter.getFirOpBuilder().create<mlir::omp::FlushOp>( 236 converter.getCurrentLocation(), operandRange); 237 }, 238 [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) { 239 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 240 }, 241 [&](const Fortran::parser::OpenMPCancellationPointConstruct 242 &cancellationPointConstruct) { 243 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 244 }, 245 }, 246 standaloneConstruct.u); 247 } 248 249 static void 250 genOMP(Fortran::lower::AbstractConverter &converter, 251 Fortran::lower::pft::Evaluation &eval, 252 const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 253 const auto &beginBlockDirective = 254 std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t); 255 const auto &blockDirective = 256 std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t); 257 const auto &endBlockDirective = 258 std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t); 259 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 260 mlir::Location currentLocation = converter.getCurrentLocation(); 261 262 Fortran::lower::StatementContext stmtCtx; 263 llvm::ArrayRef<mlir::Type> argTy; 264 mlir::Value ifClauseOperand, numThreadsClauseOperand, finalClauseOperand, 265 priorityClauseOperand; 266 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 267 SmallVector<Value> allocateOperands, allocatorOperands; 268 mlir::UnitAttr nowaitAttr, untiedAttr, mergeableAttr; 269 270 const auto &opClauseList = 271 std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t); 272 for (const auto &clause : opClauseList.v) { 273 if (const auto &ifClause = 274 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 275 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 276 mlir::Value ifVal = fir::getBase( 277 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 278 ifClauseOperand = firOpBuilder.createConvert( 279 currentLocation, firOpBuilder.getI1Type(), ifVal); 280 } else if (const auto &numThreadsClause = 281 std::get_if<Fortran::parser::OmpClause::NumThreads>( 282 &clause.u)) { 283 // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`. 284 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 285 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 286 } else if (const auto &procBindClause = 287 std::get_if<Fortran::parser::OmpClause::ProcBind>( 288 &clause.u)) { 289 omp::ClauseProcBindKind pbKind; 290 switch (procBindClause->v.v) { 291 case Fortran::parser::OmpProcBindClause::Type::Master: 292 pbKind = omp::ClauseProcBindKind::Master; 293 break; 294 case Fortran::parser::OmpProcBindClause::Type::Close: 295 pbKind = omp::ClauseProcBindKind::Close; 296 break; 297 case Fortran::parser::OmpProcBindClause::Type::Spread: 298 pbKind = omp::ClauseProcBindKind::Spread; 299 break; 300 case Fortran::parser::OmpProcBindClause::Type::Primary: 301 pbKind = omp::ClauseProcBindKind::Primary; 302 break; 303 } 304 procBindKindAttr = 305 omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind); 306 } else if (const auto &allocateClause = 307 std::get_if<Fortran::parser::OmpClause::Allocate>( 308 &clause.u)) { 309 genAllocateClause(converter, allocateClause->v, allocatorOperands, 310 allocateOperands); 311 } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) || 312 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 313 &clause.u)) { 314 // Privatisation clauses are handled elsewhere. 315 continue; 316 } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) { 317 // Nothing needs to be done for threads clause. 318 continue; 319 } else if (const auto &finalClause = 320 std::get_if<Fortran::parser::OmpClause::Final>(&clause.u)) { 321 mlir::Value finalVal = fir::getBase(converter.genExprValue( 322 *Fortran::semantics::GetExpr(finalClause->v), stmtCtx)); 323 finalClauseOperand = firOpBuilder.createConvert( 324 currentLocation, firOpBuilder.getI1Type(), finalVal); 325 } else if (std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u)) { 326 untiedAttr = firOpBuilder.getUnitAttr(); 327 } else if (std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u)) { 328 mergeableAttr = firOpBuilder.getUnitAttr(); 329 } else if (const auto &priorityClause = 330 std::get_if<Fortran::parser::OmpClause::Priority>( 331 &clause.u)) { 332 priorityClauseOperand = fir::getBase(converter.genExprValue( 333 *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx)); 334 } else { 335 TODO(currentLocation, "OpenMP Block construct clauses"); 336 } 337 } 338 339 for (const auto &clause : 340 std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) { 341 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 342 nowaitAttr = firOpBuilder.getUnitAttr(); 343 } 344 345 if (blockDirective.v == llvm::omp::OMPD_parallel) { 346 // Create and insert the operation. 347 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 348 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 349 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 350 /*reductions=*/nullptr, procBindKindAttr); 351 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, 352 &opClauseList); 353 } else if (blockDirective.v == llvm::omp::OMPD_master) { 354 auto masterOp = 355 firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy); 356 createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation); 357 } else if (blockDirective.v == llvm::omp::OMPD_single) { 358 auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>( 359 currentLocation, allocateOperands, allocatorOperands, nowaitAttr); 360 createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation); 361 } else if (blockDirective.v == llvm::omp::OMPD_ordered) { 362 auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>( 363 currentLocation, /*simd=*/nullptr); 364 createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation); 365 } else if (blockDirective.v == llvm::omp::OMPD_task) { 366 auto taskOp = firOpBuilder.create<mlir::omp::TaskOp>( 367 currentLocation, ifClauseOperand, finalClauseOperand, untiedAttr, 368 mergeableAttr, /*in_reduction_vars=*/ValueRange(), 369 /*in_reductions=*/nullptr, priorityClauseOperand, allocateOperands, 370 allocatorOperands); 371 createBodyOfOp(taskOp, converter, currentLocation, &opClauseList); 372 } else { 373 TODO(converter.getCurrentLocation(), "Unhandled block directive"); 374 } 375 } 376 377 static void genOMP(Fortran::lower::AbstractConverter &converter, 378 Fortran::lower::pft::Evaluation &eval, 379 const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 380 381 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 382 mlir::Location currentLocation = converter.getCurrentLocation(); 383 llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars, 384 linearStepVars, reductionVars; 385 mlir::Value scheduleChunkClauseOperand; 386 mlir::Attribute scheduleClauseOperand, collapseClauseOperand, 387 noWaitClauseOperand, orderedClauseOperand, orderClauseOperand; 388 const auto &wsLoopOpClauseList = std::get<Fortran::parser::OmpClauseList>( 389 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t); 390 if (llvm::omp::OMPD_do != 391 std::get<Fortran::parser::OmpLoopDirective>( 392 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t) 393 .v) { 394 TODO(converter.getCurrentLocation(), "Combined worksharing loop construct"); 395 } 396 397 Fortran::lower::pft::Evaluation *doConstructEval = 398 &eval.getFirstNestedEvaluation(); 399 400 Fortran::lower::pft::Evaluation *doLoop = 401 &doConstructEval->getFirstNestedEvaluation(); 402 auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>(); 403 assert(doStmt && "Expected do loop to be in the nested evaluation"); 404 const auto &loopControl = 405 std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t); 406 const Fortran::parser::LoopControl::Bounds *bounds = 407 std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u); 408 assert(bounds && "Expected bounds for worksharing do loop"); 409 Fortran::semantics::Symbol *iv = nullptr; 410 Fortran::lower::StatementContext stmtCtx; 411 lowerBound.push_back(fir::getBase(converter.genExprValue( 412 *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); 413 upperBound.push_back(fir::getBase(converter.genExprValue( 414 *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); 415 if (bounds->step) { 416 step.push_back(fir::getBase(converter.genExprValue( 417 *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); 418 } else { // If `step` is not present, assume it as `1`. 419 step.push_back(firOpBuilder.createIntegerConstant( 420 currentLocation, firOpBuilder.getIntegerType(32), 1)); 421 } 422 iv = bounds->name.thing.symbol; 423 424 // FIXME: Add support for following clauses: 425 // 1. linear 426 // 2. order 427 // 3. collapse 428 // 4. schedule (with chunk) 429 auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>( 430 currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars, 431 reductionVars, /*reductions=*/nullptr, 432 scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(), 433 scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr, 434 /*simd_modifier=*/nullptr, 435 collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(), 436 noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(), 437 orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(), 438 orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(), 439 /*inclusive=*/firOpBuilder.getUnitAttr()); 440 441 // Handle attribute based clauses. 442 for (const Fortran::parser::OmpClause &clause : wsLoopOpClauseList.v) { 443 if (const auto &scheduleClause = 444 std::get_if<Fortran::parser::OmpClause::Schedule>(&clause.u)) { 445 mlir::MLIRContext *context = firOpBuilder.getContext(); 446 const auto &scheduleType = scheduleClause->v; 447 const auto &scheduleKind = 448 std::get<Fortran::parser::OmpScheduleClause::ScheduleType>( 449 scheduleType.t); 450 switch (scheduleKind) { 451 case Fortran::parser::OmpScheduleClause::ScheduleType::Static: 452 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 453 context, omp::ClauseScheduleKind::Static)); 454 break; 455 case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic: 456 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 457 context, omp::ClauseScheduleKind::Dynamic)); 458 break; 459 case Fortran::parser::OmpScheduleClause::ScheduleType::Guided: 460 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 461 context, omp::ClauseScheduleKind::Guided)); 462 break; 463 case Fortran::parser::OmpScheduleClause::ScheduleType::Auto: 464 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 465 context, omp::ClauseScheduleKind::Auto)); 466 break; 467 case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime: 468 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 469 context, omp::ClauseScheduleKind::Runtime)); 470 break; 471 } 472 } 473 } 474 // In FORTRAN `nowait` clause occur at the end of `omp do` directive. 475 // i.e 476 // !$omp do 477 // <...> 478 // !$omp end do nowait 479 if (const auto &endClauseList = 480 std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>( 481 loopConstruct.t)) { 482 const auto &clauseList = 483 std::get<Fortran::parser::OmpClauseList>((*endClauseList).t); 484 for (const Fortran::parser::OmpClause &clause : clauseList.v) 485 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 486 wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr()); 487 } 488 489 createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, 490 &wsLoopOpClauseList, iv); 491 } 492 493 static void 494 genOMP(Fortran::lower::AbstractConverter &converter, 495 Fortran::lower::pft::Evaluation &eval, 496 const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) { 497 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 498 mlir::Location currentLocation = converter.getCurrentLocation(); 499 std::string name; 500 const Fortran::parser::OmpCriticalDirective &cd = 501 std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t); 502 if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) { 503 name = 504 std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString(); 505 } 506 507 uint64_t hint = 0; 508 const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t); 509 for (const Fortran::parser::OmpClause &clause : clauseList.v) 510 if (auto hintClause = 511 std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) { 512 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 513 hint = *Fortran::evaluate::ToInt64(*expr); 514 break; 515 } 516 517 mlir::omp::CriticalOp criticalOp = [&]() { 518 if (name.empty()) { 519 return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation, 520 FlatSymbolRefAttr()); 521 } else { 522 mlir::ModuleOp module = firOpBuilder.getModule(); 523 mlir::OpBuilder modBuilder(module.getBodyRegion()); 524 auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name); 525 if (!global) 526 global = modBuilder.create<mlir::omp::CriticalDeclareOp>( 527 currentLocation, name, hint); 528 return firOpBuilder.create<mlir::omp::CriticalOp>( 529 currentLocation, mlir::FlatSymbolRefAttr::get( 530 firOpBuilder.getContext(), global.sym_name())); 531 } 532 }(); 533 createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation); 534 } 535 536 static void 537 genOMP(Fortran::lower::AbstractConverter &converter, 538 Fortran::lower::pft::Evaluation &eval, 539 const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 540 541 auto &firOpBuilder = converter.getFirOpBuilder(); 542 auto currentLocation = converter.getCurrentLocation(); 543 mlir::omp::SectionOp sectionOp = 544 firOpBuilder.create<mlir::omp::SectionOp>(currentLocation); 545 createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation); 546 } 547 548 // TODO: Add support for reduction 549 static void 550 genOMP(Fortran::lower::AbstractConverter &converter, 551 Fortran::lower::pft::Evaluation &eval, 552 const Fortran::parser::OpenMPSectionsConstruct §ionsConstruct) { 553 auto &firOpBuilder = converter.getFirOpBuilder(); 554 auto currentLocation = converter.getCurrentLocation(); 555 SmallVector<Value> reductionVars, allocateOperands, allocatorOperands; 556 mlir::UnitAttr noWaitClauseOperand; 557 const auto §ionsClauseList = std::get<Fortran::parser::OmpClauseList>( 558 std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t) 559 .t); 560 for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) { 561 562 // Reduction Clause 563 if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) { 564 TODO(currentLocation, "OMPC_Reduction"); 565 566 // Allocate clause 567 } else if (const auto &allocateClause = 568 std::get_if<Fortran::parser::OmpClause::Allocate>( 569 &clause.u)) { 570 genAllocateClause(converter, allocateClause->v, allocatorOperands, 571 allocateOperands); 572 } 573 } 574 const auto &endSectionsClauseList = 575 std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t); 576 const auto &clauseList = 577 std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t); 578 for (const auto &clause : clauseList.v) { 579 // Nowait clause 580 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) { 581 noWaitClauseOperand = firOpBuilder.getUnitAttr(); 582 } 583 } 584 585 llvm::omp::Directive dir = 586 std::get<Fortran::parser::OmpSectionsDirective>( 587 std::get<Fortran::parser::OmpBeginSectionsDirective>( 588 sectionsConstruct.t) 589 .t) 590 .v; 591 592 // Parallel Sections Construct 593 if (dir == llvm::omp::Directive::OMPD_parallel_sections) { 594 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 595 currentLocation, /*if_expr_var*/ nullptr, /*num_threads_var*/ nullptr, 596 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 597 /*reductions=*/nullptr, /*proc_bind_val*/ nullptr); 598 createBodyOfOp(parallelOp, converter, currentLocation); 599 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 600 currentLocation, /*reduction_vars*/ ValueRange(), 601 /*reductions=*/nullptr, /*allocate_vars*/ ValueRange(), 602 /*allocators_vars*/ ValueRange(), /*nowait=*/nullptr); 603 createBodyOfOp(sectionsOp, converter, currentLocation); 604 605 // Sections Construct 606 } else if (dir == llvm::omp::Directive::OMPD_sections) { 607 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 608 currentLocation, reductionVars, /*reductions = */ nullptr, 609 allocateOperands, allocatorOperands, noWaitClauseOperand); 610 createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation); 611 } 612 } 613 614 static void genOmpAtomicHintAndMemoryOrderClauses( 615 Fortran::lower::AbstractConverter &converter, 616 const Fortran::parser::OmpAtomicClauseList &clauseList, 617 mlir::IntegerAttr &hint, 618 mlir::omp::ClauseMemoryOrderKindAttr &memory_order) { 619 auto &firOpBuilder = converter.getFirOpBuilder(); 620 for (const auto &clause : clauseList.v) { 621 if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) { 622 if (auto hintClause = 623 std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) { 624 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 625 uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr); 626 hint = firOpBuilder.getI64IntegerAttr(hintExprValue); 627 } 628 } else if (auto ompMemoryOrderClause = 629 std::get_if<Fortran::parser::OmpMemoryOrderClause>( 630 &clause.u)) { 631 if (std::get_if<Fortran::parser::OmpClause::Acquire>( 632 &ompMemoryOrderClause->v.u)) { 633 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 634 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire); 635 } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>( 636 &ompMemoryOrderClause->v.u)) { 637 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 638 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed); 639 } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>( 640 &ompMemoryOrderClause->v.u)) { 641 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 642 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst); 643 } else if (std::get_if<Fortran::parser::OmpClause::Release>( 644 &ompMemoryOrderClause->v.u)) { 645 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 646 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release); 647 } 648 } 649 } 650 } 651 652 static void 653 genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter, 654 Fortran::lower::pft::Evaluation &eval, 655 const Fortran::parser::OmpAtomicWrite &atomicWrite) { 656 auto &firOpBuilder = converter.getFirOpBuilder(); 657 auto currentLocation = converter.getCurrentLocation(); 658 mlir::Value address; 659 // If no hint clause is specified, the effect is as if 660 // hint(omp_sync_hint_none) had been specified. 661 mlir::IntegerAttr hint = nullptr; 662 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 663 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 664 std::get<2>(atomicWrite.t); 665 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 666 std::get<0>(atomicWrite.t); 667 const auto &assignmentStmtExpr = 668 std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t); 669 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 670 std::get<3>(atomicWrite.t).statement.t); 671 Fortran::lower::StatementContext stmtCtx; 672 auto value = fir::getBase(converter.genExprValue( 673 *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); 674 if (auto varDesignator = std::get_if< 675 Fortran::common::Indirection<Fortran::parser::Designator>>( 676 &assignmentStmtVariable.u)) { 677 if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) { 678 address = converter.getSymbolAddress(*name->symbol); 679 } 680 } 681 682 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 683 memory_order); 684 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 685 memory_order); 686 firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value, 687 hint, memory_order); 688 } 689 690 static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter, 691 Fortran::lower::pft::Evaluation &eval, 692 const Fortran::parser::OmpAtomicRead &atomicRead) { 693 auto &firOpBuilder = converter.getFirOpBuilder(); 694 auto currentLocation = converter.getCurrentLocation(); 695 mlir::Value to_address; 696 mlir::Value from_address; 697 // If no hint clause is specified, the effect is as if 698 // hint(omp_sync_hint_none) had been specified. 699 mlir::IntegerAttr hint = nullptr; 700 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 701 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 702 std::get<2>(atomicRead.t); 703 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 704 std::get<0>(atomicRead.t); 705 const auto &assignmentStmtExpr = 706 std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t); 707 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 708 std::get<3>(atomicRead.t).statement.t); 709 if (auto exprDesignator = std::get_if< 710 Fortran::common::Indirection<Fortran::parser::Designator>>( 711 &assignmentStmtExpr.u)) { 712 if (const auto *name = 713 getDesignatorNameIfDataRef(exprDesignator->value())) { 714 from_address = converter.getSymbolAddress(*name->symbol); 715 } 716 } 717 718 if (auto varDesignator = std::get_if< 719 Fortran::common::Indirection<Fortran::parser::Designator>>( 720 &assignmentStmtVariable.u)) { 721 if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) { 722 to_address = converter.getSymbolAddress(*name->symbol); 723 } 724 } 725 726 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 727 memory_order); 728 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 729 memory_order); 730 firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address, 731 to_address, hint, memory_order); 732 } 733 734 static void 735 genOMP(Fortran::lower::AbstractConverter &converter, 736 Fortran::lower::pft::Evaluation &eval, 737 const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 738 std::visit(Fortran::common::visitors{ 739 [&](const Fortran::parser::OmpAtomicRead &atomicRead) { 740 genOmpAtomicRead(converter, eval, atomicRead); 741 }, 742 [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) { 743 genOmpAtomicWrite(converter, eval, atomicWrite); 744 }, 745 [&](const auto &) { 746 TODO(converter.getCurrentLocation(), 747 "Atomic update & capture"); 748 }, 749 }, 750 atomicConstruct.u); 751 } 752 753 void Fortran::lower::genOpenMPConstruct( 754 Fortran::lower::AbstractConverter &converter, 755 Fortran::lower::pft::Evaluation &eval, 756 const Fortran::parser::OpenMPConstruct &ompConstruct) { 757 758 std::visit( 759 common::visitors{ 760 [&](const Fortran::parser::OpenMPStandaloneConstruct 761 &standaloneConstruct) { 762 genOMP(converter, eval, standaloneConstruct); 763 }, 764 [&](const Fortran::parser::OpenMPSectionsConstruct 765 §ionsConstruct) { 766 genOMP(converter, eval, sectionsConstruct); 767 }, 768 [&](const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 769 genOMP(converter, eval, sectionConstruct); 770 }, 771 [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 772 genOMP(converter, eval, loopConstruct); 773 }, 774 [&](const Fortran::parser::OpenMPDeclarativeAllocate 775 &execAllocConstruct) { 776 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 777 }, 778 [&](const Fortran::parser::OpenMPExecutableAllocate 779 &execAllocConstruct) { 780 TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate"); 781 }, 782 [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 783 genOMP(converter, eval, blockConstruct); 784 }, 785 [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 786 genOMP(converter, eval, atomicConstruct); 787 }, 788 [&](const Fortran::parser::OpenMPCriticalConstruct 789 &criticalConstruct) { 790 genOMP(converter, eval, criticalConstruct); 791 }, 792 }, 793 ompConstruct.u); 794 } 795 796 void Fortran::lower::genOpenMPDeclarativeConstruct( 797 Fortran::lower::AbstractConverter &converter, 798 Fortran::lower::pft::Evaluation &eval, 799 const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) { 800 801 std::visit( 802 common::visitors{ 803 [&](const Fortran::parser::OpenMPDeclarativeAllocate 804 &declarativeAllocate) { 805 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 806 }, 807 [&](const Fortran::parser::OpenMPDeclareReductionConstruct 808 &declareReductionConstruct) { 809 TODO(converter.getCurrentLocation(), 810 "OpenMPDeclareReductionConstruct"); 811 }, 812 [&](const Fortran::parser::OpenMPDeclareSimdConstruct 813 &declareSimdConstruct) { 814 TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct"); 815 }, 816 [&](const Fortran::parser::OpenMPDeclareTargetConstruct 817 &declareTargetConstruct) { 818 TODO(converter.getCurrentLocation(), 819 "OpenMPDeclareTargetConstruct"); 820 }, 821 [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) { 822 TODO(converter.getCurrentLocation(), "OpenMPThreadprivate"); 823 }, 824 }, 825 ompDeclConstruct.u); 826 } 827