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