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.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   auto &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   auto &block = op.getRegion().back();
209   firOpBuilder.setInsertionPointToStart(&block);
210 
211   if (eval.lowerAsUnstructured())
212     createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations());
213 
214   // Ensure the block is well-formed by inserting terminators.
215   if constexpr (std::is_same_v<Op, omp::WsLoopOp>) {
216     mlir::ValueRange results;
217     firOpBuilder.create<mlir::omp::YieldOp>(loc, results);
218   } else {
219     firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
220   }
221 
222   // Reset the insertion point to the start of the first block.
223   firOpBuilder.setInsertionPointToStart(&block);
224   // Handle privatization. Do not privatize if this is the outer operation.
225   if (clauses && !outerCombined)
226     privatizeVars(converter, *clauses);
227 }
228 
229 static void genOMP(Fortran::lower::AbstractConverter &converter,
230                    Fortran::lower::pft::Evaluation &eval,
231                    const Fortran::parser::OpenMPSimpleStandaloneConstruct
232                        &simpleStandaloneConstruct) {
233   const auto &directive =
234       std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
235           simpleStandaloneConstruct.t);
236   switch (directive.v) {
237   default:
238     break;
239   case llvm::omp::Directive::OMPD_barrier:
240     converter.getFirOpBuilder().create<mlir::omp::BarrierOp>(
241         converter.getCurrentLocation());
242     break;
243   case llvm::omp::Directive::OMPD_taskwait:
244     converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>(
245         converter.getCurrentLocation());
246     break;
247   case llvm::omp::Directive::OMPD_taskyield:
248     converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>(
249         converter.getCurrentLocation());
250     break;
251   case llvm::omp::Directive::OMPD_target_enter_data:
252     TODO(converter.getCurrentLocation(), "OMPD_target_enter_data");
253   case llvm::omp::Directive::OMPD_target_exit_data:
254     TODO(converter.getCurrentLocation(), "OMPD_target_exit_data");
255   case llvm::omp::Directive::OMPD_target_update:
256     TODO(converter.getCurrentLocation(), "OMPD_target_update");
257   case llvm::omp::Directive::OMPD_ordered:
258     TODO(converter.getCurrentLocation(), "OMPD_ordered");
259   }
260 }
261 
262 static void
263 genAllocateClause(Fortran::lower::AbstractConverter &converter,
264                   const Fortran::parser::OmpAllocateClause &ompAllocateClause,
265                   SmallVector<Value> &allocatorOperands,
266                   SmallVector<Value> &allocateOperands) {
267   auto &firOpBuilder = converter.getFirOpBuilder();
268   auto currentLocation = converter.getCurrentLocation();
269   Fortran::lower::StatementContext stmtCtx;
270 
271   mlir::Value allocatorOperand;
272   const Fortran::parser::OmpObjectList &ompObjectList =
273       std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t);
274   const auto &allocatorValue =
275       std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>(
276           ompAllocateClause.t);
277   // Check if allocate clause has allocator specified. If so, add it
278   // to list of allocators, otherwise, add default allocator to
279   // list of allocators.
280   if (allocatorValue) {
281     allocatorOperand = fir::getBase(converter.genExprValue(
282         *Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx));
283     allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
284                              allocatorOperand);
285   } else {
286     allocatorOperand = firOpBuilder.createIntegerConstant(
287         currentLocation, firOpBuilder.getI32Type(), 1);
288     allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
289                              allocatorOperand);
290   }
291   genObjectList(ompObjectList, converter, allocateOperands);
292 }
293 
294 static void
295 genOMP(Fortran::lower::AbstractConverter &converter,
296        Fortran::lower::pft::Evaluation &eval,
297        const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
298   std::visit(
299       Fortran::common::visitors{
300           [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
301                   &simpleStandaloneConstruct) {
302             genOMP(converter, eval, simpleStandaloneConstruct);
303           },
304           [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
305             SmallVector<Value, 4> operandRange;
306             if (const auto &ompObjectList =
307                     std::get<std::optional<Fortran::parser::OmpObjectList>>(
308                         flushConstruct.t))
309               genObjectList(*ompObjectList, converter, operandRange);
310             const auto &memOrderClause = std::get<std::optional<
311                 std::list<Fortran::parser::OmpMemoryOrderClause>>>(
312                 flushConstruct.t);
313             if (memOrderClause.has_value() && memOrderClause->size() > 0)
314               TODO(converter.getCurrentLocation(),
315                    "Handle OmpMemoryOrderClause");
316             converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
317                 converter.getCurrentLocation(), operandRange);
318           },
319           [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
320             TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
321           },
322           [&](const Fortran::parser::OpenMPCancellationPointConstruct
323                   &cancellationPointConstruct) {
324             TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
325           },
326       },
327       standaloneConstruct.u);
328 }
329 
330 static omp::ClauseProcBindKindAttr genProcBindKindAttr(
331     fir::FirOpBuilder &firOpBuilder,
332     const Fortran::parser::OmpClause::ProcBind *procBindClause) {
333   omp::ClauseProcBindKind pbKind;
334   switch (procBindClause->v.v) {
335   case Fortran::parser::OmpProcBindClause::Type::Master:
336     pbKind = omp::ClauseProcBindKind::Master;
337     break;
338   case Fortran::parser::OmpProcBindClause::Type::Close:
339     pbKind = omp::ClauseProcBindKind::Close;
340     break;
341   case Fortran::parser::OmpProcBindClause::Type::Spread:
342     pbKind = omp::ClauseProcBindKind::Spread;
343     break;
344   case Fortran::parser::OmpProcBindClause::Type::Primary:
345     pbKind = omp::ClauseProcBindKind::Primary;
346     break;
347   }
348   return omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind);
349 }
350 
351 /* When parallel is used in a combined construct, then use this function to
352  * create the parallel operation. It handles the parallel specific clauses
353  * and leaves the rest for handling at the inner operations.
354  * TODO: Refactor clause handling
355  */
356 template <typename Directive>
357 static void
358 createCombinedParallelOp(Fortran::lower::AbstractConverter &converter,
359                          Fortran::lower::pft::Evaluation &eval,
360                          const Directive &directive) {
361   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
362   mlir::Location currentLocation = converter.getCurrentLocation();
363   Fortran::lower::StatementContext stmtCtx;
364   llvm::ArrayRef<mlir::Type> argTy;
365   mlir::Value ifClauseOperand, numThreadsClauseOperand;
366   SmallVector<Value> allocatorOperands, allocateOperands;
367   mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
368   const auto &opClauseList =
369       std::get<Fortran::parser::OmpClauseList>(directive.t);
370   // TODO: Handle the following clauses
371   // 1. default
372   // 2. copyin
373   // Note: rest of the clauses are handled when the inner operation is created
374   for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
375     if (const auto &ifClause =
376             std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
377       auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
378       mlir::Value ifVal = fir::getBase(
379           converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
380       ifClauseOperand = firOpBuilder.createConvert(
381           currentLocation, firOpBuilder.getI1Type(), ifVal);
382     } else if (const auto &numThreadsClause =
383                    std::get_if<Fortran::parser::OmpClause::NumThreads>(
384                        &clause.u)) {
385       numThreadsClauseOperand = fir::getBase(converter.genExprValue(
386           *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
387     } else if (const auto &procBindClause =
388                    std::get_if<Fortran::parser::OmpClause::ProcBind>(
389                        &clause.u)) {
390       procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause);
391     }
392   }
393   // Create and insert the operation.
394   auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
395       currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
396       allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
397       /*reductions=*/nullptr, procBindKindAttr);
398 
399   createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, eval,
400                                   &opClauseList, /*iv=*/{},
401                                   /*isCombined=*/true);
402 }
403 
404 static void
405 genOMP(Fortran::lower::AbstractConverter &converter,
406        Fortran::lower::pft::Evaluation &eval,
407        const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
408   const auto &beginBlockDirective =
409       std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t);
410   const auto &blockDirective =
411       std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
412   const auto &endBlockDirective =
413       std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t);
414   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
415   mlir::Location currentLocation = converter.getCurrentLocation();
416 
417   Fortran::lower::StatementContext stmtCtx;
418   llvm::ArrayRef<mlir::Type> argTy;
419   mlir::Value ifClauseOperand, numThreadsClauseOperand, finalClauseOperand,
420       priorityClauseOperand;
421   mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
422   SmallVector<Value> allocateOperands, allocatorOperands;
423   mlir::UnitAttr nowaitAttr, untiedAttr, mergeableAttr;
424 
425   const auto &opClauseList =
426       std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
427   for (const auto &clause : opClauseList.v) {
428     if (const auto &ifClause =
429             std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
430       auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
431       mlir::Value ifVal = fir::getBase(
432           converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
433       ifClauseOperand = firOpBuilder.createConvert(
434           currentLocation, firOpBuilder.getI1Type(), ifVal);
435     } else if (const auto &numThreadsClause =
436                    std::get_if<Fortran::parser::OmpClause::NumThreads>(
437                        &clause.u)) {
438       // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`.
439       numThreadsClauseOperand = fir::getBase(converter.genExprValue(
440           *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
441     } else if (const auto &procBindClause =
442                    std::get_if<Fortran::parser::OmpClause::ProcBind>(
443                        &clause.u)) {
444       procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause);
445     } else if (const auto &allocateClause =
446                    std::get_if<Fortran::parser::OmpClause::Allocate>(
447                        &clause.u)) {
448       genAllocateClause(converter, allocateClause->v, allocatorOperands,
449                         allocateOperands);
450     } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) ||
451                std::get_if<Fortran::parser::OmpClause::Firstprivate>(
452                    &clause.u)) {
453       // Privatisation clauses are handled elsewhere.
454       continue;
455     } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) {
456       // Nothing needs to be done for threads clause.
457       continue;
458     } else if (const auto &finalClause =
459                    std::get_if<Fortran::parser::OmpClause::Final>(&clause.u)) {
460       mlir::Value finalVal = fir::getBase(converter.genExprValue(
461           *Fortran::semantics::GetExpr(finalClause->v), stmtCtx));
462       finalClauseOperand = firOpBuilder.createConvert(
463           currentLocation, firOpBuilder.getI1Type(), finalVal);
464     } else if (std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u)) {
465       untiedAttr = firOpBuilder.getUnitAttr();
466     } else if (std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u)) {
467       mergeableAttr = firOpBuilder.getUnitAttr();
468     } else if (const auto &priorityClause =
469                    std::get_if<Fortran::parser::OmpClause::Priority>(
470                        &clause.u)) {
471       priorityClauseOperand = fir::getBase(converter.genExprValue(
472           *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx));
473     } else {
474       TODO(currentLocation, "OpenMP Block construct clauses");
475     }
476   }
477 
478   for (const auto &clause :
479        std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) {
480     if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
481       nowaitAttr = firOpBuilder.getUnitAttr();
482   }
483 
484   if (blockDirective.v == llvm::omp::OMPD_parallel) {
485     // Create and insert the operation.
486     auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
487         currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
488         allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
489         /*reductions=*/nullptr, procBindKindAttr);
490     createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation,
491                                     eval, &opClauseList);
492   } else if (blockDirective.v == llvm::omp::OMPD_master) {
493     auto masterOp =
494         firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
495     createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation, eval);
496   } else if (blockDirective.v == llvm::omp::OMPD_single) {
497     auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
498         currentLocation, allocateOperands, allocatorOperands, nowaitAttr);
499     createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval);
500   } else if (blockDirective.v == llvm::omp::OMPD_ordered) {
501     auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>(
502         currentLocation, /*simd=*/nullptr);
503     createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation,
504                                          eval);
505   } else if (blockDirective.v == llvm::omp::OMPD_task) {
506     auto taskOp = firOpBuilder.create<mlir::omp::TaskOp>(
507         currentLocation, ifClauseOperand, finalClauseOperand, untiedAttr,
508         mergeableAttr, /*in_reduction_vars=*/ValueRange(),
509         /*in_reductions=*/nullptr, priorityClauseOperand, allocateOperands,
510         allocatorOperands);
511     createBodyOfOp(taskOp, converter, currentLocation, eval, &opClauseList);
512   } else {
513     TODO(converter.getCurrentLocation(), "Unhandled block directive");
514   }
515 }
516 
517 static void genOMP(Fortran::lower::AbstractConverter &converter,
518                    Fortran::lower::pft::Evaluation &eval,
519                    const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
520 
521   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
522   mlir::Location currentLocation = converter.getCurrentLocation();
523   llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars,
524       linearStepVars, reductionVars;
525   mlir::Value scheduleChunkClauseOperand;
526   mlir::Attribute scheduleClauseOperand, collapseClauseOperand,
527       noWaitClauseOperand, orderedClauseOperand, orderClauseOperand;
528   const auto &wsLoopOpClauseList = std::get<Fortran::parser::OmpClauseList>(
529       std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t);
530 
531   const auto ompDirective =
532       std::get<Fortran::parser::OmpLoopDirective>(
533           std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t)
534           .v;
535   if (llvm::omp::OMPD_parallel_do == ompDirective) {
536     createCombinedParallelOp<Fortran::parser::OmpBeginLoopDirective>(
537         converter, eval,
538         std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t));
539   } else if (llvm::omp::OMPD_do != ompDirective) {
540     TODO(converter.getCurrentLocation(), "Construct enclosing do loop");
541   }
542 
543   // Collect the loops to collapse.
544   auto *doConstructEval = &eval.getFirstNestedEvaluation();
545 
546   std::int64_t collapseValue =
547       Fortran::lower::getCollapseValue(wsLoopOpClauseList);
548   std::size_t loopVarTypeSize = 0;
549   SmallVector<const Fortran::semantics::Symbol *> iv;
550   do {
551     auto *doLoop = &doConstructEval->getFirstNestedEvaluation();
552     auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>();
553     assert(doStmt && "Expected do loop to be in the nested evaluation");
554     const auto &loopControl =
555         std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t);
556     const Fortran::parser::LoopControl::Bounds *bounds =
557         std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u);
558     assert(bounds && "Expected bounds for worksharing do loop");
559     Fortran::lower::StatementContext stmtCtx;
560     lowerBound.push_back(fir::getBase(converter.genExprValue(
561         *Fortran::semantics::GetExpr(bounds->lower), stmtCtx)));
562     upperBound.push_back(fir::getBase(converter.genExprValue(
563         *Fortran::semantics::GetExpr(bounds->upper), stmtCtx)));
564     if (bounds->step) {
565       step.push_back(fir::getBase(converter.genExprValue(
566           *Fortran::semantics::GetExpr(bounds->step), stmtCtx)));
567     } else { // If `step` is not present, assume it as `1`.
568       step.push_back(firOpBuilder.createIntegerConstant(
569           currentLocation, firOpBuilder.getIntegerType(32), 1));
570     }
571     iv.push_back(bounds->name.thing.symbol);
572     loopVarTypeSize = std::max(loopVarTypeSize,
573                                bounds->name.thing.symbol->GetUltimate().size());
574 
575     collapseValue--;
576     doConstructEval =
577         &*std::next(doConstructEval->getNestedEvaluations().begin());
578   } while (collapseValue > 0);
579 
580   // The types of lower bound, upper bound, and step are converted into the
581   // type of the loop variable if necessary.
582   mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
583   for (unsigned it = 0; it < (unsigned)lowerBound.size(); it++) {
584     lowerBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType,
585                                                 lowerBound[it]);
586     upperBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType,
587                                                 upperBound[it]);
588     step[it] =
589         firOpBuilder.createConvert(currentLocation, loopVarType, step[it]);
590   }
591 
592   // FIXME: Add support for following clauses:
593   // 1. linear
594   // 2. order
595   // 3. schedule (with chunk)
596   auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>(
597       currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars,
598       reductionVars, /*reductions=*/nullptr,
599       scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(),
600       scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr,
601       /*simd_modifier=*/nullptr,
602       collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(),
603       noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(),
604       orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(),
605       orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(),
606       /*inclusive=*/firOpBuilder.getUnitAttr());
607 
608   // Handle attribute based clauses.
609   for (const Fortran::parser::OmpClause &clause : wsLoopOpClauseList.v) {
610     if (const auto &orderedClause =
611             std::get_if<Fortran::parser::OmpClause::Ordered>(&clause.u)) {
612       if (orderedClause->v.has_value()) {
613         const auto *expr = Fortran::semantics::GetExpr(orderedClause->v);
614         const std::optional<std::int64_t> orderedClauseValue =
615             Fortran::evaluate::ToInt64(*expr);
616         wsLoopOp.ordered_valAttr(
617             firOpBuilder.getI64IntegerAttr(*orderedClauseValue));
618       } else {
619         wsLoopOp.ordered_valAttr(firOpBuilder.getI64IntegerAttr(0));
620       }
621     } else if (const auto &collapseClause =
622                    std::get_if<Fortran::parser::OmpClause::Collapse>(
623                        &clause.u)) {
624       const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
625       const std::optional<std::int64_t> collapseValue =
626           Fortran::evaluate::ToInt64(*expr);
627       wsLoopOp.collapse_valAttr(firOpBuilder.getI64IntegerAttr(*collapseValue));
628     } else if (const auto &scheduleClause =
629                    std::get_if<Fortran::parser::OmpClause::Schedule>(
630                        &clause.u)) {
631       mlir::MLIRContext *context = firOpBuilder.getContext();
632       const auto &scheduleType = scheduleClause->v;
633       const auto &scheduleKind =
634           std::get<Fortran::parser::OmpScheduleClause::ScheduleType>(
635               scheduleType.t);
636       switch (scheduleKind) {
637       case Fortran::parser::OmpScheduleClause::ScheduleType::Static:
638         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
639             context, omp::ClauseScheduleKind::Static));
640         break;
641       case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic:
642         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
643             context, omp::ClauseScheduleKind::Dynamic));
644         break;
645       case Fortran::parser::OmpScheduleClause::ScheduleType::Guided:
646         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
647             context, omp::ClauseScheduleKind::Guided));
648         break;
649       case Fortran::parser::OmpScheduleClause::ScheduleType::Auto:
650         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
651             context, omp::ClauseScheduleKind::Auto));
652         break;
653       case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime:
654         wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
655             context, omp::ClauseScheduleKind::Runtime));
656         break;
657       }
658     }
659   }
660   // In FORTRAN `nowait` clause occur at the end of `omp do` directive.
661   // i.e
662   // !$omp do
663   // <...>
664   // !$omp end do nowait
665   if (const auto &endClauseList =
666           std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>(
667               loopConstruct.t)) {
668     const auto &clauseList =
669         std::get<Fortran::parser::OmpClauseList>((*endClauseList).t);
670     for (const Fortran::parser::OmpClause &clause : clauseList.v)
671       if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
672         wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr());
673   }
674 
675   createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, eval,
676                                 &wsLoopOpClauseList, iv);
677 }
678 
679 static void
680 genOMP(Fortran::lower::AbstractConverter &converter,
681        Fortran::lower::pft::Evaluation &eval,
682        const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) {
683   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
684   mlir::Location currentLocation = converter.getCurrentLocation();
685   std::string name;
686   const Fortran::parser::OmpCriticalDirective &cd =
687       std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t);
688   if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) {
689     name =
690         std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString();
691   }
692 
693   uint64_t hint = 0;
694   const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
695   for (const Fortran::parser::OmpClause &clause : clauseList.v)
696     if (auto hintClause =
697             std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) {
698       const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
699       hint = *Fortran::evaluate::ToInt64(*expr);
700       break;
701     }
702 
703   mlir::omp::CriticalOp criticalOp = [&]() {
704     if (name.empty()) {
705       return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation,
706                                                         FlatSymbolRefAttr());
707     } else {
708       mlir::ModuleOp module = firOpBuilder.getModule();
709       mlir::OpBuilder modBuilder(module.getBodyRegion());
710       auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name);
711       if (!global)
712         global = modBuilder.create<mlir::omp::CriticalDeclareOp>(
713             currentLocation, name, hint);
714       return firOpBuilder.create<mlir::omp::CriticalOp>(
715           currentLocation, mlir::FlatSymbolRefAttr::get(
716                                firOpBuilder.getContext(), global.sym_name()));
717     }
718   }();
719   createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation, eval);
720 }
721 
722 static void
723 genOMP(Fortran::lower::AbstractConverter &converter,
724        Fortran::lower::pft::Evaluation &eval,
725        const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
726 
727   auto &firOpBuilder = converter.getFirOpBuilder();
728   auto currentLocation = converter.getCurrentLocation();
729   mlir::omp::SectionOp sectionOp =
730       firOpBuilder.create<mlir::omp::SectionOp>(currentLocation);
731   createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation, eval);
732 }
733 
734 // TODO: Add support for reduction
735 static void
736 genOMP(Fortran::lower::AbstractConverter &converter,
737        Fortran::lower::pft::Evaluation &eval,
738        const Fortran::parser::OpenMPSectionsConstruct &sectionsConstruct) {
739   auto &firOpBuilder = converter.getFirOpBuilder();
740   auto currentLocation = converter.getCurrentLocation();
741   SmallVector<Value> reductionVars, allocateOperands, allocatorOperands;
742   mlir::UnitAttr noWaitClauseOperand;
743   const auto &sectionsClauseList = std::get<Fortran::parser::OmpClauseList>(
744       std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t)
745           .t);
746   for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) {
747 
748     // Reduction Clause
749     if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) {
750       TODO(currentLocation, "OMPC_Reduction");
751 
752       // Allocate clause
753     } else if (const auto &allocateClause =
754                    std::get_if<Fortran::parser::OmpClause::Allocate>(
755                        &clause.u)) {
756       genAllocateClause(converter, allocateClause->v, allocatorOperands,
757                         allocateOperands);
758     }
759   }
760   const auto &endSectionsClauseList =
761       std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t);
762   const auto &clauseList =
763       std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t);
764   for (const auto &clause : clauseList.v) {
765     // Nowait clause
766     if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) {
767       noWaitClauseOperand = firOpBuilder.getUnitAttr();
768     }
769   }
770 
771   llvm::omp::Directive dir =
772       std::get<Fortran::parser::OmpSectionsDirective>(
773           std::get<Fortran::parser::OmpBeginSectionsDirective>(
774               sectionsConstruct.t)
775               .t)
776           .v;
777 
778   // Parallel Sections Construct
779   if (dir == llvm::omp::Directive::OMPD_parallel_sections) {
780     createCombinedParallelOp<Fortran::parser::OmpBeginSectionsDirective>(
781         converter, eval,
782         std::get<Fortran::parser::OmpBeginSectionsDirective>(
783             sectionsConstruct.t));
784     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
785         currentLocation, /*reduction_vars*/ ValueRange(),
786         /*reductions=*/nullptr, allocateOperands, allocatorOperands,
787         /*nowait=*/nullptr);
788     createBodyOfOp(sectionsOp, converter, currentLocation, eval);
789 
790     // Sections Construct
791   } else if (dir == llvm::omp::Directive::OMPD_sections) {
792     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
793         currentLocation, reductionVars, /*reductions = */ nullptr,
794         allocateOperands, allocatorOperands, noWaitClauseOperand);
795     createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation,
796                                     eval);
797   }
798 }
799 
800 static void genOmpAtomicHintAndMemoryOrderClauses(
801     Fortran::lower::AbstractConverter &converter,
802     const Fortran::parser::OmpAtomicClauseList &clauseList,
803     mlir::IntegerAttr &hint,
804     mlir::omp::ClauseMemoryOrderKindAttr &memory_order) {
805   auto &firOpBuilder = converter.getFirOpBuilder();
806   for (const auto &clause : clauseList.v) {
807     if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) {
808       if (auto hintClause =
809               std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) {
810         const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
811         uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr);
812         hint = firOpBuilder.getI64IntegerAttr(hintExprValue);
813       }
814     } else if (auto ompMemoryOrderClause =
815                    std::get_if<Fortran::parser::OmpMemoryOrderClause>(
816                        &clause.u)) {
817       if (std::get_if<Fortran::parser::OmpClause::Acquire>(
818               &ompMemoryOrderClause->v.u)) {
819         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
820             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire);
821       } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>(
822                      &ompMemoryOrderClause->v.u)) {
823         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
824             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed);
825       } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>(
826                      &ompMemoryOrderClause->v.u)) {
827         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
828             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst);
829       } else if (std::get_if<Fortran::parser::OmpClause::Release>(
830                      &ompMemoryOrderClause->v.u)) {
831         memory_order = mlir::omp::ClauseMemoryOrderKindAttr::get(
832             firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release);
833       }
834     }
835   }
836 }
837 
838 static void
839 genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter,
840                   Fortran::lower::pft::Evaluation &eval,
841                   const Fortran::parser::OmpAtomicWrite &atomicWrite) {
842   auto &firOpBuilder = converter.getFirOpBuilder();
843   auto currentLocation = converter.getCurrentLocation();
844   mlir::Value address;
845   // If no hint clause is specified, the effect is as if
846   // hint(omp_sync_hint_none) had been specified.
847   mlir::IntegerAttr hint = nullptr;
848   mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr;
849   const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
850       std::get<2>(atomicWrite.t);
851   const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
852       std::get<0>(atomicWrite.t);
853   const auto &assignmentStmtExpr =
854       std::get<Fortran::parser::Expr>(std::get<3>(atomicWrite.t).statement.t);
855   const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
856       std::get<3>(atomicWrite.t).statement.t);
857   Fortran::lower::StatementContext stmtCtx;
858   auto value = fir::getBase(converter.genExprValue(
859       *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx));
860   if (auto varDesignator = std::get_if<
861           Fortran::common::Indirection<Fortran::parser::Designator>>(
862           &assignmentStmtVariable.u)) {
863     if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) {
864       address = converter.getSymbolAddress(*name->symbol);
865     }
866   }
867 
868   genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
869                                         memory_order);
870   genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
871                                         memory_order);
872   firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, address, value,
873                                                 hint, memory_order);
874 }
875 
876 static void genOmpAtomicRead(Fortran::lower::AbstractConverter &converter,
877                              Fortran::lower::pft::Evaluation &eval,
878                              const Fortran::parser::OmpAtomicRead &atomicRead) {
879   auto &firOpBuilder = converter.getFirOpBuilder();
880   auto currentLocation = converter.getCurrentLocation();
881   mlir::Value to_address;
882   mlir::Value from_address;
883   // If no hint clause is specified, the effect is as if
884   // hint(omp_sync_hint_none) had been specified.
885   mlir::IntegerAttr hint = nullptr;
886   mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr;
887   const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
888       std::get<2>(atomicRead.t);
889   const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
890       std::get<0>(atomicRead.t);
891   const auto &assignmentStmtExpr =
892       std::get<Fortran::parser::Expr>(std::get<3>(atomicRead.t).statement.t);
893   const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
894       std::get<3>(atomicRead.t).statement.t);
895   if (auto exprDesignator = std::get_if<
896           Fortran::common::Indirection<Fortran::parser::Designator>>(
897           &assignmentStmtExpr.u)) {
898     if (const auto *name =
899             getDesignatorNameIfDataRef(exprDesignator->value())) {
900       from_address = converter.getSymbolAddress(*name->symbol);
901     }
902   }
903 
904   if (auto varDesignator = std::get_if<
905           Fortran::common::Indirection<Fortran::parser::Designator>>(
906           &assignmentStmtVariable.u)) {
907     if (const auto *name = getDesignatorNameIfDataRef(varDesignator->value())) {
908       to_address = converter.getSymbolAddress(*name->symbol);
909     }
910   }
911 
912   genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
913                                         memory_order);
914   genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
915                                         memory_order);
916   firOpBuilder.create<mlir::omp::AtomicReadOp>(currentLocation, from_address,
917                                                to_address, hint, memory_order);
918 }
919 
920 static void
921 genOMP(Fortran::lower::AbstractConverter &converter,
922        Fortran::lower::pft::Evaluation &eval,
923        const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
924   std::visit(Fortran::common::visitors{
925                  [&](const Fortran::parser::OmpAtomicRead &atomicRead) {
926                    genOmpAtomicRead(converter, eval, atomicRead);
927                  },
928                  [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
929                    genOmpAtomicWrite(converter, eval, atomicWrite);
930                  },
931                  [&](const auto &) {
932                    TODO(converter.getCurrentLocation(),
933                         "Atomic update & capture");
934                  },
935              },
936              atomicConstruct.u);
937 }
938 
939 void Fortran::lower::genOpenMPConstruct(
940     Fortran::lower::AbstractConverter &converter,
941     Fortran::lower::pft::Evaluation &eval,
942     const Fortran::parser::OpenMPConstruct &ompConstruct) {
943 
944   std::visit(
945       common::visitors{
946           [&](const Fortran::parser::OpenMPStandaloneConstruct
947                   &standaloneConstruct) {
948             genOMP(converter, eval, standaloneConstruct);
949           },
950           [&](const Fortran::parser::OpenMPSectionsConstruct
951                   &sectionsConstruct) {
952             genOMP(converter, eval, sectionsConstruct);
953           },
954           [&](const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
955             genOMP(converter, eval, sectionConstruct);
956           },
957           [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
958             genOMP(converter, eval, loopConstruct);
959           },
960           [&](const Fortran::parser::OpenMPDeclarativeAllocate
961                   &execAllocConstruct) {
962             TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
963           },
964           [&](const Fortran::parser::OpenMPExecutableAllocate
965                   &execAllocConstruct) {
966             TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate");
967           },
968           [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
969             genOMP(converter, eval, blockConstruct);
970           },
971           [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
972             genOMP(converter, eval, atomicConstruct);
973           },
974           [&](const Fortran::parser::OpenMPCriticalConstruct
975                   &criticalConstruct) {
976             genOMP(converter, eval, criticalConstruct);
977           },
978       },
979       ompConstruct.u);
980 }
981 
982 void Fortran::lower::genOpenMPDeclarativeConstruct(
983     Fortran::lower::AbstractConverter &converter,
984     Fortran::lower::pft::Evaluation &eval,
985     const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) {
986 
987   std::visit(
988       common::visitors{
989           [&](const Fortran::parser::OpenMPDeclarativeAllocate
990                   &declarativeAllocate) {
991             TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
992           },
993           [&](const Fortran::parser::OpenMPDeclareReductionConstruct
994                   &declareReductionConstruct) {
995             TODO(converter.getCurrentLocation(),
996                  "OpenMPDeclareReductionConstruct");
997           },
998           [&](const Fortran::parser::OpenMPDeclareSimdConstruct
999                   &declareSimdConstruct) {
1000             TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");
1001           },
1002           [&](const Fortran::parser::OpenMPDeclareTargetConstruct
1003                   &declareTargetConstruct) {
1004             TODO(converter.getCurrentLocation(),
1005                  "OpenMPDeclareTargetConstruct");
1006           },
1007           [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) {
1008             TODO(converter.getCurrentLocation(), "OpenMPThreadprivate");
1009           },
1010       },
1011       ompDeclConstruct.u);
1012 }
1013