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