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