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