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