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