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 const Fortran::parser::AccSelfClause &accSelfClause = selfClause->v; 339 if (const auto *optCondition = 340 std::get_if<std::optional<Fortran::parser::ScalarLogicalExpr>>( 341 &accSelfClause.u)) { 342 if (*optCondition) { 343 Value cond = fir::getBase(converter.genExprValue( 344 *Fortran::semantics::GetExpr(*optCondition))); 345 selfCond = firOpBuilder.createConvert(currentLocation, 346 firOpBuilder.getI1Type(), cond); 347 } else { 348 addSelfAttr = true; 349 } 350 } 351 } else if (const auto *copyClause = 352 std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) { 353 genObjectList(copyClause->v, converter, copyOperands); 354 } else if (const auto *copyinClause = 355 std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { 356 genObjectListWithModifier<Fortran::parser::AccClause::Copyin>( 357 copyinClause, converter, 358 Fortran::parser::AccDataModifier::Modifier::ReadOnly, 359 copyinReadonlyOperands, copyinOperands); 360 } else if (const auto *copyoutClause = 361 std::get_if<Fortran::parser::AccClause::Copyout>( 362 &clause.u)) { 363 genObjectListWithModifier<Fortran::parser::AccClause::Copyout>( 364 copyoutClause, converter, 365 Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands, 366 copyoutOperands); 367 } else if (const auto *createClause = 368 std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { 369 genObjectListWithModifier<Fortran::parser::AccClause::Create>( 370 createClause, converter, 371 Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands, 372 createOperands); 373 } else if (const auto *noCreateClause = 374 std::get_if<Fortran::parser::AccClause::NoCreate>( 375 &clause.u)) { 376 genObjectList(noCreateClause->v, converter, noCreateOperands); 377 } else if (const auto *presentClause = 378 std::get_if<Fortran::parser::AccClause::Present>( 379 &clause.u)) { 380 genObjectList(presentClause->v, converter, presentOperands); 381 } else if (const auto *devicePtrClause = 382 std::get_if<Fortran::parser::AccClause::Deviceptr>( 383 &clause.u)) { 384 genObjectList(devicePtrClause->v, converter, devicePtrOperands); 385 } else if (const auto *attachClause = 386 std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) { 387 genObjectList(attachClause->v, converter, attachOperands); 388 } else if (const auto *privateClause = 389 std::get_if<Fortran::parser::AccClause::Private>( 390 &clause.u)) { 391 genObjectList(privateClause->v, converter, privateOperands); 392 } else if (const auto *firstprivateClause = 393 std::get_if<Fortran::parser::AccClause::Firstprivate>( 394 &clause.u)) { 395 genObjectList(firstprivateClause->v, converter, firstprivateOperands); 396 } 397 } 398 399 // Prepare the operand segement size attribute and the operands value range. 400 SmallVector<Value, 8> operands; 401 SmallVector<int32_t, 8> operandSegments; 402 addOperand(operands, operandSegments, async); 403 addOperands(operands, operandSegments, waitOperands); 404 addOperand(operands, operandSegments, numGangs); 405 addOperand(operands, operandSegments, numWorkers); 406 addOperand(operands, operandSegments, vectorLength); 407 addOperand(operands, operandSegments, ifCond); 408 addOperand(operands, operandSegments, selfCond); 409 addOperands(operands, operandSegments, reductionOperands); 410 addOperands(operands, operandSegments, copyOperands); 411 addOperands(operands, operandSegments, copyinOperands); 412 addOperands(operands, operandSegments, copyinReadonlyOperands); 413 addOperands(operands, operandSegments, copyoutOperands); 414 addOperands(operands, operandSegments, copyoutZeroOperands); 415 addOperands(operands, operandSegments, createOperands); 416 addOperands(operands, operandSegments, createZeroOperands); 417 addOperands(operands, operandSegments, noCreateOperands); 418 addOperands(operands, operandSegments, presentOperands); 419 addOperands(operands, operandSegments, devicePtrOperands); 420 addOperands(operands, operandSegments, attachOperands); 421 addOperands(operands, operandSegments, privateOperands); 422 addOperands(operands, operandSegments, firstprivateOperands); 423 424 auto parallelOp = createRegionOp<mlir::acc::ParallelOp, mlir::acc::YieldOp>( 425 firOpBuilder, currentLocation, operands, operandSegments); 426 427 if (addAsyncAttr) 428 parallelOp->setAttr(mlir::acc::ParallelOp::getAsyncAttrName(), 429 firOpBuilder.getUnitAttr()); 430 if (addWaitAttr) 431 parallelOp->setAttr(mlir::acc::ParallelOp::getWaitAttrName(), 432 firOpBuilder.getUnitAttr()); 433 if (addSelfAttr) 434 parallelOp->setAttr(mlir::acc::ParallelOp::getSelfAttrName(), 435 firOpBuilder.getUnitAttr()); 436 } 437 438 static void genACCDataOp(Fortran::lower::AbstractConverter &converter, 439 const Fortran::parser::AccClauseList &accClauseList) { 440 mlir::Value ifCond; 441 SmallVector<Value, 2> copyOperands, copyinOperands, copyinReadonlyOperands, 442 copyoutOperands, copyoutZeroOperands, createOperands, createZeroOperands, 443 noCreateOperands, presentOperands, deviceptrOperands, attachOperands; 444 445 auto &firOpBuilder = converter.getFirOpBuilder(); 446 auto currentLocation = converter.getCurrentLocation(); 447 448 // Lower clauses values mapped to operands. 449 // Keep track of each group of operands separatly as clauses can appear 450 // more than once. 451 for (const auto &clause : accClauseList.v) { 452 if (const auto *ifClause = 453 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 454 Value cond = fir::getBase( 455 converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v))); 456 ifCond = firOpBuilder.createConvert(currentLocation, 457 firOpBuilder.getI1Type(), cond); 458 } else if (const auto *copyClause = 459 std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) { 460 genObjectList(copyClause->v, converter, copyOperands); 461 } else if (const auto *copyinClause = 462 std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { 463 genObjectListWithModifier<Fortran::parser::AccClause::Copyin>( 464 copyinClause, converter, 465 Fortran::parser::AccDataModifier::Modifier::ReadOnly, 466 copyinReadonlyOperands, copyinOperands); 467 } else if (const auto *copyoutClause = 468 std::get_if<Fortran::parser::AccClause::Copyout>( 469 &clause.u)) { 470 genObjectListWithModifier<Fortran::parser::AccClause::Copyout>( 471 copyoutClause, converter, 472 Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands, 473 copyoutOperands); 474 } else if (const auto *createClause = 475 std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { 476 genObjectListWithModifier<Fortran::parser::AccClause::Create>( 477 createClause, converter, 478 Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands, 479 createOperands); 480 } else if (const auto *noCreateClause = 481 std::get_if<Fortran::parser::AccClause::NoCreate>( 482 &clause.u)) { 483 genObjectList(noCreateClause->v, converter, noCreateOperands); 484 } else if (const auto *presentClause = 485 std::get_if<Fortran::parser::AccClause::Present>( 486 &clause.u)) { 487 genObjectList(presentClause->v, converter, presentOperands); 488 } else if (const auto *deviceptrClause = 489 std::get_if<Fortran::parser::AccClause::Deviceptr>( 490 &clause.u)) { 491 genObjectList(deviceptrClause->v, converter, deviceptrOperands); 492 } else if (const auto *attachClause = 493 std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) { 494 genObjectList(attachClause->v, converter, attachOperands); 495 } 496 } 497 498 // Prepare the operand segement size attribute and the operands value range. 499 SmallVector<Value, 8> operands; 500 SmallVector<int32_t, 8> operandSegments; 501 addOperand(operands, operandSegments, ifCond); 502 addOperands(operands, operandSegments, copyOperands); 503 addOperands(operands, operandSegments, copyinOperands); 504 addOperands(operands, operandSegments, copyinReadonlyOperands); 505 addOperands(operands, operandSegments, copyoutOperands); 506 addOperands(operands, operandSegments, copyoutZeroOperands); 507 addOperands(operands, operandSegments, createOperands); 508 addOperands(operands, operandSegments, createZeroOperands); 509 addOperands(operands, operandSegments, noCreateOperands); 510 addOperands(operands, operandSegments, presentOperands); 511 addOperands(operands, operandSegments, deviceptrOperands); 512 addOperands(operands, operandSegments, attachOperands); 513 514 createRegionOp<mlir::acc::DataOp, mlir::acc::TerminatorOp>( 515 firOpBuilder, currentLocation, operands, operandSegments); 516 } 517 518 static void 519 genACC(Fortran::lower::AbstractConverter &converter, 520 Fortran::lower::pft::Evaluation &eval, 521 const Fortran::parser::OpenACCBlockConstruct &blockConstruct) { 522 const auto &beginBlockDirective = 523 std::get<Fortran::parser::AccBeginBlockDirective>(blockConstruct.t); 524 const auto &blockDirective = 525 std::get<Fortran::parser::AccBlockDirective>(beginBlockDirective.t); 526 const auto &accClauseList = 527 std::get<Fortran::parser::AccClauseList>(beginBlockDirective.t); 528 529 if (blockDirective.v == llvm::acc::ACCD_parallel) { 530 genACCParallelOp(converter, accClauseList); 531 } else if (blockDirective.v == llvm::acc::ACCD_data) { 532 genACCDataOp(converter, accClauseList); 533 } 534 } 535 536 static void 537 genACCEnterDataOp(Fortran::lower::AbstractConverter &converter, 538 const Fortran::parser::AccClauseList &accClauseList) { 539 mlir::Value ifCond, async, waitDevnum; 540 SmallVector<Value, 2> copyinOperands, createOperands, createZeroOperands, 541 attachOperands, waitOperands; 542 543 // Async, wait and self clause have optional values but can be present with 544 // no value as well. When there is no value, the op has an attribute to 545 // represent the clause. 546 bool addAsyncAttr = false; 547 bool addWaitAttr = false; 548 549 auto &firOpBuilder = converter.getFirOpBuilder(); 550 auto currentLocation = converter.getCurrentLocation(); 551 552 // Lower clauses values mapped to operands. 553 // Keep track of each group of operands separatly as clauses can appear 554 // more than once. 555 for (const auto &clause : accClauseList.v) { 556 if (const auto *ifClause = 557 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 558 mlir::Value cond = fir::getBase( 559 converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v))); 560 ifCond = firOpBuilder.createConvert(currentLocation, 561 firOpBuilder.getI1Type(), cond); 562 } else if (const auto *asyncClause = 563 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 564 const auto &asyncClauseValue = asyncClause->v; 565 if (asyncClauseValue) { // async has a value. 566 async = fir::getBase(converter.genExprValue( 567 *Fortran::semantics::GetExpr(*asyncClauseValue))); 568 } else { 569 addAsyncAttr = true; 570 } 571 } else if (const auto *waitClause = 572 std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) { 573 const auto &waitClauseValue = waitClause->v; 574 if (waitClauseValue) { // wait has a value. 575 const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue; 576 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 577 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 578 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 579 mlir::Value v = fir::getBase( 580 converter.genExprValue(*Fortran::semantics::GetExpr(value))); 581 waitOperands.push_back(v); 582 } 583 584 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 585 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 586 if (waitDevnumValue) 587 waitDevnum = fir::getBase(converter.genExprValue( 588 *Fortran::semantics::GetExpr(*waitDevnumValue))); 589 } else { 590 addWaitAttr = true; 591 } 592 } else if (const auto *copyinClause = 593 std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { 594 const Fortran::parser::AccObjectListWithModifier &listWithModifier = 595 copyinClause->v; 596 const Fortran::parser::AccObjectList &accObjectList = 597 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 598 genObjectList(accObjectList, converter, copyinOperands); 599 } else if (const auto *createClause = 600 std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { 601 genObjectListWithModifier<Fortran::parser::AccClause::Create>( 602 createClause, converter, 603 Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands, 604 createOperands); 605 } else if (const auto *attachClause = 606 std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) { 607 genObjectList(attachClause->v, converter, attachOperands); 608 } else { 609 llvm::report_fatal_error( 610 "Unknown clause in ENTER DATA directive lowering"); 611 } 612 } 613 614 // Prepare the operand segement size attribute and the operands value range. 615 SmallVector<mlir::Value, 16> operands; 616 SmallVector<int32_t, 8> operandSegments; 617 addOperand(operands, operandSegments, ifCond); 618 addOperand(operands, operandSegments, async); 619 addOperand(operands, operandSegments, waitDevnum); 620 addOperands(operands, operandSegments, waitOperands); 621 addOperands(operands, operandSegments, copyinOperands); 622 addOperands(operands, operandSegments, createOperands); 623 addOperands(operands, operandSegments, createZeroOperands); 624 addOperands(operands, operandSegments, attachOperands); 625 626 auto enterDataOp = createSimpleOp<mlir::acc::EnterDataOp>( 627 firOpBuilder, currentLocation, operands, operandSegments); 628 629 if (addAsyncAttr) 630 enterDataOp.asyncAttr(firOpBuilder.getUnitAttr()); 631 if (addWaitAttr) 632 enterDataOp.waitAttr(firOpBuilder.getUnitAttr()); 633 } 634 635 static void 636 genACCExitDataOp(Fortran::lower::AbstractConverter &converter, 637 const Fortran::parser::AccClauseList &accClauseList) { 638 mlir::Value ifCond, async, waitDevnum; 639 SmallVector<Value, 2> copyoutOperands, deleteOperands, detachOperands, 640 waitOperands; 641 642 // Async and wait clause have optional values but can be present with 643 // no value as well. When there is no value, the op has an attribute to 644 // represent the clause. 645 bool addAsyncAttr = false; 646 bool addWaitAttr = false; 647 bool addFinalizeAttr = false; 648 649 auto &firOpBuilder = converter.getFirOpBuilder(); 650 auto currentLocation = converter.getCurrentLocation(); 651 652 // Lower clauses values mapped to operands. 653 // Keep track of each group of operands separatly as clauses can appear 654 // more than once. 655 for (const auto &clause : accClauseList.v) { 656 if (const auto *ifClause = 657 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 658 Value cond = fir::getBase( 659 converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v))); 660 ifCond = firOpBuilder.createConvert(currentLocation, 661 firOpBuilder.getI1Type(), cond); 662 } else if (const auto *asyncClause = 663 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 664 const auto &asyncClauseValue = asyncClause->v; 665 if (asyncClauseValue) { // async has a value. 666 async = fir::getBase(converter.genExprValue( 667 *Fortran::semantics::GetExpr(*asyncClauseValue))); 668 } else { 669 addAsyncAttr = true; 670 } 671 } else if (const auto *waitClause = 672 std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) { 673 const auto &waitClauseValue = waitClause->v; 674 if (waitClauseValue) { // wait has a value. 675 const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue; 676 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 677 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 678 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 679 Value v = fir::getBase( 680 converter.genExprValue(*Fortran::semantics::GetExpr(value))); 681 waitOperands.push_back(v); 682 } 683 684 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 685 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 686 if (waitDevnumValue) 687 waitDevnum = fir::getBase(converter.genExprValue( 688 *Fortran::semantics::GetExpr(*waitDevnumValue))); 689 } else { 690 addWaitAttr = true; 691 } 692 } else if (const auto *copyoutClause = 693 std::get_if<Fortran::parser::AccClause::Copyout>( 694 &clause.u)) { 695 const Fortran::parser::AccObjectListWithModifier &listWithModifier = 696 copyoutClause->v; 697 const Fortran::parser::AccObjectList &accObjectList = 698 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 699 genObjectList(accObjectList, converter, copyoutOperands); 700 } else if (const auto *deleteClause = 701 std::get_if<Fortran::parser::AccClause::Delete>(&clause.u)) { 702 genObjectList(deleteClause->v, converter, deleteOperands); 703 } else if (const auto *detachClause = 704 std::get_if<Fortran::parser::AccClause::Detach>(&clause.u)) { 705 genObjectList(detachClause->v, converter, detachOperands); 706 } else if (std::get_if<Fortran::parser::AccClause::Finalize>(&clause.u)) { 707 addFinalizeAttr = true; 708 } 709 } 710 711 // Prepare the operand segement size attribute and the operands value range. 712 SmallVector<mlir::Value, 14> operands; 713 SmallVector<int32_t, 7> operandSegments; 714 addOperand(operands, operandSegments, ifCond); 715 addOperand(operands, operandSegments, async); 716 addOperand(operands, operandSegments, waitDevnum); 717 addOperands(operands, operandSegments, waitOperands); 718 addOperands(operands, operandSegments, copyoutOperands); 719 addOperands(operands, operandSegments, deleteOperands); 720 addOperands(operands, operandSegments, detachOperands); 721 722 auto exitDataOp = createSimpleOp<mlir::acc::ExitDataOp>( 723 firOpBuilder, currentLocation, operands, operandSegments); 724 725 if (addAsyncAttr) 726 exitDataOp.asyncAttr(firOpBuilder.getUnitAttr()); 727 if (addWaitAttr) 728 exitDataOp.waitAttr(firOpBuilder.getUnitAttr()); 729 if (addFinalizeAttr) 730 exitDataOp.finalizeAttr(firOpBuilder.getUnitAttr()); 731 } 732 733 template <typename Op> 734 static void 735 genACCInitShutdownOp(Fortran::lower::AbstractConverter &converter, 736 const Fortran::parser::AccClauseList &accClauseList) { 737 mlir::Value ifCond, deviceNum; 738 SmallVector<Value, 2> deviceTypeOperands; 739 740 auto &firOpBuilder = converter.getFirOpBuilder(); 741 auto currentLocation = converter.getCurrentLocation(); 742 743 // Lower clauses values mapped to operands. 744 // Keep track of each group of operands separatly as clauses can appear 745 // more than once. 746 for (const auto &clause : accClauseList.v) { 747 if (const auto *ifClause = 748 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 749 mlir::Value cond = fir::getBase( 750 converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v))); 751 ifCond = firOpBuilder.createConvert(currentLocation, 752 firOpBuilder.getI1Type(), cond); 753 } else if (const auto *deviceNumClause = 754 std::get_if<Fortran::parser::AccClause::DeviceNum>( 755 &clause.u)) { 756 deviceNum = fir::getBase(converter.genExprValue( 757 *Fortran::semantics::GetExpr(deviceNumClause->v))); 758 } else if (const auto *deviceTypeClause = 759 std::get_if<Fortran::parser::AccClause::DeviceType>( 760 &clause.u)) { 761 762 const auto &deviceTypeValue = deviceTypeClause->v; 763 if (deviceTypeValue) { 764 for (const auto &scalarIntExpr : *deviceTypeValue) { 765 mlir::Value expr = fir::getBase(converter.genExprValue( 766 *Fortran::semantics::GetExpr(scalarIntExpr))); 767 deviceTypeOperands.push_back(expr); 768 } 769 } else { 770 // * was passed as value and will be represented as a -1 constant 771 // integer. 772 mlir::Value star = firOpBuilder.createIntegerConstant( 773 currentLocation, firOpBuilder.getIntegerType(32), /* STAR */ -1); 774 deviceTypeOperands.push_back(star); 775 } 776 } 777 } 778 779 // Prepare the operand segement size attribute and the operands value range. 780 SmallVector<mlir::Value, 6> operands; 781 SmallVector<int32_t, 3> operandSegments; 782 addOperands(operands, operandSegments, deviceTypeOperands); 783 addOperand(operands, operandSegments, deviceNum); 784 addOperand(operands, operandSegments, ifCond); 785 786 createSimpleOp<Op>(firOpBuilder, currentLocation, operands, operandSegments); 787 } 788 789 static void 790 genACCUpdateOp(Fortran::lower::AbstractConverter &converter, 791 const Fortran::parser::AccClauseList &accClauseList) { 792 mlir::Value ifCond, async, waitDevnum; 793 SmallVector<Value, 2> hostOperands, deviceOperands, waitOperands, 794 deviceTypeOperands; 795 796 // Async and wait clause have optional values but can be present with 797 // no value as well. When there is no value, the op has an attribute to 798 // represent the clause. 799 bool addAsyncAttr = false; 800 bool addWaitAttr = false; 801 bool addIfPresentAttr = false; 802 803 auto &firOpBuilder = converter.getFirOpBuilder(); 804 auto currentLocation = converter.getCurrentLocation(); 805 806 // Lower clauses values mapped to operands. 807 // Keep track of each group of operands separatly as clauses can appear 808 // more than once. 809 for (const auto &clause : accClauseList.v) { 810 if (const auto *ifClause = 811 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 812 mlir::Value cond = fir::getBase( 813 converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v))); 814 ifCond = firOpBuilder.createConvert(currentLocation, 815 firOpBuilder.getI1Type(), cond); 816 } else if (const auto *asyncClause = 817 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 818 const auto &asyncClauseValue = asyncClause->v; 819 if (asyncClauseValue) { // async has a value. 820 async = fir::getBase(converter.genExprValue( 821 *Fortran::semantics::GetExpr(*asyncClauseValue))); 822 } else { 823 addAsyncAttr = true; 824 } 825 } else if (const auto *waitClause = 826 std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) { 827 const auto &waitClauseValue = waitClause->v; 828 if (waitClauseValue) { // wait has a value. 829 const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue; 830 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 831 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 832 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 833 mlir::Value v = fir::getBase( 834 converter.genExprValue(*Fortran::semantics::GetExpr(value))); 835 waitOperands.push_back(v); 836 } 837 838 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 839 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 840 if (waitDevnumValue) 841 waitDevnum = fir::getBase(converter.genExprValue( 842 *Fortran::semantics::GetExpr(*waitDevnumValue))); 843 } else { 844 addWaitAttr = true; 845 } 846 } else if (const auto *deviceTypeClause = 847 std::get_if<Fortran::parser::AccClause::DeviceType>( 848 &clause.u)) { 849 850 const auto &deviceTypeValue = deviceTypeClause->v; 851 if (deviceTypeValue) { 852 for (const auto &scalarIntExpr : *deviceTypeValue) { 853 mlir::Value expr = fir::getBase(converter.genExprValue( 854 *Fortran::semantics::GetExpr(scalarIntExpr))); 855 deviceTypeOperands.push_back(expr); 856 } 857 } else { 858 // * was passed as value and will be represented as a -1 constant 859 // integer. 860 mlir::Value star = firOpBuilder.createIntegerConstant( 861 currentLocation, firOpBuilder.getIntegerType(32), /* STAR */ -1); 862 deviceTypeOperands.push_back(star); 863 } 864 } else if (const auto *hostClause = 865 std::get_if<Fortran::parser::AccClause::Host>(&clause.u)) { 866 genObjectList(hostClause->v, converter, hostOperands); 867 } else if (const auto *deviceClause = 868 std::get_if<Fortran::parser::AccClause::Device>(&clause.u)) { 869 genObjectList(deviceClause->v, converter, deviceOperands); 870 } 871 } 872 873 // Prepare the operand segement size attribute and the operands value range. 874 SmallVector<mlir::Value, 14> operands; 875 SmallVector<int32_t, 7> operandSegments; 876 addOperand(operands, operandSegments, async); 877 addOperand(operands, operandSegments, waitDevnum); 878 addOperands(operands, operandSegments, waitOperands); 879 addOperands(operands, operandSegments, deviceTypeOperands); 880 addOperand(operands, operandSegments, ifCond); 881 addOperands(operands, operandSegments, hostOperands); 882 addOperands(operands, operandSegments, deviceOperands); 883 884 auto updateOp = createSimpleOp<mlir::acc::UpdateOp>( 885 firOpBuilder, currentLocation, operands, operandSegments); 886 887 if (addAsyncAttr) 888 updateOp.asyncAttr(firOpBuilder.getUnitAttr()); 889 if (addWaitAttr) 890 updateOp.waitAttr(firOpBuilder.getUnitAttr()); 891 if (addIfPresentAttr) 892 updateOp.ifPresentAttr(firOpBuilder.getUnitAttr()); 893 } 894 895 static void 896 genACC(Fortran::lower::AbstractConverter &converter, 897 Fortran::lower::pft::Evaluation &eval, 898 const Fortran::parser::OpenACCStandaloneConstruct &standaloneConstruct) { 899 const auto &standaloneDirective = 900 std::get<Fortran::parser::AccStandaloneDirective>(standaloneConstruct.t); 901 const auto &accClauseList = 902 std::get<Fortran::parser::AccClauseList>(standaloneConstruct.t); 903 904 if (standaloneDirective.v == llvm::acc::Directive::ACCD_enter_data) { 905 genACCEnterDataOp(converter, accClauseList); 906 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_exit_data) { 907 genACCExitDataOp(converter, accClauseList); 908 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_init) { 909 genACCInitShutdownOp<mlir::acc::InitOp>(converter, accClauseList); 910 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_shutdown) { 911 genACCInitShutdownOp<mlir::acc::ShutdownOp>(converter, accClauseList); 912 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_set) { 913 TODO(converter.genLocation(), "OpenACC set directive not lowered yet!"); 914 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_update) { 915 genACCUpdateOp(converter, accClauseList); 916 } 917 } 918 919 static void genACC(Fortran::lower::AbstractConverter &converter, 920 Fortran::lower::pft::Evaluation &eval, 921 const Fortran::parser::OpenACCWaitConstruct &waitConstruct) { 922 923 const auto &waitArgument = 924 std::get<std::optional<Fortran::parser::AccWaitArgument>>( 925 waitConstruct.t); 926 const auto &accClauseList = 927 std::get<Fortran::parser::AccClauseList>(waitConstruct.t); 928 929 mlir::Value ifCond, asyncOperand, waitDevnum, async; 930 SmallVector<mlir::Value, 2> waitOperands; 931 932 // Async clause have optional values but can be present with 933 // no value as well. When there is no value, the op has an attribute to 934 // represent the clause. 935 bool addAsyncAttr = false; 936 937 auto &firOpBuilder = converter.getFirOpBuilder(); 938 auto currentLocation = converter.getCurrentLocation(); 939 940 if (waitArgument) { // wait has a value. 941 const Fortran::parser::AccWaitArgument &waitArg = *waitArgument; 942 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 943 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 944 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 945 mlir::Value v = fir::getBase( 946 converter.genExprValue(*Fortran::semantics::GetExpr(value))); 947 waitOperands.push_back(v); 948 } 949 950 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 951 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 952 if (waitDevnumValue) 953 waitDevnum = fir::getBase(converter.genExprValue( 954 *Fortran::semantics::GetExpr(*waitDevnumValue))); 955 } 956 957 // Lower clauses values mapped to operands. 958 // Keep track of each group of operands separatly as clauses can appear 959 // more than once. 960 for (const auto &clause : accClauseList.v) { 961 if (const auto *ifClause = 962 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 963 mlir::Value cond = fir::getBase( 964 converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v))); 965 ifCond = firOpBuilder.createConvert(currentLocation, 966 firOpBuilder.getI1Type(), cond); 967 } else if (const auto *asyncClause = 968 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 969 const auto &asyncClauseValue = asyncClause->v; 970 if (asyncClauseValue) { // async has a value. 971 async = fir::getBase(converter.genExprValue( 972 *Fortran::semantics::GetExpr(*asyncClauseValue))); 973 } else { 974 addAsyncAttr = true; 975 } 976 } 977 } 978 979 // Prepare the operand segement size attribute and the operands value range. 980 SmallVector<mlir::Value, 8> operands; 981 SmallVector<int32_t, 4> operandSegments; 982 addOperands(operands, operandSegments, waitOperands); 983 addOperand(operands, operandSegments, async); 984 addOperand(operands, operandSegments, waitDevnum); 985 addOperand(operands, operandSegments, ifCond); 986 987 auto waitOp = createSimpleOp<mlir::acc::WaitOp>(firOpBuilder, currentLocation, 988 operands, operandSegments); 989 990 if (addAsyncAttr) 991 waitOp.asyncAttr(firOpBuilder.getUnitAttr()); 992 } 993 994 void Fortran::lower::genOpenACCConstruct( 995 Fortran::lower::AbstractConverter &converter, 996 Fortran::lower::pft::Evaluation &eval, 997 const Fortran::parser::OpenACCConstruct &accConstruct) { 998 999 std::visit( 1000 common::visitors{ 1001 [&](const Fortran::parser::OpenACCBlockConstruct &blockConstruct) { 1002 genACC(converter, eval, blockConstruct); 1003 }, 1004 [&](const Fortran::parser::OpenACCCombinedConstruct 1005 &combinedConstruct) { 1006 TODO(converter.genLocation(), 1007 "OpenACC Combined construct not lowered yet!"); 1008 }, 1009 [&](const Fortran::parser::OpenACCLoopConstruct &loopConstruct) { 1010 genACC(converter, eval, loopConstruct); 1011 }, 1012 [&](const Fortran::parser::OpenACCStandaloneConstruct 1013 &standaloneConstruct) { 1014 genACC(converter, eval, standaloneConstruct); 1015 }, 1016 [&](const Fortran::parser::OpenACCRoutineConstruct 1017 &routineConstruct) { 1018 TODO(converter.genLocation(), 1019 "OpenACC Routine construct not lowered yet!"); 1020 }, 1021 [&](const Fortran::parser::OpenACCCacheConstruct &cacheConstruct) { 1022 TODO(converter.genLocation(), 1023 "OpenACC Cache construct not lowered yet!"); 1024 }, 1025 [&](const Fortran::parser::OpenACCWaitConstruct &waitConstruct) { 1026 genACC(converter, eval, waitConstruct); 1027 }, 1028 [&](const Fortran::parser::OpenACCAtomicConstruct &atomicConstruct) { 1029 TODO(converter.genLocation(), 1030 "OpenACC Atomic construct not lowered yet!"); 1031 }, 1032 }, 1033 accConstruct.u); 1034 } 1035