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