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 //===----------------------------------------------------------------------===//
105 // ParallelOp
106 //===----------------------------------------------------------------------===//
107 
108 /// Parse acc.parallel operation
109 /// operation := `acc.parallel` `async` `(` index `)`?
110 ///                             `wait` `(` index-list `)`?
111 ///                             `num_gangs` `(` value `)`?
112 ///                             `num_workers` `(` value `)`?
113 ///                             `vector_length` `(` value `)`?
114 ///                             `if` `(` value `)`?
115 ///                             `self` `(` value `)`?
116 ///                             `reduction` `(` value-list `)`?
117 ///                             `copy` `(` value-list `)`?
118 ///                             `copyin` `(` value-list `)`?
119 ///                             `copyout` `(` value-list `)`?
120 ///                             `create` `(` value-list `)`?
121 ///                             `no_create` `(` value-list `)`?
122 ///                             `present` `(` value-list `)`?
123 ///                             `deviceptr` `(` value-list `)`?
124 ///                             `attach` `(` value-list `)`?
125 ///                             `private` `(` value-list `)`?
126 ///                             `firstprivate` `(` value-list `)`?
127 ///                             region attr-dict?
128 static ParseResult parseParallelOp(OpAsmParser &parser,
129                                    OperationState &result) {
130   Builder &builder = parser.getBuilder();
131   SmallVector<OpAsmParser::OperandType, 8> privateOperands,
132       firstprivateOperands, createOperands, copyOperands, copyinOperands,
133       copyoutOperands, noCreateOperands, presentOperands, devicePtrOperands,
134       attachOperands, waitOperands, reductionOperands;
135   SmallVector<Type, 8> operandTypes;
136   OpAsmParser::OperandType async, numGangs, numWorkers, vectorLength, ifCond,
137       selfCond;
138   bool hasAsync = false, hasNumGangs = false, hasNumWorkers = false;
139   bool hasVectorLength = false, hasIfCond = false, hasSelfCond = false;
140 
141   Type indexType = builder.getIndexType();
142   Type i1Type = builder.getI1Type();
143 
144   // async()?
145   if (failed(parseOptionalOperand(parser, ParallelOp::getAsyncKeyword(), async,
146                                   indexType, hasAsync, result)))
147     return failure();
148 
149   // wait()?
150   if (failed(parseOperandList(parser, ParallelOp::getWaitKeyword(),
151                               waitOperands, operandTypes, result)))
152     return failure();
153 
154   // num_gangs(value)?
155   if (failed(parseOptionalOperand(parser, ParallelOp::getNumGangsKeyword(),
156                                   numGangs, indexType, hasNumGangs, result)))
157     return failure();
158 
159   // num_workers(value)?
160   if (failed(parseOptionalOperand(parser, ParallelOp::getNumWorkersKeyword(),
161                                   numWorkers, indexType, hasNumWorkers,
162                                   result)))
163     return failure();
164 
165   // vector_length(value)?
166   if (failed(parseOptionalOperand(parser, ParallelOp::getVectorLengthKeyword(),
167                                   vectorLength, indexType, hasVectorLength,
168                                   result)))
169     return failure();
170 
171   // if()?
172   if (failed(parseOptionalOperand(parser, ParallelOp::getIfKeyword(), ifCond,
173                                   i1Type, hasIfCond, result)))
174     return failure();
175 
176   // self()?
177   if (failed(parseOptionalOperand(parser, ParallelOp::getSelfKeyword(),
178                                   selfCond, i1Type, hasSelfCond, result)))
179     return failure();
180 
181   // reduction()?
182   if (failed(parseOperandList(parser, ParallelOp::getReductionKeyword(),
183                               reductionOperands, operandTypes, result)))
184     return failure();
185 
186   // copy()?
187   if (failed(parseOperandList(parser, ParallelOp::getCopyKeyword(),
188                               copyOperands, operandTypes, result)))
189     return failure();
190 
191   // copyin()?
192   if (failed(parseOperandList(parser, ParallelOp::getCopyinKeyword(),
193                               copyinOperands, operandTypes, result)))
194     return failure();
195 
196   // copyout()?
197   if (failed(parseOperandList(parser, ParallelOp::getCopyoutKeyword(),
198                               copyoutOperands, operandTypes, result)))
199     return failure();
200 
201   // create()?
202   if (failed(parseOperandList(parser, ParallelOp::getCreateKeyword(),
203                               createOperands, operandTypes, result)))
204     return failure();
205 
206   // no_create()?
207   if (failed(parseOperandList(parser, ParallelOp::getNoCreateKeyword(),
208                               noCreateOperands, operandTypes, result)))
209     return failure();
210 
211   // present()?
212   if (failed(parseOperandList(parser, ParallelOp::getPresentKeyword(),
213                               presentOperands, operandTypes, result)))
214     return failure();
215 
216   // deviceptr()?
217   if (failed(parseOperandList(parser, ParallelOp::getDevicePtrKeyword(),
218                               devicePtrOperands, operandTypes, result)))
219     return failure();
220 
221   // attach()?
222   if (failed(parseOperandList(parser, ParallelOp::getAttachKeyword(),
223                               attachOperands, operandTypes, result)))
224     return failure();
225 
226   // private()?
227   if (failed(parseOperandList(parser, ParallelOp::getPrivateKeyword(),
228                               privateOperands, operandTypes, result)))
229     return failure();
230 
231   // firstprivate()?
232   if (failed(parseOperandList(parser, ParallelOp::getFirstPrivateKeyword(),
233                               firstprivateOperands, operandTypes, result)))
234     return failure();
235 
236   // Parallel op region
237   if (failed(parseRegions<ParallelOp>(parser, result)))
238     return failure();
239 
240   result.addAttribute(ParallelOp::getOperandSegmentSizeAttr(),
241                       builder.getI32VectorAttr(
242                           {static_cast<int32_t>(hasAsync ? 1 : 0),
243                            static_cast<int32_t>(waitOperands.size()),
244                            static_cast<int32_t>(hasNumGangs ? 1 : 0),
245                            static_cast<int32_t>(hasNumWorkers ? 1 : 0),
246                            static_cast<int32_t>(hasVectorLength ? 1 : 0),
247                            static_cast<int32_t>(hasIfCond ? 1 : 0),
248                            static_cast<int32_t>(hasSelfCond ? 1 : 0),
249                            static_cast<int32_t>(reductionOperands.size()),
250                            static_cast<int32_t>(copyOperands.size()),
251                            static_cast<int32_t>(copyinOperands.size()),
252                            static_cast<int32_t>(copyoutOperands.size()),
253                            static_cast<int32_t>(createOperands.size()),
254                            static_cast<int32_t>(noCreateOperands.size()),
255                            static_cast<int32_t>(presentOperands.size()),
256                            static_cast<int32_t>(devicePtrOperands.size()),
257                            static_cast<int32_t>(attachOperands.size()),
258                            static_cast<int32_t>(privateOperands.size()),
259                            static_cast<int32_t>(firstprivateOperands.size())}));
260 
261   // Additional attributes
262   if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
263     return failure();
264 
265   return success();
266 }
267 
268 static void print(OpAsmPrinter &printer, ParallelOp &op) {
269   printer << ParallelOp::getOperationName();
270 
271   // async()?
272   if (Value async = op.async())
273     printer << " " << ParallelOp::getAsyncKeyword() << "(" << async << ")";
274 
275   // wait()?
276   printOperandList(op.waitOperands(), ParallelOp::getWaitKeyword(), printer);
277 
278   // num_gangs()?
279   if (Value numGangs = op.numGangs())
280     printer << " " << ParallelOp::getNumGangsKeyword() << "(" << numGangs
281             << ")";
282 
283   // num_workers()?
284   if (Value numWorkers = op.numWorkers())
285     printer << " " << ParallelOp::getNumWorkersKeyword() << "(" << numWorkers
286             << ")";
287 
288   // vector_length()?
289   if (Value vectorLength = op.vectorLength())
290     printer << " " << ParallelOp::getVectorLengthKeyword() << "("
291             << vectorLength << ")";
292 
293   // if()?
294   if (Value ifCond = op.ifCond())
295     printer << " " << ParallelOp::getIfKeyword() << "(" << ifCond << ")";
296 
297   // self()?
298   if (Value selfCond = op.selfCond())
299     printer << " " << ParallelOp::getSelfKeyword() << "(" << selfCond << ")";
300 
301   // reduction()?
302   printOperandList(op.reductionOperands(), ParallelOp::getReductionKeyword(),
303                    printer);
304 
305   // copy()?
306   printOperandList(op.copyOperands(), ParallelOp::getCopyKeyword(), printer);
307 
308   // copyin()?
309   printOperandList(op.copyinOperands(), ParallelOp::getCopyinKeyword(),
310                    printer);
311 
312   // copyout()?
313   printOperandList(op.copyoutOperands(), ParallelOp::getCopyoutKeyword(),
314                    printer);
315 
316   // create()?
317   printOperandList(op.createOperands(), ParallelOp::getCreateKeyword(),
318                    printer);
319 
320   // no_create()?
321   printOperandList(op.noCreateOperands(), ParallelOp::getNoCreateKeyword(),
322                    printer);
323 
324   // present()?
325   printOperandList(op.presentOperands(), ParallelOp::getPresentKeyword(),
326                    printer);
327 
328   // deviceptr()?
329   printOperandList(op.devicePtrOperands(), ParallelOp::getDevicePtrKeyword(),
330                    printer);
331 
332   // attach()?
333   printOperandList(op.attachOperands(), ParallelOp::getAttachKeyword(),
334                    printer);
335 
336   // private()?
337   printOperandList(op.gangPrivateOperands(), ParallelOp::getPrivateKeyword(),
338                    printer);
339 
340   // firstprivate()?
341   printOperandList(op.gangFirstPrivateOperands(),
342                    ParallelOp::getFirstPrivateKeyword(), printer);
343 
344   printer.printRegion(op.region(),
345                       /*printEntryBlockArgs=*/false,
346                       /*printBlockTerminators=*/true);
347   printer.printOptionalAttrDictWithKeyword(
348       op.getAttrs(), ParallelOp::getOperandSegmentSizeAttr());
349 }
350 
351 //===----------------------------------------------------------------------===//
352 // DataOp
353 //===----------------------------------------------------------------------===//
354 
355 /// Parse acc.data operation
356 /// operation := `acc.parallel` `present` `(` value-list `)`?
357 ///                             `copy` `(` value-list `)`?
358 ///                             `copyin` `(` value-list `)`?
359 ///                             `copyout` `(` value-list `)`?
360 ///                             `create` `(` value-list `)`?
361 ///                             `no_create` `(` value-list `)`?
362 ///                             `delete` `(` value-list `)`?
363 ///                             `attach` `(` value-list `)`?
364 ///                             `detach` `(` value-list `)`?
365 ///                             region attr-dict?
366 static ParseResult parseDataOp(OpAsmParser &parser, OperationState &result) {
367   Builder &builder = parser.getBuilder();
368   SmallVector<OpAsmParser::OperandType, 8> presentOperands, copyOperands,
369       copyinOperands, copyoutOperands, createOperands, noCreateOperands,
370       deleteOperands, attachOperands, detachOperands;
371   SmallVector<Type, 8> operandsTypes;
372 
373   // present(value-list)?
374   if (failed(parseOperandList(parser, DataOp::getPresentKeyword(),
375                               presentOperands, operandsTypes, result)))
376     return failure();
377 
378   // copy(value-list)?
379   if (failed(parseOperandList(parser, DataOp::getCopyKeyword(), copyOperands,
380                               operandsTypes, result)))
381     return failure();
382 
383   // copyin(value-list)?
384   if (failed(parseOperandList(parser, DataOp::getCopyinKeyword(),
385                               copyinOperands, operandsTypes, result)))
386     return failure();
387 
388   // copyout(value-list)?
389   if (failed(parseOperandList(parser, DataOp::getCopyoutKeyword(),
390                               copyoutOperands, operandsTypes, result)))
391     return failure();
392 
393   // create(value-list)?
394   if (failed(parseOperandList(parser, DataOp::getCreateKeyword(),
395                               createOperands, operandsTypes, result)))
396     return failure();
397 
398   // no_create(value-list)?
399   if (failed(parseOperandList(parser, DataOp::getCreateKeyword(),
400                               noCreateOperands, operandsTypes, result)))
401     return failure();
402 
403   // delete(value-list)?
404   if (failed(parseOperandList(parser, DataOp::getDeleteKeyword(),
405                               deleteOperands, operandsTypes, result)))
406     return failure();
407 
408   // attach(value-list)?
409   if (failed(parseOperandList(parser, DataOp::getAttachKeyword(),
410                               attachOperands, operandsTypes, result)))
411     return failure();
412 
413   // detach(value-list)?
414   if (failed(parseOperandList(parser, DataOp::getDetachKeyword(),
415                               detachOperands, operandsTypes, result)))
416     return failure();
417 
418   // Data op region
419   if (failed(parseRegions<ParallelOp>(parser, result)))
420     return failure();
421 
422   result.addAttribute(
423       ParallelOp::getOperandSegmentSizeAttr(),
424       builder.getI32VectorAttr({static_cast<int32_t>(presentOperands.size()),
425                                 static_cast<int32_t>(copyOperands.size()),
426                                 static_cast<int32_t>(copyinOperands.size()),
427                                 static_cast<int32_t>(copyoutOperands.size()),
428                                 static_cast<int32_t>(createOperands.size()),
429                                 static_cast<int32_t>(noCreateOperands.size()),
430                                 static_cast<int32_t>(deleteOperands.size()),
431                                 static_cast<int32_t>(attachOperands.size()),
432                                 static_cast<int32_t>(detachOperands.size())}));
433 
434   // Additional attributes
435   if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
436     return failure();
437 
438   return success();
439 }
440 
441 static void print(OpAsmPrinter &printer, DataOp &op) {
442   printer << DataOp::getOperationName();
443 
444   // present(value-list)?
445   printOperandList(op.presentOperands(), DataOp::getPresentKeyword(), printer);
446 
447   // copy(value-list)?
448   printOperandList(op.copyOperands(), DataOp::getCopyKeyword(), printer);
449 
450   // copyin(value-list)?
451   printOperandList(op.copyinOperands(), DataOp::getCopyinKeyword(), printer);
452 
453   // copyout(value-list)?
454   printOperandList(op.copyoutOperands(), DataOp::getCopyoutKeyword(), printer);
455 
456   // create(value-list)?
457   printOperandList(op.createOperands(), DataOp::getCreateKeyword(), printer);
458 
459   // no_create(value-list)?
460   printOperandList(op.noCreateOperands(), DataOp::getNoCreateKeyword(),
461                    printer);
462 
463   // delete(value-list)?
464   printOperandList(op.deleteOperands(), DataOp::getDeleteKeyword(), printer);
465 
466   // attach(value-list)?
467   printOperandList(op.attachOperands(), DataOp::getAttachKeyword(), printer);
468 
469   // detach(value-list)?
470   printOperandList(op.detachOperands(), DataOp::getDetachKeyword(), printer);
471 
472   printer.printRegion(op.region(),
473                       /*printEntryBlockArgs=*/false,
474                       /*printBlockTerminators=*/true);
475   printer.printOptionalAttrDictWithKeyword(
476       op.getAttrs(), ParallelOp::getOperandSegmentSizeAttr());
477 }
478 
479 //===----------------------------------------------------------------------===//
480 // LoopOp
481 //===----------------------------------------------------------------------===//
482 
483 /// Parse acc.loop operation
484 /// operation := `acc.loop` `gang`? `vector`? `worker`?
485 ///                         `private` `(` value-list `)`?
486 ///                         `reduction` `(` value-list `)`?
487 ///                         region attr-dict?
488 static ParseResult parseLoopOp(OpAsmParser &parser, OperationState &result) {
489   Builder &builder = parser.getBuilder();
490   unsigned executionMapping = OpenACCExecMapping::NONE;
491   SmallVector<Type, 8> operandTypes;
492   SmallVector<OpAsmParser::OperandType, 8> privateOperands, reductionOperands;
493   SmallVector<OpAsmParser::OperandType, 8> tileOperands;
494   bool hasWorkerNum = false, hasVectorLength = false, hasGangNum = false;
495   bool hasGangStatic = false;
496   OpAsmParser::OperandType workerNum, vectorLength, gangNum, gangStatic;
497   Type intType = builder.getI64Type();
498 
499   // gang?
500   if (succeeded(parser.parseOptionalKeyword(LoopOp::getGangKeyword())))
501     executionMapping |= OpenACCExecMapping::GANG;
502 
503   // optional gang operand
504   if (succeeded(parser.parseOptionalLParen())) {
505     if (succeeded(parser.parseOptionalKeyword(LoopOp::getGangNumKeyword()))) {
506       hasGangNum = true;
507       parser.parseColon();
508       if (parser.parseOperand(gangNum) ||
509           parser.resolveOperand(gangNum, intType, result.operands)) {
510         return failure();
511       }
512     }
513     parser.parseOptionalComma();
514     if (succeeded(
515             parser.parseOptionalKeyword(LoopOp::getGangStaticKeyword()))) {
516       hasGangStatic = true;
517       parser.parseColon();
518       if (parser.parseOperand(gangStatic) ||
519           parser.resolveOperand(gangStatic, intType, result.operands)) {
520         return failure();
521       }
522     }
523     if (failed(parser.parseRParen()))
524       return failure();
525   }
526 
527   // worker?
528   if (succeeded(parser.parseOptionalKeyword(LoopOp::getWorkerKeyword())))
529     executionMapping |= OpenACCExecMapping::WORKER;
530 
531   // optional worker operand
532   if (succeeded(parser.parseOptionalLParen())) {
533     hasWorkerNum = true;
534     if (parser.parseOperand(workerNum) ||
535         parser.resolveOperand(workerNum, intType, result.operands) ||
536         parser.parseRParen()) {
537       return failure();
538     }
539   }
540 
541   // vector?
542   if (succeeded(parser.parseOptionalKeyword(LoopOp::getVectorKeyword())))
543     executionMapping |= OpenACCExecMapping::VECTOR;
544 
545   // optional vector operand
546   if (succeeded(parser.parseOptionalLParen())) {
547     hasVectorLength = true;
548     if (parser.parseOperand(vectorLength) ||
549         parser.resolveOperand(vectorLength, intType, result.operands) ||
550         parser.parseRParen()) {
551       return failure();
552     }
553   }
554 
555   // tile()?
556   if (failed(parseOperandList(parser, LoopOp::getTileKeyword(), tileOperands,
557                               operandTypes, result)))
558     return failure();
559 
560   // private()?
561   if (failed(parseOperandList(parser, LoopOp::getPrivateKeyword(),
562                               privateOperands, operandTypes, result)))
563     return failure();
564 
565   // reduction()?
566   if (failed(parseOperandList(parser, LoopOp::getReductionKeyword(),
567                               reductionOperands, operandTypes, result)))
568     return failure();
569 
570   if (executionMapping != acc::OpenACCExecMapping::NONE)
571     result.addAttribute(LoopOp::getExecutionMappingAttrName(),
572                         builder.getI64IntegerAttr(executionMapping));
573 
574   // Parse optional results in case there is a reduce.
575   if (parser.parseOptionalArrowTypeList(result.types))
576     return failure();
577 
578   if (failed(parseRegions<LoopOp>(parser, result)))
579     return failure();
580 
581   result.addAttribute(LoopOp::getOperandSegmentSizeAttr(),
582                       builder.getI32VectorAttr(
583                           {static_cast<int32_t>(hasGangNum ? 1 : 0),
584                            static_cast<int32_t>(hasGangStatic ? 1 : 0),
585                            static_cast<int32_t>(hasWorkerNum ? 1 : 0),
586                            static_cast<int32_t>(hasVectorLength ? 1 : 0),
587                            static_cast<int32_t>(tileOperands.size()),
588                            static_cast<int32_t>(privateOperands.size()),
589                            static_cast<int32_t>(reductionOperands.size())}));
590 
591   if (failed(parser.parseOptionalAttrDictWithKeyword(result.attributes)))
592     return failure();
593 
594   return success();
595 }
596 
597 static void print(OpAsmPrinter &printer, LoopOp &op) {
598   printer << LoopOp::getOperationName();
599 
600   unsigned execMapping = op.exec_mapping();
601   if (execMapping & OpenACCExecMapping::GANG) {
602     printer << " " << LoopOp::getGangKeyword();
603     Value gangNum = op.gangNum();
604     Value gangStatic = op.gangStatic();
605 
606     // Print optional gang operands
607     if (gangNum || gangStatic) {
608       printer << "(";
609       if (gangNum) {
610         printer << LoopOp::getGangNumKeyword() << ": " << gangNum;
611         if (gangStatic)
612           printer << ", ";
613       }
614       if (gangStatic)
615         printer << LoopOp::getGangStaticKeyword() << ": " << gangStatic;
616       printer << ")";
617     }
618   }
619 
620   if (execMapping & OpenACCExecMapping::WORKER) {
621     printer << " " << LoopOp::getWorkerKeyword();
622 
623     // Print optional worker operand if present
624     if (Value workerNum = op.workerNum())
625       printer << "(" << workerNum << ")";
626   }
627 
628   if (execMapping & OpenACCExecMapping::VECTOR) {
629     printer << " " << LoopOp::getVectorKeyword();
630 
631     // Print optional vector operand if present
632     if (Value vectorLength = op.vectorLength())
633       printer << "(" << vectorLength << ")";
634   }
635 
636   // tile()?
637   printOperandList(op.tileOperands(), LoopOp::getTileKeyword(), printer);
638 
639   // private()?
640   printOperandList(op.privateOperands(), LoopOp::getPrivateKeyword(), printer);
641 
642   // reduction()?
643   printOperandList(op.reductionOperands(), LoopOp::getReductionKeyword(),
644                    printer);
645 
646   if (op.getNumResults() > 0)
647     printer << " -> (" << op.getResultTypes() << ")";
648 
649   printer.printRegion(op.region(),
650                       /*printEntryBlockArgs=*/false,
651                       /*printBlockTerminators=*/true);
652 
653   printer.printOptionalAttrDictWithKeyword(
654       op.getAttrs(), {LoopOp::getExecutionMappingAttrName(),
655                       LoopOp::getOperandSegmentSizeAttr()});
656 }
657 
658 static LogicalResult verifyLoopOp(acc::LoopOp loopOp) {
659   // auto, independent and seq attribute are mutually exclusive.
660   if ((loopOp.auto_() && (loopOp.independent() || loopOp.seq())) ||
661       (loopOp.independent() && loopOp.seq())) {
662     loopOp.emitError("only one of " + acc::LoopOp::getAutoAttrName() + ", " +
663                      acc::LoopOp::getIndependentAttrName() + ", " +
664                      acc::LoopOp::getSeqAttrName() +
665                      " can be present at the same time");
666     return failure();
667   }
668 
669   // Gang, worker and vector are incompatible with seq.
670   if (loopOp.seq() && loopOp.exec_mapping() != OpenACCExecMapping::NONE) {
671     loopOp.emitError("gang, worker or vector cannot appear with the seq attr");
672     return failure();
673   }
674 
675   // Check non-empty body().
676   if (loopOp.region().empty()) {
677     loopOp.emitError("expected non-empty body.");
678     return failure();
679   }
680 
681   return success();
682 }
683 
684 #define GET_OP_CLASSES
685 #include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc"
686