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 "Utils.h"
15
16#include "flang/Lower/ConvertExprToHLFIR.h"
17#include "flang/Lower/OpenMP/Clauses.h"
18#include "flang/Lower/PFTBuilder.h"
19#include "flang/Lower/Support/ReductionProcessor.h"
20#include "flang/Parser/tools.h"
21#include "flang/Semantics/tools.h"
22#include "llvm/Frontend/OpenMP/OMP.h.inc"
23#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
24
25namespace Fortran {
26namespace lower {
27namespace omp {
28
29using ReductionModifier =
30 Fortran::lower::omp::clause::Reduction::ReductionModifier;
31
32mlir::omp::ReductionModifier translateReductionModifier(ReductionModifier mod) {
33 switch (mod) {
34 case ReductionModifier::Default:
35 return mlir::omp::ReductionModifier::defaultmod;
36 case ReductionModifier::Inscan:
37 return mlir::omp::ReductionModifier::inscan;
38 case ReductionModifier::Task:
39 return mlir::omp::ReductionModifier::task;
40 }
41 return mlir::omp::ReductionModifier::defaultmod;
42}
43
44/// Check for unsupported map operand types.
45static void checkMapType(mlir::Location location, mlir::Type type) {
46 if (auto refType = mlir::dyn_cast<fir::ReferenceType>(type))
47 type = refType.getElementType();
48 if (auto boxType = mlir::dyn_cast_or_null<fir::BoxType>(type))
49 if (!mlir::isa<fir::PointerType>(boxType.getElementType()))
50 TODO(location, "OMPD_target_data MapOperand BoxType");
51}
52
53static mlir::omp::ScheduleModifier
54translateScheduleModifier(const omp::clause::Schedule::OrderingModifier &m) {
55 switch (m) {
56 case omp::clause::Schedule::OrderingModifier::Monotonic:
57 return mlir::omp::ScheduleModifier::monotonic;
58 case omp::clause::Schedule::OrderingModifier::Nonmonotonic:
59 return mlir::omp::ScheduleModifier::nonmonotonic;
60 }
61 return mlir::omp::ScheduleModifier::none;
62}
63
64static mlir::omp::ScheduleModifier
65getScheduleModifier(const omp::clause::Schedule &clause) {
66 using Schedule = omp::clause::Schedule;
67 const auto &modifier =
68 std::get<std::optional<Schedule::OrderingModifier>>(clause.t);
69 if (modifier)
70 return translateScheduleModifier(*modifier);
71 return mlir::omp::ScheduleModifier::none;
72}
73
74static mlir::omp::ScheduleModifier
75getSimdModifier(const omp::clause::Schedule &clause) {
76 using Schedule = omp::clause::Schedule;
77 const auto &modifier =
78 std::get<std::optional<Schedule::ChunkModifier>>(clause.t);
79 if (modifier && *modifier == Schedule::ChunkModifier::Simd)
80 return mlir::omp::ScheduleModifier::simd;
81 return mlir::omp::ScheduleModifier::none;
82}
83
84static void
85genAllocateClause(lower::AbstractConverter &converter,
86 const omp::clause::Allocate &clause,
87 llvm::SmallVectorImpl<mlir::Value> &allocatorOperands,
88 llvm::SmallVectorImpl<mlir::Value> &allocateOperands) {
89 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
90 mlir::Location currentLocation = converter.getCurrentLocation();
91 lower::StatementContext stmtCtx;
92
93 auto &objects = std::get<omp::ObjectList>(clause.t);
94
95 using Allocate = omp::clause::Allocate;
96 // ALIGN in this context is unimplemented
97 if (std::get<std::optional<Allocate::AlignModifier>>(clause.t))
98 TODO(currentLocation, "OmpAllocateClause ALIGN modifier");
99
100 // Check if allocate clause has allocator specified. If so, add it
101 // to list of allocators, otherwise, add default allocator to
102 // list of allocators.
103 using ComplexModifier = Allocate::AllocatorComplexModifier;
104 if (auto &mod = std::get<std::optional<ComplexModifier>>(clause.t)) {
105 mlir::Value operand = fir::getBase(converter.genExprValue(mod->v, stmtCtx));
106 allocatorOperands.append(objects.size(), operand);
107 } else {
108 mlir::Value operand = firOpBuilder.createIntegerConstant(
109 currentLocation, firOpBuilder.getI32Type(), 1);
110 allocatorOperands.append(objects.size(), operand);
111 }
112
113 genObjectList(objects, converter, allocateOperands);
114}
115
116static mlir::omp::ClauseBindKindAttr
117genBindKindAttr(fir::FirOpBuilder &firOpBuilder,
118 const omp::clause::Bind &clause) {
119 mlir::omp::ClauseBindKind bindKind;
120 switch (clause.v) {
121 case omp::clause::Bind::Binding::Teams:
122 bindKind = mlir::omp::ClauseBindKind::Teams;
123 break;
124 case omp::clause::Bind::Binding::Parallel:
125 bindKind = mlir::omp::ClauseBindKind::Parallel;
126 break;
127 case omp::clause::Bind::Binding::Thread:
128 bindKind = mlir::omp::ClauseBindKind::Thread;
129 break;
130 }
131 return mlir::omp::ClauseBindKindAttr::get(firOpBuilder.getContext(),
132 bindKind);
133}
134
135static mlir::omp::ClauseProcBindKindAttr
136genProcBindKindAttr(fir::FirOpBuilder &firOpBuilder,
137 const omp::clause::ProcBind &clause) {
138 mlir::omp::ClauseProcBindKind procBindKind;
139 switch (clause.v) {
140 case omp::clause::ProcBind::AffinityPolicy::Master:
141 procBindKind = mlir::omp::ClauseProcBindKind::Master;
142 break;
143 case omp::clause::ProcBind::AffinityPolicy::Close:
144 procBindKind = mlir::omp::ClauseProcBindKind::Close;
145 break;
146 case omp::clause::ProcBind::AffinityPolicy::Spread:
147 procBindKind = mlir::omp::ClauseProcBindKind::Spread;
148 break;
149 case omp::clause::ProcBind::AffinityPolicy::Primary:
150 procBindKind = mlir::omp::ClauseProcBindKind::Primary;
151 break;
152 }
153 return mlir::omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(),
154 procBindKind);
155}
156
157static mlir::omp::ClauseTaskDependAttr
158genDependKindAttr(lower::AbstractConverter &converter,
159 const omp::clause::DependenceType kind) {
160 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
161 mlir::Location currentLocation = converter.getCurrentLocation();
162
163 mlir::omp::ClauseTaskDepend pbKind;
164 switch (kind) {
165 case omp::clause::DependenceType::In:
166 pbKind = mlir::omp::ClauseTaskDepend::taskdependin;
167 break;
168 case omp::clause::DependenceType::Out:
169 pbKind = mlir::omp::ClauseTaskDepend::taskdependout;
170 break;
171 case omp::clause::DependenceType::Inout:
172 pbKind = mlir::omp::ClauseTaskDepend::taskdependinout;
173 break;
174 case omp::clause::DependenceType::Mutexinoutset:
175 pbKind = mlir::omp::ClauseTaskDepend::taskdependmutexinoutset;
176 break;
177 case omp::clause::DependenceType::Inoutset:
178 pbKind = mlir::omp::ClauseTaskDepend::taskdependinoutset;
179 break;
180 case omp::clause::DependenceType::Depobj:
181 TODO(currentLocation, "DEPOBJ dependence-type");
182 break;
183 case omp::clause::DependenceType::Sink:
184 case omp::clause::DependenceType::Source:
185 llvm_unreachable("unhandled parser task dependence type");
186 break;
187 }
188 return mlir::omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(),
189 pbKind);
190}
191
192static mlir::Value
193getIfClauseOperand(lower::AbstractConverter &converter,
194 const omp::clause::If &clause,
195 omp::clause::If::DirectiveNameModifier directiveName,
196 mlir::Location clauseLocation) {
197 // Only consider the clause if it's intended for the given directive.
198 auto &directive =
199 std::get<std::optional<omp::clause::If::DirectiveNameModifier>>(clause.t);
200 if (directive && directive.value() != directiveName)
201 return nullptr;
202
203 lower::StatementContext stmtCtx;
204 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
205 mlir::Value ifVal = fir::getBase(
206 converter.genExprValue(std::get<omp::SomeExpr>(clause.t), stmtCtx));
207 return firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(),
208 ifVal);
209}
210
211static void addUseDeviceClause(
212 lower::AbstractConverter &converter, const omp::ObjectList &objects,
213 llvm::SmallVectorImpl<mlir::Value> &operands,
214 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) {
215 genObjectList(objects, converter, operands);
216 for (mlir::Value &operand : operands)
217 checkMapType(operand.getLoc(), operand.getType());
218
219 for (const omp::Object &object : objects)
220 useDeviceSyms.push_back(object.sym());
221}
222
223//===----------------------------------------------------------------------===//
224// ClauseProcessor unique clauses
225//===----------------------------------------------------------------------===//
226
227bool ClauseProcessor::processBare(mlir::omp::BareClauseOps &result) const {
228 return markClauseOccurrence<omp::clause::OmpxBare>(result.bare);
229}
230
231bool ClauseProcessor::processBind(mlir::omp::BindClauseOps &result) const {
232 if (auto *clause = findUniqueClause<omp::clause::Bind>()) {
233 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
234 result.bindKind = genBindKindAttr(firOpBuilder, *clause);
235 return true;
236 }
237 return false;
238}
239
240bool ClauseProcessor::processCancelDirectiveName(
241 mlir::omp::CancelDirectiveNameClauseOps &result) const {
242 using ConstructType = mlir::omp::ClauseCancellationConstructType;
243 mlir::MLIRContext *context = &converter.getMLIRContext();
244
245 ConstructType directive;
246 if (auto *clause = findUniqueClause<omp::CancellationConstructType>()) {
247 switch (clause->v) {
248 case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Parallel:
249 directive = mlir::omp::ClauseCancellationConstructType::Parallel;
250 break;
251 case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Loop:
252 directive = mlir::omp::ClauseCancellationConstructType::Loop;
253 break;
254 case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Sections:
255 directive = mlir::omp::ClauseCancellationConstructType::Sections;
256 break;
257 case llvm::omp::OMP_CANCELLATION_CONSTRUCT_Taskgroup:
258 directive = mlir::omp::ClauseCancellationConstructType::Taskgroup;
259 break;
260 case llvm::omp::OMP_CANCELLATION_CONSTRUCT_None:
261 llvm_unreachable("OMP_CANCELLATION_CONSTRUCT_None");
262 break;
263 }
264 } else {
265 llvm_unreachable("cancel construct missing cancellation construct type");
266 }
267
268 result.cancelDirective =
269 mlir::omp::ClauseCancellationConstructTypeAttr::get(context, directive);
270 return true;
271}
272
273bool ClauseProcessor::processCollapse(
274 mlir::Location currentLocation, lower::pft::Evaluation &eval,
275 mlir::omp::LoopRelatedClauseOps &result,
276 llvm::SmallVectorImpl<const semantics::Symbol *> &iv) const {
277 return collectLoopRelatedInfo(converter, currentLocation, eval, clauses,
278 result, iv);
279}
280
281bool ClauseProcessor::processDevice(lower::StatementContext &stmtCtx,
282 mlir::omp::DeviceClauseOps &result) const {
283 const parser::CharBlock *source = nullptr;
284 if (auto *clause = findUniqueClause<omp::clause::Device>(&source)) {
285 mlir::Location clauseLocation = converter.genLocation(*source);
286 if (auto deviceModifier =
287 std::get<std::optional<omp::clause::Device::DeviceModifier>>(
288 clause->t)) {
289 if (deviceModifier == omp::clause::Device::DeviceModifier::Ancestor) {
290 TODO(clauseLocation, "OMPD_target Device Modifier Ancestor");
291 }
292 }
293 const auto &deviceExpr = std::get<omp::SomeExpr>(clause->t);
294 result.device = fir::getBase(converter.genExprValue(deviceExpr, stmtCtx));
295 return true;
296 }
297 return false;
298}
299
300bool ClauseProcessor::processDeviceType(
301 mlir::omp::DeviceTypeClauseOps &result) const {
302 if (auto *clause = findUniqueClause<omp::clause::DeviceType>()) {
303 // Case: declare target ... device_type(any | host | nohost)
304 switch (clause->v) {
305 case omp::clause::DeviceType::DeviceTypeDescription::Nohost:
306 result.deviceType = mlir::omp::DeclareTargetDeviceType::nohost;
307 break;
308 case omp::clause::DeviceType::DeviceTypeDescription::Host:
309 result.deviceType = mlir::omp::DeclareTargetDeviceType::host;
310 break;
311 case omp::clause::DeviceType::DeviceTypeDescription::Any:
312 result.deviceType = mlir::omp::DeclareTargetDeviceType::any;
313 break;
314 }
315 return true;
316 }
317 return false;
318}
319
320bool ClauseProcessor::processDistSchedule(
321 lower::StatementContext &stmtCtx,
322 mlir::omp::DistScheduleClauseOps &result) const {
323 if (auto *clause = findUniqueClause<omp::clause::DistSchedule>()) {
324 result.distScheduleStatic = converter.getFirOpBuilder().getUnitAttr();
325 const auto &chunkSize = std::get<std::optional<ExprTy>>(clause->t);
326 if (chunkSize)
327 result.distScheduleChunkSize =
328 fir::getBase(converter.genExprValue(*chunkSize, stmtCtx));
329 return true;
330 }
331 return false;
332}
333
334bool ClauseProcessor::processExclusive(
335 mlir::Location currentLocation,
336 mlir::omp::ExclusiveClauseOps &result) const {
337 if (auto *clause = findUniqueClause<omp::clause::Exclusive>()) {
338 for (const Object &object : clause->v) {
339 const semantics::Symbol *symbol = object.sym();
340 mlir::Value symVal = converter.getSymbolAddress(*symbol);
341 result.exclusiveVars.push_back(symVal);
342 }
343 return true;
344 }
345 return false;
346}
347
348bool ClauseProcessor::processFilter(lower::StatementContext &stmtCtx,
349 mlir::omp::FilterClauseOps &result) const {
350 if (auto *clause = findUniqueClause<omp::clause::Filter>()) {
351 result.filteredThreadId =
352 fir::getBase(converter.genExprValue(clause->v, stmtCtx));
353 return true;
354 }
355 return false;
356}
357
358bool ClauseProcessor::processFinal(lower::StatementContext &stmtCtx,
359 mlir::omp::FinalClauseOps &result) const {
360 const parser::CharBlock *source = nullptr;
361 if (auto *clause = findUniqueClause<omp::clause::Final>(&source)) {
362 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
363 mlir::Location clauseLocation = converter.genLocation(*source);
364
365 mlir::Value finalVal =
366 fir::getBase(converter.genExprValue(clause->v, stmtCtx));
367 result.final = firOpBuilder.createConvert(
368 clauseLocation, firOpBuilder.getI1Type(), finalVal);
369 return true;
370 }
371 return false;
372}
373
374bool ClauseProcessor::processHint(mlir::omp::HintClauseOps &result) const {
375 if (auto *clause = findUniqueClause<omp::clause::Hint>()) {
376 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
377 int64_t hintValue = *evaluate::ToInt64(clause->v);
378 result.hint = firOpBuilder.getI64IntegerAttr(hintValue);
379 return true;
380 }
381 return false;
382}
383
384bool ClauseProcessor::processInclusive(
385 mlir::Location currentLocation,
386 mlir::omp::InclusiveClauseOps &result) const {
387 if (auto *clause = findUniqueClause<omp::clause::Inclusive>()) {
388 for (const Object &object : clause->v) {
389 const semantics::Symbol *symbol = object.sym();
390 mlir::Value symVal = converter.getSymbolAddress(*symbol);
391 result.inclusiveVars.push_back(symVal);
392 }
393 return true;
394 }
395 return false;
396}
397
398bool ClauseProcessor::processMergeable(
399 mlir::omp::MergeableClauseOps &result) const {
400 return markClauseOccurrence<omp::clause::Mergeable>(result.mergeable);
401}
402
403bool ClauseProcessor::processNowait(mlir::omp::NowaitClauseOps &result) const {
404 return markClauseOccurrence<omp::clause::Nowait>(result.nowait);
405}
406
407bool ClauseProcessor::processNumTasks(
408 lower::StatementContext &stmtCtx,
409 mlir::omp::NumTasksClauseOps &result) const {
410 using NumTasks = omp::clause::NumTasks;
411 if (auto *clause = findUniqueClause<NumTasks>()) {
412 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
413 mlir::MLIRContext *context = firOpBuilder.getContext();
414 const auto &modifier =
415 std::get<std::optional<NumTasks::Prescriptiveness>>(clause->t);
416 if (modifier && *modifier == NumTasks::Prescriptiveness::Strict) {
417 result.numTasksMod = mlir::omp::ClauseNumTasksTypeAttr::get(
418 context, mlir::omp::ClauseNumTasksType::Strict);
419 }
420 const auto &numtasksExpr = std::get<omp::SomeExpr>(clause->t);
421 result.numTasks =
422 fir::getBase(converter.genExprValue(numtasksExpr, stmtCtx));
423 return true;
424 }
425 return false;
426}
427
428bool ClauseProcessor::processNumTeams(
429 lower::StatementContext &stmtCtx,
430 mlir::omp::NumTeamsClauseOps &result) const {
431 // TODO Get lower and upper bounds for num_teams when parser is updated to
432 // accept both.
433 if (auto *clause = findUniqueClause<omp::clause::NumTeams>()) {
434 // The num_teams directive accepts a list of team lower/upper bounds.
435 // This is an extension to support grid specification for ompx_bare.
436 // Here, only expect a single element in the list.
437 assert(clause->v.size() == 1);
438 // auto lowerBound = std::get<std::optional<ExprTy>>(clause->v[0]->t);
439 auto &upperBound = std::get<ExprTy>(clause->v[0].t);
440 result.numTeamsUpper =
441 fir::getBase(converter.genExprValue(upperBound, stmtCtx));
442 return true;
443 }
444 return false;
445}
446
447bool ClauseProcessor::processNumThreads(
448 lower::StatementContext &stmtCtx,
449 mlir::omp::NumThreadsClauseOps &result) const {
450 if (auto *clause = findUniqueClause<omp::clause::NumThreads>()) {
451 // OMPIRBuilder expects `NUM_THREADS` clause as a `Value`.
452 result.numThreads =
453 fir::getBase(converter.genExprValue(clause->v, stmtCtx));
454 return true;
455 }
456 return false;
457}
458
459bool ClauseProcessor::processOrder(mlir::omp::OrderClauseOps &result) const {
460 using Order = omp::clause::Order;
461 if (auto *clause = findUniqueClause<Order>()) {
462 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
463 result.order = mlir::omp::ClauseOrderKindAttr::get(
464 firOpBuilder.getContext(), mlir::omp::ClauseOrderKind::Concurrent);
465 const auto &modifier =
466 std::get<std::optional<Order::OrderModifier>>(clause->t);
467 if (modifier && *modifier == Order::OrderModifier::Unconstrained) {
468 result.orderMod = mlir::omp::OrderModifierAttr::get(
469 firOpBuilder.getContext(), mlir::omp::OrderModifier::unconstrained);
470 } else {
471 // "If order-modifier is not unconstrained, the behavior is as if the
472 // reproducible modifier is present."
473 result.orderMod = mlir::omp::OrderModifierAttr::get(
474 firOpBuilder.getContext(), mlir::omp::OrderModifier::reproducible);
475 }
476 return true;
477 }
478 return false;
479}
480
481bool ClauseProcessor::processOrdered(
482 mlir::omp::OrderedClauseOps &result) const {
483 if (auto *clause = findUniqueClause<omp::clause::Ordered>()) {
484 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
485 int64_t orderedClauseValue = 0l;
486 if (clause->v.has_value())
487 orderedClauseValue = *evaluate::ToInt64(*clause->v);
488 result.ordered = firOpBuilder.getI64IntegerAttr(orderedClauseValue);
489 return true;
490 }
491 return false;
492}
493
494bool ClauseProcessor::processPriority(
495 lower::StatementContext &stmtCtx,
496 mlir::omp::PriorityClauseOps &result) const {
497 if (auto *clause = findUniqueClause<omp::clause::Priority>()) {
498 result.priority = fir::getBase(converter.genExprValue(clause->v, stmtCtx));
499 return true;
500 }
501 return false;
502}
503
504bool ClauseProcessor::processDetach(mlir::omp::DetachClauseOps &result) const {
505 if (auto *clause = findUniqueClause<omp::clause::Detach>()) {
506 semantics::Symbol *sym = clause->v.sym();
507 mlir::Value symVal = converter.getSymbolAddress(*sym);
508 result.eventHandle = symVal;
509 return true;
510 }
511 return false;
512}
513
514bool ClauseProcessor::processProcBind(
515 mlir::omp::ProcBindClauseOps &result) const {
516 if (auto *clause = findUniqueClause<omp::clause::ProcBind>()) {
517 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
518 result.procBindKind = genProcBindKindAttr(firOpBuilder, *clause);
519 return true;
520 }
521 return false;
522}
523
524bool ClauseProcessor::processSafelen(
525 mlir::omp::SafelenClauseOps &result) const {
526 if (auto *clause = findUniqueClause<omp::clause::Safelen>()) {
527 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
528 const std::optional<std::int64_t> safelenVal = evaluate::ToInt64(clause->v);
529 result.safelen = firOpBuilder.getI64IntegerAttr(*safelenVal);
530 return true;
531 }
532 return false;
533}
534
535bool ClauseProcessor::processSchedule(
536 lower::StatementContext &stmtCtx,
537 mlir::omp::ScheduleClauseOps &result) const {
538 if (auto *clause = findUniqueClause<omp::clause::Schedule>()) {
539 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
540 mlir::MLIRContext *context = firOpBuilder.getContext();
541 const auto &scheduleType = std::get<omp::clause::Schedule::Kind>(clause->t);
542
543 mlir::omp::ClauseScheduleKind scheduleKind;
544 switch (scheduleType) {
545 case omp::clause::Schedule::Kind::Static:
546 scheduleKind = mlir::omp::ClauseScheduleKind::Static;
547 break;
548 case omp::clause::Schedule::Kind::Dynamic:
549 scheduleKind = mlir::omp::ClauseScheduleKind::Dynamic;
550 break;
551 case omp::clause::Schedule::Kind::Guided:
552 scheduleKind = mlir::omp::ClauseScheduleKind::Guided;
553 break;
554 case omp::clause::Schedule::Kind::Auto:
555 scheduleKind = mlir::omp::ClauseScheduleKind::Auto;
556 break;
557 case omp::clause::Schedule::Kind::Runtime:
558 scheduleKind = mlir::omp::ClauseScheduleKind::Runtime;
559 break;
560 }
561
562 result.scheduleKind =
563 mlir::omp::ClauseScheduleKindAttr::get(context, scheduleKind);
564
565 mlir::omp::ScheduleModifier scheduleMod = getScheduleModifier(*clause);
566 if (scheduleMod != mlir::omp::ScheduleModifier::none)
567 result.scheduleMod =
568 mlir::omp::ScheduleModifierAttr::get(context, scheduleMod);
569
570 if (getSimdModifier(*clause) != mlir::omp::ScheduleModifier::none)
571 result.scheduleSimd = firOpBuilder.getUnitAttr();
572
573 if (const auto &chunkExpr = std::get<omp::MaybeExpr>(clause->t))
574 result.scheduleChunk =
575 fir::getBase(converter.genExprValue(*chunkExpr, stmtCtx));
576
577 return true;
578 }
579 return false;
580}
581
582bool ClauseProcessor::processSimdlen(
583 mlir::omp::SimdlenClauseOps &result) const {
584 if (auto *clause = findUniqueClause<omp::clause::Simdlen>()) {
585 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
586 const std::optional<std::int64_t> simdlenVal = evaluate::ToInt64(clause->v);
587 result.simdlen = firOpBuilder.getI64IntegerAttr(*simdlenVal);
588 return true;
589 }
590 return false;
591}
592
593bool ClauseProcessor::processThreadLimit(
594 lower::StatementContext &stmtCtx,
595 mlir::omp::ThreadLimitClauseOps &result) const {
596 if (auto *clause = findUniqueClause<omp::clause::ThreadLimit>()) {
597 result.threadLimit =
598 fir::getBase(converter.genExprValue(clause->v, stmtCtx));
599 return true;
600 }
601 return false;
602}
603
604bool ClauseProcessor::processUntied(mlir::omp::UntiedClauseOps &result) const {
605 return markClauseOccurrence<omp::clause::Untied>(result.untied);
606}
607
608//===----------------------------------------------------------------------===//
609// ClauseProcessor repeatable clauses
610//===----------------------------------------------------------------------===//
611static llvm::StringMap<bool> getTargetFeatures(mlir::ModuleOp module) {
612 llvm::StringMap<bool> featuresMap;
613 llvm::SmallVector<llvm::StringRef> targetFeaturesVec;
614 if (mlir::LLVM::TargetFeaturesAttr features =
615 fir::getTargetFeatures(module)) {
616 llvm::ArrayRef<mlir::StringAttr> featureAttrs = features.getFeatures();
617 for (auto &featureAttr : featureAttrs) {
618 llvm::StringRef featureKeyString = featureAttr.strref();
619 featuresMap[featureKeyString.substr(1)] = (featureKeyString[0] == '+');
620 }
621 }
622 return featuresMap;
623}
624
625static void
626addAlignedClause(lower::AbstractConverter &converter,
627 const omp::clause::Aligned &clause,
628 llvm::SmallVectorImpl<mlir::Value> &alignedVars,
629 llvm::SmallVectorImpl<mlir::Attribute> &alignments) {
630 using Aligned = omp::clause::Aligned;
631 lower::StatementContext stmtCtx;
632 mlir::IntegerAttr alignmentValueAttr;
633 int64_t alignment = 0;
634 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
635
636 if (auto &alignmentValueParserExpr =
637 std::get<std::optional<Aligned::Alignment>>(clause.t)) {
638 mlir::Value operand = fir::getBase(
639 converter.genExprValue(*alignmentValueParserExpr, stmtCtx));
640 alignment = *fir::getIntIfConstant(operand);
641 } else {
642 llvm::StringMap<bool> featuresMap = getTargetFeatures(builder.getModule());
643 llvm::Triple triple = fir::getTargetTriple(builder.getModule());
644 alignment =
645 llvm::OpenMPIRBuilder::getOpenMPDefaultSimdAlign(TargetTriple: triple, Features: featuresMap);
646 }
647
648 // The default alignment for some targets is equal to 0.
649 // Do not generate alignment assumption if alignment is less than or equal to
650 // 0.
651 if (alignment > 0) {
652 // alignment value must be power of 2
653 assert((alignment & (alignment - 1)) == 0 && "alignment is not power of 2");
654 auto &objects = std::get<omp::ObjectList>(clause.t);
655 if (!objects.empty())
656 genObjectList(objects, converter, alignedVars);
657 alignmentValueAttr = builder.getI64IntegerAttr(alignment);
658 // All the list items in a aligned clause will have same alignment
659 for (std::size_t i = 0; i < objects.size(); i++)
660 alignments.push_back(alignmentValueAttr);
661 }
662}
663
664bool ClauseProcessor::processAligned(
665 mlir::omp::AlignedClauseOps &result) const {
666 return findRepeatableClause<omp::clause::Aligned>(
667 [&](const omp::clause::Aligned &clause, const parser::CharBlock &) {
668 addAlignedClause(converter, clause, result.alignedVars,
669 result.alignments);
670 });
671}
672
673bool ClauseProcessor::processAllocate(
674 mlir::omp::AllocateClauseOps &result) const {
675 return findRepeatableClause<omp::clause::Allocate>(
676 [&](const omp::clause::Allocate &clause, const parser::CharBlock &) {
677 genAllocateClause(converter, clause, result.allocatorVars,
678 result.allocateVars);
679 });
680}
681
682bool ClauseProcessor::processCopyin() const {
683 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
684 mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();
685 firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
686 auto checkAndCopyHostAssociateVar =
687 [&](semantics::Symbol *sym,
688 mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) {
689 assert(sym->has<semantics::HostAssocDetails>() &&
690 "No host-association found");
691 if (converter.isPresentShallowLookup(*sym))
692 converter.copyHostAssociateVar(*sym, copyAssignIP);
693 };
694 bool hasCopyin = findRepeatableClause<omp::clause::Copyin>(
695 [&](const omp::clause::Copyin &clause, const parser::CharBlock &) {
696 for (const omp::Object &object : clause.v) {
697 semantics::Symbol *sym = object.sym();
698 assert(sym && "Expecting symbol");
699 if (const auto *commonDetails =
700 sym->detailsIf<semantics::CommonBlockDetails>()) {
701 for (const auto &mem : commonDetails->objects())
702 checkAndCopyHostAssociateVar(&*mem, &insPt);
703 break;
704 }
705
706 assert(sym->has<semantics::HostAssocDetails>() &&
707 "No host-association found");
708 checkAndCopyHostAssociateVar(sym);
709 }
710 });
711
712 // [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to
713 // the execution of the associated structured block. Emit implicit barrier to
714 // synchronize threads and avoid data races on propagation master's thread
715 // values of threadprivate variables to local instances of that variables of
716 // all other implicit threads.
717
718 // All copies are inserted at either "insPt" (i.e. immediately before it),
719 // or at some earlier point (as determined by "copyHostAssociateVar").
720 // Unless the insertion point is given to "copyHostAssociateVar" explicitly,
721 // it will not restore the builder's insertion point. Since the copies may be
722 // inserted in any order (not following the execution order), make sure the
723 // barrier is inserted following all of them.
724 firOpBuilder.restoreInsertionPoint(insPt);
725 if (hasCopyin)
726 firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation());
727 return hasCopyin;
728}
729
730/// Class that extracts information from the specified type.
731class TypeInfo {
732public:
733 TypeInfo(mlir::Type ty) { typeScan(ty); }
734
735 // Returns the length of character types.
736 std::optional<fir::CharacterType::LenType> getCharLength() const {
737 return charLen;
738 }
739
740 // Returns the shape of array types.
741 llvm::ArrayRef<int64_t> getShape() const { return shape; }
742
743 // Is the type inside a box?
744 bool isBox() const { return inBox; }
745
746 bool isBoxChar() const { return inBoxChar; }
747
748private:
749 void typeScan(mlir::Type type);
750
751 std::optional<fir::CharacterType::LenType> charLen;
752 llvm::SmallVector<int64_t> shape;
753 bool inBox = false;
754 bool inBoxChar = false;
755};
756
757void TypeInfo::typeScan(mlir::Type ty) {
758 if (auto sty = mlir::dyn_cast<fir::SequenceType>(ty)) {
759 assert(shape.empty() && !sty.getShape().empty());
760 shape = llvm::SmallVector<int64_t>(sty.getShape());
761 typeScan(sty.getEleTy());
762 } else if (auto bty = mlir::dyn_cast<fir::BoxType>(ty)) {
763 inBox = true;
764 typeScan(bty.getEleTy());
765 } else if (auto cty = mlir::dyn_cast<fir::ClassType>(ty)) {
766 inBox = true;
767 typeScan(cty.getEleTy());
768 } else if (auto cty = mlir::dyn_cast<fir::CharacterType>(ty)) {
769 charLen = cty.getLen();
770 } else if (auto cty = mlir::dyn_cast<fir::BoxCharType>(ty)) {
771 inBoxChar = true;
772 typeScan(cty.getEleTy());
773 } else if (auto hty = mlir::dyn_cast<fir::HeapType>(ty)) {
774 typeScan(hty.getEleTy());
775 } else if (auto pty = mlir::dyn_cast<fir::PointerType>(ty)) {
776 typeScan(pty.getEleTy());
777 } else {
778 // The scan ends when reaching any built-in, record or boxproc type.
779 assert(ty.isIntOrIndexOrFloat() || mlir::isa<mlir::ComplexType>(ty) ||
780 mlir::isa<fir::LogicalType>(ty) || mlir::isa<fir::RecordType>(ty) ||
781 mlir::isa<fir::BoxProcType>(ty));
782 }
783}
784
785// Create a function that performs a copy between two variables, compatible
786// with their types and attributes.
787static mlir::func::FuncOp
788createCopyFunc(mlir::Location loc, lower::AbstractConverter &converter,
789 mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) {
790 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
791 mlir::ModuleOp module = builder.getModule();
792 mlir::Type eleTy = mlir::cast<fir::ReferenceType>(varType).getEleTy();
793 TypeInfo typeInfo(eleTy);
794 std::string copyFuncName =
795 fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy");
796
797 if (auto decl = module.lookupSymbol<mlir::func::FuncOp>(copyFuncName))
798 return decl;
799
800 // create function
801 mlir::OpBuilder::InsertionGuard guard(builder);
802 mlir::OpBuilder modBuilder(module.getBodyRegion());
803 llvm::SmallVector<mlir::Type> argsTy = {varType, varType};
804 auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {});
805 mlir::func::FuncOp funcOp =
806 modBuilder.create<mlir::func::FuncOp>(loc, copyFuncName, funcType);
807 funcOp.setVisibility(mlir::SymbolTable::Visibility::Private);
808 fir::factory::setInternalLinkage(funcOp);
809 builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy,
810 {loc, loc});
811 builder.setInsertionPointToStart(&funcOp.getRegion().back());
812 // generate body
813 fir::FortranVariableFlagsAttr attrs;
814 if (varAttrs != fir::FortranVariableFlagsEnum::None)
815 attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs);
816 mlir::Value shape;
817 if (!typeInfo.isBox() && !typeInfo.getShape().empty()) {
818 llvm::SmallVector<mlir::Value> extents;
819 for (auto extent : typeInfo.getShape())
820 extents.push_back(
821 builder.createIntegerConstant(loc, builder.getIndexType(), extent));
822 shape = builder.create<fir::ShapeOp>(loc, extents);
823 }
824 mlir::Value dst = funcOp.getArgument(0);
825 mlir::Value src = funcOp.getArgument(1);
826 llvm::SmallVector<mlir::Value> typeparams;
827 if (typeInfo.isBoxChar()) {
828 // fir.boxchar will be passed here as fir.ref<fir.boxchar>
829 auto loadDst = builder.create<fir::LoadOp>(loc, dst);
830 auto loadSrc = builder.create<fir::LoadOp>(loc, src);
831 // get the actual fir.ref<fir.char> type
832 mlir::Type refType =
833 fir::ReferenceType::get(mlir::cast<fir::BoxCharType>(eleTy).getEleTy());
834 auto unboxedDst = builder.create<fir::UnboxCharOp>(
835 loc, refType, builder.getIndexType(), loadDst);
836 auto unboxedSrc = builder.create<fir::UnboxCharOp>(
837 loc, refType, builder.getIndexType(), loadSrc);
838 // Add length to type parameters
839 typeparams.push_back(unboxedDst.getResult(1));
840 dst = unboxedDst.getResult(0);
841 src = unboxedSrc.getResult(0);
842 } else if (typeInfo.getCharLength().has_value()) {
843 mlir::Value charLen = builder.createIntegerConstant(
844 loc, builder.getCharacterLengthType(), *typeInfo.getCharLength());
845 typeparams.push_back(charLen);
846 }
847 auto declDst = builder.create<hlfir::DeclareOp>(
848 loc, dst, copyFuncName + "_dst", shape, typeparams,
849 /*dummy_scope=*/nullptr, attrs);
850 auto declSrc = builder.create<hlfir::DeclareOp>(
851 loc, src, copyFuncName + "_src", shape, typeparams,
852 /*dummy_scope=*/nullptr, attrs);
853 converter.copyVar(loc, declDst.getBase(), declSrc.getBase(), varAttrs);
854 builder.create<mlir::func::ReturnOp>(loc);
855 return funcOp;
856}
857
858bool ClauseProcessor::processCopyprivate(
859 mlir::Location currentLocation,
860 mlir::omp::CopyprivateClauseOps &result) const {
861 auto addCopyPrivateVar = [&](semantics::Symbol *sym) {
862 mlir::Value symVal = converter.getSymbolAddress(*sym);
863 auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>();
864 if (!declOp)
865 fir::emitFatalError(currentLocation,
866 "COPYPRIVATE is supported only in HLFIR mode");
867 symVal = declOp.getBase();
868 mlir::Type symType = symVal.getType();
869 fir::FortranVariableFlagsEnum attrs =
870 declOp.getFortranAttrs().has_value()
871 ? *declOp.getFortranAttrs()
872 : fir::FortranVariableFlagsEnum::None;
873 mlir::Value cpVar = symVal;
874
875 // CopyPrivate variables must be passed by reference. However, in the case
876 // of assumed shapes/vla the type is not a !fir.ref, but a !fir.box.
877 // In the case of character types, the passed in type can also be
878 // !fir.boxchar. In these cases to retrieve the appropriate
879 // !fir.ref<!fir.box<...>> or !fir.ref<!fir.boxchar<..>> to access the data
880 // we need we must perform an alloca and then store to it and retrieve the
881 // data from the new alloca.
882 if (mlir::isa<fir::BaseBoxType>(symType) ||
883 mlir::isa<fir::BoxCharType>(symType)) {
884 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
885 auto alloca = builder.create<fir::AllocaOp>(currentLocation, symType);
886 builder.create<fir::StoreOp>(currentLocation, symVal, alloca);
887 cpVar = alloca;
888 }
889
890 result.copyprivateVars.push_back(cpVar);
891 mlir::func::FuncOp funcOp =
892 createCopyFunc(currentLocation, converter, cpVar.getType(), attrs);
893 result.copyprivateSyms.push_back(mlir::SymbolRefAttr::get(funcOp));
894 };
895
896 bool hasCopyPrivate = findRepeatableClause<clause::Copyprivate>(
897 [&](const clause::Copyprivate &clause, const parser::CharBlock &) {
898 for (const Object &object : clause.v) {
899 semantics::Symbol *sym = object.sym();
900 if (const auto *commonDetails =
901 sym->detailsIf<semantics::CommonBlockDetails>()) {
902 for (const auto &mem : commonDetails->objects())
903 addCopyPrivateVar(&*mem);
904 break;
905 }
906 addCopyPrivateVar(sym);
907 }
908 });
909
910 return hasCopyPrivate;
911}
912
913template <typename T>
914static bool isVectorSubscript(const evaluate::Expr<T> &expr) {
915 if (std::optional<evaluate::DataRef> dataRef{evaluate::ExtractDataRef(expr)})
916 if (const auto *arrayRef = std::get_if<evaluate::ArrayRef>(&dataRef->u))
917 for (const evaluate::Subscript &subscript : arrayRef->subscript())
918 if (std::holds_alternative<evaluate::IndirectSubscriptIntegerExpr>(
919 subscript.u))
920 if (subscript.Rank() > 0)
921 return true;
922 return false;
923}
924
925bool ClauseProcessor::processDefaultMap(lower::StatementContext &stmtCtx,
926 DefaultMapsTy &result) const {
927 auto process = [&](const omp::clause::Defaultmap &clause,
928 const parser::CharBlock &) {
929 using Defmap = omp::clause::Defaultmap;
930 clause::Defaultmap::VariableCategory variableCategory =
931 Defmap::VariableCategory::All;
932 // Variable Category is optional, if not specified defaults to all.
933 // Multiples of the same category are illegal as are any other
934 // defaultmaps being specified when a user specified all is in place,
935 // however, this should be handled earlier during semantics.
936 if (auto varCat =
937 std::get<std::optional<Defmap::VariableCategory>>(clause.t))
938 variableCategory = varCat.value();
939 auto behaviour = std::get<Defmap::ImplicitBehavior>(clause.t);
940 result[variableCategory] = behaviour;
941 };
942 return findRepeatableClause<omp::clause::Defaultmap>(process);
943}
944
945bool ClauseProcessor::processDepend(lower::SymMap &symMap,
946 lower::StatementContext &stmtCtx,
947 mlir::omp::DependClauseOps &result) const {
948 auto process = [&](const omp::clause::Depend &clause,
949 const parser::CharBlock &) {
950 using Depend = omp::clause::Depend;
951 if (!std::holds_alternative<Depend::TaskDep>(clause.u)) {
952 TODO(converter.getCurrentLocation(),
953 "DEPEND clause with SINK or SOURCE is not supported yet");
954 }
955 auto &taskDep = std::get<Depend::TaskDep>(clause.u);
956 auto depType = std::get<clause::DependenceType>(taskDep.t);
957 auto &objects = std::get<omp::ObjectList>(taskDep.t);
958 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
959
960 if (std::get<std::optional<omp::clause::Iterator>>(taskDep.t)) {
961 TODO(converter.getCurrentLocation(),
962 "Support for iterator modifiers is not implemented yet");
963 }
964 mlir::omp::ClauseTaskDependAttr dependTypeOperand =
965 genDependKindAttr(converter, depType);
966 result.dependKinds.append(objects.size(), dependTypeOperand);
967
968 for (const omp::Object &object : objects) {
969 assert(object.ref() && "Expecting designator");
970 mlir::Value dependVar;
971 SomeExpr expr = *object.ref();
972
973 if (evaluate::IsArrayElement(expr) || evaluate::ExtractSubstring(expr)) {
974 // Array Section or character (sub)string
975 if (isVectorSubscript(expr)) {
976 // OpenMP needs the address of the first indexed element (required by
977 // the standard to be the lowest index) to identify the dependency. We
978 // don't need an accurate length for the array section because the
979 // OpenMP standard forbids overlapping array sections.
980 dependVar = genVectorSubscriptedDesignatorFirstElementAddress(
981 converter.getCurrentLocation(), converter, expr, symMap, stmtCtx);
982 } else {
983 // Ordinary array section e.g. A(1:512:2)
984 hlfir::EntityWithAttributes entity = convertExprToHLFIR(
985 converter.getCurrentLocation(), converter, expr, symMap, stmtCtx);
986 dependVar = entity.getBase();
987 }
988 } else if (evaluate::isStructureComponent(expr) ||
989 evaluate::ExtractComplexPart(expr)) {
990 SomeExpr expr = *object.ref();
991 hlfir::EntityWithAttributes entity = convertExprToHLFIR(
992 converter.getCurrentLocation(), converter, expr, symMap, stmtCtx);
993 dependVar = entity.getBase();
994 } else {
995 semantics::Symbol *sym = object.sym();
996 dependVar = converter.getSymbolAddress(*sym);
997 }
998
999 // If we pass a mutable box e.g. !fir.ref<!fir.box<!fir.heap<...>>> then
1000 // the runtime will use the address of the box not the address of the
1001 // data. Flang generates a lot of memcpys between different box
1002 // allocations so this is not a reliable way to identify the dependency.
1003 if (auto ref = mlir::dyn_cast<fir::ReferenceType>(dependVar.getType()))
1004 if (fir::isa_box_type(ref.getElementType()))
1005 dependVar = builder.create<fir::LoadOp>(
1006 converter.getCurrentLocation(), dependVar);
1007
1008 // The openmp dialect doesn't know what to do with boxes (and it would
1009 // break layering to teach it about them). The dependency variable can be
1010 // a box because it was an array section or because the original symbol
1011 // was mapped to a box.
1012 // Getting the address of the box data is okay because all the runtime
1013 // ultimately cares about is the base address of the array.
1014 if (fir::isa_box_type(dependVar.getType()))
1015 dependVar = builder.create<fir::BoxAddrOp>(
1016 converter.getCurrentLocation(), dependVar);
1017
1018 result.dependVars.push_back(dependVar);
1019 }
1020 };
1021
1022 return findRepeatableClause<omp::clause::Depend>(process);
1023}
1024
1025bool ClauseProcessor::processGrainsize(
1026 lower::StatementContext &stmtCtx,
1027 mlir::omp::GrainsizeClauseOps &result) const {
1028 using Grainsize = omp::clause::Grainsize;
1029 if (auto *clause = findUniqueClause<Grainsize>()) {
1030 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
1031 mlir::MLIRContext *context = firOpBuilder.getContext();
1032 const auto &modifier =
1033 std::get<std::optional<Grainsize::Prescriptiveness>>(clause->t);
1034 if (modifier && *modifier == Grainsize::Prescriptiveness::Strict) {
1035 result.grainsizeMod = mlir::omp::ClauseGrainsizeTypeAttr::get(
1036 context, mlir::omp::ClauseGrainsizeType::Strict);
1037 }
1038 const auto &grainsizeExpr = std::get<omp::SomeExpr>(clause->t);
1039 result.grainsize =
1040 fir::getBase(converter.genExprValue(grainsizeExpr, stmtCtx));
1041 return true;
1042 }
1043 return false;
1044}
1045
1046bool ClauseProcessor::processHasDeviceAddr(
1047 lower::StatementContext &stmtCtx, mlir::omp::HasDeviceAddrClauseOps &result,
1048 llvm::SmallVectorImpl<const semantics::Symbol *> &hasDeviceSyms) const {
1049 // For HAS_DEVICE_ADDR objects, implicitly map the top-level entities.
1050 // Their address (or the whole descriptor, if the entity had one) will be
1051 // passed to the target region.
1052 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices;
1053 bool clauseFound = findRepeatableClause<omp::clause::HasDeviceAddr>(
1054 [&](const omp::clause::HasDeviceAddr &clause,
1055 const parser::CharBlock &source) {
1056 mlir::Location location = converter.genLocation(source);
1057 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
1058 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
1059 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_IMPLICIT;
1060 omp::ObjectList baseObjects;
1061 llvm::transform(clause.v, std::back_inserter(baseObjects),
1062 [&](const omp::Object &object) {
1063 if (auto maybeBase = getBaseObject(object, semaCtx))
1064 return *maybeBase;
1065 return object;
1066 });
1067 processMapObjects(stmtCtx, location, baseObjects, mapTypeBits,
1068 parentMemberIndices, result.hasDeviceAddrVars,
1069 hasDeviceSyms);
1070 });
1071
1072 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices,
1073 result.hasDeviceAddrVars, hasDeviceSyms);
1074 return clauseFound;
1075}
1076
1077bool ClauseProcessor::processIf(
1078 omp::clause::If::DirectiveNameModifier directiveName,
1079 mlir::omp::IfClauseOps &result) const {
1080 bool found = false;
1081 findRepeatableClause<omp::clause::If>([&](const omp::clause::If &clause,
1082 const parser::CharBlock &source) {
1083 mlir::Location clauseLocation = converter.genLocation(source);
1084 mlir::Value operand =
1085 getIfClauseOperand(converter, clause, directiveName, clauseLocation);
1086 // Assume that, at most, a single 'if' clause will be applicable to the
1087 // given directive.
1088 if (operand) {
1089 result.ifExpr = operand;
1090 found = true;
1091 }
1092 });
1093 return found;
1094}
1095
1096template <typename T>
1097void collectReductionSyms(
1098 const T &reduction,
1099 llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {
1100 const auto &objectList{std::get<omp::ObjectList>(reduction.t)};
1101 for (const Object &object : objectList) {
1102 const semantics::Symbol *symbol = object.sym();
1103 reductionSyms.push_back(symbol);
1104 }
1105}
1106
1107bool ClauseProcessor::processInReduction(
1108 mlir::Location currentLocation, mlir::omp::InReductionClauseOps &result,
1109 llvm::SmallVectorImpl<const semantics::Symbol *> &outReductionSyms) const {
1110 return findRepeatableClause<omp::clause::InReduction>(
1111 [&](const omp::clause::InReduction &clause, const parser::CharBlock &) {
1112 llvm::SmallVector<mlir::Value> inReductionVars;
1113 llvm::SmallVector<bool> inReduceVarByRef;
1114 llvm::SmallVector<mlir::Attribute> inReductionDeclSymbols;
1115 llvm::SmallVector<const semantics::Symbol *> inReductionSyms;
1116 collectReductionSyms(clause, inReductionSyms);
1117
1118 ReductionProcessor rp;
1119 if (!rp.processReductionArguments<mlir::omp::DeclareReductionOp>(
1120 currentLocation, converter,
1121 std::get<typename omp::clause::ReductionOperatorList>(clause.t),
1122 inReductionVars, inReduceVarByRef, inReductionDeclSymbols,
1123 inReductionSyms))
1124 inReductionSyms.clear();
1125
1126 // Copy local lists into the output.
1127 llvm::copy(inReductionVars, std::back_inserter(result.inReductionVars));
1128 llvm::copy(inReduceVarByRef,
1129 std::back_inserter(result.inReductionByref));
1130 llvm::copy(inReductionDeclSymbols,
1131 std::back_inserter(result.inReductionSyms));
1132 llvm::copy(inReductionSyms, std::back_inserter(outReductionSyms));
1133 });
1134}
1135
1136bool ClauseProcessor::processIsDevicePtr(
1137 mlir::omp::IsDevicePtrClauseOps &result,
1138 llvm::SmallVectorImpl<const semantics::Symbol *> &isDeviceSyms) const {
1139 return findRepeatableClause<omp::clause::IsDevicePtr>(
1140 [&](const omp::clause::IsDevicePtr &devPtrClause,
1141 const parser::CharBlock &) {
1142 addUseDeviceClause(converter, devPtrClause.v, result.isDevicePtrVars,
1143 isDeviceSyms);
1144 });
1145}
1146
1147bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result) const {
1148 lower::StatementContext stmtCtx;
1149 return findRepeatableClause<
1150 omp::clause::Linear>([&](const omp::clause::Linear &clause,
1151 const parser::CharBlock &) {
1152 auto &objects = std::get<omp::ObjectList>(clause.t);
1153 for (const omp::Object &object : objects) {
1154 semantics::Symbol *sym = object.sym();
1155 const mlir::Value variable = converter.getSymbolAddress(*sym);
1156 result.linearVars.push_back(variable);
1157 }
1158 if (objects.size()) {
1159 if (auto &mod =
1160 std::get<std::optional<omp::clause::Linear::StepComplexModifier>>(
1161 clause.t)) {
1162 mlir::Value operand =
1163 fir::getBase(converter.genExprValue(toEvExpr(*mod), stmtCtx));
1164 result.linearStepVars.append(objects.size(), operand);
1165 } else if (std::get<std::optional<omp::clause::Linear::LinearModifier>>(
1166 clause.t)) {
1167 mlir::Location currentLocation = converter.getCurrentLocation();
1168 TODO(currentLocation, "Linear modifiers not yet implemented");
1169 } else {
1170 // If nothing is present, add the default step of 1.
1171 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
1172 mlir::Location currentLocation = converter.getCurrentLocation();
1173 mlir::Value operand = firOpBuilder.createIntegerConstant(
1174 currentLocation, firOpBuilder.getI32Type(), 1);
1175 result.linearStepVars.append(objects.size(), operand);
1176 }
1177 }
1178 });
1179}
1180
1181bool ClauseProcessor::processLink(
1182 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
1183 return findRepeatableClause<omp::clause::Link>(
1184 [&](const omp::clause::Link &clause, const parser::CharBlock &) {
1185 // Case: declare target link(var1, var2)...
1186 gatherFuncAndVarSyms(
1187 clause.v, mlir::omp::DeclareTargetCaptureClause::link, result);
1188 });
1189}
1190
1191void ClauseProcessor::processMapObjects(
1192 lower::StatementContext &stmtCtx, mlir::Location clauseLocation,
1193 const omp::ObjectList &objects,
1194 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits,
1195 std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices,
1196 llvm::SmallVectorImpl<mlir::Value> &mapVars,
1197 llvm::SmallVectorImpl<const semantics::Symbol *> &mapSyms,
1198 llvm::StringRef mapperIdNameRef) const {
1199 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
1200
1201 auto getDefaultMapperID = [&](const omp::Object &object,
1202 std::string &mapperIdName) {
1203 if (!mlir::isa<mlir::omp::DeclareMapperOp>(
1204 firOpBuilder.getRegion().getParentOp())) {
1205 const semantics::DerivedTypeSpec *typeSpec = nullptr;
1206
1207 if (object.sym()->owner().IsDerivedType())
1208 typeSpec = object.sym()->owner().derivedTypeSpec();
1209 else if (object.sym()->GetType() &&
1210 object.sym()->GetType()->category() ==
1211 semantics::DeclTypeSpec::TypeDerived)
1212 typeSpec = &object.sym()->GetType()->derivedTypeSpec();
1213
1214 if (typeSpec) {
1215 mapperIdName =
1216 typeSpec->name().ToString() + llvm::omp::OmpDefaultMapperName;
1217 if (auto *sym = converter.getCurrentScope().FindSymbol(mapperIdName))
1218 mapperIdName = converter.mangleName(mapperIdName, sym->owner());
1219 }
1220 }
1221 };
1222
1223 // Create the mapper symbol from its name, if specified.
1224 mlir::FlatSymbolRefAttr mapperId;
1225 if (!mapperIdNameRef.empty() && !objects.empty() &&
1226 mapperIdNameRef != "__implicit_mapper") {
1227 std::string mapperIdName = mapperIdNameRef.str();
1228 const omp::Object &object = objects.front();
1229 if (mapperIdNameRef == "default")
1230 getDefaultMapperID(object, mapperIdName);
1231 assert(converter.getModuleOp().lookupSymbol(mapperIdName) &&
1232 "mapper not found");
1233 mapperId =
1234 mlir::FlatSymbolRefAttr::get(&converter.getMLIRContext(), mapperIdName);
1235 }
1236
1237 for (const omp::Object &object : objects) {
1238 llvm::SmallVector<mlir::Value> bounds;
1239 std::stringstream asFortran;
1240 std::optional<omp::Object> parentObj;
1241
1242 fir::factory::AddrAndBoundsInfo info =
1243 lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp,
1244 mlir::omp::MapBoundsType>(
1245 converter, firOpBuilder, semaCtx, stmtCtx, *object.sym(),
1246 object.ref(), clauseLocation, asFortran, bounds,
1247 treatIndexAsSection);
1248
1249 mlir::Value baseOp = info.rawInput;
1250 if (object.sym()->owner().IsDerivedType()) {
1251 omp::ObjectList objectList = gatherObjectsOf(object, semaCtx);
1252 assert(!objectList.empty() &&
1253 "could not find parent objects of derived type member");
1254 parentObj = objectList[0];
1255 parentMemberIndices.emplace(parentObj.value(),
1256 OmpMapParentAndMemberData{});
1257
1258 if (isMemberOrParentAllocatableOrPointer(object, semaCtx)) {
1259 llvm::SmallVector<int64_t> indices;
1260 generateMemberPlacementIndices(object, indices, semaCtx);
1261 baseOp = createParentSymAndGenIntermediateMaps(
1262 clauseLocation, converter, semaCtx, stmtCtx, objectList, indices,
1263 parentMemberIndices[parentObj.value()], asFortran.str(),
1264 mapTypeBits);
1265 }
1266 }
1267
1268 if (mapperIdNameRef == "__implicit_mapper") {
1269 std::string mapperIdName;
1270 getDefaultMapperID(object, mapperIdName);
1271 mapperId = converter.getModuleOp().lookupSymbol(mapperIdName)
1272 ? mlir::FlatSymbolRefAttr::get(&converter.getMLIRContext(),
1273 mapperIdName)
1274 : mlir::FlatSymbolRefAttr();
1275 }
1276
1277 // Explicit map captures are captured ByRef by default,
1278 // optimisation passes may alter this to ByCopy or other capture
1279 // types to optimise
1280 auto location = mlir::NameLoc::get(
1281 mlir::StringAttr::get(firOpBuilder.getContext(), asFortran.str()),
1282 baseOp.getLoc());
1283 mlir::omp::MapInfoOp mapOp = createMapInfoOp(
1284 firOpBuilder, location, baseOp,
1285 /*varPtrPtr=*/mlir::Value{}, asFortran.str(), bounds,
1286 /*members=*/{}, /*membersIndex=*/mlir::ArrayAttr{},
1287 static_cast<
1288 std::underlying_type_t<llvm::omp::OpenMPOffloadMappingFlags>>(
1289 mapTypeBits),
1290 mlir::omp::VariableCaptureKind::ByRef, baseOp.getType(),
1291 /*partialMap=*/false, mapperId);
1292
1293 if (parentObj.has_value()) {
1294 parentMemberIndices[parentObj.value()].addChildIndexAndMapToParent(
1295 object, mapOp, semaCtx);
1296 } else {
1297 mapVars.push_back(mapOp);
1298 mapSyms.push_back(object.sym());
1299 }
1300 }
1301}
1302
1303bool ClauseProcessor::processMap(
1304 mlir::Location currentLocation, lower::StatementContext &stmtCtx,
1305 mlir::omp::MapClauseOps &result, llvm::omp::Directive directive,
1306 llvm::SmallVectorImpl<const semantics::Symbol *> *mapSyms) const {
1307 // We always require tracking of symbols, even if the caller does not,
1308 // so we create an optionally used local set of symbols when the mapSyms
1309 // argument is not present.
1310 llvm::SmallVector<const semantics::Symbol *> localMapSyms;
1311 llvm::SmallVectorImpl<const semantics::Symbol *> *ptrMapSyms =
1312 mapSyms ? mapSyms : &localMapSyms;
1313 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices;
1314
1315 auto process = [&](const omp::clause::Map &clause,
1316 const parser::CharBlock &source) {
1317 using Map = omp::clause::Map;
1318 mlir::Location clauseLocation = converter.genLocation(source);
1319 const auto &[mapType, typeMods, mappers, iterator, objects] = clause.t;
1320 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
1321 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
1322 std::string mapperIdName = "__implicit_mapper";
1323 // If the map type is specified, then process it else set the appropriate
1324 // default value
1325 Map::MapType type;
1326 if (directive == llvm::omp::Directive::OMPD_target_enter_data &&
1327 semaCtx.langOptions().OpenMPVersion >= 52)
1328 type = mapType.value_or(Map::MapType::To);
1329 else if (directive == llvm::omp::Directive::OMPD_target_exit_data &&
1330 semaCtx.langOptions().OpenMPVersion >= 52)
1331 type = mapType.value_or(Map::MapType::From);
1332 else
1333 type = mapType.value_or(Map::MapType::Tofrom);
1334
1335 switch (type) {
1336 case Map::MapType::To:
1337 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;
1338 break;
1339 case Map::MapType::From:
1340 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
1341 break;
1342 case Map::MapType::Tofrom:
1343 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
1344 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
1345 break;
1346 case Map::MapType::Alloc:
1347 case Map::MapType::Release:
1348 // alloc and release is the default map_type for the Target Data
1349 // Ops, i.e. if no bits for map_type is supplied then alloc/release
1350 // is implicitly assumed based on the target directive. Default
1351 // value for Target Data and Enter Data is alloc and for Exit Data
1352 // it is release.
1353 break;
1354 case Map::MapType::Delete:
1355 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;
1356 }
1357
1358 if (typeMods) {
1359 // TODO: Still requires "self" modifier, an OpenMP 6.0+ feature
1360 if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Always))
1361 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;
1362 if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Present))
1363 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_PRESENT;
1364 if (llvm::is_contained(*typeMods, Map::MapTypeModifier::Close))
1365 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_CLOSE;
1366 if (llvm::is_contained(*typeMods, Map::MapTypeModifier::OmpxHold))
1367 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_OMPX_HOLD;
1368 }
1369
1370 if (iterator) {
1371 TODO(currentLocation,
1372 "Support for iterator modifiers is not implemented yet");
1373 }
1374 if (mappers) {
1375 assert(mappers->size() == 1 && "more than one mapper");
1376 mapperIdName = mappers->front().v.id().symbol->name().ToString();
1377 if (mapperIdName != "default")
1378 mapperIdName = converter.mangleName(
1379 mapperIdName, mappers->front().v.id().symbol->owner());
1380 }
1381
1382 processMapObjects(stmtCtx, clauseLocation,
1383 std::get<omp::ObjectList>(clause.t), mapTypeBits,
1384 parentMemberIndices, result.mapVars, *ptrMapSyms,
1385 mapperIdName);
1386 };
1387
1388 bool clauseFound = findRepeatableClause<omp::clause::Map>(process);
1389 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices,
1390 result.mapVars, *ptrMapSyms);
1391
1392 return clauseFound;
1393}
1394
1395bool ClauseProcessor::processMotionClauses(lower::StatementContext &stmtCtx,
1396 mlir::omp::MapClauseOps &result) {
1397 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices;
1398 llvm::SmallVector<const semantics::Symbol *> mapSymbols;
1399
1400 auto callbackFn = [&](const auto &clause, const parser::CharBlock &source) {
1401 mlir::Location clauseLocation = converter.genLocation(source);
1402 const auto &[expectation, mapper, iterator, objects] = clause.t;
1403
1404 // TODO Support motion modifiers: mapper, iterator.
1405 if (mapper) {
1406 TODO(clauseLocation, "Mapper modifier is not supported yet");
1407 } else if (iterator) {
1408 TODO(clauseLocation, "Iterator modifier is not supported yet");
1409 }
1410
1411 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
1412 std::is_same_v<llvm::remove_cvref_t<decltype(clause)>, omp::clause::To>
1413 ? llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO
1414 : llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
1415 if (expectation && *expectation == omp::clause::To::Expectation::Present)
1416 mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_PRESENT;
1417 processMapObjects(stmtCtx, clauseLocation, objects, mapTypeBits,
1418 parentMemberIndices, result.mapVars, mapSymbols);
1419 };
1420
1421 bool clauseFound = findRepeatableClause<omp::clause::To>(callbackFn);
1422 clauseFound =
1423 findRepeatableClause<omp::clause::From>(callbackFn) || clauseFound;
1424
1425 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices,
1426 result.mapVars, mapSymbols);
1427
1428 return clauseFound;
1429}
1430
1431bool ClauseProcessor::processNontemporal(
1432 mlir::omp::NontemporalClauseOps &result) const {
1433 return findRepeatableClause<omp::clause::Nontemporal>(
1434 [&](const omp::clause::Nontemporal &clause, const parser::CharBlock &) {
1435 for (const Object &object : clause.v) {
1436 semantics::Symbol *sym = object.sym();
1437 mlir::Value symVal = converter.getSymbolAddress(*sym);
1438 result.nontemporalVars.push_back(symVal);
1439 }
1440 });
1441}
1442
1443bool ClauseProcessor::processReduction(
1444 mlir::Location currentLocation, mlir::omp::ReductionClauseOps &result,
1445 llvm::SmallVectorImpl<const semantics::Symbol *> &outReductionSyms) const {
1446 return findRepeatableClause<omp::clause::Reduction>(
1447 [&](const omp::clause::Reduction &clause, const parser::CharBlock &) {
1448 llvm::SmallVector<mlir::Value> reductionVars;
1449 llvm::SmallVector<bool> reduceVarByRef;
1450 llvm::SmallVector<mlir::Attribute> reductionDeclSymbols;
1451 llvm::SmallVector<const semantics::Symbol *> reductionSyms;
1452 collectReductionSyms(clause, reductionSyms);
1453
1454 auto mod = std::get<std::optional<ReductionModifier>>(clause.t);
1455 if (mod.has_value()) {
1456 if (mod.value() == ReductionModifier::Task)
1457 TODO(currentLocation, "Reduction modifier `task` is not supported");
1458 else
1459 result.reductionMod = mlir::omp::ReductionModifierAttr::get(
1460 converter.getFirOpBuilder().getContext(),
1461 translateReductionModifier(mod.value()));
1462 }
1463
1464 ReductionProcessor rp;
1465 if (!rp.processReductionArguments<mlir::omp::DeclareReductionOp>(
1466 currentLocation, converter,
1467 std::get<typename omp::clause::ReductionOperatorList>(clause.t),
1468 reductionVars, reduceVarByRef, reductionDeclSymbols,
1469 reductionSyms))
1470 reductionSyms.clear();
1471 // Copy local lists into the output.
1472 llvm::copy(reductionVars, std::back_inserter(result.reductionVars));
1473 llvm::copy(reduceVarByRef, std::back_inserter(result.reductionByref));
1474 llvm::copy(reductionDeclSymbols,
1475 std::back_inserter(result.reductionSyms));
1476 llvm::copy(reductionSyms, std::back_inserter(outReductionSyms));
1477 });
1478}
1479
1480bool ClauseProcessor::processTaskReduction(
1481 mlir::Location currentLocation, mlir::omp::TaskReductionClauseOps &result,
1482 llvm::SmallVectorImpl<const semantics::Symbol *> &outReductionSyms) const {
1483 return findRepeatableClause<omp::clause::TaskReduction>(
1484 [&](const omp::clause::TaskReduction &clause, const parser::CharBlock &) {
1485 llvm::SmallVector<mlir::Value> taskReductionVars;
1486 llvm::SmallVector<bool> taskReduceVarByRef;
1487 llvm::SmallVector<mlir::Attribute> taskReductionDeclSymbols;
1488 llvm::SmallVector<const semantics::Symbol *> taskReductionSyms;
1489 collectReductionSyms(clause, taskReductionSyms);
1490
1491 ReductionProcessor rp;
1492 if (!rp.processReductionArguments<mlir::omp::DeclareReductionOp>(
1493 currentLocation, converter,
1494 std::get<typename omp::clause::ReductionOperatorList>(clause.t),
1495 taskReductionVars, taskReduceVarByRef, taskReductionDeclSymbols,
1496 taskReductionSyms))
1497 taskReductionSyms.clear();
1498 // Copy local lists into the output.
1499 llvm::copy(taskReductionVars,
1500 std::back_inserter(result.taskReductionVars));
1501 llvm::copy(taskReduceVarByRef,
1502 std::back_inserter(result.taskReductionByref));
1503 llvm::copy(taskReductionDeclSymbols,
1504 std::back_inserter(result.taskReductionSyms));
1505 llvm::copy(taskReductionSyms, std::back_inserter(outReductionSyms));
1506 });
1507}
1508
1509bool ClauseProcessor::processTo(
1510 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
1511 return findRepeatableClause<omp::clause::To>(
1512 [&](const omp::clause::To &clause, const parser::CharBlock &) {
1513 // Case: declare target to(func, var1, var2)...
1514 gatherFuncAndVarSyms(std::get<ObjectList>(clause.t),
1515 mlir::omp::DeclareTargetCaptureClause::to, result);
1516 });
1517}
1518
1519bool ClauseProcessor::processEnter(
1520 llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
1521 return findRepeatableClause<omp::clause::Enter>(
1522 [&](const omp::clause::Enter &clause, const parser::CharBlock &) {
1523 // Case: declare target enter(func, var1, var2)...
1524 gatherFuncAndVarSyms(
1525 clause.v, mlir::omp::DeclareTargetCaptureClause::enter, result);
1526 });
1527}
1528
1529bool ClauseProcessor::processUseDeviceAddr(
1530 lower::StatementContext &stmtCtx, mlir::omp::UseDeviceAddrClauseOps &result,
1531 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) const {
1532 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices;
1533 bool clauseFound = findRepeatableClause<omp::clause::UseDeviceAddr>(
1534 [&](const omp::clause::UseDeviceAddr &clause,
1535 const parser::CharBlock &source) {
1536 mlir::Location location = converter.genLocation(source);
1537 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
1538 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_RETURN_PARAM;
1539 processMapObjects(stmtCtx, location, clause.v, mapTypeBits,
1540 parentMemberIndices, result.useDeviceAddrVars,
1541 useDeviceSyms);
1542 });
1543
1544 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices,
1545 result.useDeviceAddrVars, useDeviceSyms);
1546 return clauseFound;
1547}
1548
1549bool ClauseProcessor::processUseDevicePtr(
1550 lower::StatementContext &stmtCtx, mlir::omp::UseDevicePtrClauseOps &result,
1551 llvm::SmallVectorImpl<const semantics::Symbol *> &useDeviceSyms) const {
1552 std::map<Object, OmpMapParentAndMemberData> parentMemberIndices;
1553
1554 bool clauseFound = findRepeatableClause<omp::clause::UseDevicePtr>(
1555 [&](const omp::clause::UseDevicePtr &clause,
1556 const parser::CharBlock &source) {
1557 mlir::Location location = converter.genLocation(source);
1558 llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
1559 llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_RETURN_PARAM;
1560 processMapObjects(stmtCtx, location, clause.v, mapTypeBits,
1561 parentMemberIndices, result.useDevicePtrVars,
1562 useDeviceSyms);
1563 });
1564
1565 insertChildMapInfoIntoParent(converter, semaCtx, stmtCtx, parentMemberIndices,
1566 result.useDevicePtrVars, useDeviceSyms);
1567 return clauseFound;
1568}
1569
1570} // namespace omp
1571} // namespace lower
1572} // namespace Fortran
1573

source code of flang/lib/Lower/OpenMP/ClauseProcessor.cpp