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