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