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/Optimizer/Builder/BoxValue.h" 20 #include "flang/Optimizer/Builder/FIRBuilder.h" 21 #include "flang/Optimizer/Builder/Todo.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 static Fortran::semantics::Symbol * 48 getOmpObjectSymbol(const Fortran::parser::OmpObject &ompObject) { 49 Fortran::semantics::Symbol *sym = nullptr; 50 std::visit(Fortran::common::visitors{ 51 [&](const Fortran::parser::Designator &designator) { 52 if (const Fortran::parser::Name *name = 53 getDesignatorNameIfDataRef(designator)) { 54 sym = name->symbol; 55 } 56 }, 57 [&](const Fortran::parser::Name &name) { sym = name.symbol; }}, 58 ompObject.u); 59 return sym; 60 } 61 62 template <typename T> 63 static void createPrivateVarSyms(Fortran::lower::AbstractConverter &converter, 64 const T *clause) { 65 const Fortran::parser::OmpObjectList &ompObjectList = clause->v; 66 for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) { 67 Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject); 68 // Privatization for symbols which are pre-determined (like loop index 69 // variables) happen separately, for everything else privatize here. 70 if (sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined)) 71 continue; 72 bool success = converter.createHostAssociateVarClone(*sym); 73 (void)success; 74 assert(success && "Privatization failed due to existing binding"); 75 if constexpr (std::is_same_v<T, Fortran::parser::OmpClause::Firstprivate>) { 76 converter.copyHostAssociateVar(*sym); 77 } 78 } 79 } 80 81 static void privatizeVars(Fortran::lower::AbstractConverter &converter, 82 const Fortran::parser::OmpClauseList &opClauseList) { 83 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 84 auto insPt = firOpBuilder.saveInsertionPoint(); 85 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); 86 bool hasFirstPrivateOp = false; 87 for (const Fortran::parser::OmpClause &clause : opClauseList.v) { 88 if (const auto &privateClause = 89 std::get_if<Fortran::parser::OmpClause::Private>(&clause.u)) { 90 createPrivateVarSyms(converter, privateClause); 91 } else if (const auto &firstPrivateClause = 92 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 93 &clause.u)) { 94 createPrivateVarSyms(converter, firstPrivateClause); 95 hasFirstPrivateOp = true; 96 } 97 } 98 if (hasFirstPrivateOp) 99 firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation()); 100 firOpBuilder.restoreInsertionPoint(insPt); 101 } 102 103 /// The COMMON block is a global structure. \p commonValue is the base address 104 /// of the the COMMON block. As the offset from the symbol \p sym, generate the 105 /// COMMON block member value (commonValue + offset) for the symbol. 106 /// FIXME: Share the code with `instantiateCommon` in ConvertVariable.cpp. 107 static mlir::Value 108 genCommonBlockMember(Fortran::lower::AbstractConverter &converter, 109 const Fortran::semantics::Symbol &sym, 110 mlir::Value commonValue) { 111 auto &firOpBuilder = converter.getFirOpBuilder(); 112 mlir::Location currentLocation = converter.getCurrentLocation(); 113 mlir::IntegerType i8Ty = firOpBuilder.getIntegerType(8); 114 mlir::Type i8Ptr = firOpBuilder.getRefType(i8Ty); 115 mlir::Type seqTy = firOpBuilder.getRefType(firOpBuilder.getVarLenSeqTy(i8Ty)); 116 mlir::Value base = 117 firOpBuilder.createConvert(currentLocation, seqTy, commonValue); 118 std::size_t byteOffset = sym.GetUltimate().offset(); 119 mlir::Value offs = firOpBuilder.createIntegerConstant( 120 currentLocation, firOpBuilder.getIndexType(), byteOffset); 121 mlir::Value varAddr = firOpBuilder.create<fir::CoordinateOp>( 122 currentLocation, i8Ptr, base, mlir::ValueRange{offs}); 123 mlir::Type symType = converter.genType(sym); 124 return firOpBuilder.createConvert(currentLocation, 125 firOpBuilder.getRefType(symType), varAddr); 126 } 127 128 // Get the extended value for \p val by extracting additional variable 129 // information from \p base. 130 static fir::ExtendedValue getExtendedValue(fir::ExtendedValue base, 131 mlir::Value val) { 132 return base.match( 133 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue { 134 return fir::MutableBoxValue(val, box.nonDeferredLenParams(), {}); 135 }, 136 [&](const auto &) -> fir::ExtendedValue { 137 return fir::substBase(base, val); 138 }); 139 } 140 141 static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter, 142 Fortran::lower::pft::Evaluation &eval) { 143 auto &firOpBuilder = converter.getFirOpBuilder(); 144 mlir::Location currentLocation = converter.getCurrentLocation(); 145 auto insPt = firOpBuilder.saveInsertionPoint(); 146 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); 147 148 // Get the original ThreadprivateOp corresponding to the symbol and use the 149 // symbol value from that opeartion to create one ThreadprivateOp copy 150 // operation inside the parallel region. 151 auto genThreadprivateOp = [&](Fortran::lower::SymbolRef sym) -> mlir::Value { 152 mlir::Value symOriThreadprivateValue = converter.getSymbolAddress(sym); 153 mlir::Operation *op = symOriThreadprivateValue.getDefiningOp(); 154 assert(mlir::isa<mlir::omp::ThreadprivateOp>(op) && 155 "The threadprivate operation not created"); 156 mlir::Value symValue = 157 mlir::dyn_cast<mlir::omp::ThreadprivateOp>(op).sym_addr(); 158 return firOpBuilder.create<mlir::omp::ThreadprivateOp>( 159 currentLocation, symValue.getType(), symValue); 160 }; 161 162 llvm::SetVector<const Fortran::semantics::Symbol *> threadprivateSyms; 163 converter.collectSymbolSet(eval, threadprivateSyms, 164 Fortran::semantics::Symbol::Flag::OmpThreadprivate, 165 /*isUltimateSymbol=*/false); 166 std::set<Fortran::semantics::SourceName> threadprivateSymNames; 167 168 // For a COMMON block, the ThreadprivateOp is generated for itself instead of 169 // its members, so only bind the value of the new copied ThreadprivateOp 170 // inside the parallel region to the common block symbol only once for 171 // multiple members in one COMMON block. 172 llvm::SetVector<const Fortran::semantics::Symbol *> commonSyms; 173 for (std::size_t i = 0; i < threadprivateSyms.size(); i++) { 174 auto sym = threadprivateSyms[i]; 175 mlir::Value symThreadprivateValue; 176 // The variable may be used more than once, and each reference has one 177 // symbol with the same name. Only do once for references of one variable. 178 if (threadprivateSymNames.find(sym->name()) != threadprivateSymNames.end()) 179 continue; 180 threadprivateSymNames.insert(sym->name()); 181 if (const Fortran::semantics::Symbol *common = 182 Fortran::semantics::FindCommonBlockContaining(sym->GetUltimate())) { 183 mlir::Value commonThreadprivateValue; 184 if (commonSyms.contains(common)) { 185 commonThreadprivateValue = converter.getSymbolAddress(*common); 186 } else { 187 commonThreadprivateValue = genThreadprivateOp(*common); 188 converter.bindSymbol(*common, commonThreadprivateValue); 189 commonSyms.insert(common); 190 } 191 symThreadprivateValue = 192 genCommonBlockMember(converter, *sym, commonThreadprivateValue); 193 } else { 194 symThreadprivateValue = genThreadprivateOp(*sym); 195 } 196 197 fir::ExtendedValue sexv = converter.getSymbolExtendedValue(*sym); 198 fir::ExtendedValue symThreadprivateExv = 199 getExtendedValue(sexv, symThreadprivateValue); 200 converter.bindSymbol(*sym, symThreadprivateExv); 201 } 202 203 firOpBuilder.restoreInsertionPoint(insPt); 204 } 205 206 static void 207 genCopyinClause(Fortran::lower::AbstractConverter &converter, 208 const Fortran::parser::OmpClauseList &opClauseList) { 209 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 210 mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint(); 211 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); 212 bool hasCopyin = false; 213 for (const Fortran::parser::OmpClause &clause : opClauseList.v) { 214 if (const auto ©inClause = 215 std::get_if<Fortran::parser::OmpClause::Copyin>(&clause.u)) { 216 hasCopyin = true; 217 const Fortran::parser::OmpObjectList &ompObjectList = copyinClause->v; 218 for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) { 219 Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject); 220 if (sym->has<Fortran::semantics::CommonBlockDetails>()) 221 TODO(converter.getCurrentLocation(), "common block in Copyin clause"); 222 if (Fortran::semantics::IsAllocatableOrPointer(sym->GetUltimate())) 223 TODO(converter.getCurrentLocation(), 224 "pointer or allocatable variables in Copyin clause"); 225 assert(sym->has<Fortran::semantics::HostAssocDetails>() && 226 "No host-association found"); 227 converter.copyHostAssociateVar(*sym); 228 } 229 } 230 } 231 if (hasCopyin) 232 firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation()); 233 firOpBuilder.restoreInsertionPoint(insPt); 234 } 235 236 static void genObjectList(const Fortran::parser::OmpObjectList &objectList, 237 Fortran::lower::AbstractConverter &converter, 238 llvm::SmallVectorImpl<Value> &operands) { 239 auto addOperands = [&](Fortran::lower::SymbolRef sym) { 240 const mlir::Value variable = converter.getSymbolAddress(sym); 241 if (variable) { 242 operands.push_back(variable); 243 } else { 244 if (const auto *details = 245 sym->detailsIf<Fortran::semantics::HostAssocDetails>()) { 246 operands.push_back(converter.getSymbolAddress(details->symbol())); 247 converter.copySymbolBinding(details->symbol(), sym); 248 } 249 } 250 }; 251 for (const Fortran::parser::OmpObject &ompObject : objectList.v) { 252 Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject); 253 addOperands(*sym); 254 } 255 } 256 257 static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter, 258 std::size_t loopVarTypeSize) { 259 // OpenMP runtime requires 32-bit or 64-bit loop variables. 260 loopVarTypeSize = loopVarTypeSize * 8; 261 if (loopVarTypeSize < 32) { 262 loopVarTypeSize = 32; 263 } else if (loopVarTypeSize > 64) { 264 loopVarTypeSize = 64; 265 mlir::emitWarning(converter.getCurrentLocation(), 266 "OpenMP loop iteration variable cannot have more than 64 " 267 "bits size and will be narrowed into 64 bits."); 268 } 269 assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) && 270 "OpenMP loop iteration variable size must be transformed into 32-bit " 271 "or 64-bit"); 272 return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize); 273 } 274 275 /// Create empty blocks for the current region. 276 /// These blocks replace blocks parented to an enclosing region. 277 void createEmptyRegionBlocks( 278 fir::FirOpBuilder &firOpBuilder, 279 std::list<Fortran::lower::pft::Evaluation> &evaluationList) { 280 auto *region = &firOpBuilder.getRegion(); 281 for (auto &eval : evaluationList) { 282 if (eval.block) { 283 if (eval.block->empty()) { 284 eval.block->erase(); 285 eval.block = firOpBuilder.createBlock(region); 286 } else { 287 [[maybe_unused]] auto &terminatorOp = eval.block->back(); 288 assert((mlir::isa<mlir::omp::TerminatorOp>(terminatorOp) || 289 mlir::isa<mlir::omp::YieldOp>(terminatorOp)) && 290 "expected terminator op"); 291 } 292 } 293 if (!eval.isDirective() && eval.hasNestedEvaluations()) 294 createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations()); 295 } 296 } 297 298 /// Create the body (block) for an OpenMP Operation. 299 /// 300 /// \param [in] op - the operation the body belongs to. 301 /// \param [inout] converter - converter to use for the clauses. 302 /// \param [in] loc - location in source code. 303 /// \param [in] eval - current PFT node/evaluation. 304 /// \oaran [in] clauses - list of clauses to process. 305 /// \param [in] args - block arguments (induction variable[s]) for the 306 //// region. 307 /// \param [in] outerCombined - is this an outer operation - prevents 308 /// privatization. 309 template <typename Op> 310 static void 311 createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter, 312 mlir::Location &loc, Fortran::lower::pft::Evaluation &eval, 313 const Fortran::parser::OmpClauseList *clauses = nullptr, 314 const SmallVector<const Fortran::semantics::Symbol *> &args = {}, 315 bool outerCombined = false) { 316 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 317 // If an argument for the region is provided then create the block with that 318 // argument. Also update the symbol's address with the mlir argument value. 319 // e.g. For loops the argument is the induction variable. And all further 320 // uses of the induction variable should use this mlir value. 321 mlir::Operation *storeOp = nullptr; 322 if (args.size()) { 323 std::size_t loopVarTypeSize = 0; 324 for (const Fortran::semantics::Symbol *arg : args) 325 loopVarTypeSize = std::max(loopVarTypeSize, arg->GetUltimate().size()); 326 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize); 327 SmallVector<Type> tiv; 328 SmallVector<Location> locs; 329 for (int i = 0; i < (int)args.size(); i++) { 330 tiv.push_back(loopVarType); 331 locs.push_back(loc); 332 } 333 firOpBuilder.createBlock(&op.getRegion(), {}, tiv, locs); 334 int argIndex = 0; 335 // The argument is not currently in memory, so make a temporary for the 336 // argument, and store it there, then bind that location to the argument. 337 for (const Fortran::semantics::Symbol *arg : args) { 338 mlir::Value val = 339 fir::getBase(op.getRegion().front().getArgument(argIndex)); 340 mlir::Value temp = firOpBuilder.createTemporary( 341 loc, loopVarType, 342 llvm::ArrayRef<mlir::NamedAttribute>{ 343 Fortran::lower::getAdaptToByRefAttr(firOpBuilder)}); 344 storeOp = firOpBuilder.create<fir::StoreOp>(loc, val, temp); 345 converter.bindSymbol(*arg, temp); 346 argIndex++; 347 } 348 } else { 349 firOpBuilder.createBlock(&op.getRegion()); 350 } 351 // Set the insert for the terminator operation to go at the end of the 352 // block - this is either empty or the block with the stores above, 353 // the end of the block works for both. 354 mlir::Block &block = op.getRegion().back(); 355 firOpBuilder.setInsertionPointToEnd(&block); 356 357 // If it is an unstructured region and is not the outer region of a combined 358 // construct, create empty blocks for all evaluations. 359 if (eval.lowerAsUnstructured() && !outerCombined) 360 createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations()); 361 362 // Insert the terminator. 363 if constexpr (std::is_same_v<Op, omp::WsLoopOp> || 364 std::is_same_v<Op, omp::SimdLoopOp>) { 365 mlir::ValueRange results; 366 firOpBuilder.create<mlir::omp::YieldOp>(loc, results); 367 } else { 368 firOpBuilder.create<mlir::omp::TerminatorOp>(loc); 369 } 370 371 // Reset the insert point to before the terminator. 372 if (storeOp) 373 firOpBuilder.setInsertionPointAfter(storeOp); 374 else 375 firOpBuilder.setInsertionPointToStart(&block); 376 377 // Handle privatization. Do not privatize if this is the outer operation. 378 if (clauses && !outerCombined) 379 privatizeVars(converter, *clauses); 380 381 if (std::is_same_v<Op, omp::ParallelOp>) { 382 threadPrivatizeVars(converter, eval); 383 if (clauses) 384 genCopyinClause(converter, *clauses); 385 } 386 } 387 388 static void genOMP(Fortran::lower::AbstractConverter &converter, 389 Fortran::lower::pft::Evaluation &eval, 390 const Fortran::parser::OpenMPSimpleStandaloneConstruct 391 &simpleStandaloneConstruct) { 392 const auto &directive = 393 std::get<Fortran::parser::OmpSimpleStandaloneDirective>( 394 simpleStandaloneConstruct.t); 395 switch (directive.v) { 396 default: 397 break; 398 case llvm::omp::Directive::OMPD_barrier: 399 converter.getFirOpBuilder().create<mlir::omp::BarrierOp>( 400 converter.getCurrentLocation()); 401 break; 402 case llvm::omp::Directive::OMPD_taskwait: 403 converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>( 404 converter.getCurrentLocation()); 405 break; 406 case llvm::omp::Directive::OMPD_taskyield: 407 converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>( 408 converter.getCurrentLocation()); 409 break; 410 case llvm::omp::Directive::OMPD_target_enter_data: 411 TODO(converter.getCurrentLocation(), "OMPD_target_enter_data"); 412 case llvm::omp::Directive::OMPD_target_exit_data: 413 TODO(converter.getCurrentLocation(), "OMPD_target_exit_data"); 414 case llvm::omp::Directive::OMPD_target_update: 415 TODO(converter.getCurrentLocation(), "OMPD_target_update"); 416 case llvm::omp::Directive::OMPD_ordered: 417 TODO(converter.getCurrentLocation(), "OMPD_ordered"); 418 } 419 } 420 421 static void 422 genAllocateClause(Fortran::lower::AbstractConverter &converter, 423 const Fortran::parser::OmpAllocateClause &ompAllocateClause, 424 SmallVector<Value> &allocatorOperands, 425 SmallVector<Value> &allocateOperands) { 426 auto &firOpBuilder = converter.getFirOpBuilder(); 427 auto currentLocation = converter.getCurrentLocation(); 428 Fortran::lower::StatementContext stmtCtx; 429 430 mlir::Value allocatorOperand; 431 const Fortran::parser::OmpObjectList &ompObjectList = 432 std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t); 433 const auto &allocatorValue = 434 std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>( 435 ompAllocateClause.t); 436 // Check if allocate clause has allocator specified. If so, add it 437 // to list of allocators, otherwise, add default allocator to 438 // list of allocators. 439 if (allocatorValue) { 440 allocatorOperand = fir::getBase(converter.genExprValue( 441 *Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx)); 442 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 443 allocatorOperand); 444 } else { 445 allocatorOperand = firOpBuilder.createIntegerConstant( 446 currentLocation, firOpBuilder.getI32Type(), 1); 447 allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(), 448 allocatorOperand); 449 } 450 genObjectList(ompObjectList, converter, allocateOperands); 451 } 452 453 static void 454 genOMP(Fortran::lower::AbstractConverter &converter, 455 Fortran::lower::pft::Evaluation &eval, 456 const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) { 457 std::visit( 458 Fortran::common::visitors{ 459 [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct 460 &simpleStandaloneConstruct) { 461 genOMP(converter, eval, simpleStandaloneConstruct); 462 }, 463 [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) { 464 SmallVector<Value, 4> operandRange; 465 if (const auto &ompObjectList = 466 std::get<std::optional<Fortran::parser::OmpObjectList>>( 467 flushConstruct.t)) 468 genObjectList(*ompObjectList, converter, operandRange); 469 const auto &memOrderClause = std::get<std::optional< 470 std::list<Fortran::parser::OmpMemoryOrderClause>>>( 471 flushConstruct.t); 472 if (memOrderClause.has_value() && memOrderClause->size() > 0) 473 TODO(converter.getCurrentLocation(), 474 "Handle OmpMemoryOrderClause"); 475 converter.getFirOpBuilder().create<mlir::omp::FlushOp>( 476 converter.getCurrentLocation(), operandRange); 477 }, 478 [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) { 479 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 480 }, 481 [&](const Fortran::parser::OpenMPCancellationPointConstruct 482 &cancellationPointConstruct) { 483 TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct"); 484 }, 485 }, 486 standaloneConstruct.u); 487 } 488 489 static omp::ClauseProcBindKindAttr genProcBindKindAttr( 490 fir::FirOpBuilder &firOpBuilder, 491 const Fortran::parser::OmpClause::ProcBind *procBindClause) { 492 omp::ClauseProcBindKind pbKind; 493 switch (procBindClause->v.v) { 494 case Fortran::parser::OmpProcBindClause::Type::Master: 495 pbKind = omp::ClauseProcBindKind::Master; 496 break; 497 case Fortran::parser::OmpProcBindClause::Type::Close: 498 pbKind = omp::ClauseProcBindKind::Close; 499 break; 500 case Fortran::parser::OmpProcBindClause::Type::Spread: 501 pbKind = omp::ClauseProcBindKind::Spread; 502 break; 503 case Fortran::parser::OmpProcBindClause::Type::Primary: 504 pbKind = omp::ClauseProcBindKind::Primary; 505 break; 506 } 507 return omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind); 508 } 509 510 /* When parallel is used in a combined construct, then use this function to 511 * create the parallel operation. It handles the parallel specific clauses 512 * and leaves the rest for handling at the inner operations. 513 * TODO: Refactor clause handling 514 */ 515 template <typename Directive> 516 static void 517 createCombinedParallelOp(Fortran::lower::AbstractConverter &converter, 518 Fortran::lower::pft::Evaluation &eval, 519 const Directive &directive) { 520 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 521 mlir::Location currentLocation = converter.getCurrentLocation(); 522 Fortran::lower::StatementContext stmtCtx; 523 llvm::ArrayRef<mlir::Type> argTy; 524 mlir::Value ifClauseOperand, numThreadsClauseOperand; 525 SmallVector<Value> allocatorOperands, allocateOperands; 526 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 527 const auto &opClauseList = 528 std::get<Fortran::parser::OmpClauseList>(directive.t); 529 // TODO: Handle the following clauses 530 // 1. default 531 // Note: rest of the clauses are handled when the inner operation is created 532 for (const Fortran::parser::OmpClause &clause : opClauseList.v) { 533 if (const auto &ifClause = 534 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 535 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 536 mlir::Value ifVal = fir::getBase( 537 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 538 ifClauseOperand = firOpBuilder.createConvert( 539 currentLocation, firOpBuilder.getI1Type(), ifVal); 540 } else if (const auto &numThreadsClause = 541 std::get_if<Fortran::parser::OmpClause::NumThreads>( 542 &clause.u)) { 543 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 544 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 545 } else if (const auto &procBindClause = 546 std::get_if<Fortran::parser::OmpClause::ProcBind>( 547 &clause.u)) { 548 procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause); 549 } 550 } 551 // Create and insert the operation. 552 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 553 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 554 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 555 /*reductions=*/nullptr, procBindKindAttr); 556 557 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, eval, 558 &opClauseList, /*iv=*/{}, 559 /*isCombined=*/true); 560 } 561 562 static void 563 genOMP(Fortran::lower::AbstractConverter &converter, 564 Fortran::lower::pft::Evaluation &eval, 565 const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 566 const auto &beginBlockDirective = 567 std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t); 568 const auto &blockDirective = 569 std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t); 570 const auto &endBlockDirective = 571 std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t); 572 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 573 mlir::Location currentLocation = converter.getCurrentLocation(); 574 575 Fortran::lower::StatementContext stmtCtx; 576 llvm::ArrayRef<mlir::Type> argTy; 577 mlir::Value ifClauseOperand, numThreadsClauseOperand, finalClauseOperand, 578 priorityClauseOperand; 579 mlir::omp::ClauseProcBindKindAttr procBindKindAttr; 580 SmallVector<Value> allocateOperands, allocatorOperands; 581 mlir::UnitAttr nowaitAttr, untiedAttr, mergeableAttr; 582 583 const auto &opClauseList = 584 std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t); 585 for (const auto &clause : opClauseList.v) { 586 if (const auto &ifClause = 587 std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) { 588 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t); 589 mlir::Value ifVal = fir::getBase( 590 converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx)); 591 ifClauseOperand = firOpBuilder.createConvert( 592 currentLocation, firOpBuilder.getI1Type(), ifVal); 593 } else if (const auto &numThreadsClause = 594 std::get_if<Fortran::parser::OmpClause::NumThreads>( 595 &clause.u)) { 596 // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`. 597 numThreadsClauseOperand = fir::getBase(converter.genExprValue( 598 *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx)); 599 } else if (const auto &procBindClause = 600 std::get_if<Fortran::parser::OmpClause::ProcBind>( 601 &clause.u)) { 602 procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause); 603 } else if (const auto &allocateClause = 604 std::get_if<Fortran::parser::OmpClause::Allocate>( 605 &clause.u)) { 606 genAllocateClause(converter, allocateClause->v, allocatorOperands, 607 allocateOperands); 608 } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) || 609 std::get_if<Fortran::parser::OmpClause::Firstprivate>( 610 &clause.u) || 611 std::get_if<Fortran::parser::OmpClause::Copyin>(&clause.u)) { 612 // Privatisation and copyin clauses are handled elsewhere. 613 continue; 614 } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) { 615 // Nothing needs to be done for threads clause. 616 continue; 617 } else if (const auto &finalClause = 618 std::get_if<Fortran::parser::OmpClause::Final>(&clause.u)) { 619 mlir::Value finalVal = fir::getBase(converter.genExprValue( 620 *Fortran::semantics::GetExpr(finalClause->v), stmtCtx)); 621 finalClauseOperand = firOpBuilder.createConvert( 622 currentLocation, firOpBuilder.getI1Type(), finalVal); 623 } else if (std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u)) { 624 untiedAttr = firOpBuilder.getUnitAttr(); 625 } else if (std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u)) { 626 mergeableAttr = firOpBuilder.getUnitAttr(); 627 } else if (const auto &priorityClause = 628 std::get_if<Fortran::parser::OmpClause::Priority>( 629 &clause.u)) { 630 priorityClauseOperand = fir::getBase(converter.genExprValue( 631 *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx)); 632 } else { 633 TODO(currentLocation, "OpenMP Block construct clauses"); 634 } 635 } 636 637 for (const auto &clause : 638 std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) { 639 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 640 nowaitAttr = firOpBuilder.getUnitAttr(); 641 } 642 643 if (blockDirective.v == llvm::omp::OMPD_parallel) { 644 // Create and insert the operation. 645 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>( 646 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand, 647 allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(), 648 /*reductions=*/nullptr, procBindKindAttr); 649 createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, 650 eval, &opClauseList); 651 } else if (blockDirective.v == llvm::omp::OMPD_master) { 652 auto masterOp = 653 firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy); 654 createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation, eval); 655 } else if (blockDirective.v == llvm::omp::OMPD_single) { 656 auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>( 657 currentLocation, allocateOperands, allocatorOperands, nowaitAttr); 658 createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval); 659 } else if (blockDirective.v == llvm::omp::OMPD_ordered) { 660 auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>( 661 currentLocation, /*simd=*/nullptr); 662 createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation, 663 eval); 664 } else if (blockDirective.v == llvm::omp::OMPD_task) { 665 auto taskOp = firOpBuilder.create<mlir::omp::TaskOp>( 666 currentLocation, ifClauseOperand, finalClauseOperand, untiedAttr, 667 mergeableAttr, /*in_reduction_vars=*/ValueRange(), 668 /*in_reductions=*/nullptr, priorityClauseOperand, allocateOperands, 669 allocatorOperands); 670 createBodyOfOp(taskOp, converter, currentLocation, eval, &opClauseList); 671 } else { 672 TODO(converter.getCurrentLocation(), "Unhandled block directive"); 673 } 674 } 675 676 static mlir::omp::ScheduleModifier 677 translateModifier(const Fortran::parser::OmpScheduleModifierType &m) { 678 switch (m.v) { 679 case Fortran::parser::OmpScheduleModifierType::ModType::Monotonic: 680 return mlir::omp::ScheduleModifier::monotonic; 681 case Fortran::parser::OmpScheduleModifierType::ModType::Nonmonotonic: 682 return mlir::omp::ScheduleModifier::nonmonotonic; 683 case Fortran::parser::OmpScheduleModifierType::ModType::Simd: 684 return mlir::omp::ScheduleModifier::simd; 685 } 686 return mlir::omp::ScheduleModifier::none; 687 } 688 689 static mlir::omp::ScheduleModifier 690 getScheduleModifier(const Fortran::parser::OmpScheduleClause &x) { 691 const auto &modifier = 692 std::get<std::optional<Fortran::parser::OmpScheduleModifier>>(x.t); 693 // The input may have the modifier any order, so we look for one that isn't 694 // SIMD. If modifier is not set at all, fall down to the bottom and return 695 // "none". 696 if (modifier) { 697 const auto &modType1 = 698 std::get<Fortran::parser::OmpScheduleModifier::Modifier1>(modifier->t); 699 if (modType1.v.v == 700 Fortran::parser::OmpScheduleModifierType::ModType::Simd) { 701 const auto &modType2 = std::get< 702 std::optional<Fortran::parser::OmpScheduleModifier::Modifier2>>( 703 modifier->t); 704 if (modType2 && 705 modType2->v.v != 706 Fortran::parser::OmpScheduleModifierType::ModType::Simd) 707 return translateModifier(modType2->v); 708 709 return mlir::omp::ScheduleModifier::none; 710 } 711 712 return translateModifier(modType1.v); 713 } 714 return mlir::omp::ScheduleModifier::none; 715 } 716 717 static mlir::omp::ScheduleModifier 718 getSIMDModifier(const Fortran::parser::OmpScheduleClause &x) { 719 const auto &modifier = 720 std::get<std::optional<Fortran::parser::OmpScheduleModifier>>(x.t); 721 // Either of the two possible modifiers in the input can be the SIMD modifier, 722 // so look in either one, and return simd if we find one. Not found = return 723 // "none". 724 if (modifier) { 725 const auto &modType1 = 726 std::get<Fortran::parser::OmpScheduleModifier::Modifier1>(modifier->t); 727 if (modType1.v.v == Fortran::parser::OmpScheduleModifierType::ModType::Simd) 728 return mlir::omp::ScheduleModifier::simd; 729 730 const auto &modType2 = std::get< 731 std::optional<Fortran::parser::OmpScheduleModifier::Modifier2>>( 732 modifier->t); 733 if (modType2 && modType2->v.v == 734 Fortran::parser::OmpScheduleModifierType::ModType::Simd) 735 return mlir::omp::ScheduleModifier::simd; 736 } 737 return mlir::omp::ScheduleModifier::none; 738 } 739 740 static void genOMP(Fortran::lower::AbstractConverter &converter, 741 Fortran::lower::pft::Evaluation &eval, 742 const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 743 744 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 745 mlir::Location currentLocation = converter.getCurrentLocation(); 746 llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars, 747 linearStepVars, reductionVars; 748 mlir::Value scheduleChunkClauseOperand; 749 mlir::Attribute scheduleClauseOperand, collapseClauseOperand, 750 noWaitClauseOperand, orderedClauseOperand, orderClauseOperand; 751 const auto &loopOpClauseList = std::get<Fortran::parser::OmpClauseList>( 752 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t); 753 754 const auto ompDirective = 755 std::get<Fortran::parser::OmpLoopDirective>( 756 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t) 757 .v; 758 if (llvm::omp::OMPD_parallel_do == ompDirective) { 759 createCombinedParallelOp<Fortran::parser::OmpBeginLoopDirective>( 760 converter, eval, 761 std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t)); 762 } else if (llvm::omp::OMPD_do != ompDirective && 763 llvm::omp::OMPD_simd != ompDirective) { 764 TODO(converter.getCurrentLocation(), "Construct enclosing do loop"); 765 } 766 767 // Collect the loops to collapse. 768 auto *doConstructEval = &eval.getFirstNestedEvaluation(); 769 770 std::int64_t collapseValue = 771 Fortran::lower::getCollapseValue(loopOpClauseList); 772 std::size_t loopVarTypeSize = 0; 773 SmallVector<const Fortran::semantics::Symbol *> iv; 774 do { 775 auto *doLoop = &doConstructEval->getFirstNestedEvaluation(); 776 auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>(); 777 assert(doStmt && "Expected do loop to be in the nested evaluation"); 778 const auto &loopControl = 779 std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t); 780 const Fortran::parser::LoopControl::Bounds *bounds = 781 std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u); 782 assert(bounds && "Expected bounds for worksharing do loop"); 783 Fortran::lower::StatementContext stmtCtx; 784 lowerBound.push_back(fir::getBase(converter.genExprValue( 785 *Fortran::semantics::GetExpr(bounds->lower), stmtCtx))); 786 upperBound.push_back(fir::getBase(converter.genExprValue( 787 *Fortran::semantics::GetExpr(bounds->upper), stmtCtx))); 788 if (bounds->step) { 789 step.push_back(fir::getBase(converter.genExprValue( 790 *Fortran::semantics::GetExpr(bounds->step), stmtCtx))); 791 } else { // If `step` is not present, assume it as `1`. 792 step.push_back(firOpBuilder.createIntegerConstant( 793 currentLocation, firOpBuilder.getIntegerType(32), 1)); 794 } 795 iv.push_back(bounds->name.thing.symbol); 796 loopVarTypeSize = std::max(loopVarTypeSize, 797 bounds->name.thing.symbol->GetUltimate().size()); 798 799 collapseValue--; 800 doConstructEval = 801 &*std::next(doConstructEval->getNestedEvaluations().begin()); 802 } while (collapseValue > 0); 803 804 for (const auto &clause : loopOpClauseList.v) { 805 if (const auto &scheduleClause = 806 std::get_if<Fortran::parser::OmpClause::Schedule>(&clause.u)) { 807 if (const auto &chunkExpr = 808 std::get<std::optional<Fortran::parser::ScalarIntExpr>>( 809 scheduleClause->v.t)) { 810 if (const auto *expr = Fortran::semantics::GetExpr(*chunkExpr)) { 811 Fortran::lower::StatementContext stmtCtx; 812 scheduleChunkClauseOperand = 813 fir::getBase(converter.genExprValue(*expr, stmtCtx)); 814 } 815 } 816 } 817 } 818 819 // The types of lower bound, upper bound, and step are converted into the 820 // type of the loop variable if necessary. 821 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize); 822 for (unsigned it = 0; it < (unsigned)lowerBound.size(); it++) { 823 lowerBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType, 824 lowerBound[it]); 825 upperBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType, 826 upperBound[it]); 827 step[it] = 828 firOpBuilder.createConvert(currentLocation, loopVarType, step[it]); 829 } 830 831 // 2.9.3.1 SIMD construct 832 // TODO: Support all the clauses 833 if (llvm::omp::OMPD_simd == ompDirective) { 834 TypeRange resultType; 835 auto SimdLoopOp = firOpBuilder.create<mlir::omp::SimdLoopOp>( 836 currentLocation, resultType, lowerBound, upperBound, step); 837 createBodyOfOp<omp::SimdLoopOp>(SimdLoopOp, converter, currentLocation, 838 eval, &loopOpClauseList, iv); 839 return; 840 } 841 842 // FIXME: Add support for following clauses: 843 // 1. linear 844 // 2. order 845 auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>( 846 currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars, 847 reductionVars, /*reductions=*/nullptr, 848 scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(), 849 scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr, 850 /*simd_modifier=*/nullptr, 851 collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(), 852 noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(), 853 orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(), 854 orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(), 855 /*inclusive=*/firOpBuilder.getUnitAttr()); 856 857 // Handle attribute based clauses. 858 for (const Fortran::parser::OmpClause &clause : loopOpClauseList.v) { 859 if (const auto &orderedClause = 860 std::get_if<Fortran::parser::OmpClause::Ordered>(&clause.u)) { 861 if (orderedClause->v.has_value()) { 862 const auto *expr = Fortran::semantics::GetExpr(orderedClause->v); 863 const std::optional<std::int64_t> orderedClauseValue = 864 Fortran::evaluate::ToInt64(*expr); 865 wsLoopOp.ordered_valAttr( 866 firOpBuilder.getI64IntegerAttr(*orderedClauseValue)); 867 } else { 868 wsLoopOp.ordered_valAttr(firOpBuilder.getI64IntegerAttr(0)); 869 } 870 } else if (const auto &collapseClause = 871 std::get_if<Fortran::parser::OmpClause::Collapse>( 872 &clause.u)) { 873 const auto *expr = Fortran::semantics::GetExpr(collapseClause->v); 874 const std::optional<std::int64_t> collapseValue = 875 Fortran::evaluate::ToInt64(*expr); 876 wsLoopOp.collapse_valAttr(firOpBuilder.getI64IntegerAttr(*collapseValue)); 877 } else if (const auto &scheduleClause = 878 std::get_if<Fortran::parser::OmpClause::Schedule>( 879 &clause.u)) { 880 mlir::MLIRContext *context = firOpBuilder.getContext(); 881 const auto &scheduleType = scheduleClause->v; 882 const auto &scheduleKind = 883 std::get<Fortran::parser::OmpScheduleClause::ScheduleType>( 884 scheduleType.t); 885 switch (scheduleKind) { 886 case Fortran::parser::OmpScheduleClause::ScheduleType::Static: 887 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 888 context, omp::ClauseScheduleKind::Static)); 889 break; 890 case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic: 891 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 892 context, omp::ClauseScheduleKind::Dynamic)); 893 break; 894 case Fortran::parser::OmpScheduleClause::ScheduleType::Guided: 895 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 896 context, omp::ClauseScheduleKind::Guided)); 897 break; 898 case Fortran::parser::OmpScheduleClause::ScheduleType::Auto: 899 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 900 context, omp::ClauseScheduleKind::Auto)); 901 break; 902 case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime: 903 wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get( 904 context, omp::ClauseScheduleKind::Runtime)); 905 break; 906 } 907 mlir::omp::ScheduleModifier scheduleModifier = 908 getScheduleModifier(scheduleClause->v); 909 if (scheduleModifier != mlir::omp::ScheduleModifier::none) 910 wsLoopOp.schedule_modifierAttr( 911 omp::ScheduleModifierAttr::get(context, scheduleModifier)); 912 if (getSIMDModifier(scheduleClause->v) != 913 mlir::omp::ScheduleModifier::none) 914 wsLoopOp.simd_modifierAttr(firOpBuilder.getUnitAttr()); 915 } 916 } 917 // In FORTRAN `nowait` clause occur at the end of `omp do` directive. 918 // i.e 919 // !$omp do 920 // <...> 921 // !$omp end do nowait 922 if (const auto &endClauseList = 923 std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>( 924 loopConstruct.t)) { 925 const auto &clauseList = 926 std::get<Fortran::parser::OmpClauseList>((*endClauseList).t); 927 for (const Fortran::parser::OmpClause &clause : clauseList.v) 928 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) 929 wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr()); 930 } 931 932 createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, eval, 933 &loopOpClauseList, iv); 934 } 935 936 static void 937 genOMP(Fortran::lower::AbstractConverter &converter, 938 Fortran::lower::pft::Evaluation &eval, 939 const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) { 940 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 941 mlir::Location currentLocation = converter.getCurrentLocation(); 942 std::string name; 943 const Fortran::parser::OmpCriticalDirective &cd = 944 std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t); 945 if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) { 946 name = 947 std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString(); 948 } 949 950 uint64_t hint = 0; 951 const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t); 952 for (const Fortran::parser::OmpClause &clause : clauseList.v) 953 if (auto hintClause = 954 std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) { 955 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 956 hint = *Fortran::evaluate::ToInt64(*expr); 957 break; 958 } 959 960 mlir::omp::CriticalOp criticalOp = [&]() { 961 if (name.empty()) { 962 return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation, 963 FlatSymbolRefAttr()); 964 } else { 965 mlir::ModuleOp module = firOpBuilder.getModule(); 966 mlir::OpBuilder modBuilder(module.getBodyRegion()); 967 auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name); 968 if (!global) 969 global = modBuilder.create<mlir::omp::CriticalDeclareOp>( 970 currentLocation, name, hint); 971 return firOpBuilder.create<mlir::omp::CriticalOp>( 972 currentLocation, mlir::FlatSymbolRefAttr::get( 973 firOpBuilder.getContext(), global.sym_name())); 974 } 975 }(); 976 createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation, eval); 977 } 978 979 static void 980 genOMP(Fortran::lower::AbstractConverter &converter, 981 Fortran::lower::pft::Evaluation &eval, 982 const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 983 984 auto &firOpBuilder = converter.getFirOpBuilder(); 985 auto currentLocation = converter.getCurrentLocation(); 986 mlir::omp::SectionOp sectionOp = 987 firOpBuilder.create<mlir::omp::SectionOp>(currentLocation); 988 createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation, eval); 989 } 990 991 // TODO: Add support for reduction 992 static void 993 genOMP(Fortran::lower::AbstractConverter &converter, 994 Fortran::lower::pft::Evaluation &eval, 995 const Fortran::parser::OpenMPSectionsConstruct §ionsConstruct) { 996 auto &firOpBuilder = converter.getFirOpBuilder(); 997 auto currentLocation = converter.getCurrentLocation(); 998 SmallVector<Value> reductionVars, allocateOperands, allocatorOperands; 999 mlir::UnitAttr noWaitClauseOperand; 1000 const auto §ionsClauseList = std::get<Fortran::parser::OmpClauseList>( 1001 std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t) 1002 .t); 1003 for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) { 1004 1005 // Reduction Clause 1006 if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) { 1007 TODO(currentLocation, "OMPC_Reduction"); 1008 1009 // Allocate clause 1010 } else if (const auto &allocateClause = 1011 std::get_if<Fortran::parser::OmpClause::Allocate>( 1012 &clause.u)) { 1013 genAllocateClause(converter, allocateClause->v, allocatorOperands, 1014 allocateOperands); 1015 } 1016 } 1017 const auto &endSectionsClauseList = 1018 std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t); 1019 const auto &clauseList = 1020 std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t); 1021 for (const auto &clause : clauseList.v) { 1022 // Nowait clause 1023 if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) { 1024 noWaitClauseOperand = firOpBuilder.getUnitAttr(); 1025 } 1026 } 1027 1028 llvm::omp::Directive dir = 1029 std::get<Fortran::parser::OmpSectionsDirective>( 1030 std::get<Fortran::parser::OmpBeginSectionsDirective>( 1031 sectionsConstruct.t) 1032 .t) 1033 .v; 1034 1035 // Parallel Sections Construct 1036 if (dir == llvm::omp::Directive::OMPD_parallel_sections) { 1037 createCombinedParallelOp<Fortran::parser::OmpBeginSectionsDirective>( 1038 converter, eval, 1039 std::get<Fortran::parser::OmpBeginSectionsDirective>( 1040 sectionsConstruct.t)); 1041 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 1042 currentLocation, /*reduction_vars*/ ValueRange(), 1043 /*reductions=*/nullptr, allocateOperands, allocatorOperands, 1044 /*nowait=*/nullptr); 1045 createBodyOfOp(sectionsOp, converter, currentLocation, eval); 1046 1047 // Sections Construct 1048 } else if (dir == llvm::omp::Directive::OMPD_sections) { 1049 auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>( 1050 currentLocation, reductionVars, /*reductions = */ nullptr, 1051 allocateOperands, allocatorOperands, noWaitClauseOperand); 1052 createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation, 1053 eval); 1054 } 1055 } 1056 1057 static void genOmpAtomicHintAndMemoryOrderClauses( 1058 Fortran::lower::AbstractConverter &converter, 1059 const Fortran::parser::OmpAtomicClauseList &clauseList, 1060 mlir::IntegerAttr &hint, 1061 mlir::omp::ClauseMemoryOrderKindAttr &memory_order) { 1062 auto &firOpBuilder = converter.getFirOpBuilder(); 1063 for (const auto &clause : clauseList.v) { 1064 if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) { 1065 if (auto hintClause = 1066 std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) { 1067 const auto *expr = Fortran::semantics::GetExpr(hintClause->v); 1068 uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr); 1069 hint = firOpBuilder.getI64IntegerAttr(hintExprValue); 1070 } 1071 } else if (auto ompMemoryOrderClause = 1072 std::get_if<Fortran::parser::OmpMemoryOrderClause>( 1073 &clause.u)) { 1074 if (std::get_if<Fortran::parser::OmpClause::Acquire>( 1075 &ompMemoryOrderClause->v.u)) { 1076 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 1077 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire); 1078 } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>( 1079 &ompMemoryOrderClause->v.u)) { 1080 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 1081 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed); 1082 } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>( 1083 &ompMemoryOrderClause->v.u)) { 1084 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 1085 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst); 1086 } else if (std::get_if<Fortran::parser::OmpClause::Release>( 1087 &ompMemoryOrderClause->v.u)) { 1088 memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get( 1089 firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release); 1090 } 1091 } 1092 } 1093 } 1094 1095 static void 1096 genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter, 1097 Fortran::lower::pft::Evaluation &eval, 1098 const Fortran::parser::OmpAtomicWrite &atomicWrite) { 1099 auto &firOpBuilder = converter.getFirOpBuilder(); 1100 auto currentLocation = converter.getCurrentLocation(); 1101 // Get the value and address of atomic write operands. 1102 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 1103 std::get<2>(atomicWrite.t); 1104 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 1105 std::get<0>(atomicWrite.t); 1106 const auto &assignmentStmtExpr = 1107 std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t); 1108 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 1109 std::get<3>(atomicWrite.t).statement.t); 1110 Fortran::lower::StatementContext stmtCtx; 1111 mlir::Value value = fir::getBase(converter.genExprValue( 1112 *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); 1113 mlir::Value address = fir::getBase(converter.genExprAddr( 1114 *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); 1115 // If no hint clause is specified, the effect is as if 1116 // hint(omp_sync_hint_none) had been specified. 1117 mlir::IntegerAttr hint = nullptr; 1118 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 1119 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 1120 memory_order); 1121 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 1122 memory_order); 1123 firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value, 1124 hint, memory_order); 1125 } 1126 1127 static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter, 1128 Fortran::lower::pft::Evaluation &eval, 1129 const Fortran::parser::OmpAtomicRead &atomicRead) { 1130 auto &firOpBuilder = converter.getFirOpBuilder(); 1131 auto currentLocation = converter.getCurrentLocation(); 1132 // Get the address of atomic read operands. 1133 const Fortran::parser::OmpAtomicClauseList &rightHandClauseList = 1134 std::get<2>(atomicRead.t); 1135 const Fortran::parser::OmpAtomicClauseList &leftHandClauseList = 1136 std::get<0>(atomicRead.t); 1137 const auto &assignmentStmtExpr = 1138 std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t); 1139 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>( 1140 std::get<3>(atomicRead.t).statement.t); 1141 Fortran::lower::StatementContext stmtCtx; 1142 mlir::Value from_address = fir::getBase(converter.genExprAddr( 1143 *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx)); 1144 mlir::Value to_address = fir::getBase(converter.genExprAddr( 1145 *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx)); 1146 // If no hint clause is specified, the effect is as if 1147 // hint(omp_sync_hint_none) had been specified. 1148 mlir::IntegerAttr hint = nullptr; 1149 mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr; 1150 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint, 1151 memory_order); 1152 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint, 1153 memory_order); 1154 firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address, 1155 to_address, hint, memory_order); 1156 } 1157 1158 static void 1159 genOMP(Fortran::lower::AbstractConverter &converter, 1160 Fortran::lower::pft::Evaluation &eval, 1161 const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 1162 std::visit(Fortran::common::visitors{ 1163 [&](const Fortran::parser::OmpAtomicRead &atomicRead) { 1164 genOmpAtomicRead(converter, eval, atomicRead); 1165 }, 1166 [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) { 1167 genOmpAtomicWrite(converter, eval, atomicWrite); 1168 }, 1169 [&](const auto &) { 1170 TODO(converter.getCurrentLocation(), 1171 "Atomic update & capture"); 1172 }, 1173 }, 1174 atomicConstruct.u); 1175 } 1176 1177 void Fortran::lower::genOpenMPConstruct( 1178 Fortran::lower::AbstractConverter &converter, 1179 Fortran::lower::pft::Evaluation &eval, 1180 const Fortran::parser::OpenMPConstruct &ompConstruct) { 1181 1182 std::visit( 1183 common::visitors{ 1184 [&](const Fortran::parser::OpenMPStandaloneConstruct 1185 &standaloneConstruct) { 1186 genOMP(converter, eval, standaloneConstruct); 1187 }, 1188 [&](const Fortran::parser::OpenMPSectionsConstruct 1189 §ionsConstruct) { 1190 genOMP(converter, eval, sectionsConstruct); 1191 }, 1192 [&](const Fortran::parser::OpenMPSectionConstruct §ionConstruct) { 1193 genOMP(converter, eval, sectionConstruct); 1194 }, 1195 [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) { 1196 genOMP(converter, eval, loopConstruct); 1197 }, 1198 [&](const Fortran::parser::OpenMPDeclarativeAllocate 1199 &execAllocConstruct) { 1200 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 1201 }, 1202 [&](const Fortran::parser::OpenMPExecutableAllocate 1203 &execAllocConstruct) { 1204 TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate"); 1205 }, 1206 [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) { 1207 genOMP(converter, eval, blockConstruct); 1208 }, 1209 [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) { 1210 genOMP(converter, eval, atomicConstruct); 1211 }, 1212 [&](const Fortran::parser::OpenMPCriticalConstruct 1213 &criticalConstruct) { 1214 genOMP(converter, eval, criticalConstruct); 1215 }, 1216 }, 1217 ompConstruct.u); 1218 } 1219 1220 void Fortran::lower::genThreadprivateOp( 1221 Fortran::lower::AbstractConverter &converter, 1222 const Fortran::lower::pft::Variable &var) { 1223 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 1224 mlir::Location currentLocation = converter.getCurrentLocation(); 1225 1226 const Fortran::semantics::Symbol &sym = var.getSymbol(); 1227 mlir::Value symThreadprivateValue; 1228 if (const Fortran::semantics::Symbol *common = 1229 Fortran::semantics::FindCommonBlockContaining(sym.GetUltimate())) { 1230 mlir::Value commonValue = converter.getSymbolAddress(*common); 1231 if (mlir::isa<mlir::omp::ThreadprivateOp>(commonValue.getDefiningOp())) { 1232 // Generate ThreadprivateOp for a common block instead of its members and 1233 // only do it once for a common block. 1234 return; 1235 } 1236 // Generate ThreadprivateOp and rebind the common block. 1237 mlir::Value commonThreadprivateValue = 1238 firOpBuilder.create<mlir::omp::ThreadprivateOp>( 1239 currentLocation, commonValue.getType(), commonValue); 1240 converter.bindSymbol(*common, commonThreadprivateValue); 1241 // Generate the threadprivate value for the common block member. 1242 symThreadprivateValue = 1243 genCommonBlockMember(converter, sym, commonThreadprivateValue); 1244 } else { 1245 mlir::Value symValue = converter.getSymbolAddress(sym); 1246 symThreadprivateValue = firOpBuilder.create<mlir::omp::ThreadprivateOp>( 1247 currentLocation, symValue.getType(), symValue); 1248 } 1249 1250 fir::ExtendedValue sexv = converter.getSymbolExtendedValue(sym); 1251 fir::ExtendedValue symThreadprivateExv = 1252 getExtendedValue(sexv, symThreadprivateValue); 1253 converter.bindSymbol(sym, symThreadprivateExv); 1254 } 1255 1256 void Fortran::lower::genOpenMPDeclarativeConstruct( 1257 Fortran::lower::AbstractConverter &converter, 1258 Fortran::lower::pft::Evaluation &eval, 1259 const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) { 1260 1261 std::visit( 1262 common::visitors{ 1263 [&](const Fortran::parser::OpenMPDeclarativeAllocate 1264 &declarativeAllocate) { 1265 TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate"); 1266 }, 1267 [&](const Fortran::parser::OpenMPDeclareReductionConstruct 1268 &declareReductionConstruct) { 1269 TODO(converter.getCurrentLocation(), 1270 "OpenMPDeclareReductionConstruct"); 1271 }, 1272 [&](const Fortran::parser::OpenMPDeclareSimdConstruct 1273 &declareSimdConstruct) { 1274 TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct"); 1275 }, 1276 [&](const Fortran::parser::OpenMPDeclareTargetConstruct 1277 &declareTargetConstruct) { 1278 TODO(converter.getCurrentLocation(), 1279 "OpenMPDeclareTargetConstruct"); 1280 }, 1281 [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) { 1282 // The directive is lowered when instantiating the variable to 1283 // support the case of threadprivate variable declared in module. 1284 }, 1285 }, 1286 ompDeclConstruct.u); 1287 } 1288