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/StatementContext.h" 18 #include "flang/Lower/Todo.h" 19 #include "flang/Optimizer/Builder/BoxValue.h" 20 #include "flang/Optimizer/Builder/FIRBuilder.h" 21 #include "flang/Parser/parse-tree.h" 22 #include "flang/Semantics/tools.h" 23 #include "mlir/Dialect/OpenACC/OpenACC.h" 24 #include "llvm/Frontend/OpenACC/ACC.h.inc" 25 26 static const Fortran::parser::Name * 27 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) { 28 const auto *dataRef{std::get_if<Fortran::parser::DataRef>(&designator.u)}; 29 return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr; 30 } 31 32 static void genObjectList(const Fortran::parser::AccObjectList &objectList, 33 Fortran::lower::AbstractConverter &converter, 34 SmallVectorImpl<Value> &operands) { 35 for (const auto &accObject : objectList.v) { 36 std::visit( 37 Fortran::common::visitors{ 38 [&](const Fortran::parser::Designator &designator) { 39 if (const auto *name = getDesignatorNameIfDataRef(designator)) { 40 const auto variable = converter.getSymbolAddress(*name->symbol); 41 operands.push_back(variable); 42 } 43 }, 44 [&](const Fortran::parser::Name &name) { 45 const auto variable = converter.getSymbolAddress(*name.symbol); 46 operands.push_back(variable); 47 }}, 48 accObject.u); 49 } 50 } 51 52 template <typename Clause> 53 static void 54 genObjectListWithModifier(const Clause *x, 55 Fortran::lower::AbstractConverter &converter, 56 Fortran::parser::AccDataModifier::Modifier mod, 57 SmallVectorImpl<Value> &operandsWithModifier, 58 SmallVectorImpl<Value> &operands) { 59 const Fortran::parser::AccObjectListWithModifier &listWithModifier = x->v; 60 const Fortran::parser::AccObjectList &accObjectList = 61 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 62 const auto &modifier = 63 std::get<std::optional<Fortran::parser::AccDataModifier>>( 64 listWithModifier.t); 65 if (modifier && (*modifier).v == mod) { 66 genObjectList(accObjectList, converter, operandsWithModifier); 67 } else { 68 genObjectList(accObjectList, converter, operands); 69 } 70 } 71 72 static void addOperands(SmallVectorImpl<Value> &operands, 73 SmallVectorImpl<int32_t> &operandSegments, 74 const SmallVectorImpl<Value> &clauseOperands) { 75 operands.append(clauseOperands.begin(), clauseOperands.end()); 76 operandSegments.push_back(clauseOperands.size()); 77 } 78 79 static void addOperand(SmallVectorImpl<Value> &operands, 80 SmallVectorImpl<int32_t> &operandSegments, 81 const Value &clauseOperand) { 82 if (clauseOperand) { 83 operands.push_back(clauseOperand); 84 operandSegments.push_back(1); 85 } else { 86 operandSegments.push_back(0); 87 } 88 } 89 90 template <typename Op, typename Terminator> 91 static Op createRegionOp(fir::FirOpBuilder &builder, 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(fir::FirOpBuilder &builder, mlir::Location loc, 112 const SmallVectorImpl<Value> &operands, 113 const SmallVectorImpl<int32_t> &operandSegments) { 114 llvm::ArrayRef<mlir::Type> argTy; 115 Op op = builder.create<Op>(loc, argTy, operands); 116 op->setAttr(Op::getOperandSegmentSizeAttr(), 117 builder.getI32VectorAttr(operandSegments)); 118 return op; 119 } 120 121 static void genACC(Fortran::lower::AbstractConverter &converter, 122 Fortran::lower::pft::Evaluation &eval, 123 const Fortran::parser::OpenACCLoopConstruct &loopConstruct) { 124 Fortran::lower::StatementContext stmtCtx; 125 const auto &beginLoopDirective = 126 std::get<Fortran::parser::AccBeginLoopDirective>(loopConstruct.t); 127 const auto &loopDirective = 128 std::get<Fortran::parser::AccLoopDirective>(beginLoopDirective.t); 129 130 if (loopDirective.v == llvm::acc::ACCD_loop) { 131 auto &firOpBuilder = converter.getFirOpBuilder(); 132 auto currentLocation = converter.getCurrentLocation(); 133 134 // Add attribute extracted from clauses. 135 const auto &accClauseList = 136 std::get<Fortran::parser::AccClauseList>(beginLoopDirective.t); 137 138 mlir::Value workerNum; 139 mlir::Value vectorLength; 140 mlir::Value gangNum; 141 mlir::Value gangStatic; 142 SmallVector<Value, 2> tileOperands, privateOperands, reductionOperands; 143 std::int64_t executionMapping = mlir::acc::OpenACCExecMapping::NONE; 144 145 // Lower clauses values mapped to operands. 146 for (const auto &clause : accClauseList.v) { 147 if (const auto *gangClause = 148 std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) { 149 if (gangClause->v) { 150 const Fortran::parser::AccGangArgument &x = *gangClause->v; 151 if (const auto &gangNumValue = 152 std::get<std::optional<Fortran::parser::ScalarIntExpr>>( 153 x.t)) { 154 gangNum = fir::getBase(converter.genExprValue( 155 *Fortran::semantics::GetExpr(gangNumValue.value()), stmtCtx)); 156 } 157 if (const auto &gangStaticValue = 158 std::get<std::optional<Fortran::parser::AccSizeExpr>>(x.t)) { 159 const auto &expr = 160 std::get<std::optional<Fortran::parser::ScalarIntExpr>>( 161 gangStaticValue.value().t); 162 if (expr) { 163 gangStatic = fir::getBase(converter.genExprValue( 164 *Fortran::semantics::GetExpr(*expr), stmtCtx)); 165 } else { 166 // * was passed as value and will be represented as a -1 constant 167 // integer. 168 gangStatic = firOpBuilder.createIntegerConstant( 169 currentLocation, firOpBuilder.getIntegerType(32), 170 /* STAR */ -1); 171 } 172 } 173 } 174 executionMapping |= mlir::acc::OpenACCExecMapping::GANG; 175 } else if (const auto *workerClause = 176 std::get_if<Fortran::parser::AccClause::Worker>( 177 &clause.u)) { 178 if (workerClause->v) { 179 workerNum = fir::getBase(converter.genExprValue( 180 *Fortran::semantics::GetExpr(*workerClause->v), stmtCtx)); 181 } 182 executionMapping |= mlir::acc::OpenACCExecMapping::WORKER; 183 } else if (const auto *vectorClause = 184 std::get_if<Fortran::parser::AccClause::Vector>( 185 &clause.u)) { 186 if (vectorClause->v) { 187 vectorLength = fir::getBase(converter.genExprValue( 188 *Fortran::semantics::GetExpr(*vectorClause->v), stmtCtx)); 189 } 190 executionMapping |= mlir::acc::OpenACCExecMapping::VECTOR; 191 } else if (const auto *tileClause = 192 std::get_if<Fortran::parser::AccClause::Tile>(&clause.u)) { 193 const Fortran::parser::AccTileExprList &accTileExprList = tileClause->v; 194 for (const auto &accTileExpr : accTileExprList.v) { 195 const auto &expr = 196 std::get<std::optional<Fortran::parser::ScalarIntConstantExpr>>( 197 accTileExpr.t); 198 if (expr) { 199 tileOperands.push_back(fir::getBase(converter.genExprValue( 200 *Fortran::semantics::GetExpr(*expr), stmtCtx))); 201 } else { 202 // * was passed as value and will be represented as a -1 constant 203 // integer. 204 mlir::Value tileStar = firOpBuilder.createIntegerConstant( 205 currentLocation, firOpBuilder.getIntegerType(32), 206 /* STAR */ -1); 207 tileOperands.push_back(tileStar); 208 } 209 } 210 } else if (const auto *privateClause = 211 std::get_if<Fortran::parser::AccClause::Private>( 212 &clause.u)) { 213 genObjectList(privateClause->v, converter, privateOperands); 214 } 215 // Reduction clause is left out for the moment as the clause will probably 216 // end up having its own operation. 217 } 218 219 // Prepare the operand segement size attribute and the operands value range. 220 SmallVector<Value, 8> operands; 221 SmallVector<int32_t, 8> operandSegments; 222 addOperand(operands, operandSegments, gangNum); 223 addOperand(operands, operandSegments, gangStatic); 224 addOperand(operands, operandSegments, workerNum); 225 addOperand(operands, operandSegments, vectorLength); 226 addOperands(operands, operandSegments, tileOperands); 227 addOperands(operands, operandSegments, privateOperands); 228 addOperands(operands, operandSegments, reductionOperands); 229 230 auto loopOp = createRegionOp<mlir::acc::LoopOp, mlir::acc::YieldOp>( 231 firOpBuilder, currentLocation, operands, operandSegments); 232 233 loopOp->setAttr(mlir::acc::LoopOp::getExecutionMappingAttrName(), 234 firOpBuilder.getI64IntegerAttr(executionMapping)); 235 236 // Lower clauses mapped to attributes 237 for (const auto &clause : accClauseList.v) { 238 if (const auto *collapseClause = 239 std::get_if<Fortran::parser::AccClause::Collapse>(&clause.u)) { 240 const auto *expr = Fortran::semantics::GetExpr(collapseClause->v); 241 const auto collapseValue = Fortran::evaluate::ToInt64(*expr); 242 if (collapseValue) { 243 loopOp->setAttr(mlir::acc::LoopOp::getCollapseAttrName(), 244 firOpBuilder.getI64IntegerAttr(*collapseValue)); 245 } 246 } else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) { 247 loopOp->setAttr(mlir::acc::LoopOp::getSeqAttrName(), 248 firOpBuilder.getUnitAttr()); 249 } else if (std::get_if<Fortran::parser::AccClause::Independent>( 250 &clause.u)) { 251 loopOp->setAttr(mlir::acc::LoopOp::getIndependentAttrName(), 252 firOpBuilder.getUnitAttr()); 253 } else if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) { 254 loopOp->setAttr(mlir::acc::LoopOp::getAutoAttrName(), 255 firOpBuilder.getUnitAttr()); 256 } 257 } 258 } 259 } 260 261 static void 262 genACCParallelOp(Fortran::lower::AbstractConverter &converter, 263 const Fortran::parser::AccClauseList &accClauseList) { 264 mlir::Value async; 265 mlir::Value numGangs; 266 mlir::Value numWorkers; 267 mlir::Value vectorLength; 268 mlir::Value ifCond; 269 mlir::Value selfCond; 270 SmallVector<Value, 2> waitOperands, reductionOperands, copyOperands, 271 copyinOperands, copyinReadonlyOperands, copyoutOperands, 272 copyoutZeroOperands, createOperands, createZeroOperands, noCreateOperands, 273 presentOperands, devicePtrOperands, attachOperands, privateOperands, 274 firstprivateOperands; 275 276 // Async, wait and self clause have optional values but can be present with 277 // no value as well. When there is no value, the op has an attribute to 278 // represent the clause. 279 bool addAsyncAttr = false; 280 bool addWaitAttr = false; 281 bool addSelfAttr = false; 282 283 auto &firOpBuilder = converter.getFirOpBuilder(); 284 auto currentLocation = converter.getCurrentLocation(); 285 Fortran::lower::StatementContext stmtCtx; 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), stmtCtx)); 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(converter.genExprValue( 309 *Fortran::semantics::GetExpr(value), stmtCtx)); 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), stmtCtx)); 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), stmtCtx)); 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), stmtCtx)); 330 } else if (const auto *ifClause = 331 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 332 Value cond = fir::getBase(converter.genExprValue( 333 *Fortran::semantics::GetExpr(ifClause->v), stmtCtx)); 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), stmtCtx)); 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 Fortran::lower::StatementContext stmtCtx; 448 449 // Lower clauses values mapped to operands. 450 // Keep track of each group of operands separatly as clauses can appear 451 // more than once. 452 for (const auto &clause : accClauseList.v) { 453 if (const auto *ifClause = 454 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 455 Value cond = fir::getBase(converter.genExprValue( 456 *Fortran::semantics::GetExpr(ifClause->v), stmtCtx)); 457 ifCond = firOpBuilder.createConvert(currentLocation, 458 firOpBuilder.getI1Type(), cond); 459 } else if (const auto *copyClause = 460 std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) { 461 genObjectList(copyClause->v, converter, copyOperands); 462 } else if (const auto *copyinClause = 463 std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { 464 genObjectListWithModifier<Fortran::parser::AccClause::Copyin>( 465 copyinClause, converter, 466 Fortran::parser::AccDataModifier::Modifier::ReadOnly, 467 copyinReadonlyOperands, copyinOperands); 468 } else if (const auto *copyoutClause = 469 std::get_if<Fortran::parser::AccClause::Copyout>( 470 &clause.u)) { 471 genObjectListWithModifier<Fortran::parser::AccClause::Copyout>( 472 copyoutClause, converter, 473 Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands, 474 copyoutOperands); 475 } else if (const auto *createClause = 476 std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { 477 genObjectListWithModifier<Fortran::parser::AccClause::Create>( 478 createClause, converter, 479 Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands, 480 createOperands); 481 } else if (const auto *noCreateClause = 482 std::get_if<Fortran::parser::AccClause::NoCreate>( 483 &clause.u)) { 484 genObjectList(noCreateClause->v, converter, noCreateOperands); 485 } else if (const auto *presentClause = 486 std::get_if<Fortran::parser::AccClause::Present>( 487 &clause.u)) { 488 genObjectList(presentClause->v, converter, presentOperands); 489 } else if (const auto *deviceptrClause = 490 std::get_if<Fortran::parser::AccClause::Deviceptr>( 491 &clause.u)) { 492 genObjectList(deviceptrClause->v, converter, deviceptrOperands); 493 } else if (const auto *attachClause = 494 std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) { 495 genObjectList(attachClause->v, converter, attachOperands); 496 } 497 } 498 499 // Prepare the operand segement size attribute and the operands value range. 500 SmallVector<Value, 8> operands; 501 SmallVector<int32_t, 8> operandSegments; 502 addOperand(operands, operandSegments, ifCond); 503 addOperands(operands, operandSegments, copyOperands); 504 addOperands(operands, operandSegments, copyinOperands); 505 addOperands(operands, operandSegments, copyinReadonlyOperands); 506 addOperands(operands, operandSegments, copyoutOperands); 507 addOperands(operands, operandSegments, copyoutZeroOperands); 508 addOperands(operands, operandSegments, createOperands); 509 addOperands(operands, operandSegments, createZeroOperands); 510 addOperands(operands, operandSegments, noCreateOperands); 511 addOperands(operands, operandSegments, presentOperands); 512 addOperands(operands, operandSegments, deviceptrOperands); 513 addOperands(operands, operandSegments, attachOperands); 514 515 createRegionOp<mlir::acc::DataOp, mlir::acc::TerminatorOp>( 516 firOpBuilder, currentLocation, operands, operandSegments); 517 } 518 519 static void 520 genACC(Fortran::lower::AbstractConverter &converter, 521 Fortran::lower::pft::Evaluation &eval, 522 const Fortran::parser::OpenACCBlockConstruct &blockConstruct) { 523 const auto &beginBlockDirective = 524 std::get<Fortran::parser::AccBeginBlockDirective>(blockConstruct.t); 525 const auto &blockDirective = 526 std::get<Fortran::parser::AccBlockDirective>(beginBlockDirective.t); 527 const auto &accClauseList = 528 std::get<Fortran::parser::AccClauseList>(beginBlockDirective.t); 529 530 if (blockDirective.v == llvm::acc::ACCD_parallel) { 531 genACCParallelOp(converter, accClauseList); 532 } else if (blockDirective.v == llvm::acc::ACCD_data) { 533 genACCDataOp(converter, accClauseList); 534 } 535 } 536 537 static void 538 genACCEnterDataOp(Fortran::lower::AbstractConverter &converter, 539 const Fortran::parser::AccClauseList &accClauseList) { 540 mlir::Value ifCond, async, waitDevnum; 541 SmallVector<Value, 2> copyinOperands, createOperands, createZeroOperands, 542 attachOperands, waitOperands; 543 544 // Async, wait and self clause have optional values but can be present with 545 // no value as well. When there is no value, the op has an attribute to 546 // represent the clause. 547 bool addAsyncAttr = false; 548 bool addWaitAttr = false; 549 550 auto &firOpBuilder = converter.getFirOpBuilder(); 551 auto currentLocation = converter.getCurrentLocation(); 552 Fortran::lower::StatementContext stmtCtx; 553 554 // Lower clauses values mapped to operands. 555 // Keep track of each group of operands separatly as clauses can appear 556 // more than once. 557 for (const auto &clause : accClauseList.v) { 558 if (const auto *ifClause = 559 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 560 mlir::Value cond = fir::getBase(converter.genExprValue( 561 *Fortran::semantics::GetExpr(ifClause->v), stmtCtx)); 562 ifCond = firOpBuilder.createConvert(currentLocation, 563 firOpBuilder.getI1Type(), cond); 564 } else if (const auto *asyncClause = 565 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 566 const auto &asyncClauseValue = asyncClause->v; 567 if (asyncClauseValue) { // async has a value. 568 async = fir::getBase(converter.genExprValue( 569 *Fortran::semantics::GetExpr(*asyncClauseValue), stmtCtx)); 570 } else { 571 addAsyncAttr = true; 572 } 573 } else if (const auto *waitClause = 574 std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) { 575 const auto &waitClauseValue = waitClause->v; 576 if (waitClauseValue) { // wait has a value. 577 const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue; 578 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 579 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 580 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 581 mlir::Value v = fir::getBase(converter.genExprValue( 582 *Fortran::semantics::GetExpr(value), stmtCtx)); 583 waitOperands.push_back(v); 584 } 585 586 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 587 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 588 if (waitDevnumValue) 589 waitDevnum = fir::getBase(converter.genExprValue( 590 *Fortran::semantics::GetExpr(*waitDevnumValue), stmtCtx)); 591 } else { 592 addWaitAttr = true; 593 } 594 } else if (const auto *copyinClause = 595 std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { 596 const Fortran::parser::AccObjectListWithModifier &listWithModifier = 597 copyinClause->v; 598 const Fortran::parser::AccObjectList &accObjectList = 599 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 600 genObjectList(accObjectList, converter, copyinOperands); 601 } else if (const auto *createClause = 602 std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { 603 genObjectListWithModifier<Fortran::parser::AccClause::Create>( 604 createClause, converter, 605 Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands, 606 createOperands); 607 } else if (const auto *attachClause = 608 std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) { 609 genObjectList(attachClause->v, converter, attachOperands); 610 } else { 611 llvm::report_fatal_error( 612 "Unknown clause in ENTER DATA directive lowering"); 613 } 614 } 615 616 // Prepare the operand segement size attribute and the operands value range. 617 SmallVector<mlir::Value, 16> operands; 618 SmallVector<int32_t, 8> operandSegments; 619 addOperand(operands, operandSegments, ifCond); 620 addOperand(operands, operandSegments, async); 621 addOperand(operands, operandSegments, waitDevnum); 622 addOperands(operands, operandSegments, waitOperands); 623 addOperands(operands, operandSegments, copyinOperands); 624 addOperands(operands, operandSegments, createOperands); 625 addOperands(operands, operandSegments, createZeroOperands); 626 addOperands(operands, operandSegments, attachOperands); 627 628 auto enterDataOp = createSimpleOp<mlir::acc::EnterDataOp>( 629 firOpBuilder, currentLocation, operands, operandSegments); 630 631 if (addAsyncAttr) 632 enterDataOp.asyncAttr(firOpBuilder.getUnitAttr()); 633 if (addWaitAttr) 634 enterDataOp.waitAttr(firOpBuilder.getUnitAttr()); 635 } 636 637 static void 638 genACCExitDataOp(Fortran::lower::AbstractConverter &converter, 639 const Fortran::parser::AccClauseList &accClauseList) { 640 mlir::Value ifCond, async, waitDevnum; 641 SmallVector<Value, 2> copyoutOperands, deleteOperands, detachOperands, 642 waitOperands; 643 644 // Async and wait clause have optional values but can be present with 645 // no value as well. When there is no value, the op has an attribute to 646 // represent the clause. 647 bool addAsyncAttr = false; 648 bool addWaitAttr = false; 649 bool addFinalizeAttr = false; 650 651 auto &firOpBuilder = converter.getFirOpBuilder(); 652 auto currentLocation = converter.getCurrentLocation(); 653 Fortran::lower::StatementContext stmtCtx; 654 655 // Lower clauses values mapped to operands. 656 // Keep track of each group of operands separatly as clauses can appear 657 // more than once. 658 for (const auto &clause : accClauseList.v) { 659 if (const auto *ifClause = 660 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 661 Value cond = fir::getBase(converter.genExprValue( 662 *Fortran::semantics::GetExpr(ifClause->v), stmtCtx)); 663 ifCond = firOpBuilder.createConvert(currentLocation, 664 firOpBuilder.getI1Type(), cond); 665 } else if (const auto *asyncClause = 666 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 667 const auto &asyncClauseValue = asyncClause->v; 668 if (asyncClauseValue) { // async has a value. 669 async = fir::getBase(converter.genExprValue( 670 *Fortran::semantics::GetExpr(*asyncClauseValue), stmtCtx)); 671 } else { 672 addAsyncAttr = true; 673 } 674 } else if (const auto *waitClause = 675 std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) { 676 const auto &waitClauseValue = waitClause->v; 677 if (waitClauseValue) { // wait has a value. 678 const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue; 679 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 680 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 681 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 682 Value v = fir::getBase(converter.genExprValue( 683 *Fortran::semantics::GetExpr(value), stmtCtx)); 684 waitOperands.push_back(v); 685 } 686 687 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 688 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 689 if (waitDevnumValue) 690 waitDevnum = fir::getBase(converter.genExprValue( 691 *Fortran::semantics::GetExpr(*waitDevnumValue), stmtCtx)); 692 } else { 693 addWaitAttr = true; 694 } 695 } else if (const auto *copyoutClause = 696 std::get_if<Fortran::parser::AccClause::Copyout>( 697 &clause.u)) { 698 const Fortran::parser::AccObjectListWithModifier &listWithModifier = 699 copyoutClause->v; 700 const Fortran::parser::AccObjectList &accObjectList = 701 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 702 genObjectList(accObjectList, converter, copyoutOperands); 703 } else if (const auto *deleteClause = 704 std::get_if<Fortran::parser::AccClause::Delete>(&clause.u)) { 705 genObjectList(deleteClause->v, converter, deleteOperands); 706 } else if (const auto *detachClause = 707 std::get_if<Fortran::parser::AccClause::Detach>(&clause.u)) { 708 genObjectList(detachClause->v, converter, detachOperands); 709 } else if (std::get_if<Fortran::parser::AccClause::Finalize>(&clause.u)) { 710 addFinalizeAttr = true; 711 } 712 } 713 714 // Prepare the operand segement size attribute and the operands value range. 715 SmallVector<mlir::Value, 14> operands; 716 SmallVector<int32_t, 7> operandSegments; 717 addOperand(operands, operandSegments, ifCond); 718 addOperand(operands, operandSegments, async); 719 addOperand(operands, operandSegments, waitDevnum); 720 addOperands(operands, operandSegments, waitOperands); 721 addOperands(operands, operandSegments, copyoutOperands); 722 addOperands(operands, operandSegments, deleteOperands); 723 addOperands(operands, operandSegments, detachOperands); 724 725 auto exitDataOp = createSimpleOp<mlir::acc::ExitDataOp>( 726 firOpBuilder, currentLocation, operands, operandSegments); 727 728 if (addAsyncAttr) 729 exitDataOp.asyncAttr(firOpBuilder.getUnitAttr()); 730 if (addWaitAttr) 731 exitDataOp.waitAttr(firOpBuilder.getUnitAttr()); 732 if (addFinalizeAttr) 733 exitDataOp.finalizeAttr(firOpBuilder.getUnitAttr()); 734 } 735 736 template <typename Op> 737 static void 738 genACCInitShutdownOp(Fortran::lower::AbstractConverter &converter, 739 const Fortran::parser::AccClauseList &accClauseList) { 740 mlir::Value ifCond, deviceNum; 741 SmallVector<Value, 2> deviceTypeOperands; 742 743 auto &firOpBuilder = converter.getFirOpBuilder(); 744 auto currentLocation = converter.getCurrentLocation(); 745 Fortran::lower::StatementContext stmtCtx; 746 747 // Lower clauses values mapped to operands. 748 // Keep track of each group of operands separatly as clauses can appear 749 // more than once. 750 for (const auto &clause : accClauseList.v) { 751 if (const auto *ifClause = 752 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 753 mlir::Value cond = fir::getBase(converter.genExprValue( 754 *Fortran::semantics::GetExpr(ifClause->v), stmtCtx)); 755 ifCond = firOpBuilder.createConvert(currentLocation, 756 firOpBuilder.getI1Type(), cond); 757 } else if (const auto *deviceNumClause = 758 std::get_if<Fortran::parser::AccClause::DeviceNum>( 759 &clause.u)) { 760 deviceNum = fir::getBase(converter.genExprValue( 761 *Fortran::semantics::GetExpr(deviceNumClause->v), stmtCtx)); 762 } else if (const auto *deviceTypeClause = 763 std::get_if<Fortran::parser::AccClause::DeviceType>( 764 &clause.u)) { 765 766 const auto &deviceTypeValue = deviceTypeClause->v; 767 if (deviceTypeValue) { 768 for (const auto &scalarIntExpr : *deviceTypeValue) { 769 mlir::Value expr = fir::getBase(converter.genExprValue( 770 *Fortran::semantics::GetExpr(scalarIntExpr), stmtCtx)); 771 deviceTypeOperands.push_back(expr); 772 } 773 } else { 774 // * was passed as value and will be represented as a -1 constant 775 // integer. 776 mlir::Value star = firOpBuilder.createIntegerConstant( 777 currentLocation, firOpBuilder.getIntegerType(32), /* STAR */ -1); 778 deviceTypeOperands.push_back(star); 779 } 780 } 781 } 782 783 // Prepare the operand segement size attribute and the operands value range. 784 SmallVector<mlir::Value, 6> operands; 785 SmallVector<int32_t, 3> operandSegments; 786 addOperands(operands, operandSegments, deviceTypeOperands); 787 addOperand(operands, operandSegments, deviceNum); 788 addOperand(operands, operandSegments, ifCond); 789 790 createSimpleOp<Op>(firOpBuilder, currentLocation, operands, operandSegments); 791 } 792 793 static void 794 genACCUpdateOp(Fortran::lower::AbstractConverter &converter, 795 const Fortran::parser::AccClauseList &accClauseList) { 796 mlir::Value ifCond, async, waitDevnum; 797 SmallVector<Value, 2> hostOperands, deviceOperands, waitOperands, 798 deviceTypeOperands; 799 800 // Async and wait clause have optional values but can be present with 801 // no value as well. When there is no value, the op has an attribute to 802 // represent the clause. 803 bool addAsyncAttr = false; 804 bool addWaitAttr = false; 805 bool addIfPresentAttr = false; 806 807 auto &firOpBuilder = converter.getFirOpBuilder(); 808 auto currentLocation = converter.getCurrentLocation(); 809 Fortran::lower::StatementContext stmtCtx; 810 811 // Lower clauses values mapped to operands. 812 // Keep track of each group of operands separatly as clauses can appear 813 // more than once. 814 for (const auto &clause : accClauseList.v) { 815 if (const auto *ifClause = 816 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 817 mlir::Value cond = fir::getBase(converter.genExprValue( 818 *Fortran::semantics::GetExpr(ifClause->v), stmtCtx)); 819 ifCond = firOpBuilder.createConvert(currentLocation, 820 firOpBuilder.getI1Type(), cond); 821 } else if (const auto *asyncClause = 822 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 823 const auto &asyncClauseValue = asyncClause->v; 824 if (asyncClauseValue) { // async has a value. 825 async = fir::getBase(converter.genExprValue( 826 *Fortran::semantics::GetExpr(*asyncClauseValue), stmtCtx)); 827 } else { 828 addAsyncAttr = true; 829 } 830 } else if (const auto *waitClause = 831 std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) { 832 const auto &waitClauseValue = waitClause->v; 833 if (waitClauseValue) { // wait has a value. 834 const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue; 835 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 836 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 837 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 838 mlir::Value v = fir::getBase(converter.genExprValue( 839 *Fortran::semantics::GetExpr(value), stmtCtx)); 840 waitOperands.push_back(v); 841 } 842 843 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 844 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 845 if (waitDevnumValue) 846 waitDevnum = fir::getBase(converter.genExprValue( 847 *Fortran::semantics::GetExpr(*waitDevnumValue), stmtCtx)); 848 } else { 849 addWaitAttr = true; 850 } 851 } else if (const auto *deviceTypeClause = 852 std::get_if<Fortran::parser::AccClause::DeviceType>( 853 &clause.u)) { 854 855 const auto &deviceTypeValue = deviceTypeClause->v; 856 if (deviceTypeValue) { 857 for (const auto &scalarIntExpr : *deviceTypeValue) { 858 mlir::Value expr = fir::getBase(converter.genExprValue( 859 *Fortran::semantics::GetExpr(scalarIntExpr), stmtCtx)); 860 deviceTypeOperands.push_back(expr); 861 } 862 } else { 863 // * was passed as value and will be represented as a -1 constant 864 // integer. 865 mlir::Value star = firOpBuilder.createIntegerConstant( 866 currentLocation, firOpBuilder.getIntegerType(32), /* STAR */ -1); 867 deviceTypeOperands.push_back(star); 868 } 869 } else if (const auto *hostClause = 870 std::get_if<Fortran::parser::AccClause::Host>(&clause.u)) { 871 genObjectList(hostClause->v, converter, hostOperands); 872 } else if (const auto *deviceClause = 873 std::get_if<Fortran::parser::AccClause::Device>(&clause.u)) { 874 genObjectList(deviceClause->v, converter, deviceOperands); 875 } 876 } 877 878 // Prepare the operand segement size attribute and the operands value range. 879 SmallVector<mlir::Value, 14> operands; 880 SmallVector<int32_t, 7> operandSegments; 881 addOperand(operands, operandSegments, async); 882 addOperand(operands, operandSegments, waitDevnum); 883 addOperands(operands, operandSegments, waitOperands); 884 addOperands(operands, operandSegments, deviceTypeOperands); 885 addOperand(operands, operandSegments, ifCond); 886 addOperands(operands, operandSegments, hostOperands); 887 addOperands(operands, operandSegments, deviceOperands); 888 889 auto updateOp = createSimpleOp<mlir::acc::UpdateOp>( 890 firOpBuilder, currentLocation, operands, operandSegments); 891 892 if (addAsyncAttr) 893 updateOp.asyncAttr(firOpBuilder.getUnitAttr()); 894 if (addWaitAttr) 895 updateOp.waitAttr(firOpBuilder.getUnitAttr()); 896 if (addIfPresentAttr) 897 updateOp.ifPresentAttr(firOpBuilder.getUnitAttr()); 898 } 899 900 static void 901 genACC(Fortran::lower::AbstractConverter &converter, 902 Fortran::lower::pft::Evaluation &eval, 903 const Fortran::parser::OpenACCStandaloneConstruct &standaloneConstruct) { 904 const auto &standaloneDirective = 905 std::get<Fortran::parser::AccStandaloneDirective>(standaloneConstruct.t); 906 const auto &accClauseList = 907 std::get<Fortran::parser::AccClauseList>(standaloneConstruct.t); 908 909 if (standaloneDirective.v == llvm::acc::Directive::ACCD_enter_data) { 910 genACCEnterDataOp(converter, accClauseList); 911 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_exit_data) { 912 genACCExitDataOp(converter, accClauseList); 913 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_init) { 914 genACCInitShutdownOp<mlir::acc::InitOp>(converter, accClauseList); 915 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_shutdown) { 916 genACCInitShutdownOp<mlir::acc::ShutdownOp>(converter, accClauseList); 917 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_set) { 918 TODO(converter.getCurrentLocation(), 919 "OpenACC set directive not lowered yet!"); 920 } else if (standaloneDirective.v == llvm::acc::Directive::ACCD_update) { 921 genACCUpdateOp(converter, accClauseList); 922 } 923 } 924 925 static void genACC(Fortran::lower::AbstractConverter &converter, 926 Fortran::lower::pft::Evaluation &eval, 927 const Fortran::parser::OpenACCWaitConstruct &waitConstruct) { 928 929 const auto &waitArgument = 930 std::get<std::optional<Fortran::parser::AccWaitArgument>>( 931 waitConstruct.t); 932 const auto &accClauseList = 933 std::get<Fortran::parser::AccClauseList>(waitConstruct.t); 934 935 mlir::Value ifCond, waitDevnum, async; 936 SmallVector<mlir::Value, 2> waitOperands; 937 938 // Async clause have optional values but can be present with 939 // no value as well. When there is no value, the op has an attribute to 940 // represent the clause. 941 bool addAsyncAttr = false; 942 943 auto &firOpBuilder = converter.getFirOpBuilder(); 944 auto currentLocation = converter.getCurrentLocation(); 945 Fortran::lower::StatementContext stmtCtx; 946 947 if (waitArgument) { // wait has a value. 948 const Fortran::parser::AccWaitArgument &waitArg = *waitArgument; 949 const std::list<Fortran::parser::ScalarIntExpr> &waitList = 950 std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t); 951 for (const Fortran::parser::ScalarIntExpr &value : waitList) { 952 mlir::Value v = fir::getBase( 953 converter.genExprValue(*Fortran::semantics::GetExpr(value), stmtCtx)); 954 waitOperands.push_back(v); 955 } 956 957 const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue = 958 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t); 959 if (waitDevnumValue) 960 waitDevnum = fir::getBase(converter.genExprValue( 961 *Fortran::semantics::GetExpr(*waitDevnumValue), stmtCtx)); 962 } 963 964 // Lower clauses values mapped to operands. 965 // Keep track of each group of operands separatly as clauses can appear 966 // more than once. 967 for (const auto &clause : accClauseList.v) { 968 if (const auto *ifClause = 969 std::get_if<Fortran::parser::AccClause::If>(&clause.u)) { 970 mlir::Value cond = fir::getBase(converter.genExprValue( 971 *Fortran::semantics::GetExpr(ifClause->v), stmtCtx)); 972 ifCond = firOpBuilder.createConvert(currentLocation, 973 firOpBuilder.getI1Type(), cond); 974 } else if (const auto *asyncClause = 975 std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) { 976 const auto &asyncClauseValue = asyncClause->v; 977 if (asyncClauseValue) { // async has a value. 978 async = fir::getBase(converter.genExprValue( 979 *Fortran::semantics::GetExpr(*asyncClauseValue), stmtCtx)); 980 } else { 981 addAsyncAttr = true; 982 } 983 } 984 } 985 986 // Prepare the operand segement size attribute and the operands value range. 987 SmallVector<mlir::Value, 8> operands; 988 SmallVector<int32_t, 4> operandSegments; 989 addOperands(operands, operandSegments, waitOperands); 990 addOperand(operands, operandSegments, async); 991 addOperand(operands, operandSegments, waitDevnum); 992 addOperand(operands, operandSegments, ifCond); 993 994 auto waitOp = createSimpleOp<mlir::acc::WaitOp>(firOpBuilder, currentLocation, 995 operands, operandSegments); 996 997 if (addAsyncAttr) 998 waitOp.asyncAttr(firOpBuilder.getUnitAttr()); 999 } 1000 1001 void Fortran::lower::genOpenACCConstruct( 1002 Fortran::lower::AbstractConverter &converter, 1003 Fortran::lower::pft::Evaluation &eval, 1004 const Fortran::parser::OpenACCConstruct &accConstruct) { 1005 1006 std::visit( 1007 common::visitors{ 1008 [&](const Fortran::parser::OpenACCBlockConstruct &blockConstruct) { 1009 genACC(converter, eval, blockConstruct); 1010 }, 1011 [&](const Fortran::parser::OpenACCCombinedConstruct 1012 &combinedConstruct) { 1013 TODO(converter.getCurrentLocation(), 1014 "OpenACC Combined construct not lowered yet!"); 1015 }, 1016 [&](const Fortran::parser::OpenACCLoopConstruct &loopConstruct) { 1017 genACC(converter, eval, loopConstruct); 1018 }, 1019 [&](const Fortran::parser::OpenACCStandaloneConstruct 1020 &standaloneConstruct) { 1021 genACC(converter, eval, standaloneConstruct); 1022 }, 1023 [&](const Fortran::parser::OpenACCRoutineConstruct 1024 &routineConstruct) { 1025 TODO(converter.getCurrentLocation(), 1026 "OpenACC Routine construct not lowered yet!"); 1027 }, 1028 [&](const Fortran::parser::OpenACCCacheConstruct &cacheConstruct) { 1029 TODO(converter.getCurrentLocation(), 1030 "OpenACC Cache construct not lowered yet!"); 1031 }, 1032 [&](const Fortran::parser::OpenACCWaitConstruct &waitConstruct) { 1033 genACC(converter, eval, waitConstruct); 1034 }, 1035 [&](const Fortran::parser::OpenACCAtomicConstruct &atomicConstruct) { 1036 TODO(converter.getCurrentLocation(), 1037 "OpenACC Atomic construct not lowered yet!"); 1038 }, 1039 }, 1040 accConstruct.u); 1041 } 1042