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