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; 265 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 266 SmallVector<Value> allocateOperands, allocatorOperands; 267 mlir::UnitAttr nowaitAttr; 268 269 const auto &opClauseList = 270 std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t); 271 for (const auto &clause : opClauseList.v) { 272 if (const auto &ifClause = 273 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 274 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 275 mlir::Value ifVal = fir::getBase( 276 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 277 ifClauseOperand = firOpBuilder.createConvert( 278 currentLocation, firOpBuilder.getI1Type(), ifVal); 279 } else if (const auto &numThreadsClause = 280 std::get_if<Fortran::parser::OmpClause::NumThreads>( 281 &clause.u)) { 282 // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`. 283 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 284 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 285 } else if (const auto &procBindClause = 286 std::get_if<Fortran::parser::OmpClause::ProcBind>( 287 &clause.u)) { 288 omp::ClauseProcBindKind pbKind; 289 switch (procBindClause->v.v) { 290 case Fortran::parser::OmpProcBindClause::Type::Master: 291 pbKind = omp::ClauseProcBindKind::Master; 292 break; 293 case Fortran::parser::OmpProcBindClause::Type::Close: 294 pbKind = omp::ClauseProcBindKind::Close; 295 break; 296 case Fortran::parser::OmpProcBindClause::Type::Spread: 297 pbKind = omp::ClauseProcBindKind::Spread; 298 break; 299 case Fortran::parser::OmpProcBindClause::Type::Primary: 300 pbKind = omp::ClauseProcBindKind::Primary; 301 break; 302 } 303 procBindKindAttr = 304 omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind); 305 } else if (const auto &allocateClause = 306 std::get_if<Fortran::parser::OmpClause::Allocate>( 307 &clause.u)) { 308 genAllocateClause(converter, allocateClause->v, allocatorOperands, 309 allocateOperands); 310 } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) || 311 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 312 &clause.u)) { 313 // Privatisation clauses are handled elsewhere. 314 continue; 315 } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) { 316 // Nothing needs to be done for threads clause. 317 continue; 318 } else { 319 TODO(currentLocation, "OpenMP Block construct clauses"); 320 } 321 } 322 323 for (const auto &clause : 324 std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) { 325 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 326 nowaitAttr = firOpBuilder.getUnitAttr(); 327 } 328 329 if (blockDirective.v == llvm::omp::OMPD_parallel) { 330 // Create and insert the operation. 331 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 332 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 333 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 334 /*reductions=*/nullptr, procBindKindAttr); 335 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, 336 &opClauseList); 337 } else if (blockDirective.v == llvm::omp::OMPD_master) { 338 auto masterOp = 339 firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy); 340 createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation); 341 } else if (blockDirective.v == llvm::omp::OMPD_single) { 342 auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>( 343 currentLocation, allocateOperands, allocatorOperands, nowaitAttr); 344 createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation); 345 } else if (blockDirective.v == llvm::omp::OMPD_ordered) { 346 auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>( 347 currentLocation, /*simd=*/nullptr); 348 createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation); 349 } else { 350 TODO(converter.getCurrentLocation(), "Unhandled block directive"); 351 } 352 } 353 354 static void genOMP(Fortran::lower::AbstractConverter &converter, 355 Fortran::lower::pft::Evaluation &eval, 356 const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 357 358 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 359 mlir::Location currentLocation = converter.getCurrentLocation(); 360 llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars, 361 linearStepVars, reductionVars; 362 mlir::Value scheduleChunkClauseOperand; 363 mlir::Attribute scheduleClauseOperand, collapseClauseOperand, 364 noWaitClauseOperand, orderedClauseOperand, orderClauseOperand; 365 const auto &wsLoopOpClauseList = std::get<Fortran::parser::OmpClauseList>( 366 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t); 367 if (llvm::omp::OMPD_do != 368 std::get<Fortran::parser::OmpLoopDirective>( 369 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t) 370 .v) { 371 TODO(converter.getCurrentLocation(), "Combined worksharing loop construct"); 372 } 373 374 Fortran::lower::pft::Evaluation *doConstructEval = 375 &eval.getFirstNestedEvaluation(); 376 377 Fortran::lower::pft::Evaluation *doLoop = 378 &doConstructEval->getFirstNestedEvaluation(); 379 auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>(); 380 assert(doStmt && "Expected do loop to be in the nested evaluation"); 381 const auto &loopControl = 382 std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t); 383 const Fortran::parser::LoopControl::Bounds *bounds = 384 std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u); 385 assert(bounds && "Expected bounds for worksharing do loop"); 386 Fortran::semantics::Symbol *iv = nullptr; 387 Fortran::lower::StatementContext stmtCtx; 388 lowerBound.push_back(fir::getBase(converter.genExprValue( 389 *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); 390 upperBound.push_back(fir::getBase(converter.genExprValue( 391 *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); 392 if (bounds->step) { 393 step.push_back(fir::getBase(converter.genExprValue( 394 *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); 395 } else { // If `step` is not present, assume it as `1`. 396 step.push_back(firOpBuilder.createIntegerConstant( 397 currentLocation, firOpBuilder.getIntegerType(32), 1)); 398 } 399 iv = bounds->name.thing.symbol; 400 401 // FIXME: Add support for following clauses: 402 // 1. linear 403 // 2. order 404 // 3. collapse 405 // 4. schedule (with chunk) 406 auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>( 407 currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars, 408 reductionVars, /*reductions=*/nullptr, 409 scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(), 410 scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr, 411 /*simd_modifier=*/nullptr, 412 collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(), 413 noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(), 414 orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(), 415 orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(), 416 /*inclusive=*/firOpBuilder.getUnitAttr()); 417 418 // Handle attribute based clauses. 419 for (const Fortran::parser::OmpClause &clause : wsLoopOpClauseList.v) { 420 if (const auto &scheduleClause = 421 std::get_if<Fortran::parser::OmpClause::Schedule>(&clause.u)) { 422 mlir::MLIRContext *context = firOpBuilder.getContext(); 423 const auto &scheduleType = scheduleClause->v; 424 const auto &scheduleKind = 425 std::get<Fortran::parser::OmpScheduleClause::ScheduleType>( 426 scheduleType.t); 427 switch (scheduleKind) { 428 case Fortran::parser::OmpScheduleClause::ScheduleType::Static: 429 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 430 context, omp::ClauseScheduleKind::Static)); 431 break; 432 case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic: 433 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 434 context, omp::ClauseScheduleKind::Dynamic)); 435 break; 436 case Fortran::parser::OmpScheduleClause::ScheduleType::Guided: 437 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 438 context, omp::ClauseScheduleKind::Guided)); 439 break; 440 case Fortran::parser::OmpScheduleClause::ScheduleType::Auto: 441 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 442 context, omp::ClauseScheduleKind::Auto)); 443 break; 444 case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime: 445 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 446 context, omp::ClauseScheduleKind::Runtime)); 447 break; 448 } 449 } 450 } 451 // In FORTRAN `nowait` clause occur at the end of `omp do` directive. 452 // i.e 453 // !$omp do 454 // <...> 455 // !$omp end do nowait 456 if (const auto &endClauseList = 457 std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>( 458 loopConstruct.t)) { 459 const auto &clauseList = 460 std::get<Fortran::parser::OmpClauseList>((*endClauseList).t); 461 for (const Fortran::parser::OmpClause &clause : clauseList.v) 462 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 463 wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr()); 464 } 465 466 createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, 467 &wsLoopOpClauseList, iv); 468 } 469 470 static void 471 genOMP(Fortran::lower::AbstractConverter &converter, 472 Fortran::lower::pft::Evaluation &eval, 473 const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) { 474 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 475 mlir::Location currentLocation = converter.getCurrentLocation(); 476 std::string name; 477 const Fortran::parser::OmpCriticalDirective &cd = 478 std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t); 479 if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) { 480 name = 481 std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString(); 482 } 483 484 uint64_t hint = 0; 485 const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t); 486 for (const Fortran::parser::OmpClause &clause : clauseList.v) 487 if (auto hintClause = 488 std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) { 489 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 490 hint = *Fortran::evaluate::ToInt64(*expr); 491 break; 492 } 493 494 mlir::omp::CriticalOp criticalOp = [&]() { 495 if (name.empty()) { 496 return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation, 497 FlatSymbolRefAttr()); 498 } else { 499 mlir::ModuleOp module = firOpBuilder.getModule(); 500 mlir::OpBuilder modBuilder(module.getBodyRegion()); 501 auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name); 502 if (!global) 503 global = modBuilder.create<mlir::omp::CriticalDeclareOp>( 504 currentLocation, name, hint); 505 return firOpBuilder.create<mlir::omp::CriticalOp>( 506 currentLocation, mlir::FlatSymbolRefAttr::get( 507 firOpBuilder.getContext(), global.sym_name())); 508 } 509 }(); 510 createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation); 511 } 512 513 static void 514 genOMP(Fortran::lower::AbstractConverter &converter, 515 Fortran::lower::pft::Evaluation &eval, 516 const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 517 518 auto &firOpBuilder = converter.getFirOpBuilder(); 519 auto currentLocation = converter.getCurrentLocation(); 520 mlir::omp::SectionOp sectionOp = 521 firOpBuilder.create<mlir::omp::SectionOp>(currentLocation); 522 createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation); 523 } 524 525 // TODO: Add support for reduction 526 static void 527 genOMP(Fortran::lower::AbstractConverter &converter, 528 Fortran::lower::pft::Evaluation &eval, 529 const Fortran::parser::OpenMPSectionsConstruct §ionsConstruct) { 530 auto &firOpBuilder = converter.getFirOpBuilder(); 531 auto currentLocation = converter.getCurrentLocation(); 532 SmallVector<Value> reductionVars, allocateOperands, allocatorOperands; 533 mlir::UnitAttr noWaitClauseOperand; 534 const auto §ionsClauseList = std::get<Fortran::parser::OmpClauseList>( 535 std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t) 536 .t); 537 for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) { 538 539 // Reduction Clause 540 if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) { 541 TODO(currentLocation, "OMPC_Reduction"); 542 543 // Allocate clause 544 } else if (const auto &allocateClause = 545 std::get_if<Fortran::parser::OmpClause::Allocate>( 546 &clause.u)) { 547 genAllocateClause(converter, allocateClause->v, allocatorOperands, 548 allocateOperands); 549 } 550 } 551 const auto &endSectionsClauseList = 552 std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t); 553 const auto &clauseList = 554 std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t); 555 for (const auto &clause : clauseList.v) { 556 // Nowait clause 557 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) { 558 noWaitClauseOperand = firOpBuilder.getUnitAttr(); 559 } 560 } 561 562 llvm::omp::Directive dir = 563 std::get<Fortran::parser::OmpSectionsDirective>( 564 std::get<Fortran::parser::OmpBeginSectionsDirective>( 565 sectionsConstruct.t) 566 .t) 567 .v; 568 569 // Parallel Sections Construct 570 if (dir == llvm::omp::Directive::OMPD_parallel_sections) { 571 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 572 currentLocation, /*if_expr_var*/ nullptr, /*num_threads_var*/ nullptr, 573 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 574 /*reductions=*/nullptr, /*proc_bind_val*/ nullptr); 575 createBodyOfOp(parallelOp, converter, currentLocation); 576 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 577 currentLocation, /*reduction_vars*/ ValueRange(), 578 /*reductions=*/nullptr, /*allocate_vars*/ ValueRange(), 579 /*allocators_vars*/ ValueRange(), /*nowait=*/nullptr); 580 createBodyOfOp(sectionsOp, converter, currentLocation); 581 582 // Sections Construct 583 } else if (dir == llvm::omp::Directive::OMPD_sections) { 584 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 585 currentLocation, reductionVars, /*reductions = */ nullptr, 586 allocateOperands, allocatorOperands, noWaitClauseOperand); 587 createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation); 588 } 589 } 590 591 static void genOmpAtomicHintAndMemoryOrderClauses( 592 Fortran::lower::AbstractConverter &converter, 593 const Fortran::parser::OmpAtomicClauseList &clauseList, 594 mlir::IntegerAttr &hint, 595 mlir::omp::ClauseMemoryOrderKindAttr &memory_order) { 596 auto &firOpBuilder = converter.getFirOpBuilder(); 597 for (const auto &clause : clauseList.v) { 598 if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) { 599 if (auto hintClause = 600 std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) { 601 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 602 uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr); 603 hint = firOpBuilder.getI64IntegerAttr(hintExprValue); 604 } 605 } else if (auto ompMemoryOrderClause = 606 std::get_if<Fortran::parser::OmpMemoryOrderClause>( 607 &clause.u)) { 608 if (std::get_if<Fortran::parser::OmpClause::Acquire>( 609 &ompMemoryOrderClause->v.u)) { 610 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 611 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire); 612 } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>( 613 &ompMemoryOrderClause->v.u)) { 614 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 615 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed); 616 } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>( 617 &ompMemoryOrderClause->v.u)) { 618 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 619 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst); 620 } else if (std::get_if<Fortran::parser::OmpClause::Release>( 621 &ompMemoryOrderClause->v.u)) { 622 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 623 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release); 624 } 625 } 626 } 627 } 628 629 static void 630 genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter, 631 Fortran::lower::pft::Evaluation &eval, 632 const Fortran::parser::OmpAtomicWrite &atomicWrite) { 633 auto &firOpBuilder = converter.getFirOpBuilder(); 634 auto currentLocation = converter.getCurrentLocation(); 635 mlir::Value address; 636 // If no hint clause is specified, the effect is as if 637 // hint(omp_sync_hint_none) had been specified. 638 mlir::IntegerAttr hint = nullptr; 639 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 640 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 641 std::get<2>(atomicWrite.t); 642 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 643 std::get<0>(atomicWrite.t); 644 const auto &assignmentStmtExpr = 645 std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t); 646 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 647 std::get<3>(atomicWrite.t).statement.t); 648 Fortran::lower::StatementContext stmtCtx; 649 auto value = fir::getBase(converter.genExprValue( 650 *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); 651 if (auto varDesignator = std::get_if< 652 Fortran::common::Indirection<Fortran::parser::Designator>>( 653 &assignmentStmtVariable.u)) { 654 if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) { 655 address = converter.getSymbolAddress(*name->symbol); 656 } 657 } 658 659 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 660 memory_order); 661 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 662 memory_order); 663 firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value, 664 hint, memory_order); 665 } 666 667 static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter, 668 Fortran::lower::pft::Evaluation &eval, 669 const Fortran::parser::OmpAtomicRead &atomicRead) { 670 auto &firOpBuilder = converter.getFirOpBuilder(); 671 auto currentLocation = converter.getCurrentLocation(); 672 mlir::Value to_address; 673 mlir::Value from_address; 674 // If no hint clause is specified, the effect is as if 675 // hint(omp_sync_hint_none) had been specified. 676 mlir::IntegerAttr hint = nullptr; 677 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 678 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 679 std::get<2>(atomicRead.t); 680 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 681 std::get<0>(atomicRead.t); 682 const auto &assignmentStmtExpr = 683 std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t); 684 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 685 std::get<3>(atomicRead.t).statement.t); 686 if (auto exprDesignator = std::get_if< 687 Fortran::common::Indirection<Fortran::parser::Designator>>( 688 &assignmentStmtExpr.u)) { 689 if (const auto *name = 690 getDesignatorNameIfDataRef(exprDesignator->value())) { 691 from_address = converter.getSymbolAddress(*name->symbol); 692 } 693 } 694 695 if (auto varDesignator = std::get_if< 696 Fortran::common::Indirection<Fortran::parser::Designator>>( 697 &assignmentStmtVariable.u)) { 698 if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) { 699 to_address = converter.getSymbolAddress(*name->symbol); 700 } 701 } 702 703 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 704 memory_order); 705 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 706 memory_order); 707 firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address, 708 to_address, hint, memory_order); 709 } 710 711 static void 712 genOMP(Fortran::lower::AbstractConverter &converter, 713 Fortran::lower::pft::Evaluation &eval, 714 const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 715 std::visit(Fortran::common::visitors{ 716 [&](const Fortran::parser::OmpAtomicRead &atomicRead) { 717 genOmpAtomicRead(converter, eval, atomicRead); 718 }, 719 [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) { 720 genOmpAtomicWrite(converter, eval, atomicWrite); 721 }, 722 [&](const auto &) { 723 TODO(converter.getCurrentLocation(), 724 "Atomic update & capture"); 725 }, 726 }, 727 atomicConstruct.u); 728 } 729 730 void Fortran::lower::genOpenMPConstruct( 731 Fortran::lower::AbstractConverter &converter, 732 Fortran::lower::pft::Evaluation &eval, 733 const Fortran::parser::OpenMPConstruct &ompConstruct) { 734 735 std::visit( 736 common::visitors{ 737 [&](const Fortran::parser::OpenMPStandaloneConstruct 738 &standaloneConstruct) { 739 genOMP(converter, eval, standaloneConstruct); 740 }, 741 [&](const Fortran::parser::OpenMPSectionsConstruct 742 §ionsConstruct) { 743 genOMP(converter, eval, sectionsConstruct); 744 }, 745 [&](const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 746 genOMP(converter, eval, sectionConstruct); 747 }, 748 [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 749 genOMP(converter, eval, loopConstruct); 750 }, 751 [&](const Fortran::parser::OpenMPDeclarativeAllocate 752 &execAllocConstruct) { 753 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 754 }, 755 [&](const Fortran::parser::OpenMPExecutableAllocate 756 &execAllocConstruct) { 757 TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate"); 758 }, 759 [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 760 genOMP(converter, eval, blockConstruct); 761 }, 762 [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 763 genOMP(converter, eval, atomicConstruct); 764 }, 765 [&](const Fortran::parser::OpenMPCriticalConstruct 766 &criticalConstruct) { 767 genOMP(converter, eval, criticalConstruct); 768 }, 769 }, 770 ompConstruct.u); 771 } 772 773 void Fortran::lower::genOpenMPDeclarativeConstruct( 774 Fortran::lower::AbstractConverter &converter, 775 Fortran::lower::pft::Evaluation &eval, 776 const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) { 777 778 std::visit( 779 common::visitors{ 780 [&](const Fortran::parser::OpenMPDeclarativeAllocate 781 &declarativeAllocate) { 782 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 783 }, 784 [&](const Fortran::parser::OpenMPDeclareReductionConstruct 785 &declareReductionConstruct) { 786 TODO(converter.getCurrentLocation(), 787 "OpenMPDeclareReductionConstruct"); 788 }, 789 [&](const Fortran::parser::OpenMPDeclareSimdConstruct 790 &declareSimdConstruct) { 791 TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct"); 792 }, 793 [&](const Fortran::parser::OpenMPDeclareTargetConstruct 794 &declareTargetConstruct) { 795 TODO(converter.getCurrentLocation(), 796 "OpenMPDeclareTargetConstruct"); 797 }, 798 [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) { 799 TODO(converter.getCurrentLocation(), "OpenMPThreadprivate"); 800 }, 801 }, 802 ompDeclConstruct.u); 803 } 804