1 //===-- OpenACC.cpp -- OpenACC 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/OpenACC.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/Optimizer/Builder/BoxValue.h"
19 #include "flang/Optimizer/Builder/FIRBuilder.h"
20 #include "flang/Optimizer/Builder/Todo.h"
21 #include "flang/Parser/parse-tree.h"
22 #include "flang/Semantics/tools.h"
23 #include "mlir/Dialect/OpenACC/OpenACC.h"
24 #include "llvm/Frontend/OpenACC/ACC.h.inc"
25 
26 // Special value for * passed in device_type or gang clauses.
27 static constexpr std::int64_t starCst = -1;
28 
29 static const Fortran::parser::Name *
30 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) {
31   const auto *dataRef = std::get_if<Fortran::parser::DataRef>(&designator.u);
32   return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr;
33 }
34 
35 static void genObjectList(const Fortran::parser::AccObjectList &objectList,
36                           Fortran::lower::AbstractConverter &converter,
37                           llvm::SmallVectorImpl<mlir::Value> &operands) {
38   auto addOperands = [&](Fortran::lower::SymbolRef sym) {
39     const auto variable = converter.getSymbolAddress(sym);
40     // TODO: Might need revisiting to handle for non-shared clauses
41     if (variable) {
42       operands.push_back(variable);
43     } else {
44       if (const auto *details =
45               sym->detailsIf<Fortran::semantics::HostAssocDetails>())
46         operands.push_back(converter.getSymbolAddress(details->symbol()));
47     }
48   };
49 
50   for (const auto &accObject : objectList.v) {
51     std::visit(Fortran::common::visitors{
52                    [&](const Fortran::parser::Designator &designator) {
53                      if (const auto *name =
54                              getDesignatorNameIfDataRef(designator)) {
55                        addOperands(*name->symbol);
56                      }
57                    },
58                    [&](const Fortran::parser::Name &name) {
59                      addOperands(*name.symbol);
60                    }},
61                accObject.u);
62   }
63 }
64 
65 template <typename Clause>
66 static void genObjectListWithModifier(
67     const Clause *x, Fortran::lower::AbstractConverter &converter,
68     Fortran::parser::AccDataModifier::Modifier mod,
69     llvm::SmallVectorImpl<mlir::Value> &operandsWithModifier,
70     llvm::SmallVectorImpl<mlir::Value> &operands) {
71   const Fortran::parser::AccObjectListWithModifier &listWithModifier = x->v;
72   const auto &accObjectList =
73       std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
74   const auto &modifier =
75       std::get<std::optional<Fortran::parser::AccDataModifier>>(
76           listWithModifier.t);
77   if (modifier && (*modifier).v == mod) {
78     genObjectList(accObjectList, converter, operandsWithModifier);
79   } else {
80     genObjectList(accObjectList, converter, operands);
81   }
82 }
83 
84 static void
85 addOperands(llvm::SmallVectorImpl<mlir::Value> &operands,
86             llvm::SmallVectorImpl<int32_t> &operandSegments,
87             const llvm::SmallVectorImpl<mlir::Value> &clauseOperands) {
88   operands.append(clauseOperands.begin(), clauseOperands.end());
89   operandSegments.push_back(clauseOperands.size());
90 }
91 
92 static void addOperand(llvm::SmallVectorImpl<mlir::Value> &operands,
93                        llvm::SmallVectorImpl<int32_t> &operandSegments,
94                        const mlir::Value &clauseOperand) {
95   if (clauseOperand) {
96     operands.push_back(clauseOperand);
97     operandSegments.push_back(1);
98   } else {
99     operandSegments.push_back(0);
100   }
101 }
102 
103 template <typename Op, typename Terminator>
104 static Op
105 createRegionOp(fir::FirOpBuilder &builder, mlir::Location loc,
106                const llvm::SmallVectorImpl<mlir::Value> &operands,
107                const llvm::SmallVectorImpl<int32_t> &operandSegments) {
108   llvm::ArrayRef<mlir::Type> argTy;
109   Op op = builder.create<Op>(loc, argTy, operands);
110   builder.createBlock(&op.getRegion());
111   mlir::Block &block = op.getRegion().back();
112   builder.setInsertionPointToStart(&block);
113   builder.create<Terminator>(loc);
114 
115   op->setAttr(Op::getOperandSegmentSizeAttr(),
116               builder.getI32VectorAttr(operandSegments));
117 
118   // Place the insertion point to the start of the first block.
119   builder.setInsertionPointToStart(&block);
120 
121   return op;
122 }
123 
124 template <typename Op>
125 static Op
126 createSimpleOp(fir::FirOpBuilder &builder, mlir::Location loc,
127                const llvm::SmallVectorImpl<mlir::Value> &operands,
128                const llvm::SmallVectorImpl<int32_t> &operandSegments) {
129   llvm::ArrayRef<mlir::Type> argTy;
130   Op op = builder.create<Op>(loc, argTy, operands);
131   op->setAttr(Op::getOperandSegmentSizeAttr(),
132               builder.getI32VectorAttr(operandSegments));
133   return op;
134 }
135 
136 static void genAsyncClause(Fortran::lower::AbstractConverter &converter,
137                            const Fortran::parser::AccClause::Async *asyncClause,
138                            mlir::Value &async, bool &addAsyncAttr,
139                            Fortran::lower::StatementContext &stmtCtx) {
140   const auto &asyncClauseValue = asyncClause->v;
141   if (asyncClauseValue) { // async has a value.
142     async = fir::getBase(converter.genExprValue(
143         *Fortran::semantics::GetExpr(*asyncClauseValue), stmtCtx));
144   } else {
145     addAsyncAttr = true;
146   }
147 }
148 
149 static void genDeviceTypeClause(
150     Fortran::lower::AbstractConverter &converter,
151     const Fortran::parser::AccClause::DeviceType *deviceTypeClause,
152     llvm::SmallVectorImpl<mlir::Value> &operands,
153     Fortran::lower::StatementContext &stmtCtx) {
154   const auto &deviceTypeValue = deviceTypeClause->v;
155   if (deviceTypeValue) {
156     for (const auto &scalarIntExpr : *deviceTypeValue) {
157       mlir::Value expr = fir::getBase(converter.genExprValue(
158           *Fortran::semantics::GetExpr(scalarIntExpr), stmtCtx));
159       operands.push_back(expr);
160     }
161   } else {
162     fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
163     // * was passed as value and will be represented as a special constant.
164     mlir::Value star = firOpBuilder.createIntegerConstant(
165         converter.getCurrentLocation(), firOpBuilder.getIndexType(), starCst);
166     operands.push_back(star);
167   }
168 }
169 
170 static void genIfClause(Fortran::lower::AbstractConverter &converter,
171                         const Fortran::parser::AccClause::If *ifClause,
172                         mlir::Value &ifCond,
173                         Fortran::lower::StatementContext &stmtCtx) {
174   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
175   mlir::Value cond = fir::getBase(converter.genExprValue(
176       *Fortran::semantics::GetExpr(ifClause->v), stmtCtx));
177   ifCond = firOpBuilder.createConvert(converter.getCurrentLocation(),
178                                       firOpBuilder.getI1Type(), cond);
179 }
180 
181 static void genWaitClause(Fortran::lower::AbstractConverter &converter,
182                           const Fortran::parser::AccClause::Wait *waitClause,
183                           llvm::SmallVectorImpl<mlir::Value> &operands,
184                           mlir::Value &waitDevnum, bool &addWaitAttr,
185                           Fortran::lower::StatementContext &stmtCtx) {
186   const auto &waitClauseValue = waitClause->v;
187   if (waitClauseValue) { // wait has a value.
188     const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue;
189     const auto &waitList =
190         std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
191     for (const Fortran::parser::ScalarIntExpr &value : waitList) {
192       mlir::Value v = fir::getBase(
193           converter.genExprValue(*Fortran::semantics::GetExpr(value), stmtCtx));
194       operands.push_back(v);
195     }
196 
197     const auto &waitDevnumValue =
198         std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t);
199     if (waitDevnumValue)
200       waitDevnum = fir::getBase(converter.genExprValue(
201           *Fortran::semantics::GetExpr(*waitDevnumValue), stmtCtx));
202   } else {
203     addWaitAttr = true;
204   }
205 }
206 
207 static mlir::acc::LoopOp
208 createLoopOp(Fortran::lower::AbstractConverter &converter,
209              const Fortran::parser::AccClauseList &accClauseList) {
210   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
211   mlir::Location currentLocation = converter.getCurrentLocation();
212   Fortran::lower::StatementContext stmtCtx;
213 
214   mlir::Value workerNum;
215   mlir::Value vectorNum;
216   mlir::Value gangNum;
217   mlir::Value gangStatic;
218   llvm::SmallVector<mlir::Value, 2> tileOperands, privateOperands,
219       reductionOperands;
220   std::int64_t executionMapping = mlir::acc::OpenACCExecMapping::NONE;
221 
222   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
223     if (const auto *gangClause =
224             std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
225       if (gangClause->v) {
226         const Fortran::parser::AccGangArgument &x = *gangClause->v;
227         if (const auto &gangNumValue =
228                 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(x.t)) {
229           gangNum = fir::getBase(converter.genExprValue(
230               *Fortran::semantics::GetExpr(gangNumValue.value()), stmtCtx));
231         }
232         if (const auto &gangStaticValue =
233                 std::get<std::optional<Fortran::parser::AccSizeExpr>>(x.t)) {
234           const auto &expr =
235               std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
236                   gangStaticValue.value().t);
237           if (expr) {
238             gangStatic = fir::getBase(converter.genExprValue(
239                 *Fortran::semantics::GetExpr(*expr), stmtCtx));
240           } else {
241             // * was passed as value and will be represented as a special
242             // constant.
243             gangStatic = firOpBuilder.createIntegerConstant(
244                 currentLocation, firOpBuilder.getIndexType(), starCst);
245           }
246         }
247       }
248       executionMapping |= mlir::acc::OpenACCExecMapping::GANG;
249     } else if (const auto *workerClause =
250                    std::get_if<Fortran::parser::AccClause::Worker>(&clause.u)) {
251       if (workerClause->v) {
252         workerNum = fir::getBase(converter.genExprValue(
253             *Fortran::semantics::GetExpr(*workerClause->v), stmtCtx));
254       }
255       executionMapping |= mlir::acc::OpenACCExecMapping::WORKER;
256     } else if (const auto *vectorClause =
257                    std::get_if<Fortran::parser::AccClause::Vector>(&clause.u)) {
258       if (vectorClause->v) {
259         vectorNum = fir::getBase(converter.genExprValue(
260             *Fortran::semantics::GetExpr(*vectorClause->v), stmtCtx));
261       }
262       executionMapping |= mlir::acc::OpenACCExecMapping::VECTOR;
263     } else if (const auto *tileClause =
264                    std::get_if<Fortran::parser::AccClause::Tile>(&clause.u)) {
265       const Fortran::parser::AccTileExprList &accTileExprList = tileClause->v;
266       for (const auto &accTileExpr : accTileExprList.v) {
267         const auto &expr =
268             std::get<std::optional<Fortran::parser::ScalarIntConstantExpr>>(
269                 accTileExpr.t);
270         if (expr) {
271           tileOperands.push_back(fir::getBase(converter.genExprValue(
272               *Fortran::semantics::GetExpr(*expr), stmtCtx)));
273         } else {
274           // * was passed as value and will be represented as a -1 constant
275           // integer.
276           mlir::Value tileStar = firOpBuilder.createIntegerConstant(
277               currentLocation, firOpBuilder.getIntegerType(32),
278               /* STAR */ -1);
279           tileOperands.push_back(tileStar);
280         }
281       }
282     } else if (const auto *privateClause =
283                    std::get_if<Fortran::parser::AccClause::Private>(
284                        &clause.u)) {
285       genObjectList(privateClause->v, converter, privateOperands);
286     }
287     // Reduction clause is left out for the moment as the clause will probably
288     // end up having its own operation.
289   }
290 
291   // Prepare the operand segement size attribute and the operands value range.
292   llvm::SmallVector<mlir::Value> operands;
293   llvm::SmallVector<int32_t> operandSegments;
294   addOperand(operands, operandSegments, gangNum);
295   addOperand(operands, operandSegments, gangStatic);
296   addOperand(operands, operandSegments, workerNum);
297   addOperand(operands, operandSegments, vectorNum);
298   addOperands(operands, operandSegments, tileOperands);
299   addOperands(operands, operandSegments, privateOperands);
300   addOperands(operands, operandSegments, reductionOperands);
301 
302   auto loopOp = createRegionOp<mlir::acc::LoopOp, mlir::acc::YieldOp>(
303       firOpBuilder, currentLocation, operands, operandSegments);
304 
305   loopOp->setAttr(mlir::acc::LoopOp::getExecutionMappingAttrName(),
306                   firOpBuilder.getI64IntegerAttr(executionMapping));
307 
308   // Lower clauses mapped to attributes
309   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
310     if (const auto *collapseClause =
311             std::get_if<Fortran::parser::AccClause::Collapse>(&clause.u)) {
312       const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
313       const std::optional<int64_t> collapseValue =
314           Fortran::evaluate::ToInt64(*expr);
315       if (collapseValue) {
316         loopOp->setAttr(mlir::acc::LoopOp::getCollapseAttrName(),
317                         firOpBuilder.getI64IntegerAttr(*collapseValue));
318       }
319     } else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
320       loopOp->setAttr(mlir::acc::LoopOp::getSeqAttrName(),
321                       firOpBuilder.getUnitAttr());
322     } else if (std::get_if<Fortran::parser::AccClause::Independent>(
323                    &clause.u)) {
324       loopOp->setAttr(mlir::acc::LoopOp::getIndependentAttrName(),
325                       firOpBuilder.getUnitAttr());
326     } else if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) {
327       loopOp->setAttr(mlir::acc::LoopOp::getAutoAttrName(),
328                       firOpBuilder.getUnitAttr());
329     }
330   }
331   return loopOp;
332 }
333 
334 static void genACC(Fortran::lower::AbstractConverter &converter,
335                    Fortran::lower::pft::Evaluation &eval,
336                    const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
337 
338   const auto &beginLoopDirective =
339       std::get<Fortran::parser::AccBeginLoopDirective>(loopConstruct.t);
340   const auto &loopDirective =
341       std::get<Fortran::parser::AccLoopDirective>(beginLoopDirective.t);
342 
343   if (loopDirective.v == llvm::acc::ACCD_loop) {
344     const auto &accClauseList =
345         std::get<Fortran::parser::AccClauseList>(beginLoopDirective.t);
346     createLoopOp(converter, accClauseList);
347   }
348 }
349 
350 static mlir::acc::ParallelOp
351 createParallelOp(Fortran::lower::AbstractConverter &converter,
352                  const Fortran::parser::AccClauseList &accClauseList) {
353 
354   // Parallel operation operands
355   mlir::Value async;
356   mlir::Value numGangs;
357   mlir::Value numWorkers;
358   mlir::Value vectorLength;
359   mlir::Value ifCond;
360   mlir::Value selfCond;
361   mlir::Value waitDevnum;
362   llvm::SmallVector<mlir::Value, 2> waitOperands, reductionOperands,
363       copyOperands, copyinOperands, copyinReadonlyOperands, copyoutOperands,
364       copyoutZeroOperands, createOperands, createZeroOperands, noCreateOperands,
365       presentOperands, devicePtrOperands, attachOperands, firstprivateOperands,
366       privateOperands;
367 
368   // Async, wait and self clause have optional values but can be present with
369   // no value as well. When there is no value, the op has an attribute to
370   // represent the clause.
371   bool addAsyncAttr = false;
372   bool addWaitAttr = false;
373   bool addSelfAttr = false;
374 
375   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
376   mlir::Location currentLocation = converter.getCurrentLocation();
377   Fortran::lower::StatementContext stmtCtx;
378 
379   // Lower clauses values mapped to operands.
380   // Keep track of each group of operands separatly as clauses can appear
381   // more than once.
382   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
383     if (const auto *asyncClause =
384             std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
385       genAsyncClause(converter, asyncClause, async, addAsyncAttr, stmtCtx);
386     } else if (const auto *waitClause =
387                    std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
388       genWaitClause(converter, waitClause, waitOperands, waitDevnum,
389                     addWaitAttr, stmtCtx);
390     } else if (const auto *numGangsClause =
391                    std::get_if<Fortran::parser::AccClause::NumGangs>(
392                        &clause.u)) {
393       numGangs = fir::getBase(converter.genExprValue(
394           *Fortran::semantics::GetExpr(numGangsClause->v), stmtCtx));
395     } else if (const auto *numWorkersClause =
396                    std::get_if<Fortran::parser::AccClause::NumWorkers>(
397                        &clause.u)) {
398       numWorkers = fir::getBase(converter.genExprValue(
399           *Fortran::semantics::GetExpr(numWorkersClause->v), stmtCtx));
400     } else if (const auto *vectorLengthClause =
401                    std::get_if<Fortran::parser::AccClause::VectorLength>(
402                        &clause.u)) {
403       vectorLength = fir::getBase(converter.genExprValue(
404           *Fortran::semantics::GetExpr(vectorLengthClause->v), stmtCtx));
405     } else if (const auto *ifClause =
406                    std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
407       genIfClause(converter, ifClause, ifCond, stmtCtx);
408     } else if (const auto *selfClause =
409                    std::get_if<Fortran::parser::AccClause::Self>(&clause.u)) {
410       const Fortran::parser::AccSelfClause &accSelfClause = selfClause->v;
411       if (const auto *optCondition =
412               std::get_if<std::optional<Fortran::parser::ScalarLogicalExpr>>(
413                   &accSelfClause.u)) {
414         if (*optCondition) {
415           mlir::Value cond = fir::getBase(converter.genExprValue(
416               *Fortran::semantics::GetExpr(*optCondition), stmtCtx));
417           selfCond = firOpBuilder.createConvert(currentLocation,
418                                                 firOpBuilder.getI1Type(), cond);
419         } else {
420           addSelfAttr = true;
421         }
422       } else if (const auto *accClauseList =
423                      std::get_if<Fortran::parser::AccObjectList>(
424                          &accSelfClause.u)) {
425         // TODO This would be nicer to be done in canonicalization step.
426         if (accClauseList->v.size() == 1) {
427           const auto &accObject = accClauseList->v.front();
428           if (const auto *designator =
429                   std::get_if<Fortran::parser::Designator>(&accObject.u)) {
430             if (const auto *name = getDesignatorNameIfDataRef(*designator)) {
431               auto cond = converter.getSymbolAddress(*name->symbol);
432               selfCond = firOpBuilder.createConvert(
433                   currentLocation, firOpBuilder.getI1Type(), cond);
434             }
435           }
436         }
437       }
438     } else if (const auto *copyClause =
439                    std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
440       genObjectList(copyClause->v, converter, copyOperands);
441     } else if (const auto *copyinClause =
442                    std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
443       genObjectListWithModifier<Fortran::parser::AccClause::Copyin>(
444           copyinClause, converter,
445           Fortran::parser::AccDataModifier::Modifier::ReadOnly,
446           copyinReadonlyOperands, copyinOperands);
447     } else if (const auto *copyoutClause =
448                    std::get_if<Fortran::parser::AccClause::Copyout>(
449                        &clause.u)) {
450       genObjectListWithModifier<Fortran::parser::AccClause::Copyout>(
451           copyoutClause, converter,
452           Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands,
453           copyoutOperands);
454     } else if (const auto *createClause =
455                    std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
456       genObjectListWithModifier<Fortran::parser::AccClause::Create>(
457           createClause, converter,
458           Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
459           createOperands);
460     } else if (const auto *noCreateClause =
461                    std::get_if<Fortran::parser::AccClause::NoCreate>(
462                        &clause.u)) {
463       genObjectList(noCreateClause->v, converter, noCreateOperands);
464     } else if (const auto *presentClause =
465                    std::get_if<Fortran::parser::AccClause::Present>(
466                        &clause.u)) {
467       genObjectList(presentClause->v, converter, presentOperands);
468     } else if (const auto *devicePtrClause =
469                    std::get_if<Fortran::parser::AccClause::Deviceptr>(
470                        &clause.u)) {
471       genObjectList(devicePtrClause->v, converter, devicePtrOperands);
472     } else if (const auto *attachClause =
473                    std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
474       genObjectList(attachClause->v, converter, attachOperands);
475     } else if (const auto *privateClause =
476                    std::get_if<Fortran::parser::AccClause::Private>(
477                        &clause.u)) {
478       genObjectList(privateClause->v, converter, privateOperands);
479     } else if (const auto *firstprivateClause =
480                    std::get_if<Fortran::parser::AccClause::Firstprivate>(
481                        &clause.u)) {
482       genObjectList(firstprivateClause->v, converter, firstprivateOperands);
483     }
484   }
485 
486   // Prepare the operand segement size attribute and the operands value range.
487   llvm::SmallVector<mlir::Value, 8> operands;
488   llvm::SmallVector<int32_t, 8> operandSegments;
489   addOperand(operands, operandSegments, async);
490   addOperands(operands, operandSegments, waitOperands);
491   addOperand(operands, operandSegments, numGangs);
492   addOperand(operands, operandSegments, numWorkers);
493   addOperand(operands, operandSegments, vectorLength);
494   addOperand(operands, operandSegments, ifCond);
495   addOperand(operands, operandSegments, selfCond);
496   addOperands(operands, operandSegments, reductionOperands);
497   addOperands(operands, operandSegments, copyOperands);
498   addOperands(operands, operandSegments, copyinOperands);
499   addOperands(operands, operandSegments, copyinReadonlyOperands);
500   addOperands(operands, operandSegments, copyoutOperands);
501   addOperands(operands, operandSegments, copyoutZeroOperands);
502   addOperands(operands, operandSegments, createOperands);
503   addOperands(operands, operandSegments, createZeroOperands);
504   addOperands(operands, operandSegments, noCreateOperands);
505   addOperands(operands, operandSegments, presentOperands);
506   addOperands(operands, operandSegments, devicePtrOperands);
507   addOperands(operands, operandSegments, attachOperands);
508   addOperands(operands, operandSegments, privateOperands);
509   addOperands(operands, operandSegments, firstprivateOperands);
510 
511   mlir::acc::ParallelOp parallelOp =
512       createRegionOp<mlir::acc::ParallelOp, mlir::acc::YieldOp>(
513           firOpBuilder, currentLocation, operands, operandSegments);
514 
515   if (addAsyncAttr)
516     parallelOp->setAttr(mlir::acc::ParallelOp::getAsyncAttrName(),
517                         firOpBuilder.getUnitAttr());
518   if (addWaitAttr)
519     parallelOp->setAttr(mlir::acc::ParallelOp::getWaitAttrName(),
520                         firOpBuilder.getUnitAttr());
521   if (addSelfAttr)
522     parallelOp->setAttr(mlir::acc::ParallelOp::getSelfAttrName(),
523                         firOpBuilder.getUnitAttr());
524 
525   return parallelOp;
526 }
527 
528 static void
529 genACCParallelOp(Fortran::lower::AbstractConverter &converter,
530                  const Fortran::parser::AccClauseList &accClauseList) {
531   createParallelOp(converter, accClauseList);
532 }
533 
534 static void genACCDataOp(Fortran::lower::AbstractConverter &converter,
535                          const Fortran::parser::AccClauseList &accClauseList) {
536   mlir::Value ifCond;
537   llvm::SmallVector<mlir::Value> copyOperands, copyinOperands,
538       copyinReadonlyOperands, copyoutOperands, copyoutZeroOperands,
539       createOperands, createZeroOperands, noCreateOperands, presentOperands,
540       deviceptrOperands, attachOperands;
541 
542   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
543   mlir::Location currentLocation = converter.getCurrentLocation();
544   Fortran::lower::StatementContext stmtCtx;
545 
546   // Lower clauses values mapped to operands.
547   // Keep track of each group of operands separatly as clauses can appear
548   // more than once.
549   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
550     if (const auto *ifClause =
551             std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
552       genIfClause(converter, ifClause, ifCond, stmtCtx);
553     } else if (const auto *copyClause =
554                    std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
555       genObjectList(copyClause->v, converter, copyOperands);
556     } else if (const auto *copyinClause =
557                    std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
558       genObjectListWithModifier<Fortran::parser::AccClause::Copyin>(
559           copyinClause, converter,
560           Fortran::parser::AccDataModifier::Modifier::ReadOnly,
561           copyinReadonlyOperands, copyinOperands);
562     } else if (const auto *copyoutClause =
563                    std::get_if<Fortran::parser::AccClause::Copyout>(
564                        &clause.u)) {
565       genObjectListWithModifier<Fortran::parser::AccClause::Copyout>(
566           copyoutClause, converter,
567           Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands,
568           copyoutOperands);
569     } else if (const auto *createClause =
570                    std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
571       genObjectListWithModifier<Fortran::parser::AccClause::Create>(
572           createClause, converter,
573           Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
574           createOperands);
575     } else if (const auto *noCreateClause =
576                    std::get_if<Fortran::parser::AccClause::NoCreate>(
577                        &clause.u)) {
578       genObjectList(noCreateClause->v, converter, noCreateOperands);
579     } else if (const auto *presentClause =
580                    std::get_if<Fortran::parser::AccClause::Present>(
581                        &clause.u)) {
582       genObjectList(presentClause->v, converter, presentOperands);
583     } else if (const auto *deviceptrClause =
584                    std::get_if<Fortran::parser::AccClause::Deviceptr>(
585                        &clause.u)) {
586       genObjectList(deviceptrClause->v, converter, deviceptrOperands);
587     } else if (const auto *attachClause =
588                    std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
589       genObjectList(attachClause->v, converter, attachOperands);
590     }
591   }
592 
593   // Prepare the operand segement size attribute and the operands value range.
594   llvm::SmallVector<mlir::Value> operands;
595   llvm::SmallVector<int32_t> operandSegments;
596   addOperand(operands, operandSegments, ifCond);
597   addOperands(operands, operandSegments, copyOperands);
598   addOperands(operands, operandSegments, copyinOperands);
599   addOperands(operands, operandSegments, copyinReadonlyOperands);
600   addOperands(operands, operandSegments, copyoutOperands);
601   addOperands(operands, operandSegments, copyoutZeroOperands);
602   addOperands(operands, operandSegments, createOperands);
603   addOperands(operands, operandSegments, createZeroOperands);
604   addOperands(operands, operandSegments, noCreateOperands);
605   addOperands(operands, operandSegments, presentOperands);
606   addOperands(operands, operandSegments, deviceptrOperands);
607   addOperands(operands, operandSegments, attachOperands);
608 
609   createRegionOp<mlir::acc::DataOp, mlir::acc::TerminatorOp>(
610       firOpBuilder, currentLocation, operands, operandSegments);
611 }
612 
613 static void
614 genACC(Fortran::lower::AbstractConverter &converter,
615        Fortran::lower::pft::Evaluation &eval,
616        const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
617   const auto &beginBlockDirective =
618       std::get<Fortran::parser::AccBeginBlockDirective>(blockConstruct.t);
619   const auto &blockDirective =
620       std::get<Fortran::parser::AccBlockDirective>(beginBlockDirective.t);
621   const auto &accClauseList =
622       std::get<Fortran::parser::AccClauseList>(beginBlockDirective.t);
623 
624   if (blockDirective.v == llvm::acc::ACCD_parallel) {
625     genACCParallelOp(converter, accClauseList);
626   } else if (blockDirective.v == llvm::acc::ACCD_data) {
627     genACCDataOp(converter, accClauseList);
628   }
629 }
630 
631 static void
632 genACCParallelLoopOps(Fortran::lower::AbstractConverter &converter,
633                       const Fortran::parser::AccClauseList &accClauseList) {
634   createParallelOp(converter, accClauseList);
635   createLoopOp(converter, accClauseList);
636 }
637 
638 static void
639 genACC(Fortran::lower::AbstractConverter &converter,
640        Fortran::lower::pft::Evaluation &eval,
641        const Fortran::parser::OpenACCCombinedConstruct &combinedConstruct) {
642   const auto &beginCombinedDirective =
643       std::get<Fortran::parser::AccBeginCombinedDirective>(combinedConstruct.t);
644   const auto &combinedDirective =
645       std::get<Fortran::parser::AccCombinedDirective>(beginCombinedDirective.t);
646   const auto &accClauseList =
647       std::get<Fortran::parser::AccClauseList>(beginCombinedDirective.t);
648 
649   if (combinedDirective.v == llvm::acc::ACCD_kernels_loop) {
650     TODO(converter.getCurrentLocation(),
651          "OpenACC Kernels Loop construct not lowered yet!");
652   } else if (combinedDirective.v == llvm::acc::ACCD_parallel_loop) {
653     genACCParallelLoopOps(converter, accClauseList);
654   } else if (combinedDirective.v == llvm::acc::ACCD_serial_loop) {
655     TODO(converter.getCurrentLocation(),
656          "OpenACC Serial Loop construct not lowered yet!");
657   } else {
658     llvm::report_fatal_error("Unknown combined construct encountered");
659   }
660 }
661 
662 static void
663 genACCEnterDataOp(Fortran::lower::AbstractConverter &converter,
664                   const Fortran::parser::AccClauseList &accClauseList) {
665   mlir::Value ifCond, async, waitDevnum;
666   llvm::SmallVector<mlir::Value> copyinOperands, createOperands,
667       createZeroOperands, attachOperands, waitOperands;
668 
669   // Async, wait and self clause have optional values but can be present with
670   // no value as well. When there is no value, the op has an attribute to
671   // represent the clause.
672   bool addAsyncAttr = false;
673   bool addWaitAttr = false;
674 
675   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
676   mlir::Location currentLocation = converter.getCurrentLocation();
677   Fortran::lower::StatementContext stmtCtx;
678 
679   // Lower clauses values mapped to operands.
680   // Keep track of each group of operands separatly as clauses can appear
681   // more than once.
682   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
683     if (const auto *ifClause =
684             std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
685       genIfClause(converter, ifClause, ifCond, stmtCtx);
686     } else if (const auto *asyncClause =
687                    std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
688       genAsyncClause(converter, asyncClause, async, addAsyncAttr, stmtCtx);
689     } else if (const auto *waitClause =
690                    std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
691       genWaitClause(converter, waitClause, waitOperands, waitDevnum,
692                     addWaitAttr, stmtCtx);
693     } else if (const auto *copyinClause =
694                    std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
695       const Fortran::parser::AccObjectListWithModifier &listWithModifier =
696           copyinClause->v;
697       const auto &accObjectList =
698           std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
699       genObjectList(accObjectList, converter, copyinOperands);
700     } else if (const auto *createClause =
701                    std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
702       genObjectListWithModifier<Fortran::parser::AccClause::Create>(
703           createClause, converter,
704           Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
705           createOperands);
706     } else if (const auto *attachClause =
707                    std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
708       genObjectList(attachClause->v, converter, attachOperands);
709     } else {
710       llvm::report_fatal_error(
711           "Unknown clause in ENTER DATA directive lowering");
712     }
713   }
714 
715   // Prepare the operand segement size attribute and the operands value range.
716   llvm::SmallVector<mlir::Value, 16> operands;
717   llvm::SmallVector<int32_t, 8> operandSegments;
718   addOperand(operands, operandSegments, ifCond);
719   addOperand(operands, operandSegments, async);
720   addOperand(operands, operandSegments, waitDevnum);
721   addOperands(operands, operandSegments, waitOperands);
722   addOperands(operands, operandSegments, copyinOperands);
723   addOperands(operands, operandSegments, createOperands);
724   addOperands(operands, operandSegments, createZeroOperands);
725   addOperands(operands, operandSegments, attachOperands);
726 
727   mlir::acc::EnterDataOp enterDataOp = createSimpleOp<mlir::acc::EnterDataOp>(
728       firOpBuilder, currentLocation, operands, operandSegments);
729 
730   if (addAsyncAttr)
731     enterDataOp.asyncAttr(firOpBuilder.getUnitAttr());
732   if (addWaitAttr)
733     enterDataOp.waitAttr(firOpBuilder.getUnitAttr());
734 }
735 
736 static void
737 genACCExitDataOp(Fortran::lower::AbstractConverter &converter,
738                  const Fortran::parser::AccClauseList &accClauseList) {
739   mlir::Value ifCond, async, waitDevnum;
740   llvm::SmallVector<mlir::Value> copyoutOperands, deleteOperands,
741       detachOperands, waitOperands;
742 
743   // Async and wait clause have optional values but can be present with
744   // no value as well. When there is no value, the op has an attribute to
745   // represent the clause.
746   bool addAsyncAttr = false;
747   bool addWaitAttr = false;
748   bool addFinalizeAttr = false;
749 
750   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
751   mlir::Location currentLocation = converter.getCurrentLocation();
752   Fortran::lower::StatementContext stmtCtx;
753 
754   // Lower clauses values mapped to operands.
755   // Keep track of each group of operands separatly as clauses can appear
756   // more than once.
757   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
758     if (const auto *ifClause =
759             std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
760       genIfClause(converter, ifClause, ifCond, stmtCtx);
761     } else if (const auto *asyncClause =
762                    std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
763       genAsyncClause(converter, asyncClause, async, addAsyncAttr, stmtCtx);
764     } else if (const auto *waitClause =
765                    std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
766       genWaitClause(converter, waitClause, waitOperands, waitDevnum,
767                     addWaitAttr, stmtCtx);
768     } else if (const auto *copyoutClause =
769                    std::get_if<Fortran::parser::AccClause::Copyout>(
770                        &clause.u)) {
771       const Fortran::parser::AccObjectListWithModifier &listWithModifier =
772           copyoutClause->v;
773       const auto &accObjectList =
774           std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
775       genObjectList(accObjectList, converter, copyoutOperands);
776     } else if (const auto *deleteClause =
777                    std::get_if<Fortran::parser::AccClause::Delete>(&clause.u)) {
778       genObjectList(deleteClause->v, converter, deleteOperands);
779     } else if (const auto *detachClause =
780                    std::get_if<Fortran::parser::AccClause::Detach>(&clause.u)) {
781       genObjectList(detachClause->v, converter, detachOperands);
782     } else if (std::get_if<Fortran::parser::AccClause::Finalize>(&clause.u)) {
783       addFinalizeAttr = true;
784     }
785   }
786 
787   // Prepare the operand segement size attribute and the operands value range.
788   llvm::SmallVector<mlir::Value, 14> operands;
789   llvm::SmallVector<int32_t, 7> operandSegments;
790   addOperand(operands, operandSegments, ifCond);
791   addOperand(operands, operandSegments, async);
792   addOperand(operands, operandSegments, waitDevnum);
793   addOperands(operands, operandSegments, waitOperands);
794   addOperands(operands, operandSegments, copyoutOperands);
795   addOperands(operands, operandSegments, deleteOperands);
796   addOperands(operands, operandSegments, detachOperands);
797 
798   mlir::acc::ExitDataOp exitDataOp = createSimpleOp<mlir::acc::ExitDataOp>(
799       firOpBuilder, currentLocation, operands, operandSegments);
800 
801   if (addAsyncAttr)
802     exitDataOp.asyncAttr(firOpBuilder.getUnitAttr());
803   if (addWaitAttr)
804     exitDataOp.waitAttr(firOpBuilder.getUnitAttr());
805   if (addFinalizeAttr)
806     exitDataOp.finalizeAttr(firOpBuilder.getUnitAttr());
807 }
808 
809 template <typename Op>
810 static void
811 genACCInitShutdownOp(Fortran::lower::AbstractConverter &converter,
812                      const Fortran::parser::AccClauseList &accClauseList) {
813   mlir::Value ifCond, deviceNum;
814   llvm::SmallVector<mlir::Value> deviceTypeOperands;
815 
816   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
817   mlir::Location currentLocation = converter.getCurrentLocation();
818   Fortran::lower::StatementContext stmtCtx;
819 
820   // Lower clauses values mapped to operands.
821   // Keep track of each group of operands separatly as clauses can appear
822   // more than once.
823   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
824     if (const auto *ifClause =
825             std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
826       genIfClause(converter, ifClause, ifCond, stmtCtx);
827     } else if (const auto *deviceNumClause =
828                    std::get_if<Fortran::parser::AccClause::DeviceNum>(
829                        &clause.u)) {
830       deviceNum = fir::getBase(converter.genExprValue(
831           *Fortran::semantics::GetExpr(deviceNumClause->v), stmtCtx));
832     } else if (const auto *deviceTypeClause =
833                    std::get_if<Fortran::parser::AccClause::DeviceType>(
834                        &clause.u)) {
835       genDeviceTypeClause(converter, deviceTypeClause, deviceTypeOperands,
836                           stmtCtx);
837     }
838   }
839 
840   // Prepare the operand segement size attribute and the operands value range.
841   llvm::SmallVector<mlir::Value, 6> operands;
842   llvm::SmallVector<int32_t, 3> operandSegments;
843   addOperands(operands, operandSegments, deviceTypeOperands);
844   addOperand(operands, operandSegments, deviceNum);
845   addOperand(operands, operandSegments, ifCond);
846 
847   createSimpleOp<Op>(firOpBuilder, currentLocation, operands, operandSegments);
848 }
849 
850 static void
851 genACCUpdateOp(Fortran::lower::AbstractConverter &converter,
852                const Fortran::parser::AccClauseList &accClauseList) {
853   mlir::Value ifCond, async, waitDevnum;
854   llvm::SmallVector<mlir::Value> hostOperands, deviceOperands, waitOperands,
855       deviceTypeOperands;
856 
857   // Async and wait clause have optional values but can be present with
858   // no value as well. When there is no value, the op has an attribute to
859   // represent the clause.
860   bool addAsyncAttr = false;
861   bool addWaitAttr = false;
862   bool addIfPresentAttr = false;
863 
864   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
865   mlir::Location currentLocation = converter.getCurrentLocation();
866   Fortran::lower::StatementContext stmtCtx;
867 
868   // Lower clauses values mapped to operands.
869   // Keep track of each group of operands separatly as clauses can appear
870   // more than once.
871   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
872     if (const auto *ifClause =
873             std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
874       genIfClause(converter, ifClause, ifCond, stmtCtx);
875     } else if (const auto *asyncClause =
876                    std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
877       genAsyncClause(converter, asyncClause, async, addAsyncAttr, stmtCtx);
878     } else if (const auto *waitClause =
879                    std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
880       genWaitClause(converter, waitClause, waitOperands, waitDevnum,
881                     addWaitAttr, stmtCtx);
882     } else if (const auto *deviceTypeClause =
883                    std::get_if<Fortran::parser::AccClause::DeviceType>(
884                        &clause.u)) {
885       genDeviceTypeClause(converter, deviceTypeClause, deviceTypeOperands,
886                           stmtCtx);
887     } else if (const auto *hostClause =
888                    std::get_if<Fortran::parser::AccClause::Host>(&clause.u)) {
889       genObjectList(hostClause->v, converter, hostOperands);
890     } else if (const auto *deviceClause =
891                    std::get_if<Fortran::parser::AccClause::Device>(&clause.u)) {
892       genObjectList(deviceClause->v, converter, deviceOperands);
893     }
894   }
895 
896   // Prepare the operand segement size attribute and the operands value range.
897   llvm::SmallVector<mlir::Value> operands;
898   llvm::SmallVector<int32_t> operandSegments;
899   addOperand(operands, operandSegments, ifCond);
900   addOperand(operands, operandSegments, async);
901   addOperand(operands, operandSegments, waitDevnum);
902   addOperands(operands, operandSegments, waitOperands);
903   addOperands(operands, operandSegments, deviceTypeOperands);
904   addOperands(operands, operandSegments, hostOperands);
905   addOperands(operands, operandSegments, deviceOperands);
906 
907   mlir::acc::UpdateOp updateOp = createSimpleOp<mlir::acc::UpdateOp>(
908       firOpBuilder, currentLocation, operands, operandSegments);
909 
910   if (addAsyncAttr)
911     updateOp.asyncAttr(firOpBuilder.getUnitAttr());
912   if (addWaitAttr)
913     updateOp.waitAttr(firOpBuilder.getUnitAttr());
914   if (addIfPresentAttr)
915     updateOp.ifPresentAttr(firOpBuilder.getUnitAttr());
916 }
917 
918 static void
919 genACC(Fortran::lower::AbstractConverter &converter,
920        Fortran::lower::pft::Evaluation &eval,
921        const Fortran::parser::OpenACCStandaloneConstruct &standaloneConstruct) {
922   const auto &standaloneDirective =
923       std::get<Fortran::parser::AccStandaloneDirective>(standaloneConstruct.t);
924   const auto &accClauseList =
925       std::get<Fortran::parser::AccClauseList>(standaloneConstruct.t);
926 
927   if (standaloneDirective.v == llvm::acc::Directive::ACCD_enter_data) {
928     genACCEnterDataOp(converter, accClauseList);
929   } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_exit_data) {
930     genACCExitDataOp(converter, accClauseList);
931   } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_init) {
932     genACCInitShutdownOp<mlir::acc::InitOp>(converter, accClauseList);
933   } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_shutdown) {
934     genACCInitShutdownOp<mlir::acc::ShutdownOp>(converter, accClauseList);
935   } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_set) {
936     TODO(converter.getCurrentLocation(),
937          "OpenACC set directive not lowered yet!");
938   } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_update) {
939     genACCUpdateOp(converter, accClauseList);
940   }
941 }
942 
943 static void genACC(Fortran::lower::AbstractConverter &converter,
944                    Fortran::lower::pft::Evaluation &eval,
945                    const Fortran::parser::OpenACCWaitConstruct &waitConstruct) {
946 
947   const auto &waitArgument =
948       std::get<std::optional<Fortran::parser::AccWaitArgument>>(
949           waitConstruct.t);
950   const auto &accClauseList =
951       std::get<Fortran::parser::AccClauseList>(waitConstruct.t);
952 
953   mlir::Value ifCond, waitDevnum, async;
954   llvm::SmallVector<mlir::Value> waitOperands;
955 
956   // Async clause have optional values but can be present with
957   // no value as well. When there is no value, the op has an attribute to
958   // represent the clause.
959   bool addAsyncAttr = false;
960 
961   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
962   mlir::Location currentLocation = converter.getCurrentLocation();
963   Fortran::lower::StatementContext stmtCtx;
964 
965   if (waitArgument) { // wait has a value.
966     const Fortran::parser::AccWaitArgument &waitArg = *waitArgument;
967     const auto &waitList =
968         std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
969     for (const Fortran::parser::ScalarIntExpr &value : waitList) {
970       mlir::Value v = fir::getBase(
971           converter.genExprValue(*Fortran::semantics::GetExpr(value), stmtCtx));
972       waitOperands.push_back(v);
973     }
974 
975     const auto &waitDevnumValue =
976         std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t);
977     if (waitDevnumValue)
978       waitDevnum = fir::getBase(converter.genExprValue(
979           *Fortran::semantics::GetExpr(*waitDevnumValue), stmtCtx));
980   }
981 
982   // Lower clauses values mapped to operands.
983   // Keep track of each group of operands separatly as clauses can appear
984   // more than once.
985   for (const Fortran::parser::AccClause &clause : accClauseList.v) {
986     if (const auto *ifClause =
987             std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
988       genIfClause(converter, ifClause, ifCond, stmtCtx);
989     } else if (const auto *asyncClause =
990                    std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
991       genAsyncClause(converter, asyncClause, async, addAsyncAttr, stmtCtx);
992     }
993   }
994 
995   // Prepare the operand segement size attribute and the operands value range.
996   llvm::SmallVector<mlir::Value> operands;
997   llvm::SmallVector<int32_t> operandSegments;
998   addOperands(operands, operandSegments, waitOperands);
999   addOperand(operands, operandSegments, async);
1000   addOperand(operands, operandSegments, waitDevnum);
1001   addOperand(operands, operandSegments, ifCond);
1002 
1003   mlir::acc::WaitOp waitOp = createSimpleOp<mlir::acc::WaitOp>(
1004       firOpBuilder, currentLocation, operands, operandSegments);
1005 
1006   if (addAsyncAttr)
1007     waitOp.asyncAttr(firOpBuilder.getUnitAttr());
1008 }
1009 
1010 void Fortran::lower::genOpenACCConstruct(
1011     Fortran::lower::AbstractConverter &converter,
1012     Fortran::lower::pft::Evaluation &eval,
1013     const Fortran::parser::OpenACCConstruct &accConstruct) {
1014 
1015   std::visit(
1016       common::visitors{
1017           [&](const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
1018             genACC(converter, eval, blockConstruct);
1019           },
1020           [&](const Fortran::parser::OpenACCCombinedConstruct
1021                   &combinedConstruct) {
1022             genACC(converter, eval, combinedConstruct);
1023           },
1024           [&](const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
1025             genACC(converter, eval, loopConstruct);
1026           },
1027           [&](const Fortran::parser::OpenACCStandaloneConstruct
1028                   &standaloneConstruct) {
1029             genACC(converter, eval, standaloneConstruct);
1030           },
1031           [&](const Fortran::parser::OpenACCCacheConstruct &cacheConstruct) {
1032             TODO(converter.getCurrentLocation(),
1033                  "OpenACC Cache construct not lowered yet!");
1034           },
1035           [&](const Fortran::parser::OpenACCWaitConstruct &waitConstruct) {
1036             genACC(converter, eval, waitConstruct);
1037           },
1038           [&](const Fortran::parser::OpenACCAtomicConstruct &atomicConstruct) {
1039             TODO(converter.getCurrentLocation(),
1040                  "OpenACC Atomic construct not lowered yet!");
1041           },
1042       },
1043       accConstruct.u);
1044 }
1045 
1046 void Fortran::lower::genOpenACCDeclarativeConstruct(
1047     Fortran::lower::AbstractConverter &converter,
1048     Fortran::lower::pft::Evaluation &eval,
1049     const Fortran::parser::OpenACCDeclarativeConstruct &accDeclConstruct) {
1050 
1051   std::visit(
1052       common::visitors{
1053           [&](const Fortran::parser::OpenACCStandaloneDeclarativeConstruct
1054                   &standaloneDeclarativeConstruct) {
1055             TODO(converter.getCurrentLocation(),
1056                  "OpenACC Standalone Declarative construct not lowered yet!");
1057           },
1058           [&](const Fortran::parser::OpenACCRoutineConstruct
1059                   &routineConstruct) {
1060             TODO(converter.getCurrentLocation(),
1061                  "OpenACC Routine construct not lowered yet!");
1062           },
1063       },
1064       accDeclConstruct.u);
1065 }
1066