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 #include "mlir/Dialect/OpenACC/OpenACCOpsDialect.cpp.inc" 21 22 //===----------------------------------------------------------------------===// 23 // OpenACC operations 24 //===----------------------------------------------------------------------===// 25 26 void OpenACCDialect::initialize() { 27 addOperations< 28 #define GET_OP_LIST 29 #include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc" 30 >(); 31 } 32 33 template <typename StructureOp> 34 static ParseResult parseRegions(OpAsmParser &parser, OperationState &state, 35 unsigned nRegions = 1) { 36 37 SmallVector<Region *, 2> regions; 38 for (unsigned i = 0; i < nRegions; ++i) 39 regions.push_back(state.addRegion()); 40 41 for (Region *region : regions) { 42 if (parser.parseRegion(*region, /*arguments=*/{}, /*argTypes=*/{})) 43 return failure(); 44 } 45 46 return success(); 47 } 48 49 static ParseResult 50 parseOperandList(OpAsmParser &parser, StringRef keyword, 51 SmallVectorImpl<OpAsmParser::OperandType> &args, 52 SmallVectorImpl<Type> &argTypes, OperationState &result) { 53 if (failed(parser.parseOptionalKeyword(keyword))) 54 return success(); 55 56 if (failed(parser.parseLParen())) 57 return failure(); 58 59 // Exit early if the list is empty. 60 if (succeeded(parser.parseOptionalRParen())) 61 return success(); 62 63 do { 64 OpAsmParser::OperandType arg; 65 Type type; 66 67 if (parser.parseRegionArgument(arg) || parser.parseColonType(type)) 68 return failure(); 69 70 args.push_back(arg); 71 argTypes.push_back(type); 72 } while (succeeded(parser.parseOptionalComma())); 73 74 if (failed(parser.parseRParen())) 75 return failure(); 76 77 return parser.resolveOperands(args, argTypes, parser.getCurrentLocation(), 78 result.operands); 79 } 80 81 static void printOperandList(Operation::operand_range operands, 82 StringRef listName, OpAsmPrinter &printer) { 83 84 if (operands.size() > 0) { 85 printer << " " << listName << "("; 86 llvm::interleaveComma(operands, printer, [&](Value op) { 87 printer << op << ": " << op.getType(); 88 }); 89 printer << ")"; 90 } 91 } 92 93 static ParseResult parseOptionalOperand(OpAsmParser &parser, StringRef keyword, 94 OpAsmParser::OperandType &operand, 95 Type type, bool &hasOptional, 96 OperationState &result) { 97 hasOptional = false; 98 if (succeeded(parser.parseOptionalKeyword(keyword))) { 99 hasOptional = true; 100 if (parser.parseLParen() || parser.parseOperand(operand) || 101 parser.resolveOperand(operand, type, result.operands) || 102 parser.parseRParen()) 103 return failure(); 104 } 105 return success(); 106 } 107 108 static ParseResult parseOperandAndType(OpAsmParser &parser, 109 OperationState &result) { 110 OpAsmParser::OperandType operand; 111 Type type; 112 if (parser.parseOperand(operand) || parser.parseColonType(type) || 113 parser.resolveOperand(operand, type, result.operands)) 114 return failure(); 115 return success(); 116 } 117 118 /// Parse optional operand and its type wrapped in parenthesis prefixed with 119 /// a keyword. 120 /// Example: 121 /// keyword `(` %vectorLength: i64 `)` 122 static OptionalParseResult parseOptionalOperandAndType(OpAsmParser &parser, 123 StringRef keyword, 124 OperationState &result) { 125 OpAsmParser::OperandType operand; 126 if (succeeded(parser.parseOptionalKeyword(keyword))) { 127 return failure(parser.parseLParen() || 128 parseOperandAndType(parser, result) || parser.parseRParen()); 129 } 130 return llvm::None; 131 } 132 133 /// Parse optional operand and its type wrapped in parenthesis. 134 /// Example: 135 /// `(` %vectorLength: i64 `)` 136 static OptionalParseResult parseOptionalOperandAndType(OpAsmParser &parser, 137 OperationState &result) { 138 if (succeeded(parser.parseOptionalLParen())) { 139 return failure(parseOperandAndType(parser, result) || parser.parseRParen()); 140 } 141 return llvm::None; 142 } 143 144 /// Parse optional operand with its type prefixed with prefixKeyword `=`. 145 /// Example: 146 /// num=%gangNum: i32 147 static OptionalParseResult parserOptionalOperandAndTypeWithPrefix( 148 OpAsmParser &parser, OperationState &result, StringRef prefixKeyword) { 149 if (succeeded(parser.parseOptionalKeyword(prefixKeyword))) { 150 parser.parseEqual(); 151 return parseOperandAndType(parser, result); 152 } 153 return llvm::None; 154 } 155 156 static bool isComputeOperation(Operation *op) { 157 return isa<acc::ParallelOp>(op) || isa<acc::LoopOp>(op); 158 } 159 160 namespace { 161 /// Pattern to remove operation without region that have constant false `ifCond` 162 /// and remove the condition from the operation if the `ifCond` is a true 163 /// constant. 164 template <typename OpTy> 165 struct RemoveConstantIfCondition : public OpRewritePattern<OpTy> { 166 using OpRewritePattern<OpTy>::OpRewritePattern; 167 168 LogicalResult matchAndRewrite(OpTy op, 169 PatternRewriter &rewriter) const override { 170 // Early return if there is no condition. 171 if (!op.ifCond()) 172 return success(); 173 174 auto constOp = op.ifCond().template getDefiningOp<ConstantOp>(); 175 if (constOp && constOp.getValue().template cast<IntegerAttr>().getInt()) 176 rewriter.updateRootInPlace(op, [&]() { op.ifCondMutable().erase(0); }); 177 else if (constOp) 178 rewriter.eraseOp(op); 179 180 return success(); 181 } 182 }; 183 } // namespace 184 185 //===----------------------------------------------------------------------===// 186 // ParallelOp 187 //===----------------------------------------------------------------------===// 188 189 /// Parse acc.parallel operation 190 /// operation := `acc.parallel` `async` `(` index `)`? 191 /// `wait` `(` index-list `)`? 192 /// `num_gangs` `(` value `)`? 193 /// `num_workers` `(` value `)`? 194 /// `vector_length` `(` value `)`? 195 /// `if` `(` value `)`? 196 /// `self` `(` value `)`? 197 /// `reduction` `(` value-list `)`? 198 /// `copy` `(` value-list `)`? 199 /// `copyin` `(` value-list `)`? 200 /// `copyin_readonly` `(` value-list `)`? 201 /// `copyout` `(` value-list `)`? 202 /// `copyout_zero` `(` value-list `)`? 203 /// `create` `(` value-list `)`? 204 /// `create_zero` `(` value-list `)`? 205 /// `no_create` `(` value-list `)`? 206 /// `present` `(` value-list `)`? 207 /// `deviceptr` `(` value-list `)`? 208 /// `attach` `(` value-list `)`? 209 /// `private` `(` value-list `)`? 210 /// `firstprivate` `(` value-list `)`? 211 /// region attr-dict? 212 static ParseResult parseParallelOp(OpAsmParser &parser, 213 OperationState &result) { 214 Builder &builder = parser.getBuilder(); 215 SmallVector<OpAsmParser::OperandType, 8> privateOperands, 216 firstprivateOperands, copyOperands, copyinOperands, 217 copyinReadonlyOperands, copyoutOperands, copyoutZeroOperands, 218 createOperands, createZeroOperands, noCreateOperands, presentOperands, 219 devicePtrOperands, attachOperands, waitOperands, reductionOperands; 220 SmallVector<Type, 8> waitOperandTypes, reductionOperandTypes, 221 copyOperandTypes, copyinOperandTypes, copyinReadonlyOperandTypes, 222 copyoutOperandTypes, copyoutZeroOperandTypes, createOperandTypes, 223 createZeroOperandTypes, noCreateOperandTypes, presentOperandTypes, 224 deviceptrOperandTypes, attachOperandTypes, privateOperandTypes, 225 firstprivateOperandTypes; 226 227 SmallVector<Type, 8> operandTypes; 228 OpAsmParser::OperandType ifCond, selfCond; 229 bool hasIfCond = false, hasSelfCond = false; 230 OptionalParseResult async, numGangs, numWorkers, vectorLength; 231 Type i1Type = builder.getI1Type(); 232 233 // async()? 234 async = parseOptionalOperandAndType(parser, ParallelOp::getAsyncKeyword(), 235 result); 236 if (async.hasValue() && failed(*async)) 237 return failure(); 238 239 // wait()? 240 if (failed(parseOperandList(parser, ParallelOp::getWaitKeyword(), 241 waitOperands, waitOperandTypes, result))) 242 return failure(); 243 244 // num_gangs(value)? 245 numGangs = parseOptionalOperandAndType( 246 parser, ParallelOp::getNumGangsKeyword(), result); 247 if (numGangs.hasValue() && failed(*numGangs)) 248 return failure(); 249 250 // num_workers(value)? 251 numWorkers = parseOptionalOperandAndType( 252 parser, ParallelOp::getNumWorkersKeyword(), result); 253 if (numWorkers.hasValue() && failed(*numWorkers)) 254 return failure(); 255 256 // vector_length(value)? 257 vectorLength = parseOptionalOperandAndType( 258 parser, ParallelOp::getVectorLengthKeyword(), result); 259 if (vectorLength.hasValue() && failed(*vectorLength)) 260 return failure(); 261 262 // if()? 263 if (failed(parseOptionalOperand(parser, ParallelOp::getIfKeyword(), ifCond, 264 i1Type, hasIfCond, result))) 265 return failure(); 266 267 // self()? 268 if (failed(parseOptionalOperand(parser, ParallelOp::getSelfKeyword(), 269 selfCond, i1Type, hasSelfCond, result))) 270 return failure(); 271 272 // reduction()? 273 if (failed(parseOperandList(parser, ParallelOp::getReductionKeyword(), 274 reductionOperands, reductionOperandTypes, 275 result))) 276 return failure(); 277 278 // copy()? 279 if (failed(parseOperandList(parser, ParallelOp::getCopyKeyword(), 280 copyOperands, copyOperandTypes, result))) 281 return failure(); 282 283 // copyin()? 284 if (failed(parseOperandList(parser, ParallelOp::getCopyinKeyword(), 285 copyinOperands, copyinOperandTypes, result))) 286 return failure(); 287 288 // copyin_readonly()? 289 if (failed(parseOperandList(parser, ParallelOp::getCopyinReadonlyKeyword(), 290 copyinReadonlyOperands, 291 copyinReadonlyOperandTypes, result))) 292 return failure(); 293 294 // copyout()? 295 if (failed(parseOperandList(parser, ParallelOp::getCopyoutKeyword(), 296 copyoutOperands, copyoutOperandTypes, result))) 297 return failure(); 298 299 // copyout_zero()? 300 if (failed(parseOperandList(parser, ParallelOp::getCopyoutZeroKeyword(), 301 copyoutZeroOperands, copyoutZeroOperandTypes, 302 result))) 303 return failure(); 304 305 // create()? 306 if (failed(parseOperandList(parser, ParallelOp::getCreateKeyword(), 307 createOperands, createOperandTypes, result))) 308 return failure(); 309 310 // create_zero()? 311 if (failed(parseOperandList(parser, ParallelOp::getCreateZeroKeyword(), 312 createZeroOperands, createZeroOperandTypes, 313 result))) 314 return failure(); 315 316 // no_create()? 317 if (failed(parseOperandList(parser, ParallelOp::getNoCreateKeyword(), 318 noCreateOperands, noCreateOperandTypes, result))) 319 return failure(); 320 321 // present()? 322 if (failed(parseOperandList(parser, ParallelOp::getPresentKeyword(), 323 presentOperands, presentOperandTypes, result))) 324 return failure(); 325 326 // deviceptr()? 327 if (failed(parseOperandList(parser, ParallelOp::getDevicePtrKeyword(), 328 devicePtrOperands, deviceptrOperandTypes, 329 result))) 330 return failure(); 331 332 // attach()? 333 if (failed(parseOperandList(parser, ParallelOp::getAttachKeyword(), 334 attachOperands, attachOperandTypes, result))) 335 return failure(); 336 337 // private()? 338 if (failed(parseOperandList(parser, ParallelOp::getPrivateKeyword(), 339 privateOperands, privateOperandTypes, result))) 340 return failure(); 341 342 // firstprivate()? 343 if (failed(parseOperandList(parser, ParallelOp::getFirstPrivateKeyword(), 344 firstprivateOperands, firstprivateOperandTypes, 345 result))) 346 return failure(); 347 348 // Parallel op region 349 if (failed(parseRegions<ParallelOp>(parser, result))) 350 return failure(); 351 352 result.addAttribute( 353 ParallelOp::getOperandSegmentSizeAttr(), 354 builder.getI32VectorAttr( 355 {static_cast<int32_t>(async.hasValue() ? 1 : 0), 356 static_cast<int32_t>(waitOperands.size()), 357 static_cast<int32_t>(numGangs.hasValue() ? 1 : 0), 358 static_cast<int32_t>(numWorkers.hasValue() ? 1 : 0), 359 static_cast<int32_t>(vectorLength.hasValue() ? 1 : 0), 360 static_cast<int32_t>(hasIfCond ? 1 : 0), 361 static_cast<int32_t>(hasSelfCond ? 1 : 0), 362 static_cast<int32_t>(reductionOperands.size()), 363 static_cast<int32_t>(copyOperands.size()), 364 static_cast<int32_t>(copyinOperands.size()), 365 static_cast<int32_t>(copyinReadonlyOperands.size()), 366 static_cast<int32_t>(copyoutOperands.size()), 367 static_cast<int32_t>(copyoutZeroOperands.size()), 368 static_cast<int32_t>(createOperands.size()), 369 static_cast<int32_t>(createZeroOperands.size()), 370 static_cast<int32_t>(noCreateOperands.size()), 371 static_cast<int32_t>(presentOperands.size()), 372 static_cast<int32_t>(devicePtrOperands.size()), 373 static_cast<int32_t>(attachOperands.size()), 374 static_cast<int32_t>(privateOperands.size()), 375 static_cast<int32_t>(firstprivateOperands.size())})); 376 377 // Additional attributes 378 if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes))) 379 return failure(); 380 381 return success(); 382 } 383 384 static void print(OpAsmPrinter &printer, ParallelOp &op) { 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 unsigned execMapping = op.exec_mapping(); 601 if (execMapping & OpenACCExecMapping::GANG) { 602 printer << " " << LoopOp::getGangKeyword(); 603 Value gangNum = op.gangNum(); 604 Value gangStatic = op.gangStatic(); 605 606 // Print optional gang operands 607 if (gangNum || gangStatic) { 608 printer << "("; 609 if (gangNum) { 610 printer << LoopOp::getGangNumKeyword() << "=" << gangNum << ": " 611 << gangNum.getType(); 612 if (gangStatic) 613 printer << ", "; 614 } 615 if (gangStatic) 616 printer << LoopOp::getGangStaticKeyword() << "=" << gangStatic << ": " 617 << gangStatic.getType(); 618 printer << ")"; 619 } 620 } 621 622 if (execMapping & OpenACCExecMapping::WORKER) { 623 printer << " " << LoopOp::getWorkerKeyword(); 624 625 // Print optional worker operand if present 626 if (Value workerNum = op.workerNum()) 627 printer << "(" << workerNum << ": " << workerNum.getType() << ")"; 628 } 629 630 if (execMapping & OpenACCExecMapping::VECTOR) { 631 printer << " " << LoopOp::getVectorKeyword(); 632 633 // Print optional vector operand if present 634 if (Value vectorLength = op.vectorLength()) 635 printer << "(" << vectorLength << ": " << vectorLength.getType() << ")"; 636 } 637 638 // tile()? 639 printOperandList(op.tileOperands(), LoopOp::getTileKeyword(), printer); 640 641 // private()? 642 printOperandList(op.privateOperands(), LoopOp::getPrivateKeyword(), printer); 643 644 // reduction()? 645 printOperandList(op.reductionOperands(), LoopOp::getReductionKeyword(), 646 printer); 647 648 if (op.getNumResults() > 0) 649 printer << " -> (" << op.getResultTypes() << ")"; 650 651 printer.printRegion(op.region(), 652 /*printEntryBlockArgs=*/false, 653 /*printBlockTerminators=*/true); 654 655 printer.printOptionalAttrDictWithKeyword( 656 op->getAttrs(), {LoopOp::getExecutionMappingAttrName(), 657 LoopOp::getOperandSegmentSizeAttr()}); 658 } 659 660 static LogicalResult verifyLoopOp(acc::LoopOp loopOp) { 661 // auto, independent and seq attribute are mutually exclusive. 662 if ((loopOp.auto_() && (loopOp.independent() || loopOp.seq())) || 663 (loopOp.independent() && loopOp.seq())) { 664 loopOp.emitError("only one of " + acc::LoopOp::getAutoAttrName() + ", " + 665 acc::LoopOp::getIndependentAttrName() + ", " + 666 acc::LoopOp::getSeqAttrName() + 667 " can be present at the same time"); 668 return failure(); 669 } 670 671 // Gang, worker and vector are incompatible with seq. 672 if (loopOp.seq() && loopOp.exec_mapping() != OpenACCExecMapping::NONE) { 673 loopOp.emitError("gang, worker or vector cannot appear with the seq attr"); 674 return failure(); 675 } 676 677 // Check non-empty body(). 678 if (loopOp.region().empty()) { 679 loopOp.emitError("expected non-empty body."); 680 return failure(); 681 } 682 683 return success(); 684 } 685 686 //===----------------------------------------------------------------------===// 687 // DataOp 688 //===----------------------------------------------------------------------===// 689 690 static LogicalResult verify(acc::DataOp dataOp) { 691 // 2.6.5. Data Construct restriction 692 // At least one copy, copyin, copyout, create, no_create, present, deviceptr, 693 // attach, or default clause must appear on a data construct. 694 if (dataOp.getOperands().size() == 0 && !dataOp.defaultAttr()) 695 return dataOp.emitError("at least one operand or the default attribute " 696 "must appear on the data operation"); 697 return success(); 698 } 699 700 unsigned DataOp::getNumDataOperands() { 701 return copyOperands().size() + copyinOperands().size() + 702 copyinReadonlyOperands().size() + copyoutOperands().size() + 703 copyoutZeroOperands().size() + createOperands().size() + 704 createZeroOperands().size() + noCreateOperands().size() + 705 presentOperands().size() + deviceptrOperands().size() + 706 attachOperands().size(); 707 } 708 709 Value DataOp::getDataOperand(unsigned i) { 710 unsigned numOptional = ifCond() ? 1 : 0; 711 return getOperand(numOptional + i); 712 } 713 714 //===----------------------------------------------------------------------===// 715 // ExitDataOp 716 //===----------------------------------------------------------------------===// 717 718 static LogicalResult verify(acc::ExitDataOp op) { 719 // 2.6.6. Data Exit Directive restriction 720 // At least one copyout, delete, or detach clause must appear on an exit data 721 // directive. 722 if (op.copyoutOperands().empty() && op.deleteOperands().empty() && 723 op.detachOperands().empty()) 724 return op.emitError( 725 "at least one operand in copyout, delete or detach must appear on the " 726 "exit data operation"); 727 728 // The async attribute represent the async clause without value. Therefore the 729 // attribute and operand cannot appear at the same time. 730 if (op.asyncOperand() && op.async()) 731 return op.emitError("async attribute cannot appear with asyncOperand"); 732 733 // The wait attribute represent the wait clause without values. Therefore the 734 // attribute and operands cannot appear at the same time. 735 if (!op.waitOperands().empty() && op.wait()) 736 return op.emitError("wait attribute cannot appear with waitOperands"); 737 738 if (op.waitDevnum() && op.waitOperands().empty()) 739 return op.emitError("wait_devnum cannot appear without waitOperands"); 740 741 return success(); 742 } 743 744 unsigned ExitDataOp::getNumDataOperands() { 745 return copyoutOperands().size() + deleteOperands().size() + 746 detachOperands().size(); 747 } 748 749 Value ExitDataOp::getDataOperand(unsigned i) { 750 unsigned numOptional = ifCond() ? 1 : 0; 751 numOptional += asyncOperand() ? 1 : 0; 752 numOptional += waitDevnum() ? 1 : 0; 753 return getOperand(waitOperands().size() + numOptional + i); 754 } 755 756 void ExitDataOp::getCanonicalizationPatterns(RewritePatternSet &results, 757 MLIRContext *context) { 758 results.add<RemoveConstantIfCondition<ExitDataOp>>(context); 759 } 760 761 //===----------------------------------------------------------------------===// 762 // EnterDataOp 763 //===----------------------------------------------------------------------===// 764 765 static LogicalResult verify(acc::EnterDataOp op) { 766 // 2.6.6. Data Enter Directive restriction 767 // At least one copyin, create, or attach clause must appear on an enter data 768 // directive. 769 if (op.copyinOperands().empty() && op.createOperands().empty() && 770 op.createZeroOperands().empty() && op.attachOperands().empty()) 771 return op.emitError( 772 "at least one operand in copyin, create, " 773 "create_zero or attach must appear on the enter data operation"); 774 775 // The async attribute represent the async clause without value. Therefore the 776 // attribute and operand cannot appear at the same time. 777 if (op.asyncOperand() && op.async()) 778 return op.emitError("async attribute cannot appear with asyncOperand"); 779 780 // The wait attribute represent the wait clause without values. Therefore the 781 // attribute and operands cannot appear at the same time. 782 if (!op.waitOperands().empty() && op.wait()) 783 return op.emitError("wait attribute cannot appear with waitOperands"); 784 785 if (op.waitDevnum() && op.waitOperands().empty()) 786 return op.emitError("wait_devnum cannot appear without waitOperands"); 787 788 return success(); 789 } 790 791 unsigned EnterDataOp::getNumDataOperands() { 792 return copyinOperands().size() + createOperands().size() + 793 createZeroOperands().size() + attachOperands().size(); 794 } 795 796 Value EnterDataOp::getDataOperand(unsigned i) { 797 unsigned numOptional = ifCond() ? 1 : 0; 798 numOptional += asyncOperand() ? 1 : 0; 799 numOptional += waitDevnum() ? 1 : 0; 800 return getOperand(waitOperands().size() + numOptional + i); 801 } 802 803 void EnterDataOp::getCanonicalizationPatterns(RewritePatternSet &results, 804 MLIRContext *context) { 805 results.add<RemoveConstantIfCondition<EnterDataOp>>(context); 806 } 807 808 //===----------------------------------------------------------------------===// 809 // InitOp 810 //===----------------------------------------------------------------------===// 811 812 static LogicalResult verify(acc::InitOp initOp) { 813 Operation *currOp = initOp; 814 while ((currOp = currOp->getParentOp())) { 815 if (isComputeOperation(currOp)) 816 return initOp.emitOpError("cannot be nested in a compute operation"); 817 } 818 return success(); 819 } 820 821 //===----------------------------------------------------------------------===// 822 // ShutdownOp 823 //===----------------------------------------------------------------------===// 824 825 static LogicalResult verify(acc::ShutdownOp op) { 826 Operation *currOp = op; 827 while ((currOp = currOp->getParentOp())) { 828 if (isComputeOperation(currOp)) 829 return op.emitOpError("cannot be nested in a compute operation"); 830 } 831 return success(); 832 } 833 834 //===----------------------------------------------------------------------===// 835 // UpdateOp 836 //===----------------------------------------------------------------------===// 837 838 static LogicalResult verify(acc::UpdateOp updateOp) { 839 // At least one of host or device should have a value. 840 if (updateOp.hostOperands().size() == 0 && 841 updateOp.deviceOperands().size() == 0) 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().size() > 0 && updateOp.wait()) 854 return updateOp.emitError("wait attribute cannot appear with waitOperands"); 855 856 if (updateOp.waitDevnum() && updateOp.waitOperands().size() == 0) 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