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/FIRBuilder.h"
17 #include "flang/Lower/PFTBuilder.h"
18 #include "flang/Lower/Support/BoxValue.h"
19 #include "flang/Lower/Todo.h"
20 #include "flang/Parser/parse-tree.h"
21 #include "flang/Semantics/tools.h"
22 #include "mlir/Dialect/OpenACC/OpenACC.h"
23 #include "llvm/Frontend/OpenACC/ACC.h.inc"
24 
25 static const Fortran::parser::Name *
26 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) {
27   const auto *dataRef{std::get_if<Fortran::parser::DataRef>(&designator.u)};
28   return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr;
29 }
30 
31 static void genObjectList(const Fortran::parser::AccObjectList &objectList,
32                           Fortran::lower::AbstractConverter &converter,
33                           SmallVectorImpl<Value> &operands) {
34   for (const auto &accObject : objectList.v) {
35     std::visit(
36         Fortran::common::visitors{
37             [&](const Fortran::parser::Designator &designator) {
38               if (const auto *name = getDesignatorNameIfDataRef(designator)) {
39                 const auto variable = converter.getSymbolAddress(*name->symbol);
40                 operands.push_back(variable);
41               }
42             },
43             [&](const Fortran::parser::Name &name) {
44               const auto variable = converter.getSymbolAddress(*name.symbol);
45               operands.push_back(variable);
46             }},
47         accObject.u);
48   }
49 }
50 
51 template <typename Clause>
52 static void
53 genObjectListWithModifier(const Clause *x,
54                           Fortran::lower::AbstractConverter &converter,
55                           Fortran::parser::AccDataModifier::Modifier mod,
56                           SmallVectorImpl<Value> &operandsWithModifier,
57                           SmallVectorImpl<Value> &operands) {
58   const Fortran::parser::AccObjectListWithModifier &listWithModifier = x->v;
59   const Fortran::parser::AccObjectList &accObjectList =
60       std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
61   const auto &modifier =
62       std::get<std::optional<Fortran::parser::AccDataModifier>>(
63           listWithModifier.t);
64   if (modifier && (*modifier).v == mod) {
65     genObjectList(accObjectList, converter, operandsWithModifier);
66   } else {
67     genObjectList(accObjectList, converter, operands);
68   }
69 }
70 
71 static void addOperands(SmallVectorImpl<Value> &operands,
72                         SmallVectorImpl<int32_t> &operandSegments,
73                         const SmallVectorImpl<Value> &clauseOperands) {
74   operands.append(clauseOperands.begin(), clauseOperands.end());
75   operandSegments.push_back(clauseOperands.size());
76 }
77 
78 static void addOperand(SmallVectorImpl<Value> &operands,
79                        SmallVectorImpl<int32_t> &operandSegments,
80                        const Value &clauseOperand) {
81   if (clauseOperand) {
82     operands.push_back(clauseOperand);
83     operandSegments.push_back(1);
84   } else {
85     operandSegments.push_back(0);
86   }
87 }
88 
89 template <typename Op, typename Terminator>
90 static Op createRegionOp(Fortran::lower::FirOpBuilder &builder,
91                          mlir::Location loc,
92                          const SmallVectorImpl<Value> &operands,
93                          const SmallVectorImpl<int32_t> &operandSegments) {
94   llvm::ArrayRef<mlir::Type> argTy;
95   Op op = builder.create<Op>(loc, argTy, operands);
96   builder.createBlock(&op.getRegion());
97   auto &block = op.getRegion().back();
98   builder.setInsertionPointToStart(&block);
99   builder.create<Terminator>(loc);
100 
101   op.setAttr(Op::getOperandSegmentSizeAttr(),
102              builder.getI32VectorAttr(operandSegments));
103 
104   // Place the insertion point to the start of the first block.
105   builder.setInsertionPointToStart(&block);
106 
107   return op;
108 }
109 
110 static void genACC(Fortran::lower::AbstractConverter &converter,
111                    Fortran::lower::pft::Evaluation &eval,
112                    const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
113 
114   const auto &beginLoopDirective =
115       std::get<Fortran::parser::AccBeginLoopDirective>(loopConstruct.t);
116   const auto &loopDirective =
117       std::get<Fortran::parser::AccLoopDirective>(beginLoopDirective.t);
118 
119   if (loopDirective.v == llvm::acc::ACCD_loop) {
120     auto &firOpBuilder = converter.getFirOpBuilder();
121     auto currentLocation = converter.getCurrentLocation();
122 
123     // Add attribute extracted from clauses.
124     const auto &accClauseList =
125         std::get<Fortran::parser::AccClauseList>(beginLoopDirective.t);
126 
127     mlir::Value workerNum;
128     mlir::Value vectorLength;
129     mlir::Value gangNum;
130     mlir::Value gangStatic;
131     SmallVector<Value, 2> tileOperands, privateOperands, reductionOperands;
132     std::int64_t executionMapping = mlir::acc::OpenACCExecMapping::NONE;
133 
134     // Lower clauses values mapped to operands.
135     for (const auto &clause : accClauseList.v) {
136       if (const auto *gangClause =
137               std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
138         if (gangClause->v) {
139           const Fortran::parser::AccGangArgument &x = *gangClause->v;
140           if (const auto &gangNumValue =
141                   std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
142                       x.t)) {
143             gangNum = fir::getBase(converter.genExprValue(
144                 *Fortran::semantics::GetExpr(gangNumValue.value())));
145           }
146           if (const auto &gangStaticValue =
147                   std::get<std::optional<Fortran::parser::AccSizeExpr>>(x.t)) {
148             const auto &expr =
149                 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
150                     gangStaticValue.value().t);
151             if (expr) {
152               gangStatic = fir::getBase(
153                   converter.genExprValue(*Fortran::semantics::GetExpr(*expr)));
154             } else {
155               // * was passed as value and will be represented as a -1 constant
156               // integer.
157               gangStatic = firOpBuilder.createIntegerConstant(
158                   currentLocation, firOpBuilder.getIntegerType(32),
159                   /* STAR */ -1);
160             }
161           }
162         }
163         executionMapping |= mlir::acc::OpenACCExecMapping::GANG;
164       } else if (const auto *workerClause =
165                      std::get_if<Fortran::parser::AccClause::Worker>(
166                          &clause.u)) {
167         if (workerClause->v) {
168           workerNum = fir::getBase(converter.genExprValue(
169               *Fortran::semantics::GetExpr(*workerClause->v)));
170         }
171         executionMapping |= mlir::acc::OpenACCExecMapping::WORKER;
172       } else if (const auto *vectorClause =
173                      std::get_if<Fortran::parser::AccClause::Vector>(
174                          &clause.u)) {
175         if (vectorClause->v) {
176           vectorLength = fir::getBase(converter.genExprValue(
177               *Fortran::semantics::GetExpr(*vectorClause->v)));
178         }
179         executionMapping |= mlir::acc::OpenACCExecMapping::VECTOR;
180       } else if (const auto *tileClause =
181                      std::get_if<Fortran::parser::AccClause::Tile>(&clause.u)) {
182         const Fortran::parser::AccTileExprList &accTileExprList = tileClause->v;
183         for (const auto &accTileExpr : accTileExprList.v) {
184           const auto &expr =
185               std::get<std::optional<Fortran::parser::ScalarIntConstantExpr>>(
186                   accTileExpr.t);
187           if (expr) {
188             tileOperands.push_back(fir::getBase(
189                 converter.genExprValue(*Fortran::semantics::GetExpr(*expr))));
190           } else {
191             // * was passed as value and will be represented as a -1 constant
192             // integer.
193             mlir::Value tileStar = firOpBuilder.createIntegerConstant(
194                 currentLocation, firOpBuilder.getIntegerType(32),
195                 /* STAR */ -1);
196             tileOperands.push_back(tileStar);
197           }
198         }
199       } else if (const auto *privateClause =
200                      std::get_if<Fortran::parser::AccClause::Private>(
201                          &clause.u)) {
202         genObjectList(privateClause->v, converter, privateOperands);
203       }
204       // Reduction clause is left out for the moment as the clause will probably
205       // end up having its own operation.
206     }
207 
208     // Prepare the operand segement size attribute and the operands value range.
209     SmallVector<Value, 8> operands;
210     SmallVector<int32_t, 8> operandSegments;
211     addOperand(operands, operandSegments, gangNum);
212     addOperand(operands, operandSegments, gangStatic);
213     addOperand(operands, operandSegments, workerNum);
214     addOperand(operands, operandSegments, vectorLength);
215     addOperands(operands, operandSegments, tileOperands);
216     addOperands(operands, operandSegments, privateOperands);
217     addOperands(operands, operandSegments, reductionOperands);
218 
219     auto loopOp = createRegionOp<mlir::acc::LoopOp, mlir::acc::YieldOp>(
220         firOpBuilder, currentLocation, operands, operandSegments);
221 
222     loopOp.setAttr(mlir::acc::LoopOp::getExecutionMappingAttrName(),
223                    firOpBuilder.getI64IntegerAttr(executionMapping));
224 
225     // Lower clauses mapped to attributes
226     for (const auto &clause : accClauseList.v) {
227       if (const auto *collapseClause =
228               std::get_if<Fortran::parser::AccClause::Collapse>(&clause.u)) {
229         const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
230         const auto collapseValue = Fortran::evaluate::ToInt64(*expr);
231         if (collapseValue) {
232           loopOp.setAttr(mlir::acc::LoopOp::getCollapseAttrName(),
233                          firOpBuilder.getI64IntegerAttr(*collapseValue));
234         }
235       } else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
236         loopOp.setAttr(mlir::acc::LoopOp::getSeqAttrName(),
237                        firOpBuilder.getUnitAttr());
238       } else if (std::get_if<Fortran::parser::AccClause::Independent>(
239                      &clause.u)) {
240         loopOp.setAttr(mlir::acc::LoopOp::getIndependentAttrName(),
241                        firOpBuilder.getUnitAttr());
242       } else if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) {
243         loopOp.setAttr(mlir::acc::LoopOp::getAutoAttrName(),
244                        firOpBuilder.getUnitAttr());
245       }
246     }
247   }
248 }
249 
250 static void
251 genACCParallelOp(Fortran::lower::AbstractConverter &converter,
252                  const Fortran::parser::AccClauseList &accClauseList) {
253   mlir::Value async;
254   mlir::Value numGangs;
255   mlir::Value numWorkers;
256   mlir::Value vectorLength;
257   mlir::Value ifCond;
258   mlir::Value selfCond;
259   SmallVector<Value, 2> waitOperands, reductionOperands, copyOperands,
260       copyinOperands, copyinReadonlyOperands, copyoutOperands,
261       copyoutZeroOperands, createOperands, createZeroOperands, noCreateOperands,
262       presentOperands, devicePtrOperands, attachOperands, privateOperands,
263       firstprivateOperands;
264 
265   // Async, wait and self clause have optional values but can be present with
266   // no value as well. When there is no value, the op has an attribute to
267   // represent the clause.
268   bool addAsyncAttr = false;
269   bool addWaitAttr = false;
270   bool addSelfAttr = false;
271 
272   auto &firOpBuilder = converter.getFirOpBuilder();
273   auto currentLocation = converter.getCurrentLocation();
274 
275   // Lower clauses values mapped to operands.
276   // Keep track of each group of operands separatly as clauses can appear
277   // more than once.
278   for (const auto &clause : accClauseList.v) {
279     if (const auto *asyncClause =
280             std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
281       const auto &asyncClauseValue = asyncClause->v;
282       if (asyncClauseValue) { // async has a value.
283         async = fir::getBase(converter.genExprValue(
284             *Fortran::semantics::GetExpr(*asyncClauseValue)));
285       } else {
286         addAsyncAttr = true;
287       }
288     } else if (const auto *waitClause =
289                    std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
290       const auto &waitClauseValue = waitClause->v;
291       if (waitClauseValue) { // wait has a value.
292         const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue;
293         const std::list<Fortran::parser::ScalarIntExpr> &waitList =
294             std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
295         for (const Fortran::parser::ScalarIntExpr &value : waitList) {
296           Value v = fir::getBase(
297               converter.genExprValue(*Fortran::semantics::GetExpr(value)));
298           waitOperands.push_back(v);
299         }
300       } else {
301         addWaitAttr = true;
302       }
303     } else if (const auto *numGangsClause =
304                    std::get_if<Fortran::parser::AccClause::NumGangs>(
305                        &clause.u)) {
306       numGangs = fir::getBase(converter.genExprValue(
307           *Fortran::semantics::GetExpr(numGangsClause->v)));
308     } else if (const auto *numWorkersClause =
309                    std::get_if<Fortran::parser::AccClause::NumWorkers>(
310                        &clause.u)) {
311       numWorkers = fir::getBase(converter.genExprValue(
312           *Fortran::semantics::GetExpr(numWorkersClause->v)));
313     } else if (const auto *vectorLengthClause =
314                    std::get_if<Fortran::parser::AccClause::VectorLength>(
315                        &clause.u)) {
316       vectorLength = fir::getBase(converter.genExprValue(
317           *Fortran::semantics::GetExpr(vectorLengthClause->v)));
318     } else if (const auto *ifClause =
319                    std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
320       Value cond = fir::getBase(
321           converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
322       ifCond = firOpBuilder.createConvert(currentLocation,
323                                           firOpBuilder.getI1Type(), cond);
324     } else if (const auto *selfClause =
325                    std::get_if<Fortran::parser::AccClause::Self>(&clause.u)) {
326       if (selfClause->v) {
327         Value cond = fir::getBase(converter.genExprValue(
328             *Fortran::semantics::GetExpr(*(selfClause->v))));
329         selfCond = firOpBuilder.createConvert(currentLocation,
330                                               firOpBuilder.getI1Type(), cond);
331       } else {
332         addSelfAttr = true;
333       }
334     } else if (const auto *copyClause =
335                    std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
336       genObjectList(copyClause->v, converter, copyOperands);
337     } else if (const auto *copyinClause =
338                    std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
339       genObjectListWithModifier<Fortran::parser::AccClause::Copyin>(
340           copyinClause, converter,
341           Fortran::parser::AccDataModifier::Modifier::ReadOnly,
342           copyinReadonlyOperands, copyinOperands);
343     } else if (const auto *copyoutClause =
344                    std::get_if<Fortran::parser::AccClause::Copyout>(
345                        &clause.u)) {
346       genObjectListWithModifier<Fortran::parser::AccClause::Copyout>(
347           copyoutClause, converter,
348           Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands,
349           copyoutOperands);
350     } else if (const auto *createClause =
351                    std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
352       genObjectListWithModifier<Fortran::parser::AccClause::Create>(
353           createClause, converter,
354           Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
355           createOperands);
356     } else if (const auto *noCreateClause =
357                    std::get_if<Fortran::parser::AccClause::NoCreate>(
358                        &clause.u)) {
359       genObjectList(noCreateClause->v, converter, noCreateOperands);
360     } else if (const auto *presentClause =
361                    std::get_if<Fortran::parser::AccClause::Present>(
362                        &clause.u)) {
363       genObjectList(presentClause->v, converter, presentOperands);
364     } else if (const auto *devicePtrClause =
365                    std::get_if<Fortran::parser::AccClause::Deviceptr>(
366                        &clause.u)) {
367       genObjectList(devicePtrClause->v, converter, devicePtrOperands);
368     } else if (const auto *attachClause =
369                    std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
370       genObjectList(attachClause->v, converter, attachOperands);
371     } else if (const auto *privateClause =
372                    std::get_if<Fortran::parser::AccClause::Private>(
373                        &clause.u)) {
374       genObjectList(privateClause->v, converter, privateOperands);
375     } else if (const auto *firstprivateClause =
376                    std::get_if<Fortran::parser::AccClause::Firstprivate>(
377                        &clause.u)) {
378       genObjectList(firstprivateClause->v, converter, firstprivateOperands);
379     }
380   }
381 
382   // Prepare the operand segement size attribute and the operands value range.
383   SmallVector<Value, 8> operands;
384   SmallVector<int32_t, 8> operandSegments;
385   addOperand(operands, operandSegments, async);
386   addOperands(operands, operandSegments, waitOperands);
387   addOperand(operands, operandSegments, numGangs);
388   addOperand(operands, operandSegments, numWorkers);
389   addOperand(operands, operandSegments, vectorLength);
390   addOperand(operands, operandSegments, ifCond);
391   addOperand(operands, operandSegments, selfCond);
392   addOperands(operands, operandSegments, reductionOperands);
393   addOperands(operands, operandSegments, copyOperands);
394   addOperands(operands, operandSegments, copyinOperands);
395   addOperands(operands, operandSegments, copyinReadonlyOperands);
396   addOperands(operands, operandSegments, copyoutOperands);
397   addOperands(operands, operandSegments, copyoutZeroOperands);
398   addOperands(operands, operandSegments, createOperands);
399   addOperands(operands, operandSegments, createZeroOperands);
400   addOperands(operands, operandSegments, noCreateOperands);
401   addOperands(operands, operandSegments, presentOperands);
402   addOperands(operands, operandSegments, devicePtrOperands);
403   addOperands(operands, operandSegments, attachOperands);
404   addOperands(operands, operandSegments, privateOperands);
405   addOperands(operands, operandSegments, firstprivateOperands);
406 
407   auto parallelOp = createRegionOp<mlir::acc::ParallelOp, mlir::acc::YieldOp>(
408       firOpBuilder, currentLocation, operands, operandSegments);
409 
410   if (addAsyncAttr)
411     parallelOp.setAttr(mlir::acc::ParallelOp::getAsyncAttrName(),
412                        firOpBuilder.getUnitAttr());
413   if (addWaitAttr)
414     parallelOp.setAttr(mlir::acc::ParallelOp::getWaitAttrName(),
415                        firOpBuilder.getUnitAttr());
416   if (addSelfAttr)
417     parallelOp.setAttr(mlir::acc::ParallelOp::getSelfAttrName(),
418                        firOpBuilder.getUnitAttr());
419 }
420 
421 static void genACCDataOp(Fortran::lower::AbstractConverter &converter,
422                          const Fortran::parser::AccClauseList &accClauseList) {
423   mlir::Value ifCond;
424   SmallVector<Value, 2> copyOperands, copyinOperands, copyinReadonlyOperands,
425       copyoutOperands, copyoutZeroOperands, createOperands, createZeroOperands,
426       noCreateOperands, presentOperands, deviceptrOperands, attachOperands;
427 
428   auto &firOpBuilder = converter.getFirOpBuilder();
429   auto currentLocation = converter.getCurrentLocation();
430 
431   // Lower clauses values mapped to operands.
432   // Keep track of each group of operands separatly as clauses can appear
433   // more than once.
434   for (const auto &clause : accClauseList.v) {
435     if (const auto *ifClause =
436             std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
437       Value cond = fir::getBase(
438           converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
439       ifCond = firOpBuilder.createConvert(currentLocation,
440                                           firOpBuilder.getI1Type(), cond);
441     } else if (const auto *copyClause =
442                    std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
443       genObjectList(copyClause->v, converter, copyOperands);
444     } else if (const auto *copyinClause =
445                    std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
446       genObjectListWithModifier<Fortran::parser::AccClause::Copyin>(
447           copyinClause, converter,
448           Fortran::parser::AccDataModifier::Modifier::ReadOnly,
449           copyinReadonlyOperands, copyinOperands);
450     } else if (const auto *copyoutClause =
451                    std::get_if<Fortran::parser::AccClause::Copyout>(
452                        &clause.u)) {
453       genObjectListWithModifier<Fortran::parser::AccClause::Copyout>(
454           copyoutClause, converter,
455           Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands,
456           copyoutOperands);
457     } else if (const auto *createClause =
458                    std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
459       genObjectListWithModifier<Fortran::parser::AccClause::Create>(
460           createClause, converter,
461           Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
462           createOperands);
463     } else if (const auto *noCreateClause =
464                    std::get_if<Fortran::parser::AccClause::NoCreate>(
465                        &clause.u)) {
466       genObjectList(noCreateClause->v, converter, noCreateOperands);
467     } else if (const auto *presentClause =
468                    std::get_if<Fortran::parser::AccClause::Present>(
469                        &clause.u)) {
470       genObjectList(presentClause->v, converter, presentOperands);
471     } else if (const auto *deviceptrClause =
472                    std::get_if<Fortran::parser::AccClause::Deviceptr>(
473                        &clause.u)) {
474       genObjectList(deviceptrClause->v, converter, deviceptrOperands);
475     } else if (const auto *attachClause =
476                    std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
477       genObjectList(attachClause->v, converter, attachOperands);
478     }
479   }
480 
481   // Prepare the operand segement size attribute and the operands value range.
482   SmallVector<Value, 8> operands;
483   SmallVector<int32_t, 8> operandSegments;
484   addOperand(operands, operandSegments, ifCond);
485   addOperands(operands, operandSegments, copyOperands);
486   addOperands(operands, operandSegments, copyinOperands);
487   addOperands(operands, operandSegments, copyinReadonlyOperands);
488   addOperands(operands, operandSegments, copyoutOperands);
489   addOperands(operands, operandSegments, copyoutZeroOperands);
490   addOperands(operands, operandSegments, createOperands);
491   addOperands(operands, operandSegments, createZeroOperands);
492   addOperands(operands, operandSegments, noCreateOperands);
493   addOperands(operands, operandSegments, presentOperands);
494   addOperands(operands, operandSegments, deviceptrOperands);
495   addOperands(operands, operandSegments, attachOperands);
496 
497   createRegionOp<mlir::acc::DataOp, mlir::acc::TerminatorOp>(
498       firOpBuilder, currentLocation, operands, operandSegments);
499 }
500 
501 static void
502 genACC(Fortran::lower::AbstractConverter &converter,
503        Fortran::lower::pft::Evaluation &eval,
504        const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
505   const auto &beginBlockDirective =
506       std::get<Fortran::parser::AccBeginBlockDirective>(blockConstruct.t);
507   const auto &blockDirective =
508       std::get<Fortran::parser::AccBlockDirective>(beginBlockDirective.t);
509   const auto &accClauseList =
510       std::get<Fortran::parser::AccClauseList>(beginBlockDirective.t);
511 
512   if (blockDirective.v == llvm::acc::ACCD_parallel) {
513     genACCParallelOp(converter, accClauseList);
514   } else if (blockDirective.v == llvm::acc::ACCD_data) {
515     genACCDataOp(converter, accClauseList);
516   }
517 }
518 
519 void Fortran::lower::genOpenACCConstruct(
520     Fortran::lower::AbstractConverter &converter,
521     Fortran::lower::pft::Evaluation &eval,
522     const Fortran::parser::OpenACCConstruct &accConstruct) {
523 
524   std::visit(
525       common::visitors{
526           [&](const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
527             genACC(converter, eval, blockConstruct);
528           },
529           [&](const Fortran::parser::OpenACCCombinedConstruct
530                   &combinedConstruct) {
531             TODO("OpenACC Combined construct not lowered yet!");
532           },
533           [&](const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
534             genACC(converter, eval, loopConstruct);
535           },
536           [&](const Fortran::parser::OpenACCStandaloneConstruct
537                   &standaloneConstruct) {
538             TODO("OpenACC Standalone construct not lowered yet!");
539           },
540           [&](const Fortran::parser::OpenACCRoutineConstruct
541                   &routineConstruct) {
542             TODO("OpenACC Routine construct not lowered yet!");
543           },
544           [&](const Fortran::parser::OpenACCCacheConstruct &cacheConstruct) {
545             TODO("OpenACC Cache construct not lowered yet!");
546           },
547           [&](const Fortran::parser::OpenACCWaitConstruct &waitConstruct) {
548             TODO("OpenACC Wait construct not lowered yet!");
549           },
550           [&](const Fortran::parser::OpenACCAtomicConstruct &atomicConstruct) {
551             TODO("OpenACC Atomic construct not lowered yet!");
552           },
553       },
554       accConstruct.u);
555 }
556