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