1 //===-- OpenMP.cpp -- Open MP directive lowering --------------------------===//
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 "flang/Lower/OpenMP.h"
14 #include "flang/Common/idioms.h"
15 #include "flang/Lower/Bridge.h"
16 #include "flang/Lower/PFTBuilder.h"
17 #include "flang/Lower/StatementContext.h"
18 #include "flang/Lower/Todo.h"
19 #include "flang/Optimizer/Builder/BoxValue.h"
20 #include "flang/Optimizer/Builder/FIRBuilder.h"
21 #include "flang/Parser/parse-tree.h"
22 #include "flang/Semantics/tools.h"
23 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
24 #include "llvm/Frontend/OpenMP/OMPConstants.h"
25 
26 using namespace mlir;
27 
28 int64_t Fortran::lower::getCollapseValue(
29     const Fortran::parser::OmpClauseList &clauseList) {
30   for (const auto &clause : clauseList.v) {
31     if (const auto &collapseClause =
32             std::get_if<Fortran::parser::OmpClause::Collapse>(&clause.u)) {
33       const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
34       return Fortran::evaluate::ToInt64(*expr).value();
35     }
36   }
37   return 1;
38 }
39 
40 static const Fortran::parser::Name *
41 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) {
42   const auto *dataRef = std::get_if<Fortran::parser::DataRef>(&designator.u);
43   return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr;
44 }
45 
46 template <typename T>
47 static void createPrivateVarSyms(Fortran::lower::AbstractConverter &converter,
48                                  const T *clause) {
49   Fortran::semantics::Symbol *sym = nullptr;
50   const Fortran::parser::OmpObjectList &ompObjectList = clause->v;
51   for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
52     std::visit(
53         Fortran::common::visitors{
54             [&](const Fortran::parser::Designator &designator) {
55               if (const Fortran::parser::Name *name =
56                       getDesignatorNameIfDataRef(designator)) {
57                 sym = name->symbol;
58               }
59             },
60             [&](const Fortran::parser::Name &name) { sym = name.symbol; }},
61         ompObject.u);
62 
63     // Privatization for symbols which are pre-determined (like loop index
64     // variables) happen separately, for everything else privatize here
65     if constexpr (std::is_same_v<T, Fortran::parser::OmpClause::Firstprivate>) {
66       converter.copyHostAssociateVar(*sym);
67     } else {
68       bool success = converter.createHostAssociateVarClone(*sym);
69       (void)success;
70       assert(success && "Privatization failed due to existing binding");
71     }
72   }
73 }
74 
75 static void privatizeVars(Fortran::lower::AbstractConverter &converter,
76                           const Fortran::parser::OmpClauseList &opClauseList) {
77   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
78   auto insPt = firOpBuilder.saveInsertionPoint();
79   firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
80   for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
81     if (const auto &privateClause =
82             std::get_if<Fortran::parser::OmpClause::Private>(&clause.u)) {
83       createPrivateVarSyms(converter, privateClause);
84     } else if (const auto &firstPrivateClause =
85                    std::get_if<Fortran::parser::OmpClause::Firstprivate>(
86                        &clause.u)) {
87       createPrivateVarSyms(converter, firstPrivateClause);
88     }
89   }
90   firOpBuilder.restoreInsertionPoint(insPt);
91 }
92 
93 static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
94                           Fortran::lower::AbstractConverter &converter,
95                           llvm::SmallVectorImpl<Value> &operands) {
96   auto addOperands = [&](Fortran::lower::SymbolRef sym) {
97     const mlir::Value variable = converter.getSymbolAddress(sym);
98     if (variable) {
99       operands.push_back(variable);
100     } else {
101       if (const auto *details =
102               sym->detailsIf<Fortran::semantics::HostAssocDetails>()) {
103         operands.push_back(converter.getSymbolAddress(details->symbol()));
104         converter.copySymbolBinding(details->symbol(), sym);
105       }
106     }
107   };
108   for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
109     std::visit(Fortran::common::visitors{
110                    [&](const Fortran::parser::Designator &designator) {
111                      if (const Fortran::parser::Name *name =
112                              getDesignatorNameIfDataRef(designator)) {
113                        addOperands(*name->symbol);
114                      }
115                    },
116                    [&](const Fortran::parser::Name &name) {
117                      addOperands(*name.symbol);
118                    }},
119                ompObject.u);
120   }
121 }
122 
123 static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter,
124                                  std::size_t loopVarTypeSize) {
125   // OpenMP runtime requires 32-bit or 64-bit loop variables.
126   loopVarTypeSize = loopVarTypeSize * 8;
127   if (loopVarTypeSize < 32) {
128     loopVarTypeSize = 32;
129   } else if (loopVarTypeSize > 64) {
130     loopVarTypeSize = 64;
131     mlir::emitWarning(converter.getCurrentLocation(),
132                       "OpenMP loop iteration variable cannot have more than 64 "
133                       "bits size and will be narrowed into 64 bits.");
134   }
135   assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) &&
136          "OpenMP loop iteration variable size must be transformed into 32-bit "
137          "or 64-bit");
138   return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize);
139 }
140 
141 /// Create empty blocks for the current region.
142 /// These blocks replace blocks parented to an enclosing region.
143 void createEmptyRegionBlocks(
144     fir::FirOpBuilder &firOpBuilder,
145     std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
146   auto *region = &firOpBuilder.getRegion();
147   for (auto &eval : evaluationList) {
148     if (eval.block) {
149       if (eval.block->empty()) {
150         eval.block->erase();
151         eval.block = firOpBuilder.createBlock(region);
152       } else {
153         [[maybe_unused]] auto &terminatorOp = eval.block->back();
154         assert((mlir::isa<mlir::omp::TerminatorOp>(terminatorOp) ||
155                 mlir::isa<mlir::omp::YieldOp>(terminatorOp)) &&
156                "expected terminator op");
157       }
158     }
159     if (!eval.isDirective() && eval.hasNestedEvaluations())
160       createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations());
161   }
162 }
163 
164 /// Create the body (block) for an OpenMP Operation.
165 ///
166 /// \param [in]    op - the operation the body belongs to.
167 /// \param [inout] converter - converter to use for the clauses.
168 /// \param [in]    loc - location in source code.
169 /// \param [in]    eval - current PFT node/evaluation.
170 /// \oaran [in]    clauses - list of clauses to process.
171 /// \param [in]    args - block arguments (induction variable[s]) for the
172 ////                      region.
173 /// \param [in]    outerCombined - is this an outer operation - prevents
174 ///                                privatization.
175 template <typename Op>
176 static void
177 createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter,
178                mlir::Location &loc, Fortran::lower::pft::Evaluation &eval,
179                const Fortran::parser::OmpClauseList *clauses = nullptr,
180                const SmallVector<const Fortran::semantics::Symbol *> &args = {},
181                bool outerCombined = false) {
182   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
183   // If an argument for the region is provided then create the block with that
184   // argument. Also update the symbol's address with the mlir argument value.
185   // e.g. For loops the argument is the induction variable. And all further
186   // uses of the induction variable should use this mlir value.
187   if (args.size()) {
188     std::size_t loopVarTypeSize = 0;
189     for (const Fortran::semantics::Symbol *arg : args)
190       loopVarTypeSize = std::max(loopVarTypeSize, arg->GetUltimate().size());
191     mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
192     SmallVector<Type> tiv;
193     SmallVector<Location> locs;
194     for (int i = 0; i < (int)args.size(); i++) {
195       tiv.push_back(loopVarType);
196       locs.push_back(loc);
197     }
198     firOpBuilder.createBlock(&op.getRegion(), {}, tiv, locs);
199     int argIndex = 0;
200     for (const Fortran::semantics::Symbol *arg : args) {
201       fir::ExtendedValue exval = op.getRegion().front().getArgument(argIndex);
202       converter.bindSymbol(*arg, exval);
203       argIndex++;
204     }
205   } else {
206     firOpBuilder.createBlock(&op.getRegion());
207   }
208   mlir::Block &block = op.getRegion().back();
209   firOpBuilder.setInsertionPointToEnd(&block);
210 
211   // If it is an unstructured region and is not the outer region of a combined
212   // construct, create empty blocks for all evaluations.
213   if (eval.lowerAsUnstructured() && !outerCombined)
214     createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations());
215 
216   // Insert the terminator.
217   if constexpr (std::is_same_v<Op, omp::WsLoopOp>) {
218     mlir::ValueRange results;
219     firOpBuilder.create<mlir::omp::YieldOp>(loc, results);
220   } else {
221     firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
222   }
223 
224   // Reset the insertion point to the start of the first block.
225   firOpBuilder.setInsertionPointToStart(&block);
226 
227   // Handle privatization. Do not privatize if this is the outer operation.
228   if (clauses && !outerCombined)
229     privatizeVars(converter, *clauses);
230 }
231 
232 static void genOMP(Fortran::lower::AbstractConverter &converter,
233                    Fortran::lower::pft::Evaluation &eval,
234                    const Fortran::parser::OpenMPSimpleStandaloneConstruct
235                        &simpleStandaloneConstruct) {
236   const auto &directive =
237       std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
238           simpleStandaloneConstruct.t);
239   switch (directive.v) {
240   default:
241     break;
242   case llvm::omp::Directive::OMPD_barrier:
243     converter.getFirOpBuilder().create<mlir::omp::BarrierOp>(
244         converter.getCurrentLocation());
245     break;
246   case llvm::omp::Directive::OMPD_taskwait:
247     converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>(
248         converter.getCurrentLocation());
249     break;
250   case llvm::omp::Directive::OMPD_taskyield:
251     converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>(
252         converter.getCurrentLocation());
253     break;
254   case llvm::omp::Directive::OMPD_target_enter_data:
255     TODO(converter.getCurrentLocation(), "OMPD_target_enter_data");
256   case llvm::omp::Directive::OMPD_target_exit_data:
257     TODO(converter.getCurrentLocation(), "OMPD_target_exit_data");
258   case llvm::omp::Directive::OMPD_target_update:
259     TODO(converter.getCurrentLocation(), "OMPD_target_update");
260   case llvm::omp::Directive::OMPD_ordered:
261     TODO(converter.getCurrentLocation(), "OMPD_ordered");
262   }
263 }
264 
265 static void
266 genAllocateClause(Fortran::lower::AbstractConverter &converter,
267                   const Fortran::parser::OmpAllocateClause &ompAllocateClause,
268                   SmallVector<Value> &allocatorOperands,
269                   SmallVector<Value> &allocateOperands) {
270   auto &firOpBuilder = converter.getFirOpBuilder();
271   auto currentLocation = converter.getCurrentLocation();
272   Fortran::lower::StatementContext stmtCtx;
273 
274   mlir::Value allocatorOperand;
275   const Fortran::parser::OmpObjectList &ompObjectList =
276       std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t);
277   const auto &allocatorValue =
278       std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>(
279           ompAllocateClause.t);
280   // Check if allocate clause has allocator specified. If so, add it
281   // to list of allocators, otherwise, add default allocator to
282   // list of allocators.
283   if (allocatorValue) {
284     allocatorOperand = fir::getBase(converter.genExprValue(
285         *Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx));
286     allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
287                              allocatorOperand);
288   } else {
289     allocatorOperand = firOpBuilder.createIntegerConstant(
290         currentLocation, firOpBuilder.getI32Type(), 1);
291     allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
292                              allocatorOperand);
293   }
294   genObjectList(ompObjectList, converter, allocateOperands);
295 }
296 
297 static void
298 genOMP(Fortran::lower::AbstractConverter &converter,
299        Fortran::lower::pft::Evaluation &eval,
300        const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
301   std::visit(
302       Fortran::common::visitors{
303           [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
304                   &simpleStandaloneConstruct) {
305             genOMP(converter, eval, simpleStandaloneConstruct);
306           },
307           [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
308             SmallVector<Value, 4> operandRange;
309             if (const auto &ompObjectList =
310                     std::get<std::optional<Fortran::parser::OmpObjectList>>(
311                         flushConstruct.t))
312               genObjectList(*ompObjectList, converter, operandRange);
313             const auto &memOrderClause = std::get<std::optional<
314                 std::list<Fortran::parser::OmpMemoryOrderClause>>>(
315                 flushConstruct.t);
316             if (memOrderClause.has_value() && memOrderClause->size() > 0)
317               TODO(converter.getCurrentLocation(),
318                    "Handle OmpMemoryOrderClause");
319             converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
320                 converter.getCurrentLocation(), operandRange);
321           },
322           [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
323             TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
324           },
325           [&](const Fortran::parser::OpenMPCancellationPointConstruct
326                   &cancellationPointConstruct) {
327             TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
328           },
329       },
330       standaloneConstruct.u);
331 }
332 
333 static omp::ClauseProcBindKindAttr genProcBindKindAttr(
334     fir::FirOpBuilder &firOpBuilder,
335     const Fortran::parser::OmpClause::ProcBind *procBindClause) {
336   omp::ClauseProcBindKind pbKind;
337   switch (procBindClause->v.v) {
338   case Fortran::parser::OmpProcBindClause::Type::Master:
339     pbKind = omp::ClauseProcBindKind::Master;
340     break;
341   case Fortran::parser::OmpProcBindClause::Type::Close:
342     pbKind = omp::ClauseProcBindKind::Close;
343     break;
344   case Fortran::parser::OmpProcBindClause::Type::Spread:
345     pbKind = omp::ClauseProcBindKind::Spread;
346     break;
347   case Fortran::parser::OmpProcBindClause::Type::Primary:
348     pbKind = omp::ClauseProcBindKind::Primary;
349     break;
350   }
351   return omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind);
352 }
353 
354 /* When parallel is used in a combined construct, then use this function to
355  * create the parallel operation. It handles the parallel specific clauses
356  * and leaves the rest for handling at the inner operations.
357  * TODO: Refactor clause handling
358  */
359 template <typename Directive>
360 static void
361 createCombinedParallelOp(Fortran::lower::AbstractConverter &converter,
362                          Fortran::lower::pft::Evaluation &eval,
363                          const Directive &directive) {
364   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
365   mlir::Location currentLocation = converter.getCurrentLocation();
366   Fortran::lower::StatementContext stmtCtx;
367   llvm::ArrayRef<mlir::Type> argTy;
368   mlir::Value ifClauseOperand, numThreadsClauseOperand;
369   SmallVector<Value> allocatorOperands, allocateOperands;
370   mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
371   const auto &opClauseList =
372       std::get<Fortran::parser::OmpClauseList>(directive.t);
373   // TODO: Handle the following clauses
374   // 1. default
375   // 2. copyin
376   // Note: rest of the clauses are handled when the inner operation is created
377   for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
378     if (const auto &ifClause =
379             std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
380       auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
381       mlir::Value ifVal = fir::getBase(
382           converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
383       ifClauseOperand = firOpBuilder.createConvert(
384           currentLocation, firOpBuilder.getI1Type(), ifVal);
385     } else if (const auto &numThreadsClause =
386                    std::get_if<Fortran::parser::OmpClause::NumThreads>(
387                        &clause.u)) {
388       numThreadsClauseOperand = fir::getBase(converter.genExprValue(
389           *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
390     } else if (const auto &procBindClause =
391                    std::get_if<Fortran::parser::OmpClause::ProcBind>(
392                        &clause.u)) {
393       procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause);
394     }
395   }
396   // Create and insert the operation.
397   auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
398       currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
399       allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
400       /*reductions=*/nullptr, procBindKindAttr);
401 
402   createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, eval,
403                                   &opClauseList, /*iv=*/{},
404                                   /*isCombined=*/true);
405 }
406 
407 static void
408 genOMP(Fortran::lower::AbstractConverter &converter,
409        Fortran::lower::pft::Evaluation &eval,
410        const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
411   const auto &beginBlockDirective =
412       std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t);
413   const auto &blockDirective =
414       std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
415   const auto &endBlockDirective =
416       std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t);
417   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
418   mlir::Location currentLocation = converter.getCurrentLocation();
419 
420   Fortran::lower::StatementContext stmtCtx;
421   llvm::ArrayRef<mlir::Type> argTy;
422   mlir::Value ifClauseOperand, numThreadsClauseOperand, finalClauseOperand,
423       priorityClauseOperand;
424   mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
425   SmallVector<Value> allocateOperands, allocatorOperands;
426   mlir::UnitAttr nowaitAttr, untiedAttr, mergeableAttr;
427 
428   const auto &opClauseList =
429       std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
430   for (const auto &clause : opClauseList.v) {
431     if (const auto &ifClause =
432             std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
433       auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
434       mlir::Value ifVal = fir::getBase(
435           converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
436       ifClauseOperand = firOpBuilder.createConvert(
437           currentLocation, firOpBuilder.getI1Type(), ifVal);
438     } else if (const auto &numThreadsClause =
439                    std::get_if<Fortran::parser::OmpClause::NumThreads>(
440                        &clause.u)) {
441       // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`.
442       numThreadsClauseOperand = fir::getBase(converter.genExprValue(
443           *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
444     } else if (const auto &procBindClause =
445                    std::get_if<Fortran::parser::OmpClause::ProcBind>(
446                        &clause.u)) {
447       procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause);
448     } else if (const auto &allocateClause =
449                    std::get_if<Fortran::parser::OmpClause::Allocate>(
450                        &clause.u)) {
451       genAllocateClause(converter, allocateClause->v, allocatorOperands,
452                         allocateOperands);
453     } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) ||
454                std::get_if<Fortran::parser::OmpClause::Firstprivate>(
455                    &clause.u)) {
456       // Privatisation clauses are handled elsewhere.
457       continue;
458     } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) {
459       // Nothing needs to be done for threads clause.
460       continue;
461     } else if (const auto &finalClause =
462                    std::get_if<Fortran::parser::OmpClause::Final>(&clause.u)) {
463       mlir::Value finalVal = fir::getBase(converter.genExprValue(
464           *Fortran::semantics::GetExpr(finalClause->v), stmtCtx));
465       finalClauseOperand = firOpBuilder.createConvert(
466           currentLocation, firOpBuilder.getI1Type(), finalVal);
467     } else if (std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u)) {
468       untiedAttr = firOpBuilder.getUnitAttr();
469     } else if (std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u)) {
470       mergeableAttr = firOpBuilder.getUnitAttr();
471     } else if (const auto &priorityClause =
472                    std::get_if<Fortran::parser::OmpClause::Priority>(
473                        &clause.u)) {
474       priorityClauseOperand = fir::getBase(converter.genExprValue(
475           *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx));
476     } else {
477       TODO(currentLocation, "OpenMP Block construct clauses");
478     }
479   }
480 
481   for (const auto &clause :
482        std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) {
483     if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
484       nowaitAttr = firOpBuilder.getUnitAttr();
485   }
486 
487   if (blockDirective.v == llvm::omp::OMPD_parallel) {
488     // Create and insert the operation.
489     auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
490         currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
491         allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
492         /*reductions=*/nullptr, procBindKindAttr);
493     createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation,
494                                     eval, &opClauseList);
495   } else if (blockDirective.v == llvm::omp::OMPD_master) {
496     auto masterOp =
497         firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
498     createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation, eval);
499   } else if (blockDirective.v == llvm::omp::OMPD_single) {
500     auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
501         currentLocation, allocateOperands, allocatorOperands, nowaitAttr);
502     createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval);
503   } else if (blockDirective.v == llvm::omp::OMPD_ordered) {
504     auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>(
505         currentLocation, /*simd=*/nullptr);
506     createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation,
507                                          eval);
508   } else if (blockDirective.v == llvm::omp::OMPD_task) {
509     auto taskOp = firOpBuilder.create<mlir::omp::TaskOp>(
510         currentLocation, ifClauseOperand, finalClauseOperand, untiedAttr,
511         mergeableAttr, /*in_reduction_vars=*/ValueRange(),
512         /*in_reductions=*/nullptr, priorityClauseOperand, allocateOperands,
513         allocatorOperands);
514     createBodyOfOp(taskOp, converter, currentLocation, eval, &opClauseList);
515   } else {
516     TODO(converter.getCurrentLocation(), "Unhandled block directive");
517   }
518 }
519 
520 static void genOMP(Fortran::lower::AbstractConverter &converter,
521                    Fortran::lower::pft::Evaluation &eval,
522                    const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
523 
524   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
525   mlir::Location currentLocation = converter.getCurrentLocation();
526   llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars,
527       linearStepVars, reductionVars;
528   mlir::Value scheduleChunkClauseOperand;
529   mlir::Attribute scheduleClauseOperand, collapseClauseOperand,
530       noWaitClauseOperand, orderedClauseOperand, orderClauseOperand;
531   const auto &wsLoopOpClauseList = std::get<Fortran::parser::OmpClauseList>(
532       std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t);
533 
534   const auto ompDirective =
535       std::get<Fortran::parser::OmpLoopDirective>(
536           std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t)
537           .v;
538   if (llvm::omp::OMPD_parallel_do == ompDirective) {
539     createCombinedParallelOp<Fortran::parser::OmpBeginLoopDirective>(
540         converter, eval,
541         std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t));
542   } else if (llvm::omp::OMPD_do != ompDirective) {
543     TODO(converter.getCurrentLocation(), "Construct enclosing do loop");
544   }
545 
546   // Collect the loops to collapse.
547   auto *doConstructEval = &eval.getFirstNestedEvaluation();
548 
549   std::int64_t collapseValue =
550       Fortran::lower::getCollapseValue(wsLoopOpClauseList);
551   std::size_t loopVarTypeSize = 0;
552   SmallVector<const Fortran::semantics::Symbol *> iv;
553   do {
554     auto *doLoop = &doConstructEval->getFirstNestedEvaluation();
555     auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>();
556     assert(doStmt && "Expected do loop to be in the nested evaluation");
557     const auto &loopControl =
558         std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t);
559     const Fortran::parser::LoopControl::Bounds *bounds =
560         std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u);
561     assert(bounds && "Expected bounds for worksharing do loop");
562     Fortran::lower::StatementContext stmtCtx;
563     lowerBound.push_back(fir::getBase(converter.genExprValue(
564         *Fortran::semantics::GetExpr(bounds->lower), stmtCtx)));
565     upperBound.push_back(fir::getBase(converter.genExprValue(
566         *Fortran::semantics::GetExpr(bounds->upper), stmtCtx)));
567     if (bounds->step) {
568       step.push_back(fir::getBase(converter.genExprValue(
569           *Fortran::semantics::GetExpr(bounds->step), stmtCtx)));
570     } else { // If `step` is not present, assume it as `1`.
571       step.push_back(firOpBuilder.createIntegerConstant(
572           currentLocation, firOpBuilder.getIntegerType(32), 1));
573     }
574     iv.push_back(bounds->name.thing.symbol);
575     loopVarTypeSize = std::max(loopVarTypeSize,
576                                bounds->name.thing.symbol->GetUltimate().size());
577 
578     collapseValue--;
579     doConstructEval =
580         &*std::next(doConstructEval->getNestedEvaluations().begin());
581   } while (collapseValue > 0);
582 
583   for (const auto &clause : wsLoopOpClauseList.v) {
584     if (const auto &scheduleClause =
585             std::get_if<Fortran::parser::OmpClause::Schedule>(&clause.u)) {
586       if (const auto &chunkExpr =
587               std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
588                   scheduleClause->v.t)) {
589         if (const auto *expr = Fortran::semantics::GetExpr(*chunkExpr)) {
590           Fortran::lower::StatementContext stmtCtx;
591           scheduleChunkClauseOperand =
592               fir::getBase(converter.genExprValue(*expr, stmtCtx));
593         }
594       }
595     }
596   }
597 
598   // The types of lower bound, upper bound, and step are converted into the
599   // type of the loop variable if necessary.
600   mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
601   for (unsigned it = 0; it < (unsigned)lowerBound.size(); it++) {
602     lowerBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType,
603                                                 lowerBound[it]);
604     upperBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType,
605                                                 upperBound[it]);
606     step[it] =
607         firOpBuilder.createConvert(currentLocation, loopVarType, step[it]);
608   }
609 
610   // FIXME: Add support for following clauses:
611   // 1. linear
612   // 2. order
613   auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>(
614       currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars,
615       reductionVars, /*reductions=*/nullptr,
616       scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(),
617       scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr,
618       /*simd_modifier=*/nullptr,
619       collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(),
620       noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(),
621       orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(),
622       orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(),
623       /*inclusive=*/firOpBuilder.getUnitAttr());
624 
625   // Handle attribute based clauses.
626   for (const Fortran::parser::OmpClause &clause : wsLoopOpClauseList.v) {
627     if (const auto &orderedClause =
628             std::get_if<Fortran::parser::OmpClause::Ordered>(&clause.u)) {
629       if (orderedClause->v.has_value()) {
630         const auto *expr = Fortran::semantics::GetExpr(orderedClause->v);
631         const std::optional<std::int64_t> orderedClauseValue =
632             Fortran::evaluate::ToInt64(*expr);
633         wsLoopOp.ordered_valAttr(
634             firOpBuilder.getI64IntegerAttr(*orderedClauseValue));
635       } else {
636         wsLoopOp.ordered_valAttr(firOpBuilder.getI64IntegerAttr(0));
637       }
638     } else if (const auto &collapseClause =
639                    std::get_if<Fortran::parser::OmpClause::Collapse>(
640                        &clause.u)) {
641       const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
642       const std::optional<std::int64_t> collapseValue =
643           Fortran::evaluate::ToInt64(*expr);
644       wsLoopOp.collapse_valAttr(firOpBuilder.getI64IntegerAttr(*collapseValue));
645     } else if (const auto &scheduleClause =
646                    std::get_if<Fortran::parser::OmpClause::Schedule>(
647                        &clause.u)) {
648       mlir::MLIRContext *context = firOpBuilder.getContext();
649       const auto &scheduleType = scheduleClause->v;
650       const auto &scheduleKind =
651           std::get<Fortran::parser::OmpScheduleClause::ScheduleType>(
652               scheduleType.t);
653       switch (scheduleKind) {
654       case Fortran::parser::OmpScheduleClause::ScheduleType::Static:
655         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
656             context, omp::ClauseScheduleKind::Static));
657         break;
658       case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic:
659         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
660             context, omp::ClauseScheduleKind::Dynamic));
661         break;
662       case Fortran::parser::OmpScheduleClause::ScheduleType::Guided:
663         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
664             context, omp::ClauseScheduleKind::Guided));
665         break;
666       case Fortran::parser::OmpScheduleClause::ScheduleType::Auto:
667         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
668             context, omp::ClauseScheduleKind::Auto));
669         break;
670       case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime:
671         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
672             context, omp::ClauseScheduleKind::Runtime));
673         break;
674       }
675     }
676   }
677   // In FORTRAN `nowait` clause occur at the end of `omp do` directive.
678   // i.e
679   // !$omp do
680   // <...>
681   // !$omp end do nowait
682   if (const auto &endClauseList =
683           std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>(
684               loopConstruct.t)) {
685     const auto &clauseList =
686         std::get<Fortran::parser::OmpClauseList>((*endClauseList).t);
687     for (const Fortran::parser::OmpClause &clause : clauseList.v)
688       if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
689         wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr());
690   }
691 
692   createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, eval,
693                                 &wsLoopOpClauseList, iv);
694 }
695 
696 static void
697 genOMP(Fortran::lower::AbstractConverter &converter,
698        Fortran::lower::pft::Evaluation &eval,
699        const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) {
700   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
701   mlir::Location currentLocation = converter.getCurrentLocation();
702   std::string name;
703   const Fortran::parser::OmpCriticalDirective &cd =
704       std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t);
705   if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) {
706     name =
707         std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString();
708   }
709 
710   uint64_t hint = 0;
711   const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
712   for (const Fortran::parser::OmpClause &clause : clauseList.v)
713     if (auto hintClause =
714             std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) {
715       const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
716       hint = *Fortran::evaluate::ToInt64(*expr);
717       break;
718     }
719 
720   mlir::omp::CriticalOp criticalOp = [&]() {
721     if (name.empty()) {
722       return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation,
723                                                         FlatSymbolRefAttr());
724     } else {
725       mlir::ModuleOp module = firOpBuilder.getModule();
726       mlir::OpBuilder modBuilder(module.getBodyRegion());
727       auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name);
728       if (!global)
729         global = modBuilder.create<mlir::omp::CriticalDeclareOp>(
730             currentLocation, name, hint);
731       return firOpBuilder.create<mlir::omp::CriticalOp>(
732           currentLocation, mlir::FlatSymbolRefAttr::get(
733                                firOpBuilder.getContext(), global.sym_name()));
734     }
735   }();
736   createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation, eval);
737 }
738 
739 static void
740 genOMP(Fortran::lower::AbstractConverter &converter,
741        Fortran::lower::pft::Evaluation &eval,
742        const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
743 
744   auto &firOpBuilder = converter.getFirOpBuilder();
745   auto currentLocation = converter.getCurrentLocation();
746   mlir::omp::SectionOp sectionOp =
747       firOpBuilder.create<mlir::omp::SectionOp>(currentLocation);
748   createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation, eval);
749 }
750 
751 // TODO: Add support for reduction
752 static void
753 genOMP(Fortran::lower::AbstractConverter &converter,
754        Fortran::lower::pft::Evaluation &eval,
755        const Fortran::parser::OpenMPSectionsConstruct &sectionsConstruct) {
756   auto &firOpBuilder = converter.getFirOpBuilder();
757   auto currentLocation = converter.getCurrentLocation();
758   SmallVector<Value> reductionVars, allocateOperands, allocatorOperands;
759   mlir::UnitAttr noWaitClauseOperand;
760   const auto &sectionsClauseList = std::get<Fortran::parser::OmpClauseList>(
761       std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t)
762           .t);
763   for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) {
764 
765     // Reduction Clause
766     if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) {
767       TODO(currentLocation, "OMPC_Reduction");
768 
769       // Allocate clause
770     } else if (const auto &allocateClause =
771                    std::get_if<Fortran::parser::OmpClause::Allocate>(
772                        &clause.u)) {
773       genAllocateClause(converter, allocateClause->v, allocatorOperands,
774                         allocateOperands);
775     }
776   }
777   const auto &endSectionsClauseList =
778       std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t);
779   const auto &clauseList =
780       std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t);
781   for (const auto &clause : clauseList.v) {
782     // Nowait clause
783     if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) {
784       noWaitClauseOperand = firOpBuilder.getUnitAttr();
785     }
786   }
787 
788   llvm::omp::Directive dir =
789       std::get<Fortran::parser::OmpSectionsDirective>(
790           std::get<Fortran::parser::OmpBeginSectionsDirective>(
791               sectionsConstruct.t)
792               .t)
793           .v;
794 
795   // Parallel Sections Construct
796   if (dir == llvm::omp::Directive::OMPD_parallel_sections) {
797     createCombinedParallelOp<Fortran::parser::OmpBeginSectionsDirective>(
798         converter, eval,
799         std::get<Fortran::parser::OmpBeginSectionsDirective>(
800             sectionsConstruct.t));
801     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
802         currentLocation, /*reduction_vars*/ ValueRange(),
803         /*reductions=*/nullptr, allocateOperands, allocatorOperands,
804         /*nowait=*/nullptr);
805     createBodyOfOp(sectionsOp, converter, currentLocation, eval);
806 
807     // Sections Construct
808   } else if (dir == llvm::omp::Directive::OMPD_sections) {
809     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
810         currentLocation, reductionVars, /*reductions = */ nullptr,
811         allocateOperands, allocatorOperands, noWaitClauseOperand);
812     createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation,
813                                     eval);
814   }
815 }
816 
817 static void genOmpAtomicHintAndMemoryOrderClauses(
818     Fortran::lower::AbstractConverter &converter,
819     const Fortran::parser::OmpAtomicClauseList &clauseList,
820     mlir::IntegerAttr &hint,
821     mlir::omp::ClauseMemoryOrderKindAttr &memory_order) {
822   auto &firOpBuilder = converter.getFirOpBuilder();
823   for (const auto &clause : clauseList.v) {
824     if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) {
825       if (auto hintClause =
826               std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) {
827         const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
828         uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr);
829         hint = firOpBuilder.getI64IntegerAttr(hintExprValue);
830       }
831     } else if (auto ompMemoryOrderClause =
832                    std::get_if<Fortran::parser::OmpMemoryOrderClause>(
833                        &clause.u)) {
834       if (std::get_if<Fortran::parser::OmpClause::Acquire>(
835               &ompMemoryOrderClause->v.u)) {
836         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
837             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire);
838       } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>(
839                      &ompMemoryOrderClause->v.u)) {
840         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
841             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed);
842       } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>(
843                      &ompMemoryOrderClause->v.u)) {
844         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
845             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst);
846       } else if (std::get_if<Fortran::parser::OmpClause::Release>(
847                      &ompMemoryOrderClause->v.u)) {
848         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
849             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release);
850       }
851     }
852   }
853 }
854 
855 static void
856 genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter,
857                   Fortran::lower::pft::Evaluation &eval,
858                   const Fortran::parser::OmpAtomicWrite &atomicWrite) {
859   auto &firOpBuilder = converter.getFirOpBuilder();
860   auto currentLocation = converter.getCurrentLocation();
861   // Get the value and address of atomic write operands.
862   const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
863       std::get<2>(atomicWrite.t);
864   const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
865       std::get<0>(atomicWrite.t);
866   const auto &assignmentStmtExpr =
867       std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t);
868   const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
869       std::get<3>(atomicWrite.t).statement.t);
870   Fortran::lower::StatementContext stmtCtx;
871   mlir::Value value = fir::getBase(converter.genExprValue(
872       *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx));
873   mlir::Value address = fir::getBase(converter.genExprAddr(
874       *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx));
875   // If no hint clause is specified, the effect is as if
876   // hint(omp_sync_hint_none) had been specified.
877   mlir::IntegerAttr hint = nullptr;
878   mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr;
879   genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
880                                         memory_order);
881   genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
882                                         memory_order);
883   firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value,
884                                                 hint, memory_order);
885 }
886 
887 static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter,
888                              Fortran::lower::pft::Evaluation &eval,
889                              const Fortran::parser::OmpAtomicRead &atomicRead) {
890   auto &firOpBuilder = converter.getFirOpBuilder();
891   auto currentLocation = converter.getCurrentLocation();
892   // Get the address of atomic read operands.
893   const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
894       std::get<2>(atomicRead.t);
895   const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
896       std::get<0>(atomicRead.t);
897   const auto &assignmentStmtExpr =
898       std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t);
899   const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
900       std::get<3>(atomicRead.t).statement.t);
901   Fortran::lower::StatementContext stmtCtx;
902   mlir::Value from_address = fir::getBase(converter.genExprAddr(
903       *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx));
904   mlir::Value to_address = fir::getBase(converter.genExprAddr(
905       *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx));
906   // If no hint clause is specified, the effect is as if
907   // hint(omp_sync_hint_none) had been specified.
908   mlir::IntegerAttr hint = nullptr;
909   mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr;
910   genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
911                                         memory_order);
912   genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
913                                         memory_order);
914   firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address,
915                                                to_address, hint, memory_order);
916 }
917 
918 static void
919 genOMP(Fortran::lower::AbstractConverter &converter,
920        Fortran::lower::pft::Evaluation &eval,
921        const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
922   std::visit(Fortran::common::visitors{
923                  [&](const Fortran::parser::OmpAtomicRead &atomicRead) {
924                    genOmpAtomicRead(converter, eval, atomicRead);
925                  },
926                  [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
927                    genOmpAtomicWrite(converter, eval, atomicWrite);
928                  },
929                  [&](const auto &) {
930                    TODO(converter.getCurrentLocation(),
931                         "Atomic update & capture");
932                  },
933              },
934              atomicConstruct.u);
935 }
936 
937 void Fortran::lower::genOpenMPConstruct(
938     Fortran::lower::AbstractConverter &converter,
939     Fortran::lower::pft::Evaluation &eval,
940     const Fortran::parser::OpenMPConstruct &ompConstruct) {
941 
942   std::visit(
943       common::visitors{
944           [&](const Fortran::parser::OpenMPStandaloneConstruct
945                   &standaloneConstruct) {
946             genOMP(converter, eval, standaloneConstruct);
947           },
948           [&](const Fortran::parser::OpenMPSectionsConstruct
949                   &sectionsConstruct) {
950             genOMP(converter, eval, sectionsConstruct);
951           },
952           [&](const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
953             genOMP(converter, eval, sectionConstruct);
954           },
955           [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
956             genOMP(converter, eval, loopConstruct);
957           },
958           [&](const Fortran::parser::OpenMPDeclarativeAllocate
959                   &execAllocConstruct) {
960             TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
961           },
962           [&](const Fortran::parser::OpenMPExecutableAllocate
963                   &execAllocConstruct) {
964             TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate");
965           },
966           [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
967             genOMP(converter, eval, blockConstruct);
968           },
969           [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
970             genOMP(converter, eval, atomicConstruct);
971           },
972           [&](const Fortran::parser::OpenMPCriticalConstruct
973                   &criticalConstruct) {
974             genOMP(converter, eval, criticalConstruct);
975           },
976       },
977       ompConstruct.u);
978 }
979 
980 void Fortran::lower::genOpenMPDeclarativeConstruct(
981     Fortran::lower::AbstractConverter &converter,
982     Fortran::lower::pft::Evaluation &eval,
983     const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) {
984 
985   std::visit(
986       common::visitors{
987           [&](const Fortran::parser::OpenMPDeclarativeAllocate
988                   &declarativeAllocate) {
989             TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
990           },
991           [&](const Fortran::parser::OpenMPDeclareReductionConstruct
992                   &declareReductionConstruct) {
993             TODO(converter.getCurrentLocation(),
994                  "OpenMPDeclareReductionConstruct");
995           },
996           [&](const Fortran::parser::OpenMPDeclareSimdConstruct
997                   &declareSimdConstruct) {
998             TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");
999           },
1000           [&](const Fortran::parser::OpenMPDeclareTargetConstruct
1001                   &declareTargetConstruct) {
1002             TODO(converter.getCurrentLocation(),
1003                  "OpenMPDeclareTargetConstruct");
1004           },
1005           [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) {
1006             TODO(converter.getCurrentLocation(), "OpenMPThreadprivate");
1007           },
1008       },
1009       ompDeclConstruct.u);
1010 }
1011