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