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