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