1 //===- OpenACC.cpp - OpenACC MLIR Operations ------------------------------===// 2 // 3 // Part of the MLIR 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 #include "mlir/Dialect/OpenACC/OpenACC.h" 10 #include "mlir/Dialect/OpenACC/OpenACCOpsEnums.cpp.inc" 11 #include "mlir/IR/Builders.h" 12 #include "mlir/IR/BuiltinTypes.h" 13 #include "mlir/IR/OpImplementation.h" 14 15 using namespace mlir; 16 using namespace acc; 17 18 //===----------------------------------------------------------------------===// 19 // OpenACC operations 20 //===----------------------------------------------------------------------===// 21 22 void OpenACCDialect::initialize() { 23 addOperations< 24 #define GET_OP_LIST 25 #include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc" 26 >(); 27 } 28 29 template <typename StructureOp> 30 static ParseResult parseRegions(OpAsmParser &parser, OperationState &state, 31 unsigned nRegions = 1) { 32 33 SmallVector<Region *, 2> regions; 34 for (unsigned i = 0; i < nRegions; ++i) 35 regions.push_back(state.addRegion()); 36 37 for (Region *region : regions) { 38 if (parser.parseRegion(*region, /*arguments=*/{}, /*argTypes=*/{})) 39 return failure(); 40 } 41 42 return success(); 43 } 44 45 static ParseResult 46 parseOperandList(OpAsmParser &parser, StringRef keyword, 47 SmallVectorImpl<OpAsmParser::OperandType> &args, 48 SmallVectorImpl<Type> &argTypes, OperationState &result) { 49 if (failed(parser.parseOptionalKeyword(keyword))) 50 return success(); 51 52 if (failed(parser.parseLParen())) 53 return failure(); 54 55 // Exit early if the list is empty. 56 if (succeeded(parser.parseOptionalRParen())) 57 return success(); 58 59 do { 60 OpAsmParser::OperandType arg; 61 Type type; 62 63 if (parser.parseRegionArgument(arg) || parser.parseColonType(type)) 64 return failure(); 65 66 args.push_back(arg); 67 argTypes.push_back(type); 68 } while (succeeded(parser.parseOptionalComma())); 69 70 if (failed(parser.parseRParen())) 71 return failure(); 72 73 return parser.resolveOperands(args, argTypes, parser.getCurrentLocation(), 74 result.operands); 75 } 76 77 static void printOperandList(Operation::operand_range operands, 78 StringRef listName, OpAsmPrinter &printer) { 79 80 if (operands.size() > 0) { 81 printer << " " << listName << "("; 82 llvm::interleaveComma(operands, printer, [&](Value op) { 83 printer << op << ": " << op.getType(); 84 }); 85 printer << ")"; 86 } 87 } 88 89 static ParseResult parseOptionalOperand(OpAsmParser &parser, StringRef keyword, 90 OpAsmParser::OperandType &operand, 91 Type type, bool &hasOptional, 92 OperationState &result) { 93 hasOptional = false; 94 if (succeeded(parser.parseOptionalKeyword(keyword))) { 95 hasOptional = true; 96 if (parser.parseLParen() || parser.parseOperand(operand) || 97 parser.resolveOperand(operand, type, result.operands) || 98 parser.parseRParen()) 99 return failure(); 100 } 101 return success(); 102 } 103 104 static ParseResult parseOperandAndType(OpAsmParser &parser, 105 OperationState &result) { 106 OpAsmParser::OperandType operand; 107 Type type; 108 if (parser.parseOperand(operand) || parser.parseColonType(type) || 109 parser.resolveOperand(operand, type, result.operands)) 110 return failure(); 111 return success(); 112 } 113 114 /// Parse optional operand and its type wrapped in parenthesis prefixed with 115 /// a keyword. 116 /// Example: 117 /// keyword `(` %vectorLength: i64 `)` 118 static OptionalParseResult parseOptionalOperandAndType(OpAsmParser &parser, 119 StringRef keyword, 120 OperationState &result) { 121 OpAsmParser::OperandType operand; 122 if (succeeded(parser.parseOptionalKeyword(keyword))) { 123 return failure(parser.parseLParen() || 124 parseOperandAndType(parser, result) || parser.parseRParen()); 125 } 126 return llvm::None; 127 } 128 129 /// Parse optional operand and its type wrapped in parenthesis. 130 /// Example: 131 /// `(` %vectorLength: i64 `)` 132 static OptionalParseResult parseOptionalOperandAndType(OpAsmParser &parser, 133 OperationState &result) { 134 if (succeeded(parser.parseOptionalLParen())) { 135 return failure(parseOperandAndType(parser, result) || parser.parseRParen()); 136 } 137 return llvm::None; 138 } 139 140 /// Parse optional operand with its type prefixed with prefixKeyword `=`. 141 /// Example: 142 /// num=%gangNum: i32 143 static OptionalParseResult parserOptionalOperandAndTypeWithPrefix( 144 OpAsmParser &parser, OperationState &result, StringRef prefixKeyword) { 145 if (succeeded(parser.parseOptionalKeyword(prefixKeyword))) { 146 parser.parseEqual(); 147 return parseOperandAndType(parser, result); 148 } 149 return llvm::None; 150 } 151 152 static bool isComputeOperation(Operation *op) { 153 return isa<acc::ParallelOp>(op) || isa<acc::LoopOp>(op); 154 } 155 156 //===----------------------------------------------------------------------===// 157 // ParallelOp 158 //===----------------------------------------------------------------------===// 159 160 /// Parse acc.parallel operation 161 /// operation := `acc.parallel` `async` `(` index `)`? 162 /// `wait` `(` index-list `)`? 163 /// `num_gangs` `(` value `)`? 164 /// `num_workers` `(` value `)`? 165 /// `vector_length` `(` value `)`? 166 /// `if` `(` value `)`? 167 /// `self` `(` value `)`? 168 /// `reduction` `(` value-list `)`? 169 /// `copy` `(` value-list `)`? 170 /// `copyin` `(` value-list `)`? 171 /// `copyin_readonly` `(` value-list `)`? 172 /// `copyout` `(` value-list `)`? 173 /// `copyout_zero` `(` value-list `)`? 174 /// `create` `(` value-list `)`? 175 /// `create_zero` `(` value-list `)`? 176 /// `no_create` `(` value-list `)`? 177 /// `present` `(` value-list `)`? 178 /// `deviceptr` `(` value-list `)`? 179 /// `attach` `(` value-list `)`? 180 /// `private` `(` value-list `)`? 181 /// `firstprivate` `(` value-list `)`? 182 /// region attr-dict? 183 static ParseResult parseParallelOp(OpAsmParser &parser, 184 OperationState &result) { 185 Builder &builder = parser.getBuilder(); 186 SmallVector<OpAsmParser::OperandType, 8> privateOperands, 187 firstprivateOperands, copyOperands, copyinOperands, 188 copyinReadonlyOperands, copyoutOperands, copyoutZeroOperands, 189 createOperands, createZeroOperands, noCreateOperands, presentOperands, 190 devicePtrOperands, attachOperands, waitOperands, reductionOperands; 191 SmallVector<Type, 8> waitOperandTypes, reductionOperandTypes, 192 copyOperandTypes, copyinOperandTypes, copyinReadonlyOperandTypes, 193 copyoutOperandTypes, copyoutZeroOperandTypes, createOperandTypes, 194 createZeroOperandTypes, noCreateOperandTypes, presentOperandTypes, 195 deviceptrOperandTypes, attachOperandTypes, privateOperandTypes, 196 firstprivateOperandTypes; 197 198 SmallVector<Type, 8> operandTypes; 199 OpAsmParser::OperandType ifCond, selfCond; 200 bool hasIfCond = false, hasSelfCond = false; 201 OptionalParseResult async, numGangs, numWorkers, vectorLength; 202 Type i1Type = builder.getI1Type(); 203 204 // async()? 205 async = parseOptionalOperandAndType(parser, ParallelOp::getAsyncKeyword(), 206 result); 207 if (async.hasValue() && failed(*async)) 208 return failure(); 209 210 // wait()? 211 if (failed(parseOperandList(parser, ParallelOp::getWaitKeyword(), 212 waitOperands, waitOperandTypes, result))) 213 return failure(); 214 215 // num_gangs(value)? 216 numGangs = parseOptionalOperandAndType( 217 parser, ParallelOp::getNumGangsKeyword(), result); 218 if (numGangs.hasValue() && failed(*numGangs)) 219 return failure(); 220 221 // num_workers(value)? 222 numWorkers = parseOptionalOperandAndType( 223 parser, ParallelOp::getNumWorkersKeyword(), result); 224 if (numWorkers.hasValue() && failed(*numWorkers)) 225 return failure(); 226 227 // vector_length(value)? 228 vectorLength = parseOptionalOperandAndType( 229 parser, ParallelOp::getVectorLengthKeyword(), result); 230 if (vectorLength.hasValue() && failed(*vectorLength)) 231 return failure(); 232 233 // if()? 234 if (failed(parseOptionalOperand(parser, ParallelOp::getIfKeyword(), ifCond, 235 i1Type, hasIfCond, result))) 236 return failure(); 237 238 // self()? 239 if (failed(parseOptionalOperand(parser, ParallelOp::getSelfKeyword(), 240 selfCond, i1Type, hasSelfCond, result))) 241 return failure(); 242 243 // reduction()? 244 if (failed(parseOperandList(parser, ParallelOp::getReductionKeyword(), 245 reductionOperands, reductionOperandTypes, 246 result))) 247 return failure(); 248 249 // copy()? 250 if (failed(parseOperandList(parser, ParallelOp::getCopyKeyword(), 251 copyOperands, copyOperandTypes, result))) 252 return failure(); 253 254 // copyin()? 255 if (failed(parseOperandList(parser, ParallelOp::getCopyinKeyword(), 256 copyinOperands, copyinOperandTypes, result))) 257 return failure(); 258 259 // copyin_readonly()? 260 if (failed(parseOperandList(parser, ParallelOp::getCopyinReadonlyKeyword(), 261 copyinReadonlyOperands, 262 copyinReadonlyOperandTypes, result))) 263 return failure(); 264 265 // copyout()? 266 if (failed(parseOperandList(parser, ParallelOp::getCopyoutKeyword(), 267 copyoutOperands, copyoutOperandTypes, result))) 268 return failure(); 269 270 // copyout_zero()? 271 if (failed(parseOperandList(parser, ParallelOp::getCopyoutZeroKeyword(), 272 copyoutZeroOperands, copyoutZeroOperandTypes, 273 result))) 274 return failure(); 275 276 // create()? 277 if (failed(parseOperandList(parser, ParallelOp::getCreateKeyword(), 278 createOperands, createOperandTypes, result))) 279 return failure(); 280 281 // create_zero()? 282 if (failed(parseOperandList(parser, ParallelOp::getCreateZeroKeyword(), 283 createZeroOperands, createZeroOperandTypes, 284 result))) 285 return failure(); 286 287 // no_create()? 288 if (failed(parseOperandList(parser, ParallelOp::getNoCreateKeyword(), 289 noCreateOperands, noCreateOperandTypes, result))) 290 return failure(); 291 292 // present()? 293 if (failed(parseOperandList(parser, ParallelOp::getPresentKeyword(), 294 presentOperands, presentOperandTypes, result))) 295 return failure(); 296 297 // deviceptr()? 298 if (failed(parseOperandList(parser, ParallelOp::getDevicePtrKeyword(), 299 devicePtrOperands, deviceptrOperandTypes, 300 result))) 301 return failure(); 302 303 // attach()? 304 if (failed(parseOperandList(parser, ParallelOp::getAttachKeyword(), 305 attachOperands, attachOperandTypes, result))) 306 return failure(); 307 308 // private()? 309 if (failed(parseOperandList(parser, ParallelOp::getPrivateKeyword(), 310 privateOperands, privateOperandTypes, result))) 311 return failure(); 312 313 // firstprivate()? 314 if (failed(parseOperandList(parser, ParallelOp::getFirstPrivateKeyword(), 315 firstprivateOperands, firstprivateOperandTypes, 316 result))) 317 return failure(); 318 319 // Parallel op region 320 if (failed(parseRegions<ParallelOp>(parser, result))) 321 return failure(); 322 323 result.addAttribute( 324 ParallelOp::getOperandSegmentSizeAttr(), 325 builder.getI32VectorAttr( 326 {static_cast<int32_t>(async.hasValue() ? 1 : 0), 327 static_cast<int32_t>(waitOperands.size()), 328 static_cast<int32_t>(numGangs.hasValue() ? 1 : 0), 329 static_cast<int32_t>(numWorkers.hasValue() ? 1 : 0), 330 static_cast<int32_t>(vectorLength.hasValue() ? 1 : 0), 331 static_cast<int32_t>(hasIfCond ? 1 : 0), 332 static_cast<int32_t>(hasSelfCond ? 1 : 0), 333 static_cast<int32_t>(reductionOperands.size()), 334 static_cast<int32_t>(copyOperands.size()), 335 static_cast<int32_t>(copyinOperands.size()), 336 static_cast<int32_t>(copyinReadonlyOperands.size()), 337 static_cast<int32_t>(copyoutOperands.size()), 338 static_cast<int32_t>(copyoutZeroOperands.size()), 339 static_cast<int32_t>(createOperands.size()), 340 static_cast<int32_t>(createZeroOperands.size()), 341 static_cast<int32_t>(noCreateOperands.size()), 342 static_cast<int32_t>(presentOperands.size()), 343 static_cast<int32_t>(devicePtrOperands.size()), 344 static_cast<int32_t>(attachOperands.size()), 345 static_cast<int32_t>(privateOperands.size()), 346 static_cast<int32_t>(firstprivateOperands.size())})); 347 348 // Additional attributes 349 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes))) 350 return failure(); 351 352 return success(); 353 } 354 355 static void print(OpAsmPrinter &printer, ParallelOp &op) { 356 printer << ParallelOp::getOperationName(); 357 358 // async()? 359 if (Value async = op.async()) 360 printer << " " << ParallelOp::getAsyncKeyword() << "(" << async << ": " 361 << async.getType() << ")"; 362 363 // wait()? 364 printOperandList(op.waitOperands(), ParallelOp::getWaitKeyword(), printer); 365 366 // num_gangs()? 367 if (Value numGangs = op.numGangs()) 368 printer << " " << ParallelOp::getNumGangsKeyword() << "(" << numGangs 369 << ": " << numGangs.getType() << ")"; 370 371 // num_workers()? 372 if (Value numWorkers = op.numWorkers()) 373 printer << " " << ParallelOp::getNumWorkersKeyword() << "(" << numWorkers 374 << ": " << numWorkers.getType() << ")"; 375 376 // vector_length()? 377 if (Value vectorLength = op.vectorLength()) 378 printer << " " << ParallelOp::getVectorLengthKeyword() << "(" 379 << vectorLength << ": " << vectorLength.getType() << ")"; 380 381 // if()? 382 if (Value ifCond = op.ifCond()) 383 printer << " " << ParallelOp::getIfKeyword() << "(" << ifCond << ")"; 384 385 // self()? 386 if (Value selfCond = op.selfCond()) 387 printer << " " << ParallelOp::getSelfKeyword() << "(" << selfCond << ")"; 388 389 // reduction()? 390 printOperandList(op.reductionOperands(), ParallelOp::getReductionKeyword(), 391 printer); 392 393 // copy()? 394 printOperandList(op.copyOperands(), ParallelOp::getCopyKeyword(), printer); 395 396 // copyin()? 397 printOperandList(op.copyinOperands(), ParallelOp::getCopyinKeyword(), 398 printer); 399 400 // copyin_readonly()? 401 printOperandList(op.copyinReadonlyOperands(), 402 ParallelOp::getCopyinReadonlyKeyword(), printer); 403 404 // copyout()? 405 printOperandList(op.copyoutOperands(), ParallelOp::getCopyoutKeyword(), 406 printer); 407 408 // copyout_zero()? 409 printOperandList(op.copyoutZeroOperands(), 410 ParallelOp::getCopyoutZeroKeyword(), printer); 411 412 // create()? 413 printOperandList(op.createOperands(), ParallelOp::getCreateKeyword(), 414 printer); 415 416 // create_zero()? 417 printOperandList(op.createZeroOperands(), ParallelOp::getCreateZeroKeyword(), 418 printer); 419 420 // no_create()? 421 printOperandList(op.noCreateOperands(), ParallelOp::getNoCreateKeyword(), 422 printer); 423 424 // present()? 425 printOperandList(op.presentOperands(), ParallelOp::getPresentKeyword(), 426 printer); 427 428 // deviceptr()? 429 printOperandList(op.devicePtrOperands(), ParallelOp::getDevicePtrKeyword(), 430 printer); 431 432 // attach()? 433 printOperandList(op.attachOperands(), ParallelOp::getAttachKeyword(), 434 printer); 435 436 // private()? 437 printOperandList(op.gangPrivateOperands(), ParallelOp::getPrivateKeyword(), 438 printer); 439 440 // firstprivate()? 441 printOperandList(op.gangFirstPrivateOperands(), 442 ParallelOp::getFirstPrivateKeyword(), printer); 443 444 printer.printRegion(op.region(), 445 /*printEntryBlockArgs=*/false, 446 /*printBlockTerminators=*/true); 447 printer.printOptionalAttrDictWithKeyword( 448 op->getAttrs(), ParallelOp::getOperandSegmentSizeAttr()); 449 } 450 451 //===----------------------------------------------------------------------===// 452 // LoopOp 453 //===----------------------------------------------------------------------===// 454 455 /// Parse acc.loop operation 456 /// operation := `acc.loop` 457 /// (`gang` ( `(` (`num=` value)? (`,` `static=` value `)`)? )? )? 458 /// (`vector` ( `(` value `)` )? )? (`worker` (`(` value `)`)? )? 459 /// (`vector_length` `(` value `)`)? 460 /// (`tile` `(` value-list `)`)? 461 /// (`private` `(` value-list `)`)? 462 /// (`reduction` `(` value-list `)`)? 463 /// region attr-dict? 464 static ParseResult parseLoopOp(OpAsmParser &parser, OperationState &result) { 465 Builder &builder = parser.getBuilder(); 466 unsigned executionMapping = OpenACCExecMapping::NONE; 467 SmallVector<Type, 8> operandTypes; 468 SmallVector<OpAsmParser::OperandType, 8> privateOperands, reductionOperands; 469 SmallVector<OpAsmParser::OperandType, 8> tileOperands; 470 OptionalParseResult gangNum, gangStatic, worker, vector; 471 472 // gang? 473 if (succeeded(parser.parseOptionalKeyword(LoopOp::getGangKeyword()))) 474 executionMapping |= OpenACCExecMapping::GANG; 475 476 // optional gang operand 477 if (succeeded(parser.parseOptionalLParen())) { 478 gangNum = parserOptionalOperandAndTypeWithPrefix( 479 parser, result, LoopOp::getGangNumKeyword()); 480 if (gangNum.hasValue() && failed(*gangNum)) 481 return failure(); 482 parser.parseOptionalComma(); 483 gangStatic = parserOptionalOperandAndTypeWithPrefix( 484 parser, result, LoopOp::getGangStaticKeyword()); 485 if (gangStatic.hasValue() && failed(*gangStatic)) 486 return failure(); 487 parser.parseOptionalComma(); 488 if (failed(parser.parseRParen())) 489 return failure(); 490 } 491 492 // worker? 493 if (succeeded(parser.parseOptionalKeyword(LoopOp::getWorkerKeyword()))) 494 executionMapping |= OpenACCExecMapping::WORKER; 495 496 // optional worker operand 497 worker = parseOptionalOperandAndType(parser, result); 498 if (worker.hasValue() && failed(*worker)) 499 return failure(); 500 501 // vector? 502 if (succeeded(parser.parseOptionalKeyword(LoopOp::getVectorKeyword()))) 503 executionMapping |= OpenACCExecMapping::VECTOR; 504 505 // optional vector operand 506 vector = parseOptionalOperandAndType(parser, result); 507 if (vector.hasValue() && failed(*vector)) 508 return failure(); 509 510 // tile()? 511 if (failed(parseOperandList(parser, LoopOp::getTileKeyword(), tileOperands, 512 operandTypes, result))) 513 return failure(); 514 515 // private()? 516 if (failed(parseOperandList(parser, LoopOp::getPrivateKeyword(), 517 privateOperands, operandTypes, result))) 518 return failure(); 519 520 // reduction()? 521 if (failed(parseOperandList(parser, LoopOp::getReductionKeyword(), 522 reductionOperands, operandTypes, result))) 523 return failure(); 524 525 if (executionMapping != acc::OpenACCExecMapping::NONE) 526 result.addAttribute(LoopOp::getExecutionMappingAttrName(), 527 builder.getI64IntegerAttr(executionMapping)); 528 529 // Parse optional results in case there is a reduce. 530 if (parser.parseOptionalArrowTypeList(result.types)) 531 return failure(); 532 533 if (failed(parseRegions<LoopOp>(parser, result))) 534 return failure(); 535 536 result.addAttribute(LoopOp::getOperandSegmentSizeAttr(), 537 builder.getI32VectorAttr( 538 {static_cast<int32_t>(gangNum.hasValue() ? 1 : 0), 539 static_cast<int32_t>(gangStatic.hasValue() ? 1 : 0), 540 static_cast<int32_t>(worker.hasValue() ? 1 : 0), 541 static_cast<int32_t>(vector.hasValue() ? 1 : 0), 542 static_cast<int32_t>(tileOperands.size()), 543 static_cast<int32_t>(privateOperands.size()), 544 static_cast<int32_t>(reductionOperands.size())})); 545 546 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes))) 547 return failure(); 548 549 return success(); 550 } 551 552 static void print(OpAsmPrinter &printer, LoopOp &op) { 553 printer << LoopOp::getOperationName(); 554 555 unsigned execMapping = op.exec_mapping(); 556 if (execMapping & OpenACCExecMapping::GANG) { 557 printer << " " << LoopOp::getGangKeyword(); 558 Value gangNum = op.gangNum(); 559 Value gangStatic = op.gangStatic(); 560 561 // Print optional gang operands 562 if (gangNum || gangStatic) { 563 printer << "("; 564 if (gangNum) { 565 printer << LoopOp::getGangNumKeyword() << "=" << gangNum << ": " 566 << gangNum.getType(); 567 if (gangStatic) 568 printer << ", "; 569 } 570 if (gangStatic) 571 printer << LoopOp::getGangStaticKeyword() << "=" << gangStatic << ": " 572 << gangStatic.getType(); 573 printer << ")"; 574 } 575 } 576 577 if (execMapping & OpenACCExecMapping::WORKER) { 578 printer << " " << LoopOp::getWorkerKeyword(); 579 580 // Print optional worker operand if present 581 if (Value workerNum = op.workerNum()) 582 printer << "(" << workerNum << ": " << workerNum.getType() << ")"; 583 } 584 585 if (execMapping & OpenACCExecMapping::VECTOR) { 586 printer << " " << LoopOp::getVectorKeyword(); 587 588 // Print optional vector operand if present 589 if (Value vectorLength = op.vectorLength()) 590 printer << "(" << vectorLength << ": " << vectorLength.getType() << ")"; 591 } 592 593 // tile()? 594 printOperandList(op.tileOperands(), LoopOp::getTileKeyword(), printer); 595 596 // private()? 597 printOperandList(op.privateOperands(), LoopOp::getPrivateKeyword(), printer); 598 599 // reduction()? 600 printOperandList(op.reductionOperands(), LoopOp::getReductionKeyword(), 601 printer); 602 603 if (op.getNumResults() > 0) 604 printer << " -> (" << op.getResultTypes() << ")"; 605 606 printer.printRegion(op.region(), 607 /*printEntryBlockArgs=*/false, 608 /*printBlockTerminators=*/true); 609 610 printer.printOptionalAttrDictWithKeyword( 611 op->getAttrs(), {LoopOp::getExecutionMappingAttrName(), 612 LoopOp::getOperandSegmentSizeAttr()}); 613 } 614 615 static LogicalResult verifyLoopOp(acc::LoopOp loopOp) { 616 // auto, independent and seq attribute are mutually exclusive. 617 if ((loopOp.auto_() && (loopOp.independent() || loopOp.seq())) || 618 (loopOp.independent() && loopOp.seq())) { 619 loopOp.emitError("only one of " + acc::LoopOp::getAutoAttrName() + ", " + 620 acc::LoopOp::getIndependentAttrName() + ", " + 621 acc::LoopOp::getSeqAttrName() + 622 " can be present at the same time"); 623 return failure(); 624 } 625 626 // Gang, worker and vector are incompatible with seq. 627 if (loopOp.seq() && loopOp.exec_mapping() != OpenACCExecMapping::NONE) { 628 loopOp.emitError("gang, worker or vector cannot appear with the seq attr"); 629 return failure(); 630 } 631 632 // Check non-empty body(). 633 if (loopOp.region().empty()) { 634 loopOp.emitError("expected non-empty body."); 635 return failure(); 636 } 637 638 return success(); 639 } 640 641 //===----------------------------------------------------------------------===// 642 // DataOp 643 //===----------------------------------------------------------------------===// 644 645 static LogicalResult verify(acc::DataOp dataOp) { 646 // 2.6.5. Data Construct restriction 647 // At least one copy, copyin, copyout, create, no_create, present, deviceptr, 648 // attach, or default clause must appear on a data construct. 649 if (dataOp.getOperands().size() == 0 && !dataOp.defaultAttr()) 650 return dataOp.emitError("at least one operand or the default attribute " 651 "must appear on the data operation"); 652 return success(); 653 } 654 655 //===----------------------------------------------------------------------===// 656 // ExitDataOp 657 //===----------------------------------------------------------------------===// 658 659 static LogicalResult verify(acc::ExitDataOp op) { 660 // 2.6.6. Data Exit Directive restriction 661 // At least one copyout, delete, or detach clause must appear on an exit data 662 // directive. 663 if (op.copyoutOperands().empty() && op.deleteOperands().empty() && 664 op.detachOperands().empty()) 665 return op.emitError( 666 "at least one operand in copyout, delete or detach must appear on the " 667 "exit data operation"); 668 669 // The async attribute represent the async clause without value. Therefore the 670 // attribute and operand cannot appear at the same time. 671 if (op.asyncOperand() && op.async()) 672 return op.emitError("async attribute cannot appear with asyncOperand"); 673 674 // The wait attribute represent the wait clause without values. Therefore the 675 // attribute and operands cannot appear at the same time. 676 if (!op.waitOperands().empty() && op.wait()) 677 return op.emitError("wait attribute cannot appear with waitOperands"); 678 679 if (op.waitDevnum() && op.waitOperands().empty()) 680 return op.emitError("wait_devnum cannot appear without waitOperands"); 681 682 return success(); 683 } 684 685 //===----------------------------------------------------------------------===// 686 // DataEnterOp 687 //===----------------------------------------------------------------------===// 688 689 static LogicalResult verify(acc::EnterDataOp op) { 690 // 2.6.6. Data Enter Directive restriction 691 // At least one copyin, create, or attach clause must appear on an enter data 692 // directive. 693 if (op.copyinOperands().empty() && op.createOperands().empty() && 694 op.createZeroOperands().empty() && op.attachOperands().empty()) 695 return op.emitError( 696 "at least one operand in copyin, create, " 697 "create_zero or attach must appear on the enter data operation"); 698 699 // The async attribute represent the async clause without value. Therefore the 700 // attribute and operand cannot appear at the same time. 701 if (op.asyncOperand() && op.async()) 702 return op.emitError("async attribute cannot appear with asyncOperand"); 703 704 // The wait attribute represent the wait clause without values. Therefore the 705 // attribute and operands cannot appear at the same time. 706 if (!op.waitOperands().empty() && op.wait()) 707 return op.emitError("wait attribute cannot appear with waitOperands"); 708 709 if (op.waitDevnum() && op.waitOperands().empty()) 710 return op.emitError("wait_devnum cannot appear without waitOperands"); 711 712 return success(); 713 } 714 715 //===----------------------------------------------------------------------===// 716 // InitOp 717 //===----------------------------------------------------------------------===// 718 719 static LogicalResult verify(acc::InitOp initOp) { 720 Operation *currOp = initOp; 721 while ((currOp = currOp->getParentOp())) { 722 if (isComputeOperation(currOp)) 723 return initOp.emitOpError("cannot be nested in a compute operation"); 724 } 725 return success(); 726 } 727 728 //===----------------------------------------------------------------------===// 729 // ShutdownOp 730 //===----------------------------------------------------------------------===// 731 732 static LogicalResult verify(acc::ShutdownOp op) { 733 Operation *currOp = op; 734 while ((currOp = currOp->getParentOp())) { 735 if (isComputeOperation(currOp)) 736 return op.emitOpError("cannot be nested in a compute operation"); 737 } 738 return success(); 739 } 740 741 //===----------------------------------------------------------------------===// 742 // UpdateOp 743 //===----------------------------------------------------------------------===// 744 745 static LogicalResult verify(acc::UpdateOp updateOp) { 746 // At least one of host or device should have a value. 747 if (updateOp.hostOperands().size() == 0 && 748 updateOp.deviceOperands().size() == 0) 749 return updateOp.emitError("at least one value must be present in" 750 " hostOperands or deviceOperands"); 751 752 // The async attribute represent the async clause without value. Therefore the 753 // attribute and operand cannot appear at the same time. 754 if (updateOp.asyncOperand() && updateOp.async()) 755 return updateOp.emitError("async attribute cannot appear with " 756 " asyncOperand"); 757 758 // The wait attribute represent the wait clause without values. Therefore the 759 // attribute and operands cannot appear at the same time. 760 if (updateOp.waitOperands().size() > 0 && updateOp.wait()) 761 return updateOp.emitError("wait attribute cannot appear with waitOperands"); 762 763 if (updateOp.waitDevnum() && updateOp.waitOperands().size() == 0) 764 return updateOp.emitError("wait_devnum cannot appear without waitOperands"); 765 766 return success(); 767 } 768 769 //===----------------------------------------------------------------------===// 770 // WaitOp 771 //===----------------------------------------------------------------------===// 772 773 static LogicalResult verify(acc::WaitOp waitOp) { 774 // The async attribute represent the async clause without value. Therefore the 775 // attribute and operand cannot appear at the same time. 776 if (waitOp.asyncOperand() && waitOp.async()) 777 return waitOp.emitError("async attribute cannot appear with asyncOperand"); 778 779 if (waitOp.waitDevnum() && waitOp.waitOperands().empty()) 780 return waitOp.emitError("wait_devnum cannot appear without waitOperands"); 781 782 return success(); 783 } 784 785 #define GET_OP_CLASSES 786 #include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc" 787