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