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