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