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 // alignment value must be power of 2 617 assert((alignment & (alignment - 1)) == 0 && "alignment is not power of 2"); 618 auto &objects = std::get<omp::ObjectList>(clause.t); 619 if (!objects.empty()) 620 genObjectList(objects, converter, alignedVars); 621 alignmentValueAttr = builder.getI64IntegerAttr(alignment); 622 // All the list items in a aligned clause will have same alignment 623 for (std::size_t i = 0; i < objects.size(); i++) 624 alignments.push_back(alignmentValueAttr); 625 } 626 } 627 628 bool ClauseProcessor::processAligned( 629 mlir::omp::AlignedClauseOps &result) const { 630 return findRepeatableClause<omp::clause::Aligned>( 631 [&](const omp::clause::Aligned &clause, const parser::CharBlock &) { 632 addAlignedClause(converter, clause, result.alignedVars, 633 result.alignments); 634 }); 635 } 636 637 bool ClauseProcessor::processAllocate( 638 mlir::omp::AllocateClauseOps &result) const { 639 return findRepeatableClause<omp::clause::Allocate>( 640 [&](const omp::clause::Allocate &clause, const parser::CharBlock &) { 641 genAllocateClause(converter, clause, result.allocatorVars, 642 result.allocateVars); 643 }); 644 } 645 646 bool ClauseProcessor::processCopyin() const { 647 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 648 mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint(); 649 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock()); 650 auto checkAndCopyHostAssociateVar = 651 [&](semantics::Symbol *sym, 652 mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) { 653 assert(sym->has<semantics::HostAssocDetails>() && 654 "No host-association found"); 655 if (converter.isPresentShallowLookup(*sym)) 656 converter.copyHostAssociateVar(*sym, copyAssignIP); 657 }; 658 bool hasCopyin = findRepeatableClause<omp::clause::Copyin>( 659 [&](const omp::clause::Copyin &clause, const parser::CharBlock &) { 660 for (const omp::Object &object : clause.v) { 661 semantics::Symbol *sym = object.sym(); 662 assert(sym && "Expecting symbol"); 663 if (const auto *commonDetails = 664 sym->detailsIf<semantics::CommonBlockDetails>()) { 665 for (const auto &mem : commonDetails->objects()) 666 checkAndCopyHostAssociateVar(&*mem, &insPt); 667 break; 668 } 669 670 assert(sym->has<semantics::HostAssocDetails>() && 671 "No host-association found"); 672 checkAndCopyHostAssociateVar(sym); 673 } 674 }); 675 676 // [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to 677 // the execution of the associated structured block. Emit implicit barrier to 678 // synchronize threads and avoid data races on propagation master's thread 679 // values of threadprivate variables to local instances of that variables of 680 // all other implicit threads. 681 682 // All copies are inserted at either "insPt" (i.e. immediately before it), 683 // or at some earlier point (as determined by "copyHostAssociateVar"). 684 // Unless the insertion point is given to "copyHostAssociateVar" explicitly, 685 // it will not restore the builder's insertion point. Since the copies may be 686 // inserted in any order (not following the execution order), make sure the 687 // barrier is inserted following all of them. 688 firOpBuilder.restoreInsertionPoint(insPt); 689 if (hasCopyin) 690 firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation()); 691 return hasCopyin; 692 } 693 694 /// Class that extracts information from the specified type. 695 class TypeInfo { 696 public: 697 TypeInfo(mlir::Type ty) { typeScan(ty); } 698 699 // Returns the length of character types. 700 std::optional<fir::CharacterType::LenType> getCharLength() const { 701 return charLen; 702 } 703 704 // Returns the shape of array types. 705 llvm::ArrayRef<int64_t> getShape() const { return shape; } 706 707 // Is the type inside a box? 708 bool isBox() const { return inBox; } 709 710 private: 711 void typeScan(mlir::Type type); 712 713 std::optional<fir::CharacterType::LenType> charLen; 714 llvm::SmallVector<int64_t> shape; 715 bool inBox = false; 716 }; 717 718 void TypeInfo::typeScan(mlir::Type ty) { 719 if (auto sty = mlir::dyn_cast<fir::SequenceType>(ty)) { 720 assert(shape.empty() && !sty.getShape().empty()); 721 shape = llvm::SmallVector<int64_t>(sty.getShape()); 722 typeScan(sty.getEleTy()); 723 } else if (auto bty = mlir::dyn_cast<fir::BoxType>(ty)) { 724 inBox = true; 725 typeScan(bty.getEleTy()); 726 } else if (auto cty = mlir::dyn_cast<fir::CharacterType>(ty)) { 727 charLen = cty.getLen(); 728 } else if (auto hty = mlir::dyn_cast<fir::HeapType>(ty)) { 729 typeScan(hty.getEleTy()); 730 } else if (auto pty = mlir::dyn_cast<fir::PointerType>(ty)) { 731 typeScan(pty.getEleTy()); 732 } else { 733 // The scan ends when reaching any built-in or record type. 734 assert(ty.isIntOrIndexOrFloat() || mlir::isa<mlir::ComplexType>(ty) || 735 mlir::isa<fir::LogicalType>(ty) || mlir::isa<fir::RecordType>(ty)); 736 } 737 } 738 739 // Create a function that performs a copy between two variables, compatible 740 // with their types and attributes. 741 static mlir::func::FuncOp 742 createCopyFunc(mlir::Location loc, lower::AbstractConverter &converter, 743 mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) { 744 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 745 mlir::ModuleOp module = builder.getModule(); 746 mlir::Type eleTy = mlir::cast<fir::ReferenceType>(varType).getEleTy(); 747 TypeInfo typeInfo(eleTy); 748 std::string copyFuncName = 749 fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy"); 750 751 if (auto decl = module.lookupSymbol<mlir::func::FuncOp>(copyFuncName)) 752 return decl; 753 754 // create function 755 mlir::OpBuilder::InsertionGuard guard(builder); 756 mlir::OpBuilder modBuilder(module.getBodyRegion()); 757 llvm::SmallVector<mlir::Type> argsTy = {varType, varType}; 758 auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {}); 759 mlir::func::FuncOp funcOp = 760 modBuilder.create<mlir::func::FuncOp>(loc, copyFuncName, funcType); 761 funcOp.setVisibility(mlir::SymbolTable::Visibility::Private); 762 fir::factory::setInternalLinkage(funcOp); 763 builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy, 764 {loc, loc}); 765 builder.setInsertionPointToStart(&funcOp.getRegion().back()); 766 // generate body 767 fir::FortranVariableFlagsAttr attrs; 768 if (varAttrs != fir::FortranVariableFlagsEnum::None) 769 attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs); 770 llvm::SmallVector<mlir::Value> typeparams; 771 if (typeInfo.getCharLength().has_value()) { 772 mlir::Value charLen = builder.createIntegerConstant( 773 loc, builder.getCharacterLengthType(), *typeInfo.getCharLength()); 774 typeparams.push_back(charLen); 775 } 776 mlir::Value shape; 777 if (!typeInfo.isBox() && !typeInfo.getShape().empty()) { 778 llvm::SmallVector<mlir::Value> extents; 779 for (auto extent : typeInfo.getShape()) 780 extents.push_back( 781 builder.createIntegerConstant(loc, builder.getIndexType(), extent)); 782 shape = builder.create<fir::ShapeOp>(loc, extents); 783 } 784 auto declDst = builder.create<hlfir::DeclareOp>( 785 loc, funcOp.getArgument(0), copyFuncName + "_dst", shape, typeparams, 786 /*dummy_scope=*/nullptr, attrs); 787 auto declSrc = builder.create<hlfir::DeclareOp>( 788 loc, funcOp.getArgument(1), copyFuncName + "_src", shape, typeparams, 789 /*dummy_scope=*/nullptr, attrs); 790 converter.copyVar(loc, declDst.getBase(), declSrc.getBase(), varAttrs); 791 builder.create<mlir::func::ReturnOp>(loc); 792 return funcOp; 793 } 794 795 bool ClauseProcessor::processCopyprivate( 796 mlir::Location currentLocation, 797 mlir::omp::CopyprivateClauseOps &result) const { 798 auto addCopyPrivateVar = [&](semantics::Symbol *sym) { 799 mlir::Value symVal = converter.getSymbolAddress(*sym); 800 auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>(); 801 if (!declOp) 802 fir::emitFatalError(currentLocation, 803 "COPYPRIVATE is supported only in HLFIR mode"); 804 symVal = declOp.getBase(); 805 mlir::Type symType = symVal.getType(); 806 fir::FortranVariableFlagsEnum attrs = 807 declOp.getFortranAttrs().has_value() 808 ? *declOp.getFortranAttrs() 809 : fir::FortranVariableFlagsEnum::None; 810 mlir::Value cpVar = symVal; 811 812 // CopyPrivate variables must be passed by reference. However, in the case 813 // of assumed shapes/vla the type is not a !fir.ref, but a !fir.box. 814 // In these cases to retrieve the appropriate !fir.ref<!fir.box<...>> to 815 // access the data we need we must perform an alloca and then store to it 816 // and retrieve the data from the new alloca. 817 if (mlir::isa<fir::BaseBoxType>(symType)) { 818 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 819 auto alloca = builder.create<fir::AllocaOp>(currentLocation, symType); 820 builder.create<fir::StoreOp>(currentLocation, symVal, alloca); 821 cpVar = alloca; 822 } 823 824 result.copyprivateVars.push_back(cpVar); 825 mlir::func::FuncOp funcOp = 826 createCopyFunc(currentLocation, converter, cpVar.getType(), attrs); 827 result.copyprivateSyms.push_back(mlir::SymbolRefAttr::get(funcOp)); 828 }; 829 830 bool hasCopyPrivate = findRepeatableClause<clause::Copyprivate>( 831 [&](const clause::Copyprivate &clause, const parser::CharBlock &) { 832 for (const Object &object : clause.v) { 833 semantics::Symbol *sym = object.sym(); 834 if (const auto *commonDetails = 835 sym->detailsIf<semantics::CommonBlockDetails>()) { 836 for (const auto &mem : commonDetails->objects()) 837 addCopyPrivateVar(&*mem); 838 break; 839 } 840 addCopyPrivateVar(sym); 841 } 842 }); 843 844 return hasCopyPrivate; 845 } 846 847 bool ClauseProcessor::processDepend(mlir::omp::DependClauseOps &result) const { 848 auto process = [&](const omp::clause::Depend &clause, 849 const parser::CharBlock &) { 850 using Depend = omp::clause::Depend; 851 if (!std::holds_alternative<Depend::TaskDep>(clause.u)) { 852 TODO(converter.getCurrentLocation(), 853 "DEPEND clause with SINK or SOURCE is not supported yet"); 854 } 855 auto &taskDep = std::get<Depend::TaskDep>(clause.u); 856 auto depType = std::get<clause::DependenceType>(taskDep.t); 857 auto &objects = std::get<omp::ObjectList>(taskDep.t); 858 859 if (std::get<std::optional<omp::clause::Iterator>>(taskDep.t)) { 860 TODO(converter.getCurrentLocation(), 861 "Support for iterator modifiers is not implemented yet"); 862 } 863 mlir::omp::ClauseTaskDependAttr dependTypeOperand = 864 genDependKindAttr(converter, depType); 865 result.dependKinds.append(objects.size(), dependTypeOperand); 866 867 for (const omp::Object &object : objects) { 868 assert(object.ref() && "Expecting designator"); 869 870 if (evaluate::ExtractSubstring(*object.ref())) { 871 TODO(converter.getCurrentLocation(), 872 "substring not supported for task depend"); 873 } else if (evaluate::IsArrayElement(*object.ref())) { 874 TODO(converter.getCurrentLocation(), 875 "array sections not supported for task depend"); 876 } 877 878 semantics::Symbol *sym = object.sym(); 879 const mlir::Value variable = converter.getSymbolAddress(*sym); 880 result.dependVars.push_back(variable); 881 } 882 }; 883 884 return findRepeatableClause<omp::clause::Depend>(process); 885 } 886 887 bool ClauseProcessor::processHasDeviceAddr( 888 mlir::omp::HasDeviceAddrClauseOps &result, 889 llvm::SmallVectorImpl<const semantics::Symbol *> &isDeviceSyms) const { 890 return findRepeatableClause<omp::clause::HasDeviceAddr>( 891 [&](const omp::clause::HasDeviceAddr &devAddrClause, 892 const parser::CharBlock &) { 893 addUseDeviceClause(converter, devAddrClause.v, result.hasDeviceAddrVars, 894 isDeviceSyms); 895 }); 896 } 897 898 bool ClauseProcessor::processIf( 899 omp::clause::If::DirectiveNameModifier directiveName, 900 mlir::omp::IfClauseOps &result) const { 901 bool found = false; 902 findRepeatableClause<omp::clause::If>([&](const omp::clause::If &clause, 903 const parser::CharBlock &source) { 904 mlir::Location clauseLocation = converter.genLocation(source); 905 mlir::Value operand = 906 getIfClauseOperand(converter, clause, directiveName, clauseLocation); 907 // Assume that, at most, a single 'if' clause will be applicable to the 908 // given directive. 909 if (operand) { 910 result.ifExpr = operand; 911 found = true; 912 } 913 }); 914 return found; 915 } 916 917 bool ClauseProcessor::processIsDevicePtr( 918 mlir::omp::IsDevicePtrClauseOps &result, 919 llvm::SmallVectorImpl<const semantics::Symbol *> &isDeviceSyms) const { 920 return findRepeatableClause<omp::clause::IsDevicePtr>( 921 [&](const omp::clause::IsDevicePtr &devPtrClause, 922 const parser::CharBlock &) { 923 addUseDeviceClause(converter, devPtrClause.v, result.isDevicePtrVars, 924 isDeviceSyms); 925 }); 926 } 927 928 bool ClauseProcessor::processLink( 929 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { 930 return findRepeatableClause<omp::clause::Link>( 931 [&](const omp::clause::Link &clause, const parser::CharBlock &) { 932 // Case: declare target link(var1, var2)... 933 gatherFuncAndVarSyms( 934 clause.v, mlir::omp::DeclareTargetCaptureClause::link, result); 935 }); 936 } 937 938 void ClauseProcessor::processMapObjects( 939 lower::StatementContext &stmtCtx, mlir::Location clauseLocation, 940 const omp::ObjectList &objects, 941 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits, 942 std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices, 943 llvm::SmallVectorImpl<mlir::Value> &mapVars, 944 llvm::SmallVectorImpl<const semantics::Symbol *> &mapSyms) const { 945 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); 946 947 for (const omp::Object &object : objects) { 948 llvm::SmallVector<mlir::Value> bounds; 949 std::stringstream asFortran; 950 std::optional<omp::Object> parentObj; 951 952 fir::factory::AddrAndBoundsInfo info = 953 lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp, 954 mlir::omp::MapBoundsType>( 955 converter, firOpBuilder, semaCtx, stmtCtx, *object.sym(), 956 object.ref(), clauseLocation, asFortran, bounds, 957 treatIndexAsSection); 958 959 mlir::Value baseOp = info.rawInput; 960 if (object.sym()->owner().IsDerivedType()) { 961 omp::ObjectList objectList = gatherObjectsOf(object, semaCtx); 962 assert(!objectList.empty() && 963 "could not find parent objects of derived type member"); 964 parentObj = objectList[0]; 965 parentMemberIndices.emplace(parentObj.value(), 966 OmpMapParentAndMemberData{}); 967 968 if (isMemberOrParentAllocatableOrPointer(object, semaCtx)) { 969 llvm::SmallVector<int64_t> indices; 970 generateMemberPlacementIndices(object, indices, semaCtx); 971 baseOp = createParentSymAndGenIntermediateMaps( 972 clauseLocation, converter, semaCtx, stmtCtx, objectList, indices, 973 parentMemberIndices[parentObj.value()], asFortran.str(), 974 mapTypeBits); 975 } 976 } 977 978 // Explicit map captures are captured ByRef by default, 979 // optimisation passes may alter this to ByCopy or other capture 980 // types to optimise 981 auto location = mlir::NameLoc::get( 982 mlir::StringAttr::get(firOpBuilder.getContext(), asFortran.str()), 983 baseOp.getLoc()); 984 mlir::omp::MapInfoOp mapOp = createMapInfoOp( 985 firOpBuilder, location, baseOp, 986 /*varPtrPtr=*/mlir::Value{}, asFortran.str(), bounds, 987 /*members=*/{}, /*membersIndex=*/mlir::ArrayAttr{}, 988 static_cast< 989 std::underlying_type_t<llvm::omp::OpenMPOffloadMappingFlags>>( 990 mapTypeBits), 991 mlir::omp::VariableCaptureKind::ByRef, baseOp.getType()); 992 993 if (parentObj.has_value()) { 994 parentMemberIndices[parentObj.value()].addChildIndexAndMapToParent( 995 object, mapOp, semaCtx); 996 } else { 997 mapVars.push_back(mapOp); 998 mapSyms.push_back(object.sym()); 999 } 1000 } 1001 } 1002 1003 bool ClauseProcessor::processMap( 1004 mlir::Location currentLocation, lower::StatementContext &stmtCtx, 1005 mlir::omp::MapClauseOps &result, 1006 llvm::SmallVectorImpl<const semantics::Symbol *> *mapSyms) const { 1007 // We always require tracking of symbols, even if the caller does not, 1008 // so we create an optionally used local set of symbols when the mapSyms 1009 // argument is not present. 1010 llvm::SmallVector<const semantics::Symbol *> localMapSyms; 1011 llvm::SmallVectorImpl<const semantics::Symbol *> *ptrMapSyms = 1012 mapSyms ? mapSyms : &localMapSyms; 1013 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1014 1015 auto process = [&](const omp::clause::Map &clause, 1016 const parser::CharBlock &source) { 1017 using Map = omp::clause::Map; 1018 mlir::Location clauseLocation = converter.genLocation(source); 1019 const auto &[mapType, typeMods, mappers, iterator, objects] = clause.t; 1020 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1021 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE; 1022 // If the map type is specified, then process it else Tofrom is the 1023 // default. 1024 Map::MapType type = mapType.value_or(Map::MapType::Tofrom); 1025 switch (type) { 1026 case Map::MapType::To: 1027 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO; 1028 break; 1029 case Map::MapType::From: 1030 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1031 break; 1032 case Map::MapType::Tofrom: 1033 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | 1034 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1035 break; 1036 case Map::MapType::Alloc: 1037 case Map::MapType::Release: 1038 // alloc and release is the default map_type for the Target Data 1039 // Ops, i.e. if no bits for map_type is supplied then alloc/release 1040 // is implicitly assumed based on the target directive. Default 1041 // value for Target Data and Enter Data is alloc and for Exit Data 1042 // it is release. 1043 break; 1044 case Map::MapType::Delete: 1045 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE; 1046 } 1047 1048 if (typeMods) { 1049 if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Always)) 1050 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS; 1051 // Diagnose unimplemented map-type-modifiers. 1052 if (llvm::any_of(*typeMods, [](Map::MapTypeModifier m) { 1053 return m != Map::MapTypeModifier::Always; 1054 })) { 1055 TODO(currentLocation, "Map type modifiers (other than 'ALWAYS')" 1056 " are not implemented yet"); 1057 } 1058 } 1059 1060 if (iterator) { 1061 TODO(currentLocation, 1062 "Support for iterator modifiers is not implemented yet"); 1063 } 1064 if (mappers) { 1065 TODO(currentLocation, 1066 "Support for mapper modifiers is not implemented yet"); 1067 } 1068 1069 processMapObjects(stmtCtx, clauseLocation, 1070 std::get<omp::ObjectList>(clause.t), mapTypeBits, 1071 parentMemberIndices, result.mapVars, *ptrMapSyms); 1072 }; 1073 1074 bool clauseFound = findRepeatableClause<omp::clause::Map>(process); 1075 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1076 result.mapVars, *ptrMapSyms); 1077 1078 return clauseFound; 1079 } 1080 1081 bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx, 1082 mlir::omp::MapClauseOps &result) { 1083 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1084 llvm::SmallVector<const semantics::Symbol *> mapSymbols; 1085 1086 auto callbackFn = [&](const auto &clause, const parser::CharBlock &source) { 1087 mlir::Location clauseLocation = converter.genLocation(source); 1088 const auto &[expectation, mapper, iterator, objects] = clause.t; 1089 // TODO Support motion modifiers: present, mapper, iterator. 1090 if (expectation) { 1091 TODO(clauseLocation, "PRESENT modifier is not supported yet"); 1092 } else if (mapper) { 1093 TODO(clauseLocation, "Mapper modifier is not supported yet"); 1094 } else if (iterator) { 1095 TODO(clauseLocation, "Iterator modifier is not supported yet"); 1096 } 1097 constexpr llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1098 std::is_same_v<llvm::remove_cvref_t<decltype(clause)>, omp::clause::To> 1099 ? llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO 1100 : llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1101 1102 processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits, 1103 parentMemberIndices, result.mapVars, mapSymbols); 1104 }; 1105 1106 bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn); 1107 clauseFound = 1108 findRepeatableClause<omp::clause::From>(callbackFn) || clauseFound; 1109 1110 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1111 result.mapVars, mapSymbols); 1112 1113 return clauseFound; 1114 } 1115 1116 bool ClauseProcessor::processNontemporal( 1117 mlir::omp::NontemporalClauseOps &result) const { 1118 return findRepeatableClause<omp::clause::Nontemporal>( 1119 [&](const omp::clause::Nontemporal &clause, const parser::CharBlock &) { 1120 for (const Object &object : clause.v) { 1121 semantics::Symbol *sym = object.sym(); 1122 mlir::Value symVal = converter.getSymbolAddress(*sym); 1123 result.nontemporalVars.push_back(symVal); 1124 } 1125 }); 1126 } 1127 1128 bool ClauseProcessor::processReduction( 1129 mlir::Location currentLocation, mlir::omp::ReductionClauseOps &result, 1130 llvm::SmallVectorImpl<const semantics::Symbol *> &outReductionSyms) const { 1131 return findRepeatableClause<omp::clause::Reduction>( 1132 [&](const omp::clause::Reduction &clause, const parser::CharBlock &) { 1133 llvm::SmallVector<mlir::Value> reductionVars; 1134 llvm::SmallVector<bool> reduceVarByRef; 1135 llvm::SmallVector<mlir::Attribute> reductionDeclSymbols; 1136 llvm::SmallVector<const semantics::Symbol *> reductionSyms; 1137 ReductionProcessor rp; 1138 rp.addDeclareReduction(currentLocation, converter, clause, 1139 reductionVars, reduceVarByRef, 1140 reductionDeclSymbols, reductionSyms); 1141 1142 // Copy local lists into the output. 1143 llvm::copy(reductionVars, std::back_inserter(result.reductionVars)); 1144 llvm::copy(reduceVarByRef, std::back_inserter(result.reductionByref)); 1145 llvm::copy(reductionDeclSymbols, 1146 std::back_inserter(result.reductionSyms)); 1147 llvm::copy(reductionSyms, std::back_inserter(outReductionSyms)); 1148 }); 1149 } 1150 1151 bool ClauseProcessor::processTo( 1152 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { 1153 return findRepeatableClause<omp::clause::To>( 1154 [&](const omp::clause::To &clause, const parser::CharBlock &) { 1155 // Case: declare target to(func, var1, var2)... 1156 gatherFuncAndVarSyms(std::get<ObjectList>(clause.t), 1157 mlir::omp::DeclareTargetCaptureClause::to, result); 1158 }); 1159 } 1160 1161 bool ClauseProcessor::processEnter( 1162 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const { 1163 return findRepeatableClause<omp::clause::Enter>( 1164 [&](const omp::clause::Enter &clause, const parser::CharBlock &) { 1165 // Case: declare target enter(func, var1, var2)... 1166 gatherFuncAndVarSyms( 1167 clause.v, mlir::omp::DeclareTargetCaptureClause::enter, result); 1168 }); 1169 } 1170 1171 bool ClauseProcessor::processUseDeviceAddr( 1172 lower::StatementContext &stmtCtx, mlir::omp::UseDeviceAddrClauseOps &result, 1173 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) const { 1174 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1175 bool clauseFound = findRepeatableClause<omp::clause::UseDeviceAddr>( 1176 [&](const omp::clause::UseDeviceAddr &clause, 1177 const parser::CharBlock &source) { 1178 mlir::Location location = converter.genLocation(source); 1179 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1180 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | 1181 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1182 processMapObjects(stmtCtx, location, clause.v, mapTypeBits, 1183 parentMemberIndices, result.useDeviceAddrVars, 1184 useDeviceSyms); 1185 }); 1186 1187 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1188 result.useDeviceAddrVars, useDeviceSyms); 1189 return clauseFound; 1190 } 1191 1192 bool ClauseProcessor::processUseDevicePtr( 1193 lower::StatementContext &stmtCtx, mlir::omp::UseDevicePtrClauseOps &result, 1194 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) const { 1195 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices; 1196 1197 bool clauseFound = findRepeatableClause<omp::clause::UseDevicePtr>( 1198 [&](const omp::clause::UseDevicePtr &clause, 1199 const parser::CharBlock &source) { 1200 mlir::Location location = converter.genLocation(source); 1201 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits = 1202 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO | 1203 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM; 1204 processMapObjects(stmtCtx, location, clause.v, mapTypeBits, 1205 parentMemberIndices, result.useDevicePtrVars, 1206 useDeviceSyms); 1207 }); 1208 1209 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices, 1210 result.useDevicePtrVars, useDeviceSyms); 1211 return clauseFound; 1212 } 1213 1214 } // namespace omp 1215 } // namespace lower 1216 } // namespace Fortran 1217