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