1 //===-- ClauseProcessor.cpp -------------------------------------*- C++ -*-===// 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 "ClauseProcessor.h" 14 #include "Clauses.h" 15 16 #include "flang/Lower/PFTBuilder.h" 17 #include "flang/Parser/tools.h" 18 #include "flang/Semantics/tools.h" 19 #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" 20 21 namespace Fortran { 22 namespace lower { 23 namespace omp { 24 25 /// Check for unsupported map operand types. 26 static void checkMapType(mlir::Location location, mlir::Type type) { 27 if (auto refType = mlir::dyn_cast<fir::ReferenceType>(type)) 28 type = refType.getElementType(); 29 if (auto boxType = mlir::dyn_cast_or_null<fir::BoxType>(type)) 30 if (!mlir::isa<fir::PointerType>(boxType.getElementType())) 31 TODO(location, "OMPD_target_data MapOperand BoxType"); 32 } 33 34 static mlir::omp::ScheduleModifier 35 translateScheduleModifier(const omp::clause::Schedule::OrderingModifier &m) { 36 switch (m) { 37 case omp::clause::Schedule::OrderingModifier::Monotonic: 38 return mlir::omp::ScheduleModifier::monotonic; 39 case omp::clause::Schedule::OrderingModifier::Nonmonotonic: 40 return mlir::omp::ScheduleModifier::nonmonotonic; 41 } 42 return mlir::omp::ScheduleModifier::none; 43 } 44 45 static mlir::omp::ScheduleModifier 46 getScheduleModifier(const omp::clause::Schedule &clause) { 47 using Schedule = omp::clause::Schedule; 48 const auto &modifier = 49 std::get<std::optional<Schedule::OrderingModifier>>(clause.t); 50 if (modifier) 51 return translateScheduleModifier(*modifier); 52 return mlir::omp::ScheduleModifier::none; 53 } 54 55 static mlir::omp::ScheduleModifier 56 getSimdModifier(const omp::clause::Schedule &clause) { 57 using Schedule = omp::clause::Schedule; 58 const auto &modifier = 59 std::get<std::optional<Schedule::ChunkModifier>>(clause.t); 60 if (modifier && *modifier == Schedule::ChunkModifier::Simd) 61 return mlir::omp::ScheduleModifier::simd; 62 return mlir::omp::ScheduleModifier::none; 63 } 64 65 static void 66 genAllocateClause(lower::AbstractConverter &converter, 67 const omp::clause::Allocate &clause, 68 llvm::SmallVectorImpl<mlir::Value> &allocatorOperands, 69 llvm::SmallVectorImpl<mlir::Value> &allocateOperands) { 70 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 71 mlir::Location currentLocation = converter.getCurrentLocation(); 72 lower::StatementContext stmtCtx; 73 74 auto &objects = std::get<omp::ObjectList>(clause.t); 75 76 using Allocate = omp::clause::Allocate; 77 // ALIGN in this context is unimplemented 78 if (std::get<std::optional<Allocate::AlignModifier>>(clause.t)) 79 TODO(currentLocation, "OmpAllocateClause ALIGN modifier"); 80 81 // Check if allocate clause has allocator specified. If so, add it 82 // to list of allocators, otherwise, add default allocator to 83 // list of allocators. 84 using ComplexModifier = Allocate::AllocatorComplexModifier; 85 if (auto &mod = std::get<std::optional<ComplexModifier>>(clause.t)) { 86 mlir::Value operand = fir::getBase(converter.genExprValue(mod->v, stmtCtx)); 87 allocatorOperands.append(objects.size(), operand); 88 } else { 89 mlir::Value operand = firOpBuilder.createIntegerConstant( 90 currentLocation, firOpBuilder.getI32Type(), 1); 91 allocatorOperands.append(objects.size(), operand); 92 } 93 94 genObjectList(objects, converter, allocateOperands); 95 } 96 97 static mlir::omp::ClauseBindKindAttr 98 genBindKindAttr(fir::FirOpBuilder &firOpBuilder, 99 const omp::clause::Bind &clause) { 100 mlir::omp::ClauseBindKind bindKind; 101 switch (clause.v) { 102 case omp::clause::Bind::Binding::Teams: 103 bindKind = mlir::omp::ClauseBindKind::Teams; 104 break; 105 case omp::clause::Bind::Binding::Parallel: 106 bindKind = mlir::omp::ClauseBindKind::Parallel; 107 break; 108 case omp::clause::Bind::Binding::Thread: 109 bindKind = mlir::omp::ClauseBindKind::Thread; 110 break; 111 } 112 return mlir::omp::ClauseBindKindAttr::get(firOpBuilder.getContext(), 113 bindKind); 114 } 115 116 static mlir::omp::ClauseProcBindKindAttr 117 genProcBindKindAttr(fir::FirOpBuilder &firOpBuilder, 118 const omp::clause::ProcBind &clause) { 119 mlir::omp::ClauseProcBindKind procBindKind; 120 switch (clause.v) { 121 case omp::clause::ProcBind::AffinityPolicy::Master: 122 procBindKind = mlir::omp::ClauseProcBindKind::Master; 123 break; 124 case omp::clause::ProcBind::AffinityPolicy::Close: 125 procBindKind = mlir::omp::ClauseProcBindKind::Close; 126 break; 127 case omp::clause::ProcBind::AffinityPolicy::Spread: 128 procBindKind = mlir::omp::ClauseProcBindKind::Spread; 129 break; 130 case omp::clause::ProcBind::AffinityPolicy::Primary: 131 procBindKind = mlir::omp::ClauseProcBindKind::Primary; 132 break; 133 } 134 return mlir::omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), 135 procBindKind); 136 } 137 138 static mlir::omp::ClauseTaskDependAttr 139 genDependKindAttr(lower::AbstractConverter &converter, 140 const omp::clause::DependenceType kind) { 141 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 142 mlir::Location currentLocation = converter.getCurrentLocation(); 143 144 mlir::omp::ClauseTaskDepend pbKind; 145 switch (kind) { 146 case omp::clause::DependenceType::In: 147 pbKind = mlir::omp::ClauseTaskDepend::taskdependin; 148 break; 149 case omp::clause::DependenceType::Out: 150 pbKind = mlir::omp::ClauseTaskDepend::taskdependout; 151 break; 152 case omp::clause::DependenceType::Inout: 153 pbKind = mlir::omp::ClauseTaskDepend::taskdependinout; 154 break; 155 case omp::clause::DependenceType::Mutexinoutset: 156 case omp::clause::DependenceType::Inoutset: 157 case omp::clause::DependenceType::Depobj: 158 TODO(currentLocation, 159 "INOUTSET, MUTEXINOUTSET and DEPOBJ dependence-types"); 160 break; 161 case omp::clause::DependenceType::Sink: 162 case omp::clause::DependenceType::Source: 163 llvm_unreachable("unhandled parser task dependence type"); 164 break; 165 } 166 return mlir::omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(), 167 pbKind); 168 } 169 170 static mlir::Value 171 getIfClauseOperand(lower::AbstractConverter &converter, 172 const omp::clause::If &clause, 173 omp::clause::If::DirectiveNameModifier directiveName, 174 mlir::Location clauseLocation) { 175 // Only consider the clause if it's intended for the given directive. 176 auto &directive = 177 std::get<std::optional<omp::clause::If::DirectiveNameModifier>>(clause.t); 178 if (directive && directive.value() != directiveName) 179 return nullptr; 180 181 lower::StatementContext stmtCtx; 182 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 183 mlir::Value ifVal = fir::getBase( 184 converter.genExprValue(std::get<omp::SomeExpr>(clause.t), stmtCtx)); 185 return firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(), 186 ifVal); 187 } 188 189 static void addUseDeviceClause( 190 lower::AbstractConverter &converter, const omp::ObjectList &objects, 191 llvm::SmallVectorImpl<mlir::Value> &operands, 192 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) { 193 genObjectList(objects, converter, operands); 194 for (mlir::Value &operand : operands) 195 checkMapType(operand.getLoc(), operand.getType()); 196 197 for (const omp::Object &object : objects) 198 useDeviceSyms.push_back(object.sym()); 199 } 200 201 static void convertLoopBounds(lower::AbstractConverter &converter, 202 mlir::Location loc, 203 mlir::omp::LoopRelatedClauseOps &result, 204 std::size_t loopVarTypeSize) { 205 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 206 // The types of lower bound, upper bound, and step are converted into the 207 // type of the loop variable if necessary. 208 mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize); 209 for (unsigned it = 0; it < (unsigned)result.loopLowerBounds.size(); it++) { 210 result.loopLowerBounds[it] = firOpBuilder.createConvert( 211 loc, loopVarType, result.loopLowerBounds[it]); 212 result.loopUpperBounds[it] = firOpBuilder.createConvert( 213 loc, loopVarType, result.loopUpperBounds[it]); 214 result.loopSteps[it] = 215 firOpBuilder.createConvert(loc, loopVarType, result.loopSteps[it]); 216 } 217 } 218 219 //===----------------------------------------------------------------------===// 220 // ClauseProcessor unique clauses 221 //===----------------------------------------------------------------------===// 222 223 bool ClauseProcessor::processBare(mlir::omp::BareClauseOps &result) const { 224 return markClauseOccurrence<omp::clause::OmpxBare>(result.bare); 225 } 226 227 bool ClauseProcessor::processBind(mlir::omp::BindClauseOps &result) const { 228 if (auto *clause = findUniqueClause<omp::clause::Bind>()) { 229 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 230 result.bindKind = genBindKindAttr(firOpBuilder, *clause); 231 return true; 232 } 233 return false; 234 } 235 236 bool ClauseProcessor::processCollapse( 237 mlir::Location currentLocation, lower::pft::Evaluation &eval, 238 mlir::omp::LoopRelatedClauseOps &result, 239 llvm::SmallVectorImpl<const semantics::Symbol *> &iv) const { 240 bool found = false; 241 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 242 243 // Collect the loops to collapse. 244 lower::pft::Evaluation *doConstructEval = &eval.getFirstNestedEvaluation(); 245 if (doConstructEval->getIf<parser::DoConstruct>()->IsDoConcurrent()) { 246 TODO(currentLocation, "Do Concurrent in Worksharing loop construct"); 247 } 248 249 std::int64_t collapseValue = 1l; 250 if (auto *clause = findUniqueClause<omp::clause::Collapse>()) { 251 collapseValue = evaluate::ToInt64(clause->v).value(); 252 found = true; 253 } 254 255 std::size_t loopVarTypeSize = 0; 256 do { 257 lower::pft::Evaluation *doLoop = 258 &doConstructEval->getFirstNestedEvaluation(); 259 auto *doStmt = doLoop->getIf<parser::NonLabelDoStmt>(); 260 assert(doStmt && "Expected do loop to be in the nested evaluation"); 261 const auto &loopControl = 262 std::get<std::optional<parser::LoopControl>>(doStmt->t); 263 const parser::LoopControl::Bounds *bounds = 264 std::get_if<parser::LoopControl::Bounds>(&loopControl->u); 265 assert(bounds && "Expected bounds for worksharing do loop"); 266 lower::StatementContext stmtCtx; 267 result.loopLowerBounds.push_back(fir::getBase( 268 converter.genExprValue(*semantics::GetExpr(bounds->lower), stmtCtx))); 269 result.loopUpperBounds.push_back(fir::getBase( 270 converter.genExprValue(*semantics::GetExpr(bounds->upper), stmtCtx))); 271 if (bounds->step) { 272 result.loopSteps.push_back(fir::getBase( 273 converter.genExprValue(*semantics::GetExpr(bounds->step), stmtCtx))); 274 } else { // If `step` is not present, assume it as `1`. 275 result.loopSteps.push_back(firOpBuilder.createIntegerConstant( 276 currentLocation, firOpBuilder.getIntegerType(32), 1)); 277 } 278 iv.push_back(bounds->name.thing.symbol); 279 loopVarTypeSize = std::max(loopVarTypeSize, 280 bounds->name.thing.symbol->GetUltimate().size()); 281 collapseValue--; 282 doConstructEval = 283 &*std::next(doConstructEval->getNestedEvaluations().begin()); 284 } while (collapseValue > 0); 285 286 convertLoopBounds(converter, currentLocation, result, loopVarTypeSize); 287 288 return found; 289 } 290 291 bool ClauseProcessor::processDevice(lower::StatementContext &stmtCtx, 292 mlir::omp::DeviceClauseOps &result) const { 293 const parser::CharBlock *source = nullptr; 294 if (auto *clause = findUniqueClause<omp::clause::Device>(&source)) { 295 mlir::Location clauseLocation = converter.genLocation(*source); 296 if (auto deviceModifier = 297 std::get<std::optional<omp::clause::Device::DeviceModifier>>( 298 clause->t)) { 299 if (deviceModifier == omp::clause::Device::DeviceModifier::Ancestor) { 300 TODO(clauseLocation, "OMPD_target Device Modifier Ancestor"); 301 } 302 } 303 const auto &deviceExpr = std::get<omp::SomeExpr>(clause->t); 304 result.device = fir::getBase(converter.genExprValue(deviceExpr, stmtCtx)); 305 return true; 306 } 307 return false; 308 } 309 310 bool ClauseProcessor::processDeviceType( 311 mlir::omp::DeviceTypeClauseOps &result) const { 312 if (auto *clause = findUniqueClause<omp::clause::DeviceType>()) { 313 // Case: declare target ... device_type(any | host | nohost) 314 switch (clause->v) { 315 case omp::clause::DeviceType::DeviceTypeDescription::Nohost: 316 result.deviceType = mlir::omp::DeclareTargetDeviceType::nohost; 317 break; 318 case omp::clause::DeviceType::DeviceTypeDescription::Host: 319 result.deviceType = mlir::omp::DeclareTargetDeviceType::host; 320 break; 321 case omp::clause::DeviceType::DeviceTypeDescription::Any: 322 result.deviceType = mlir::omp::DeclareTargetDeviceType::any; 323 break; 324 } 325 return true; 326 } 327 return false; 328 } 329 330 bool ClauseProcessor::processDistSchedule( 331 lower::StatementContext &stmtCtx, 332 mlir::omp::DistScheduleClauseOps &result) const { 333 if (auto *clause = findUniqueClause<omp::clause::DistSchedule>()) { 334 result.distScheduleStatic = converter.getFirOpBuilder().getUnitAttr(); 335 const auto &chunkSize = std::get<std::optional<ExprTy>>(clause->t); 336 if (chunkSize) 337 result.distScheduleChunkSize = 338 fir::getBase(converter.genExprValue(*chunkSize, stmtCtx)); 339 return true; 340 } 341 return false; 342 } 343 344 bool ClauseProcessor::processFilter(lower::StatementContext &stmtCtx, 345 mlir::omp::FilterClauseOps &result) const { 346 if (auto *clause = findUniqueClause<omp::clause::Filter>()) { 347 result.filteredThreadId = 348 fir::getBase(converter.genExprValue(clause->v, stmtCtx)); 349 return true; 350 } 351 return false; 352 } 353 354 bool ClauseProcessor::processFinal(lower::StatementContext &stmtCtx, 355 mlir::omp::FinalClauseOps &result) const { 356 const parser::CharBlock *source = nullptr; 357 if (auto *clause = findUniqueClause<omp::clause::Final>(&source)) { 358 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 359 mlir::Location clauseLocation = converter.genLocation(*source); 360 361 mlir::Value finalVal = 362 fir::getBase(converter.genExprValue(clause->v, stmtCtx)); 363 result.final = firOpBuilder.createConvert( 364 clauseLocation, firOpBuilder.getI1Type(), finalVal); 365 return true; 366 } 367 return false; 368 } 369 370 bool ClauseProcessor::processHint(mlir::omp::HintClauseOps &result) const { 371 if (auto *clause = findUniqueClause<omp::clause::Hint>()) { 372 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 373 int64_t hintValue = *evaluate::ToInt64(clause->v); 374 result.hint = firOpBuilder.getI64IntegerAttr(hintValue); 375 return true; 376 } 377 return false; 378 } 379 380 bool ClauseProcessor::processMergeable( 381 mlir::omp::MergeableClauseOps &result) const { 382 return markClauseOccurrence<omp::clause::Mergeable>(result.mergeable); 383 } 384 385 bool ClauseProcessor::processNowait(mlir::omp::NowaitClauseOps &result) const { 386 return markClauseOccurrence<omp::clause::Nowait>(result.nowait); 387 } 388 389 bool ClauseProcessor::processNumTeams( 390 lower::StatementContext &stmtCtx, 391 mlir::omp::NumTeamsClauseOps &result) const { 392 // TODO Get lower and upper bounds for num_teams when parser is updated to 393 // accept both. 394 if (auto *clause = findUniqueClause<omp::clause::NumTeams>()) { 395 // The num_teams directive accepts a list of team lower/upper bounds. 396 // This is an extension to support grid specification for ompx_bare. 397 // Here, only expect a single element in the list. 398 assert(clause->v.size() == 1); 399 // auto lowerBound = std::get<std::optional<ExprTy>>(clause->v[0]->t); 400 auto &upperBound = std::get<ExprTy>(clause->v[0].t); 401 result.numTeamsUpper = 402 fir::getBase(converter.genExprValue(upperBound, stmtCtx)); 403 return true; 404 } 405 return false; 406 } 407 408 bool ClauseProcessor::processNumThreads( 409 lower::StatementContext &stmtCtx, 410 mlir::omp::NumThreadsClauseOps &result) const { 411 if (auto *clause = findUniqueClause<omp::clause::NumThreads>()) { 412 // OMPIRBuilder expects `NUM_THREADS` clause as a `Value`. 413 result.numThreads = 414 fir::getBase(converter.genExprValue(clause->v, stmtCtx)); 415 return true; 416 } 417 return false; 418 } 419 420 bool ClauseProcessor::processOrder(mlir::omp::OrderClauseOps &result) const { 421 using Order = omp::clause::Order; 422 if (auto *clause = findUniqueClause<Order>()) { 423 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 424 result.order = mlir::omp::ClauseOrderKindAttr::get( 425 firOpBuilder.getContext(), mlir::omp::ClauseOrderKind::Concurrent); 426 const auto &modifier = 427 std::get<std::optional<Order::OrderModifier>>(clause->t); 428 if (modifier && *modifier == Order::OrderModifier::Unconstrained) { 429 result.orderMod = mlir::omp::OrderModifierAttr::get( 430 firOpBuilder.getContext(), mlir::omp::OrderModifier::unconstrained); 431 } else { 432 // "If order-modifier is not unconstrained, the behavior is as if the 433 // reproducible modifier is present." 434 result.orderMod = mlir::omp::OrderModifierAttr::get( 435 firOpBuilder.getContext(), mlir::omp::OrderModifier::reproducible); 436 } 437 return true; 438 } 439 return false; 440 } 441 442 bool ClauseProcessor::processOrdered( 443 mlir::omp::OrderedClauseOps &result) const { 444 if (auto *clause = findUniqueClause<omp::clause::Ordered>()) { 445 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 446 int64_t orderedClauseValue = 0l; 447 if (clause->v.has_value()) 448 orderedClauseValue = *evaluate::ToInt64(*clause->v); 449 result.ordered = firOpBuilder.getI64IntegerAttr(orderedClauseValue); 450 return true; 451 } 452 return false; 453 } 454 455 bool ClauseProcessor::processPriority( 456 lower::StatementContext &stmtCtx, 457 mlir::omp::PriorityClauseOps &result) const { 458 if (auto *clause = findUniqueClause<omp::clause::Priority>()) { 459 result.priority = fir::getBase(converter.genExprValue(clause->v, stmtCtx)); 460 return true; 461 } 462 return false; 463 } 464 465 bool ClauseProcessor::processDetach(mlir::omp::DetachClauseOps &result) const { 466 if (auto *clause = findUniqueClause<omp::clause::Detach>()) { 467 semantics::Symbol *sym = clause->v.sym(); 468 mlir::Value symVal = converter.getSymbolAddress(*sym); 469 result.eventHandle = symVal; 470 return true; 471 } 472 return false; 473 } 474 475 bool ClauseProcessor::processProcBind( 476 mlir::omp::ProcBindClauseOps &result) const { 477 if (auto *clause = findUniqueClause<omp::clause::ProcBind>()) { 478 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 479 result.procBindKind = genProcBindKindAttr(firOpBuilder, *clause); 480 return true; 481 } 482 return false; 483 } 484 485 bool ClauseProcessor::processSafelen( 486 mlir::omp::SafelenClauseOps &result) const { 487 if (auto *clause = findUniqueClause<omp::clause::Safelen>()) { 488 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 489 const std::optional<std::int64_t> safelenVal = evaluate::ToInt64(clause->v); 490 result.safelen = firOpBuilder.getI64IntegerAttr(*safelenVal); 491 return true; 492 } 493 return false; 494 } 495 496 bool ClauseProcessor::processSchedule( 497 lower::StatementContext &stmtCtx, 498 mlir::omp::ScheduleClauseOps &result) const { 499 if (auto *clause = findUniqueClause<omp::clause::Schedule>()) { 500 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 501 mlir::MLIRContext *context = firOpBuilder.getContext(); 502 const auto &scheduleType = std::get<omp::clause::Schedule::Kind>(clause->t); 503 504 mlir::omp::ClauseScheduleKind scheduleKind; 505 switch (scheduleType) { 506 case omp::clause::Schedule::Kind::Static: 507 scheduleKind = mlir::omp::ClauseScheduleKind::Static; 508 break; 509 case omp::clause::Schedule::Kind::Dynamic: 510 scheduleKind = mlir::omp::ClauseScheduleKind::Dynamic; 511 break; 512 case omp::clause::Schedule::Kind::Guided: 513 scheduleKind = mlir::omp::ClauseScheduleKind::Guided; 514 break; 515 case omp::clause::Schedule::Kind::Auto: 516 scheduleKind = mlir::omp::ClauseScheduleKind::Auto; 517 break; 518 case omp::clause::Schedule::Kind::Runtime: 519 scheduleKind = mlir::omp::ClauseScheduleKind::Runtime; 520 break; 521 } 522 523 result.scheduleKind = 524 mlir::omp::ClauseScheduleKindAttr::get(context, scheduleKind); 525 526 mlir::omp::ScheduleModifier scheduleMod = getScheduleModifier(*clause); 527 if (scheduleMod != mlir::omp::ScheduleModifier::none) 528 result.scheduleMod = 529 mlir::omp::ScheduleModifierAttr::get(context, scheduleMod); 530 531 if (getSimdModifier(*clause) != mlir::omp::ScheduleModifier::none) 532 result.scheduleSimd = firOpBuilder.getUnitAttr(); 533 534 if (const auto &chunkExpr = std::get<omp::MaybeExpr>(clause->t)) 535 result.scheduleChunk = 536 fir::getBase(converter.genExprValue(*chunkExpr, stmtCtx)); 537 538 return true; 539 } 540 return false; 541 } 542 543 bool ClauseProcessor::processSimdlen( 544 mlir::omp::SimdlenClauseOps &result) const { 545 if (auto *clause = findUniqueClause<omp::clause::Simdlen>()) { 546 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 547 const std::optional<std::int64_t> simdlenVal = evaluate::ToInt64(clause->v); 548 result.simdlen = firOpBuilder.getI64IntegerAttr(*simdlenVal); 549 return true; 550 } 551 return false; 552 } 553 554 bool ClauseProcessor::processThreadLimit( 555 lower::StatementContext &stmtCtx, 556 mlir::omp::ThreadLimitClauseOps &result) const { 557 if (auto *clause = findUniqueClause<omp::clause::ThreadLimit>()) { 558 result.threadLimit = 559 fir::getBase(converter.genExprValue(clause->v, stmtCtx)); 560 return true; 561 } 562 return false; 563 } 564 565 bool ClauseProcessor::processUntied(mlir::omp::UntiedClauseOps &result) const { 566 return markClauseOccurrence<omp::clause::Untied>(result.untied); 567 } 568 569 //===----------------------------------------------------------------------===// 570 // ClauseProcessor repeatable clauses 571 //===----------------------------------------------------------------------===// 572 static llvm::StringMap<bool> getTargetFeatures(mlir::ModuleOp module) { 573 llvm::StringMap<bool> featuresMap; 574 llvm::SmallVector<llvm::StringRef> targetFeaturesVec; 575 if (mlir::LLVM::TargetFeaturesAttr features = 576 fir::getTargetFeatures(module)) { 577 llvm::ArrayRef<mlir::StringAttr> featureAttrs = features.getFeatures(); 578 for (auto &featureAttr : featureAttrs) { 579 llvm::StringRef featureKeyString = featureAttr.strref(); 580 featuresMap[featureKeyString.substr(1)] = (featureKeyString[0] == '+'); 581 } 582 } 583 return featuresMap; 584 } 585 586 static void 587 addAlignedClause(lower::AbstractConverter &converter, 588 const omp::clause::Aligned &clause, 589 llvm::SmallVectorImpl<mlir::Value> &alignedVars, 590 llvm::SmallVectorImpl<mlir::Attribute> &alignments) { 591 using Aligned = omp::clause::Aligned; 592 lower::StatementContext stmtCtx; 593 mlir::IntegerAttr alignmentValueAttr; 594 int64_t alignment = 0; 595 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 596 597 if (auto &alignmentValueParserExpr = 598 std::get<std::optional<Aligned::Alignment>>(clause.t)) { 599 mlir::Value operand = fir::getBase( 600 converter.genExprValue(*alignmentValueParserExpr, stmtCtx)); 601 alignment = *fir::getIntIfConstant(operand); 602 } else { 603 llvm::StringMap<bool> featuresMap = getTargetFeatures(builder.getModule()); 604 llvm::Triple triple = fir::getTargetTriple(builder.getModule()); 605 alignment = 606 llvm::OpenMPIRBuilder::getOpenMPDefaultSimdAlign(triple, featuresMap); 607 } 608 609 // The default alignment for some targets is equal to 0. 610 // Do not generate alignment assumption if alignment is less than or equal to 611 // 0. 612 if (alignment > 0) { 613 auto &objects = std::get<omp::ObjectList>(clause.t); 614 if (!objects.empty()) 615 genObjectList(objects, converter, alignedVars); 616 alignmentValueAttr = builder.getI64IntegerAttr(alignment); 617 // All the list items in a aligned clause will have same alignment 618 for (std::size_t i = 0; i < objects.size(); i++) 619 alignments.push_back(alignmentValueAttr); 620 } 621 } 622 623 bool ClauseProcessor::processAligned( 624 mlir::omp::AlignedClauseOps &result) const { 625 return findRepeatableClause<omp::clause::Aligned>( 626 [&](const omp::clause::Aligned &clause, const parser::CharBlock &) { 627 addAlignedClause(converter, clause, result.alignedVars, 628 result.alignments); 629 }); 630 } 631 632 bool ClauseProcessor::processAllocate( 633 mlir::omp::AllocateClauseOps &result) const { 634 return findRepeatableClause<omp::clause::Allocate>( 635 [&](const omp::clause::Allocate &clause, const parser::CharBlock &) { 636 genAllocateClause(converter, clause, result.allocatorVars, 637 result.allocateVars); 638 }); 639 } 640 641 bool ClauseProcessor::processCopyin() const { 642 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 643 mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint(); 644 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); 645 auto checkAndCopyHostAssociateVar = 646 [&](semantics::Symbol *sym, 647 mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) { 648 assert(sym->has<semantics::HostAssocDetails>() && 649 "No host-association found"); 650 if (converter.isPresentShallowLookup(*sym)) 651 converter.copyHostAssociateVar(*sym, copyAssignIP); 652 }; 653 bool hasCopyin = findRepeatableClause<omp::clause::Copyin>( 654 [&](const omp::clause::Copyin &clause, const parser::CharBlock &) { 655 for (const omp::Object &object : clause.v) { 656 semantics::Symbol *sym = object.sym(); 657 assert(sym && "Expecting symbol"); 658 if (const auto *commonDetails = 659 sym->detailsIf<semantics::CommonBlockDetails>()) { 660 for (const auto &mem : commonDetails->objects()) 661 checkAndCopyHostAssociateVar(&*mem, &insPt); 662 break; 663 } 664 665 assert(sym->has<semantics::HostAssocDetails>() && 666 "No host-association found"); 667 checkAndCopyHostAssociateVar(sym); 668 } 669 }); 670 671 // [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to 672 // the execution of the associated structured block. Emit implicit barrier to 673 // synchronize threads and avoid data races on propagation master's thread 674 // values of threadprivate variables to local instances of that variables of 675 // all other implicit threads. 676 677 // All copies are inserted at either "insPt" (i.e. immediately before it), 678 // or at some earlier point (as determined by "copyHostAssociateVar"). 679 // Unless the insertion point is given to "copyHostAssociateVar" explicitly, 680 // it will not restore the builder's insertion point. Since the copies may be 681 // inserted in any order (not following the execution order), make sure the 682 // barrier is inserted following all of them. 683 firOpBuilder.restoreInsertionPoint(insPt); 684 if (hasCopyin) 685 firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation()); 686 return hasCopyin; 687 } 688 689 /// Class that extracts information from the specified type. 690 class TypeInfo { 691 public: 692 TypeInfo(mlir::Type ty) { typeScan(ty); } 693 694 // Returns the length of character types. 695 std::optional<fir::CharacterType::LenType> getCharLength() const { 696 return charLen; 697 } 698 699 // Returns the shape of array types. 700 llvm::ArrayRef<int64_t> getShape() const { return shape; } 701 702 // Is the type inside a box? 703 bool isBox() const { return inBox; } 704 705 private: 706 void typeScan(mlir::Type type); 707 708 std::optional<fir::CharacterType::LenType> charLen; 709 llvm::SmallVector<int64_t> shape; 710 bool inBox = false; 711 }; 712 713 void TypeInfo::typeScan(mlir::Type ty) { 714 if (auto sty = mlir::dyn_cast<fir::SequenceType>(ty)) { 715 assert(shape.empty() && !sty.getShape().empty()); 716 shape = llvm::SmallVector<int64_t>(sty.getShape()); 717 typeScan(sty.getEleTy()); 718 } else if (auto bty = mlir::dyn_cast<fir::BoxType>(ty)) { 719 inBox = true; 720 typeScan(bty.getEleTy()); 721 } else if (auto cty = mlir::dyn_cast<fir::CharacterType>(ty)) { 722 charLen = cty.getLen(); 723 } else if (auto hty = mlir::dyn_cast<fir::HeapType>(ty)) { 724 typeScan(hty.getEleTy()); 725 } else if (auto pty = mlir::dyn_cast<fir::PointerType>(ty)) { 726 typeScan(pty.getEleTy()); 727 } else { 728 // The scan ends when reaching any built-in or record type. 729 assert(ty.isIntOrIndexOrFloat() || mlir::isa<mlir::ComplexType>(ty) || 730 mlir::isa<fir::LogicalType>(ty) || mlir::isa<fir::RecordType>(ty)); 731 } 732 } 733 734 // Create a function that performs a copy between two variables, compatible 735 // with their types and attributes. 736 static mlir::func::FuncOp 737 createCopyFunc(mlir::Location loc, lower::AbstractConverter &converter, 738 mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) { 739 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 740 mlir::ModuleOp module = builder.getModule(); 741 mlir::Type eleTy = mlir::cast<fir::ReferenceType>(varType).getEleTy(); 742 TypeInfo typeInfo(eleTy); 743 std::string copyFuncName = 744 fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy"); 745 746 if (auto decl = module.lookupSymbol<mlir::func::FuncOp>(copyFuncName)) 747 return decl; 748 749 // create function 750 mlir::OpBuilder::InsertionGuard guard(builder); 751 mlir::OpBuilder modBuilder(module.getBodyRegion()); 752 llvm::SmallVector<mlir::Type> argsTy = {varType, varType}; 753 auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {}); 754 mlir::func::FuncOp funcOp = 755 modBuilder.create<mlir::func::FuncOp>(loc, copyFuncName, funcType); 756 funcOp.setVisibility(mlir::SymbolTable::Visibility::Private); 757 fir::factory::setInternalLinkage(funcOp); 758 builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy, 759 {loc, loc}); 760 builder.setInsertionPointToStart(&funcOp.getRegion().back()); 761 // generate body 762 fir::FortranVariableFlagsAttr attrs; 763 if (varAttrs != fir::FortranVariableFlagsEnum::None) 764 attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs); 765 llvm::SmallVector<mlir::Value> typeparams; 766 if (typeInfo.getCharLength().has_value()) { 767 mlir::Value charLen = builder.createIntegerConstant( 768 loc, builder.getCharacterLengthType(), *typeInfo.getCharLength()); 769 typeparams.push_back(charLen); 770 } 771 mlir::Value shape; 772 if (!typeInfo.isBox() && !typeInfo.getShape().empty()) { 773 llvm::SmallVector<mlir::Value> extents; 774 for (auto extent : typeInfo.getShape()) 775 extents.push_back( 776 builder.createIntegerConstant(loc, builder.getIndexType(), extent)); 777 shape = builder.create<fir::ShapeOp>(loc, extents); 778 } 779 auto declDst = builder.create<hlfir::DeclareOp>( 780 loc, funcOp.getArgument(0), copyFuncName + "_dst", shape, typeparams, 781 /*dummy_scope=*/nullptr, attrs); 782 auto declSrc = builder.create<hlfir::DeclareOp>( 783 loc, funcOp.getArgument(1), copyFuncName + "_src", shape, typeparams, 784 /*dummy_scope=*/nullptr, attrs); 785 converter.copyVar(loc, declDst.getBase(), declSrc.getBase(), varAttrs); 786 builder.create<mlir::func::ReturnOp>(loc); 787 return funcOp; 788 } 789 790 bool ClauseProcessor::processCopyprivate( 791 mlir::Location currentLocation, 792 mlir::omp::CopyprivateClauseOps &result) const { 793 auto addCopyPrivateVar = [&](semantics::Symbol *sym) { 794 mlir::Value symVal = converter.getSymbolAddress(*sym); 795 auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>(); 796 if (!declOp) 797 fir::emitFatalError(currentLocation, 798 "COPYPRIVATE is supported only in HLFIR mode"); 799 symVal = declOp.getBase(); 800 mlir::Type symType = symVal.getType(); 801 fir::FortranVariableFlagsEnum attrs = 802 declOp.getFortranAttrs().has_value() 803 ? *declOp.getFortranAttrs() 804 : fir::FortranVariableFlagsEnum::None; 805 mlir::Value cpVar = symVal; 806 807 // CopyPrivate variables must be passed by reference. However, in the case 808 // of assumed shapes/vla the type is not a !fir.ref, but a !fir.box. 809 // In these cases to retrieve the appropriate !fir.ref<!fir.box<...>> to 810 // access the data we need we must perform an alloca and then store to it 811 // and retrieve the data from the new alloca. 812 if (mlir::isa<fir::BaseBoxType>(symType)) { 813 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 814 auto alloca = builder.create<fir::AllocaOp>(currentLocation, symType); 815 builder.create<fir::StoreOp>(currentLocation, symVal, alloca); 816 cpVar = alloca; 817 } 818 819 result.copyprivateVars.push_back(cpVar); 820 mlir::func::FuncOp funcOp = 821 createCopyFunc(currentLocation, converter, cpVar.getType(), attrs); 822 result.copyprivateSyms.push_back(mlir::SymbolRefAttr::get(funcOp)); 823 }; 824 825 bool hasCopyPrivate = findRepeatableClause<clause::Copyprivate>( 826 [&](const clause::Copyprivate &clause, const parser::CharBlock &) { 827 for (const Object &object : clause.v) { 828 semantics::Symbol *sym = object.sym(); 829 if (const auto *commonDetails = 830 sym->detailsIf<semantics::CommonBlockDetails>()) { 831 for (const auto &mem : commonDetails->objects()) 832 addCopyPrivateVar(&*mem); 833 break; 834 } 835 addCopyPrivateVar(sym); 836 } 837 }); 838 839 return hasCopyPrivate; 840 } 841 842 bool ClauseProcessor::processDepend(mlir::omp::DependClauseOps &result) const { 843 auto process = [&](const omp::clause::Depend &clause, 844 const parser::CharBlock &) { 845 using Depend = omp::clause::Depend; 846 if (!std::holds_alternative<Depend::TaskDep>(clause.u)) { 847 TODO(converter.getCurrentLocation(), 848 "DEPEND clause with SINK or SOURCE is not supported yet"); 849 } 850 auto &taskDep = std::get<Depend::TaskDep>(clause.u); 851 auto depType = std::get<clause::DependenceType>(taskDep.t); 852 auto &objects = std::get<omp::ObjectList>(taskDep.t); 853 854 if (std::get<std::optional<omp::clause::Iterator>>(taskDep.t)) { 855 TODO(converter.getCurrentLocation(), 856 "Support for iterator modifiers is not implemented yet"); 857 } 858 mlir::omp::ClauseTaskDependAttr dependTypeOperand = 859 genDependKindAttr(converter, depType); 860 result.dependKinds.append(objects.size(), dependTypeOperand); 861 862 for (const omp::Object &object : objects) { 863 assert(object.ref() && "Expecting designator"); 864 865 if (evaluate::ExtractSubstring(*object.ref())) { 866 TODO(converter.getCurrentLocation(), 867 "substring not supported for task depend"); 868 } else if (evaluate::IsArrayElement(*object.ref())) { 869 TODO(converter.getCurrentLocation(), 870 "array sections not supported for task depend"); 871 } 872 873 semantics::Symbol *sym = object.sym(); 874 const mlir::Value variable = converter.getSymbolAddress(*sym); 875 result.dependVars.push_back(variable); 876 } 877 }; 878 879 return findRepeatableClause<omp::clause::Depend>(process); 880 } 881 882 bool ClauseProcessor::processHasDeviceAddr( 883 mlir::omp::HasDeviceAddrClauseOps &result, 884 llvm::SmallVectorImpl<const semantics::Symbol *> &isDeviceSyms) const { 885 return findRepeatableClause<omp::clause::HasDeviceAddr>( 886 [&](const omp::clause::HasDeviceAddr &devAddrClause, 887 const parser::CharBlock &) { 888 addUseDeviceClause(converter, devAddrClause.v, result.hasDeviceAddrVars, 889 isDeviceSyms); 890 }); 891 } 892 893 bool ClauseProcessor::processIf( 894 omp::clause::If::DirectiveNameModifier directiveName, 895 mlir::omp::IfClauseOps &result) const { 896 bool found = false; 897 findRepeatableClause<omp::clause::If>([&](const omp::clause::If &clause, 898 const parser::CharBlock &source) { 899 mlir::Location clauseLocation = converter.genLocation(source); 900 mlir::Value operand = 901 getIfClauseOperand(converter, clause, directiveName, clauseLocation); 902 // Assume that, at most, a single 'if' clause will be applicable to the 903 // given directive. 904 if (operand) { 905 result.ifExpr = operand; 906 found = true; 907 } 908 }); 909 return found; 910 } 911 912 bool ClauseProcessor::processIsDevicePtr( 913 mlir::omp::IsDevicePtrClauseOps &result, 914 llvm::SmallVectorImpl<const semantics::Symbol *> &isDeviceSyms) const { 915 return findRepeatableClause<omp::clause::IsDevicePtr>( 916 [&](const omp::clause::IsDevicePtr &devPtrClause, 917 const parser::CharBlock &) { 918 addUseDeviceClause(converter, devPtrClause.v, result.isDevicePtrVars, 919 isDeviceSyms); 920 }); 921 } 922 923 bool ClauseProcessor::processLink( 924 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { 925 return findRepeatableClause<omp::clause::Link>( 926 [&](const omp::clause::Link &clause, const parser::CharBlock &) { 927 // Case: declare target link(var1, var2)... 928 gatherFuncAndVarSyms( 929 clause.v, mlir::omp::DeclareTargetCaptureClause::link, result); 930 }); 931 } 932 933 void ClauseProcessor::processMapObjects( 934 lower::StatementContext &stmtCtx, mlir::Location clauseLocation, 935 const omp::ObjectList &objects, 936 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits, 937 std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices, 938 llvm::SmallVectorImpl<mlir::Value> &mapVars, 939 llvm::SmallVectorImpl<const semantics::Symbol *> &mapSyms) const { 940 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 941 942 for (const omp::Object &object : objects) { 943 llvm::SmallVector<mlir::Value> bounds; 944 std::stringstream asFortran; 945 std::optional<omp::Object> parentObj; 946 947 lower::AddrAndBoundsInfo info = 948 lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp, 949 mlir::omp::MapBoundsType>( 950 converter, firOpBuilder, semaCtx, stmtCtx, *object.sym(), 951 object.ref(), clauseLocation, asFortran, bounds, 952 treatIndexAsSection); 953 954 mlir::Value baseOp = info.rawInput; 955 if (object.sym()->owner().IsDerivedType()) { 956 omp::ObjectList objectList = gatherObjectsOf(object, semaCtx); 957 assert(!objectList.empty() && 958 "could not find parent objects of derived type member"); 959 parentObj = objectList[0]; 960 parentMemberIndices.emplace(parentObj.value(), 961 OmpMapParentAndMemberData{}); 962 963 if (isMemberOrParentAllocatableOrPointer(object, semaCtx)) { 964 llvm::SmallVector<int64_t> indices; 965 generateMemberPlacementIndices(object, indices, semaCtx); 966 baseOp = createParentSymAndGenIntermediateMaps( 967 clauseLocation, converter, semaCtx, stmtCtx, objectList, indices, 968 parentMemberIndices[parentObj.value()], asFortran.str(), 969 mapTypeBits); 970 } 971 } 972 973 // Explicit map captures are captured ByRef by default, 974 // optimisation passes may alter this to ByCopy or other capture 975 // types to optimise 976 auto location = mlir::NameLoc::get( 977 mlir::StringAttr::get(firOpBuilder.getContext(), asFortran.str()), 978 baseOp.getLoc()); 979 mlir::omp::MapInfoOp mapOp = createMapInfoOp( 980 firOpBuilder, location, baseOp, 981 /*varPtrPtr=*/mlir::Value{}, asFortran.str(), bounds, 982 /*members=*/{}, /*membersIndex=*/mlir::ArrayAttr{}, 983 static_cast< 984 std::underlying_type_t<llvm::omp::OpenMPOffloadMappingFlags>>( 985 mapTypeBits), 986 mlir::omp::VariableCaptureKind::ByRef, baseOp.getType()); 987 988 if (parentObj.has_value()) { 989 parentMemberIndices[parentObj.value()].addChildIndexAndMapToParent( 990 object, mapOp, semaCtx); 991 } else { 992 mapVars.push_back(mapOp); 993 mapSyms.push_back(object.sym()); 994 } 995 } 996 } 997 998 bool ClauseProcessor::processMap( 999 mlir::Location currentLocation, lower::StatementContext &stmtCtx, 1000 mlir::omp::MapClauseOps &result, 1001 llvm::SmallVectorImpl<const semantics::Symbol *> *mapSyms) const { 1002 // We always require tracking of symbols, even if the caller does not, 1003 // so we create an optionally used local set of symbols when the mapSyms 1004 // argument is not present. 1005 llvm::SmallVector<const semantics::Symbol *> localMapSyms; 1006 llvm::SmallVectorImpl<const semantics::Symbol *> *ptrMapSyms = 1007 mapSyms ? mapSyms : &localMapSyms; 1008 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1009 1010 auto process = [&](const omp::clause::Map &clause, 1011 const parser::CharBlock &source) { 1012 using Map = omp::clause::Map; 1013 mlir::Location clauseLocation = converter.genLocation(source); 1014 const auto &[mapType, typeMods, mappers, iterator, objects] = clause.t; 1015 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1016 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE; 1017 // If the map type is specified, then process it else Tofrom is the 1018 // default. 1019 Map::MapType type = mapType.value_or(Map::MapType::Tofrom); 1020 switch (type) { 1021 case Map::MapType::To: 1022 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO; 1023 break; 1024 case Map::MapType::From: 1025 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1026 break; 1027 case Map::MapType::Tofrom: 1028 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | 1029 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1030 break; 1031 case Map::MapType::Alloc: 1032 case Map::MapType::Release: 1033 // alloc and release is the default map_type for the Target Data 1034 // Ops, i.e. if no bits for map_type is supplied then alloc/release 1035 // is implicitly assumed based on the target directive. Default 1036 // value for Target Data and Enter Data is alloc and for Exit Data 1037 // it is release. 1038 break; 1039 case Map::MapType::Delete: 1040 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE; 1041 } 1042 1043 if (typeMods) { 1044 if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Always)) 1045 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS; 1046 // Diagnose unimplemented map-type-modifiers. 1047 if (llvm::any_of(*typeMods, [](Map::MapTypeModifier m) { 1048 return m != Map::MapTypeModifier::Always; 1049 })) { 1050 TODO(currentLocation, "Map type modifiers (other than 'ALWAYS')" 1051 " are not implemented yet"); 1052 } 1053 } 1054 1055 if (iterator) { 1056 TODO(currentLocation, 1057 "Support for iterator modifiers is not implemented yet"); 1058 } 1059 if (mappers) { 1060 TODO(currentLocation, 1061 "Support for mapper modifiers is not implemented yet"); 1062 } 1063 1064 processMapObjects(stmtCtx, clauseLocation, 1065 std::get<omp::ObjectList>(clause.t), mapTypeBits, 1066 parentMemberIndices, result.mapVars, *ptrMapSyms); 1067 }; 1068 1069 bool clauseFound = findRepeatableClause<omp::clause::Map>(process); 1070 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1071 result.mapVars, *ptrMapSyms); 1072 1073 return clauseFound; 1074 } 1075 1076 bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx, 1077 mlir::omp::MapClauseOps &result) { 1078 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1079 llvm::SmallVector<const semantics::Symbol *> mapSymbols; 1080 1081 auto callbackFn = [&](const auto &clause, const parser::CharBlock &source) { 1082 mlir::Location clauseLocation = converter.genLocation(source); 1083 const auto &[expectation, mapper, iterator, objects] = clause.t; 1084 // TODO Support motion modifiers: present, mapper, iterator. 1085 if (expectation) { 1086 TODO(clauseLocation, "PRESENT modifier is not supported yet"); 1087 } else if (mapper) { 1088 TODO(clauseLocation, "Mapper modifier is not supported yet"); 1089 } else if (iterator) { 1090 TODO(clauseLocation, "Iterator modifier is not supported yet"); 1091 } 1092 constexpr llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1093 std::is_same_v<llvm::remove_cvref_t<decltype(clause)>, omp::clause::To> 1094 ? llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO 1095 : llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1096 1097 processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits, 1098 parentMemberIndices, result.mapVars, mapSymbols); 1099 }; 1100 1101 bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn); 1102 clauseFound = 1103 findRepeatableClause<omp::clause::From>(callbackFn) || clauseFound; 1104 1105 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1106 result.mapVars, mapSymbols); 1107 1108 return clauseFound; 1109 } 1110 1111 bool ClauseProcessor::processNontemporal( 1112 mlir::omp::NontemporalClauseOps &result) const { 1113 return findRepeatableClause<omp::clause::Nontemporal>( 1114 [&](const omp::clause::Nontemporal &clause, const parser::CharBlock &) { 1115 for (const Object &object : clause.v) { 1116 semantics::Symbol *sym = object.sym(); 1117 mlir::Value symVal = converter.getSymbolAddress(*sym); 1118 result.nontemporalVars.push_back(symVal); 1119 } 1120 }); 1121 } 1122 1123 bool ClauseProcessor::processReduction( 1124 mlir::Location currentLocation, mlir::omp::ReductionClauseOps &result, 1125 llvm::SmallVectorImpl<const semantics::Symbol *> &outReductionSyms) const { 1126 return findRepeatableClause<omp::clause::Reduction>( 1127 [&](const omp::clause::Reduction &clause, const parser::CharBlock &) { 1128 llvm::SmallVector<mlir::Value> reductionVars; 1129 llvm::SmallVector<bool> reduceVarByRef; 1130 llvm::SmallVector<mlir::Attribute> reductionDeclSymbols; 1131 llvm::SmallVector<const semantics::Symbol *> reductionSyms; 1132 ReductionProcessor rp; 1133 rp.addDeclareReduction(currentLocation, converter, clause, 1134 reductionVars, reduceVarByRef, 1135 reductionDeclSymbols, reductionSyms); 1136 1137 // Copy local lists into the output. 1138 llvm::copy(reductionVars, std::back_inserter(result.reductionVars)); 1139 llvm::copy(reduceVarByRef, std::back_inserter(result.reductionByref)); 1140 llvm::copy(reductionDeclSymbols, 1141 std::back_inserter(result.reductionSyms)); 1142 llvm::copy(reductionSyms, std::back_inserter(outReductionSyms)); 1143 }); 1144 } 1145 1146 bool ClauseProcessor::processTo( 1147 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { 1148 return findRepeatableClause<omp::clause::To>( 1149 [&](const omp::clause::To &clause, const parser::CharBlock &) { 1150 // Case: declare target to(func, var1, var2)... 1151 gatherFuncAndVarSyms(std::get<ObjectList>(clause.t), 1152 mlir::omp::DeclareTargetCaptureClause::to, result); 1153 }); 1154 } 1155 1156 bool ClauseProcessor::processEnter( 1157 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { 1158 return findRepeatableClause<omp::clause::Enter>( 1159 [&](const omp::clause::Enter &clause, const parser::CharBlock &) { 1160 // Case: declare target enter(func, var1, var2)... 1161 gatherFuncAndVarSyms( 1162 clause.v, mlir::omp::DeclareTargetCaptureClause::enter, result); 1163 }); 1164 } 1165 1166 bool ClauseProcessor::processUseDeviceAddr( 1167 lower::StatementContext &stmtCtx, mlir::omp::UseDeviceAddrClauseOps &result, 1168 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) const { 1169 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1170 bool clauseFound = findRepeatableClause<omp::clause::UseDeviceAddr>( 1171 [&](const omp::clause::UseDeviceAddr &clause, 1172 const parser::CharBlock &source) { 1173 mlir::Location location = converter.genLocation(source); 1174 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1175 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | 1176 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1177 processMapObjects(stmtCtx, location, clause.v, mapTypeBits, 1178 parentMemberIndices, result.useDeviceAddrVars, 1179 useDeviceSyms); 1180 }); 1181 1182 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1183 result.useDeviceAddrVars, useDeviceSyms); 1184 return clauseFound; 1185 } 1186 1187 bool ClauseProcessor::processUseDevicePtr( 1188 lower::StatementContext &stmtCtx, mlir::omp::UseDevicePtrClauseOps &result, 1189 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) const { 1190 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1191 1192 bool clauseFound = findRepeatableClause<omp::clause::UseDevicePtr>( 1193 [&](const omp::clause::UseDevicePtr &clause, 1194 const parser::CharBlock &source) { 1195 mlir::Location location = converter.genLocation(source); 1196 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1197 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | 1198 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1199 processMapObjects(stmtCtx, location, clause.v, mapTypeBits, 1200 parentMemberIndices, result.useDevicePtrVars, 1201 useDeviceSyms); 1202 }); 1203 1204 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1205 result.useDevicePtrVars, useDeviceSyms); 1206 return clauseFound; 1207 } 1208 1209 } // namespace omp 1210 } // namespace lower 1211 } // namespace Fortran 1212