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