1 //===- OpenMPDialect.cpp - MLIR Dialect for OpenMP implementation ---------===//
2 //
3 // Part of the LLVM 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 // This file implements the OpenMP dialect and its operations.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
14 #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
15 #include "mlir/Dialect/StandardOps/IR/Ops.h"
16 #include "mlir/IR/Attributes.h"
17 #include "mlir/IR/OpImplementation.h"
18 #include "mlir/IR/OperationSupport.h"
19 
20 #include "llvm/ADT/BitVector.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/StringExtras.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/StringSwitch.h"
25 #include <cstddef>
26 
27 #include "mlir/Dialect/OpenMP/OpenMPOpsDialect.cpp.inc"
28 #include "mlir/Dialect/OpenMP/OpenMPOpsEnums.cpp.inc"
29 #include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.cpp.inc"
30 
31 using namespace mlir;
32 using namespace mlir::omp;
33 
34 namespace {
35 /// Model for pointer-like types that already provide a `getElementType` method.
36 template <typename T>
37 struct PointerLikeModel
38     : public PointerLikeType::ExternalModel<PointerLikeModel<T>, T> {
39   Type getElementType(Type pointer) const {
40     return pointer.cast<T>().getElementType();
41   }
42 };
43 } // end namespace
44 
45 void OpenMPDialect::initialize() {
46   addOperations<
47 #define GET_OP_LIST
48 #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
49       >();
50 
51   LLVM::LLVMPointerType::attachInterface<
52       PointerLikeModel<LLVM::LLVMPointerType>>(*getContext());
53   MemRefType::attachInterface<PointerLikeModel<MemRefType>>(*getContext());
54 }
55 
56 //===----------------------------------------------------------------------===//
57 // ParallelOp
58 //===----------------------------------------------------------------------===//
59 
60 void ParallelOp::build(OpBuilder &builder, OperationState &state,
61                        ArrayRef<NamedAttribute> attributes) {
62   ParallelOp::build(
63       builder, state, /*if_expr_var=*/nullptr, /*num_threads_var=*/nullptr,
64       /*default_val=*/nullptr, /*private_vars=*/ValueRange(),
65       /*firstprivate_vars=*/ValueRange(), /*shared_vars=*/ValueRange(),
66       /*copyin_vars=*/ValueRange(), /*allocate_vars=*/ValueRange(),
67       /*allocators_vars=*/ValueRange(), /*proc_bind_val=*/nullptr);
68   state.addAttributes(attributes);
69 }
70 
71 //===----------------------------------------------------------------------===//
72 // Parser and printer for Operand and type list
73 //===----------------------------------------------------------------------===//
74 
75 /// Parse a list of operands with types.
76 ///
77 /// operand-and-type-list ::= `(` ssa-id-and-type-list `)`
78 /// ssa-id-and-type-list ::= ssa-id-and-type |
79 ///                          ssa-id-and-type `,` ssa-id-and-type-list
80 /// ssa-id-and-type ::= ssa-id `:` type
81 static ParseResult
82 parseOperandAndTypeList(OpAsmParser &parser,
83                         SmallVectorImpl<OpAsmParser::OperandType> &operands,
84                         SmallVectorImpl<Type> &types) {
85   return parser.parseCommaSeparatedList(
86       OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
87         OpAsmParser::OperandType operand;
88         Type type;
89         if (parser.parseOperand(operand) || parser.parseColonType(type))
90           return failure();
91         operands.push_back(operand);
92         types.push_back(type);
93         return success();
94       });
95 }
96 
97 /// Print an operand and type list with parentheses
98 static void printOperandAndTypeList(OpAsmPrinter &p, OperandRange operands) {
99   p << "(";
100   llvm::interleaveComma(
101       operands, p, [&](const Value &v) { p << v << " : " << v.getType(); });
102   p << ") ";
103 }
104 
105 /// Print data variables corresponding to a data-sharing clause `name`
106 static void printDataVars(OpAsmPrinter &p, OperandRange operands,
107                           StringRef name) {
108   if (operands.size()) {
109     p << name;
110     printOperandAndTypeList(p, operands);
111   }
112 }
113 
114 //===----------------------------------------------------------------------===//
115 // Parser and printer for Allocate Clause
116 //===----------------------------------------------------------------------===//
117 
118 /// Parse an allocate clause with allocators and a list of operands with types.
119 ///
120 /// allocate ::= `allocate` `(` allocate-operand-list `)`
121 /// allocate-operand-list :: = allocate-operand |
122 ///                            allocator-operand `,` allocate-operand-list
123 /// allocate-operand :: = ssa-id-and-type -> ssa-id-and-type
124 /// ssa-id-and-type ::= ssa-id `:` type
125 static ParseResult parseAllocateAndAllocator(
126     OpAsmParser &parser,
127     SmallVectorImpl<OpAsmParser::OperandType> &operandsAllocate,
128     SmallVectorImpl<Type> &typesAllocate,
129     SmallVectorImpl<OpAsmParser::OperandType> &operandsAllocator,
130     SmallVectorImpl<Type> &typesAllocator) {
131 
132   return parser.parseCommaSeparatedList(
133       OpAsmParser::Delimiter::Paren, [&]() -> ParseResult {
134         OpAsmParser::OperandType operand;
135         Type type;
136         if (parser.parseOperand(operand) || parser.parseColonType(type))
137           return failure();
138         operandsAllocator.push_back(operand);
139         typesAllocator.push_back(type);
140         if (parser.parseArrow())
141           return failure();
142         if (parser.parseOperand(operand) || parser.parseColonType(type))
143           return failure();
144 
145         operandsAllocate.push_back(operand);
146         typesAllocate.push_back(type);
147         return success();
148       });
149 }
150 
151 /// Print allocate clause
152 static void printAllocateAndAllocator(OpAsmPrinter &p,
153                                       OperandRange varsAllocate,
154                                       OperandRange varsAllocator) {
155   if (varsAllocate.empty())
156     return;
157 
158   p << "allocate(";
159   for (unsigned i = 0; i < varsAllocate.size(); ++i) {
160     std::string separator = i == varsAllocate.size() - 1 ? ") " : ", ";
161     p << varsAllocator[i] << " : " << varsAllocator[i].getType() << " -> ";
162     p << varsAllocate[i] << " : " << varsAllocate[i].getType() << separator;
163   }
164 }
165 
166 static LogicalResult verifyParallelOp(ParallelOp op) {
167   if (op.allocate_vars().size() != op.allocators_vars().size())
168     return op.emitError(
169         "expected equal sizes for allocate and allocator variables");
170   return success();
171 }
172 
173 static void printParallelOp(OpAsmPrinter &p, ParallelOp op) {
174   p << " ";
175   if (auto ifCond = op.if_expr_var())
176     p << "if(" << ifCond << " : " << ifCond.getType() << ") ";
177 
178   if (auto threads = op.num_threads_var())
179     p << "num_threads(" << threads << " : " << threads.getType() << ") ";
180 
181   printDataVars(p, op.private_vars(), "private");
182   printDataVars(p, op.firstprivate_vars(), "firstprivate");
183   printDataVars(p, op.shared_vars(), "shared");
184   printDataVars(p, op.copyin_vars(), "copyin");
185   printAllocateAndAllocator(p, op.allocate_vars(), op.allocators_vars());
186 
187   if (auto def = op.default_val())
188     p << "default(" << def->drop_front(3) << ") ";
189 
190   if (auto bind = op.proc_bind_val())
191     p << "proc_bind(" << bind << ") ";
192 
193   p.printRegion(op.getRegion());
194 }
195 
196 //===----------------------------------------------------------------------===//
197 // Parser and printer for Linear Clause
198 //===----------------------------------------------------------------------===//
199 
200 /// linear ::= `linear` `(` linear-list `)`
201 /// linear-list := linear-val | linear-val linear-list
202 /// linear-val := ssa-id-and-type `=` ssa-id-and-type
203 static ParseResult
204 parseLinearClause(OpAsmParser &parser,
205                   SmallVectorImpl<OpAsmParser::OperandType> &vars,
206                   SmallVectorImpl<Type> &types,
207                   SmallVectorImpl<OpAsmParser::OperandType> &stepVars) {
208   if (parser.parseLParen())
209     return failure();
210 
211   do {
212     OpAsmParser::OperandType var;
213     Type type;
214     OpAsmParser::OperandType stepVar;
215     if (parser.parseOperand(var) || parser.parseEqual() ||
216         parser.parseOperand(stepVar) || parser.parseColonType(type))
217       return failure();
218 
219     vars.push_back(var);
220     types.push_back(type);
221     stepVars.push_back(stepVar);
222   } while (succeeded(parser.parseOptionalComma()));
223 
224   if (parser.parseRParen())
225     return failure();
226 
227   return success();
228 }
229 
230 /// Print Linear Clause
231 static void printLinearClause(OpAsmPrinter &p, OperandRange linearVars,
232                               OperandRange linearStepVars) {
233   size_t linearVarsSize = linearVars.size();
234   p << "(";
235   for (unsigned i = 0; i < linearVarsSize; ++i) {
236     std::string separator = i == linearVarsSize - 1 ? ") " : ", ";
237     p << linearVars[i];
238     if (linearStepVars.size() > i)
239       p << " = " << linearStepVars[i];
240     p << " : " << linearVars[i].getType() << separator;
241   }
242 }
243 
244 //===----------------------------------------------------------------------===//
245 // Parser and printer for Schedule Clause
246 //===----------------------------------------------------------------------===//
247 
248 /// schedule ::= `schedule` `(` sched-list `)`
249 /// sched-list ::= sched-val | sched-val sched-list
250 /// sched-val ::= sched-with-chunk | sched-wo-chunk
251 /// sched-with-chunk ::= sched-with-chunk-types (`=` ssa-id-and-type)?
252 /// sched-with-chunk-types ::= `static` | `dynamic` | `guided`
253 /// sched-wo-chunk ::=  `auto` | `runtime`
254 static ParseResult
255 parseScheduleClause(OpAsmParser &parser, SmallString<8> &schedule,
256                     SmallVectorImpl<SmallString<12>> &modifiers,
257                     Optional<OpAsmParser::OperandType> &chunkSize) {
258   if (parser.parseLParen())
259     return failure();
260 
261   StringRef keyword;
262   if (parser.parseKeyword(&keyword))
263     return failure();
264 
265   schedule = keyword;
266   if (keyword == "static" || keyword == "dynamic" || keyword == "guided") {
267     if (succeeded(parser.parseOptionalEqual())) {
268       chunkSize = OpAsmParser::OperandType{};
269       if (parser.parseOperand(*chunkSize))
270         return failure();
271     } else {
272       chunkSize = llvm::NoneType::None;
273     }
274   } else if (keyword == "auto" || keyword == "runtime") {
275     chunkSize = llvm::NoneType::None;
276   } else {
277     return parser.emitError(parser.getNameLoc()) << " expected schedule kind";
278   }
279 
280   // If there is a comma, we have one or more modifiers..
281   if (succeeded(parser.parseOptionalComma())) {
282     StringRef mod;
283     if (parser.parseKeyword(&mod))
284       return failure();
285     modifiers.push_back(mod);
286   }
287 
288   if (parser.parseRParen())
289     return failure();
290 
291   return success();
292 }
293 
294 /// Print schedule clause
295 static void printScheduleClause(OpAsmPrinter &p, StringRef &sched,
296                                 llvm::Optional<StringRef> modifier,
297                                 Value scheduleChunkVar) {
298   std::string schedLower = sched.lower();
299   p << "(" << schedLower;
300   if (scheduleChunkVar)
301     p << " = " << scheduleChunkVar;
302   if (modifier && modifier.getValue() != "none")
303     p << ", " << modifier;
304   p << ") ";
305 }
306 
307 //===----------------------------------------------------------------------===//
308 // Parser, printer and verifier for ReductionVarList
309 //===----------------------------------------------------------------------===//
310 
311 /// reduction ::= `reduction` `(` reduction-entry-list `)`
312 /// reduction-entry-list ::= reduction-entry
313 ///                        | reduction-entry-list `,` reduction-entry
314 /// reduction-entry ::= symbol-ref `->` ssa-id `:` type
315 static ParseResult
316 parseReductionVarList(OpAsmParser &parser,
317                       SmallVectorImpl<SymbolRefAttr> &symbols,
318                       SmallVectorImpl<OpAsmParser::OperandType> &operands,
319                       SmallVectorImpl<Type> &types) {
320   if (failed(parser.parseLParen()))
321     return failure();
322 
323   do {
324     if (parser.parseAttribute(symbols.emplace_back()) || parser.parseArrow() ||
325         parser.parseOperand(operands.emplace_back()) ||
326         parser.parseColonType(types.emplace_back()))
327       return failure();
328   } while (succeeded(parser.parseOptionalComma()));
329   return parser.parseRParen();
330 }
331 
332 /// Print Reduction clause
333 static void printReductionVarList(OpAsmPrinter &p,
334                                   Optional<ArrayAttr> reductions,
335                                   OperandRange reduction_vars) {
336   for (unsigned i = 0, e = reductions->size(); i < e; ++i) {
337     if (i != 0)
338       p << ", ";
339     p << (*reductions)[i] << " -> " << reduction_vars[i] << " : "
340       << reduction_vars[i].getType();
341   }
342   p << ") ";
343 }
344 
345 /// Verifies Reduction Clause
346 static LogicalResult verifyReductionVarList(Operation *op,
347                                             Optional<ArrayAttr> reductions,
348                                             OperandRange reduction_vars) {
349   if (reduction_vars.size() != 0) {
350     if (!reductions || reductions->size() != reduction_vars.size())
351       return op->emitOpError()
352              << "expected as many reduction symbol references "
353                 "as reduction variables";
354   } else {
355     if (reductions)
356       return op->emitOpError() << "unexpected reduction symbol references";
357     return success();
358   }
359 
360   DenseSet<Value> accumulators;
361   for (auto args : llvm::zip(reduction_vars, *reductions)) {
362     Value accum = std::get<0>(args);
363 
364     if (!accumulators.insert(accum).second)
365       return op->emitOpError() << "accumulator variable used more than once";
366 
367     Type varType = accum.getType().cast<PointerLikeType>();
368     auto symbolRef = std::get<1>(args).cast<SymbolRefAttr>();
369     auto decl =
370         SymbolTable::lookupNearestSymbolFrom<ReductionDeclareOp>(op, symbolRef);
371     if (!decl)
372       return op->emitOpError() << "expected symbol reference " << symbolRef
373                                << " to point to a reduction declaration";
374 
375     if (decl.getAccumulatorType() && decl.getAccumulatorType() != varType)
376       return op->emitOpError()
377              << "expected accumulator (" << varType
378              << ") to be the same type as reduction declaration ("
379              << decl.getAccumulatorType() << ")";
380   }
381 
382   return success();
383 }
384 
385 //===----------------------------------------------------------------------===//
386 // Parser, printer and verifier for Synchronization Hint (2.17.12)
387 //===----------------------------------------------------------------------===//
388 
389 /// Parses a Synchronization Hint clause. The value of hint is an integer
390 /// which is a combination of different hints from `omp_sync_hint_t`.
391 ///
392 /// hint-clause = `hint` `(` hint-value `)`
393 static ParseResult parseSynchronizationHint(OpAsmParser &parser,
394                                             IntegerAttr &hintAttr,
395                                             bool parseKeyword = true) {
396   if (parseKeyword && failed(parser.parseOptionalKeyword("hint"))) {
397     hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), 0);
398     return success();
399   }
400 
401   if (failed(parser.parseLParen()))
402     return failure();
403   StringRef hintKeyword;
404   int64_t hint = 0;
405   do {
406     if (failed(parser.parseKeyword(&hintKeyword)))
407       return failure();
408     if (hintKeyword == "uncontended")
409       hint |= 1;
410     else if (hintKeyword == "contended")
411       hint |= 2;
412     else if (hintKeyword == "nonspeculative")
413       hint |= 4;
414     else if (hintKeyword == "speculative")
415       hint |= 8;
416     else
417       return parser.emitError(parser.getCurrentLocation())
418              << hintKeyword << " is not a valid hint";
419   } while (succeeded(parser.parseOptionalComma()));
420   if (failed(parser.parseRParen()))
421     return failure();
422   hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), hint);
423   return success();
424 }
425 
426 /// Prints a Synchronization Hint clause
427 static void printSynchronizationHint(OpAsmPrinter &p, Operation *op,
428                                      IntegerAttr hintAttr) {
429   int64_t hint = hintAttr.getInt();
430 
431   if (hint == 0)
432     return;
433 
434   // Helper function to get n-th bit from the right end of `value`
435   auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
436 
437   bool uncontended = bitn(hint, 0);
438   bool contended = bitn(hint, 1);
439   bool nonspeculative = bitn(hint, 2);
440   bool speculative = bitn(hint, 3);
441 
442   SmallVector<StringRef> hints;
443   if (uncontended)
444     hints.push_back("uncontended");
445   if (contended)
446     hints.push_back("contended");
447   if (nonspeculative)
448     hints.push_back("nonspeculative");
449   if (speculative)
450     hints.push_back("speculative");
451 
452   p << "hint(";
453   llvm::interleaveComma(hints, p);
454   p << ") ";
455 }
456 
457 /// Verifies a synchronization hint clause
458 static LogicalResult verifySynchronizationHint(Operation *op, uint64_t hint) {
459 
460   // Helper function to get n-th bit from the right end of `value`
461   auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
462 
463   bool uncontended = bitn(hint, 0);
464   bool contended = bitn(hint, 1);
465   bool nonspeculative = bitn(hint, 2);
466   bool speculative = bitn(hint, 3);
467 
468   if (uncontended && contended)
469     return op->emitOpError() << "the hints omp_sync_hint_uncontended and "
470                                 "omp_sync_hint_contended cannot be combined";
471   if (nonspeculative && speculative)
472     return op->emitOpError() << "the hints omp_sync_hint_nonspeculative and "
473                                 "omp_sync_hint_speculative cannot be combined.";
474   return success();
475 }
476 
477 enum ClauseType {
478   ifClause,
479   numThreadsClause,
480   privateClause,
481   firstprivateClause,
482   lastprivateClause,
483   sharedClause,
484   copyinClause,
485   allocateClause,
486   defaultClause,
487   procBindClause,
488   reductionClause,
489   nowaitClause,
490   linearClause,
491   scheduleClause,
492   collapseClause,
493   orderClause,
494   orderedClause,
495   inclusiveClause,
496   memoryOrderClause,
497   hintClause,
498   COUNT
499 };
500 
501 //===----------------------------------------------------------------------===//
502 // Parser for Clause List
503 //===----------------------------------------------------------------------===//
504 
505 /// Parse a list of clauses. The clauses can appear in any order, but their
506 /// operand segment indices are in the same order that they are passed in the
507 /// `clauses` list. The operand segments are added over the prevSegments
508 
509 /// clause-list ::= clause clause-list | empty
510 /// clause ::= if | num-threads | private | firstprivate | lastprivate |
511 ///            shared | copyin | allocate | default | proc-bind | reduction |
512 ///            nowait | linear | schedule | collapse | order | ordered |
513 ///            inclusive
514 /// if ::= `if` `(` ssa-id-and-type `)`
515 /// num-threads ::= `num_threads` `(` ssa-id-and-type `)`
516 /// private ::= `private` operand-and-type-list
517 /// firstprivate ::= `firstprivate` operand-and-type-list
518 /// lastprivate ::= `lastprivate` operand-and-type-list
519 /// shared ::= `shared` operand-and-type-list
520 /// copyin ::= `copyin` operand-and-type-list
521 /// allocate ::= `allocate` `(` allocate-operand-list `)`
522 /// default ::= `default` `(` (`private` | `firstprivate` | `shared` | `none`)
523 /// proc-bind ::= `proc_bind` `(` (`master` | `close` | `spread`) `)`
524 /// reduction ::= `reduction` `(` reduction-entry-list `)`
525 /// nowait ::= `nowait`
526 /// linear ::= `linear` `(` linear-list `)`
527 /// schedule ::= `schedule` `(` sched-list `)`
528 /// collapse ::= `collapse` `(` ssa-id-and-type `)`
529 /// order ::= `order` `(` `concurrent` `)`
530 /// ordered ::= `ordered` `(` ssa-id-and-type `)`
531 /// inclusive ::= `inclusive`
532 ///
533 /// Note that each clause can only appear once in the clase-list.
534 static ParseResult parseClauses(OpAsmParser &parser, OperationState &result,
535                                 SmallVectorImpl<ClauseType> &clauses,
536                                 SmallVectorImpl<int> &segments) {
537 
538   // Check done[clause] to see if it has been parsed already
539   llvm::BitVector done(ClauseType::COUNT, false);
540 
541   // See pos[clause] to get position of clause in operand segments
542   SmallVector<int> pos(ClauseType::COUNT, -1);
543 
544   // Stores the last parsed clause keyword
545   StringRef clauseKeyword;
546   StringRef opName = result.name.getStringRef();
547 
548   // Containers for storing operands, types and attributes for various clauses
549   std::pair<OpAsmParser::OperandType, Type> ifCond;
550   std::pair<OpAsmParser::OperandType, Type> numThreads;
551 
552   SmallVector<OpAsmParser::OperandType> privates, firstprivates, lastprivates,
553       shareds, copyins;
554   SmallVector<Type> privateTypes, firstprivateTypes, lastprivateTypes,
555       sharedTypes, copyinTypes;
556 
557   SmallVector<OpAsmParser::OperandType> allocates, allocators;
558   SmallVector<Type> allocateTypes, allocatorTypes;
559 
560   SmallVector<SymbolRefAttr> reductionSymbols;
561   SmallVector<OpAsmParser::OperandType> reductionVars;
562   SmallVector<Type> reductionVarTypes;
563 
564   SmallVector<OpAsmParser::OperandType> linears;
565   SmallVector<Type> linearTypes;
566   SmallVector<OpAsmParser::OperandType> linearSteps;
567 
568   SmallString<8> schedule;
569   SmallVector<SmallString<12>> modifiers;
570   Optional<OpAsmParser::OperandType> scheduleChunkSize;
571 
572   // Compute the position of clauses in operand segments
573   int currPos = 0;
574   for (ClauseType clause : clauses) {
575 
576     // Skip the following clauses - they do not take any position in operand
577     // segments
578     if (clause == defaultClause || clause == procBindClause ||
579         clause == nowaitClause || clause == collapseClause ||
580         clause == orderClause || clause == orderedClause ||
581         clause == inclusiveClause)
582       continue;
583 
584     pos[clause] = currPos++;
585 
586     // For the following clauses, two positions are reserved in the operand
587     // segments
588     if (clause == allocateClause || clause == linearClause)
589       currPos++;
590   }
591 
592   SmallVector<int> clauseSegments(currPos);
593 
594   // Helper function to check if a clause is allowed/repeated or not
595   auto checkAllowed = [&](ClauseType clause,
596                           bool allowRepeat = false) -> ParseResult {
597     if (!llvm::is_contained(clauses, clause))
598       return parser.emitError(parser.getCurrentLocation())
599              << clauseKeyword << "is not a valid clause for the " << opName
600              << " operation";
601     if (done[clause] && !allowRepeat)
602       return parser.emitError(parser.getCurrentLocation())
603              << "at most one " << clauseKeyword << " clause can appear on the "
604              << opName << " operation";
605     done[clause] = true;
606     return success();
607   };
608 
609   while (succeeded(parser.parseOptionalKeyword(&clauseKeyword))) {
610     if (clauseKeyword == "if") {
611       if (checkAllowed(ifClause) || parser.parseLParen() ||
612           parser.parseOperand(ifCond.first) ||
613           parser.parseColonType(ifCond.second) || parser.parseRParen())
614         return failure();
615       clauseSegments[pos[ifClause]] = 1;
616     } else if (clauseKeyword == "num_threads") {
617       if (checkAllowed(numThreadsClause) || parser.parseLParen() ||
618           parser.parseOperand(numThreads.first) ||
619           parser.parseColonType(numThreads.second) || parser.parseRParen())
620         return failure();
621       clauseSegments[pos[numThreadsClause]] = 1;
622     } else if (clauseKeyword == "private") {
623       if (checkAllowed(privateClause) ||
624           parseOperandAndTypeList(parser, privates, privateTypes))
625         return failure();
626       clauseSegments[pos[privateClause]] = privates.size();
627     } else if (clauseKeyword == "firstprivate") {
628       if (checkAllowed(firstprivateClause) ||
629           parseOperandAndTypeList(parser, firstprivates, firstprivateTypes))
630         return failure();
631       clauseSegments[pos[firstprivateClause]] = firstprivates.size();
632     } else if (clauseKeyword == "lastprivate") {
633       if (checkAllowed(lastprivateClause) ||
634           parseOperandAndTypeList(parser, lastprivates, lastprivateTypes))
635         return failure();
636       clauseSegments[pos[lastprivateClause]] = lastprivates.size();
637     } else if (clauseKeyword == "shared") {
638       if (checkAllowed(sharedClause) ||
639           parseOperandAndTypeList(parser, shareds, sharedTypes))
640         return failure();
641       clauseSegments[pos[sharedClause]] = shareds.size();
642     } else if (clauseKeyword == "copyin") {
643       if (checkAllowed(copyinClause) ||
644           parseOperandAndTypeList(parser, copyins, copyinTypes))
645         return failure();
646       clauseSegments[pos[copyinClause]] = copyins.size();
647     } else if (clauseKeyword == "allocate") {
648       if (checkAllowed(allocateClause) ||
649           parseAllocateAndAllocator(parser, allocates, allocateTypes,
650                                     allocators, allocatorTypes))
651         return failure();
652       clauseSegments[pos[allocateClause]] = allocates.size();
653       clauseSegments[pos[allocateClause] + 1] = allocators.size();
654     } else if (clauseKeyword == "default") {
655       StringRef defval;
656       if (checkAllowed(defaultClause) || parser.parseLParen() ||
657           parser.parseKeyword(&defval) || parser.parseRParen())
658         return failure();
659       // The def prefix is required for the attribute as "private" is a keyword
660       // in C++.
661       auto attr = parser.getBuilder().getStringAttr("def" + defval);
662       result.addAttribute("default_val", attr);
663     } else if (clauseKeyword == "proc_bind") {
664       StringRef bind;
665       if (checkAllowed(procBindClause) || parser.parseLParen() ||
666           parser.parseKeyword(&bind) || parser.parseRParen())
667         return failure();
668       auto attr = parser.getBuilder().getStringAttr(bind);
669       result.addAttribute("proc_bind_val", attr);
670     } else if (clauseKeyword == "reduction") {
671       if (checkAllowed(reductionClause) ||
672           parseReductionVarList(parser, reductionSymbols, reductionVars,
673                                 reductionVarTypes))
674         return failure();
675       clauseSegments[pos[reductionClause]] = reductionVars.size();
676     } else if (clauseKeyword == "nowait") {
677       if (checkAllowed(nowaitClause))
678         return failure();
679       auto attr = UnitAttr::get(parser.getBuilder().getContext());
680       result.addAttribute("nowait", attr);
681     } else if (clauseKeyword == "linear") {
682       if (checkAllowed(linearClause) ||
683           parseLinearClause(parser, linears, linearTypes, linearSteps))
684         return failure();
685       clauseSegments[pos[linearClause]] = linears.size();
686       clauseSegments[pos[linearClause] + 1] = linearSteps.size();
687     } else if (clauseKeyword == "schedule") {
688       if (checkAllowed(scheduleClause) ||
689           parseScheduleClause(parser, schedule, modifiers, scheduleChunkSize))
690         return failure();
691       if (scheduleChunkSize) {
692         clauseSegments[pos[scheduleClause]] = 1;
693       }
694     } else if (clauseKeyword == "collapse") {
695       auto type = parser.getBuilder().getI64Type();
696       mlir::IntegerAttr attr;
697       if (checkAllowed(collapseClause) || parser.parseLParen() ||
698           parser.parseAttribute(attr, type) || parser.parseRParen())
699         return failure();
700       result.addAttribute("collapse_val", attr);
701     } else if (clauseKeyword == "ordered") {
702       mlir::IntegerAttr attr;
703       if (checkAllowed(orderedClause))
704         return failure();
705       if (succeeded(parser.parseOptionalLParen())) {
706         auto type = parser.getBuilder().getI64Type();
707         if (parser.parseAttribute(attr, type) || parser.parseRParen())
708           return failure();
709       } else {
710         // Use 0 to represent no ordered parameter was specified
711         attr = parser.getBuilder().getI64IntegerAttr(0);
712       }
713       result.addAttribute("ordered_val", attr);
714     } else if (clauseKeyword == "order") {
715       StringRef order;
716       if (checkAllowed(orderClause) || parser.parseLParen() ||
717           parser.parseKeyword(&order) || parser.parseRParen())
718         return failure();
719       auto attr = parser.getBuilder().getStringAttr(order);
720       result.addAttribute("order", attr);
721     } else if (clauseKeyword == "inclusive") {
722       if (checkAllowed(inclusiveClause))
723         return failure();
724       auto attr = UnitAttr::get(parser.getBuilder().getContext());
725       result.addAttribute("inclusive", attr);
726     } else if (clauseKeyword == "memory_order") {
727       StringRef memoryOrder;
728       if (checkAllowed(memoryOrderClause) || parser.parseLParen() ||
729           parser.parseKeyword(&memoryOrder) || parser.parseRParen())
730         return failure();
731       result.addAttribute("memory_order",
732                           parser.getBuilder().getStringAttr(memoryOrder));
733     } else if (clauseKeyword == "hint") {
734       IntegerAttr hint;
735       if (checkAllowed(hintClause) ||
736           parseSynchronizationHint(parser, hint, false))
737         return failure();
738       result.addAttribute("hint", hint);
739     } else {
740       return parser.emitError(parser.getNameLoc())
741              << clauseKeyword << " is not a valid clause";
742     }
743   }
744 
745   // Add if parameter.
746   if (done[ifClause] && clauseSegments[pos[ifClause]] &&
747       failed(
748           parser.resolveOperand(ifCond.first, ifCond.second, result.operands)))
749     return failure();
750 
751   // Add num_threads parameter.
752   if (done[numThreadsClause] && clauseSegments[pos[numThreadsClause]] &&
753       failed(parser.resolveOperand(numThreads.first, numThreads.second,
754                                    result.operands)))
755     return failure();
756 
757   // Add private parameters.
758   if (done[privateClause] && clauseSegments[pos[privateClause]] &&
759       failed(parser.resolveOperands(privates, privateTypes,
760                                     privates[0].location, result.operands)))
761     return failure();
762 
763   // Add firstprivate parameters.
764   if (done[firstprivateClause] && clauseSegments[pos[firstprivateClause]] &&
765       failed(parser.resolveOperands(firstprivates, firstprivateTypes,
766                                     firstprivates[0].location,
767                                     result.operands)))
768     return failure();
769 
770   // Add lastprivate parameters.
771   if (done[lastprivateClause] && clauseSegments[pos[lastprivateClause]] &&
772       failed(parser.resolveOperands(lastprivates, lastprivateTypes,
773                                     lastprivates[0].location, result.operands)))
774     return failure();
775 
776   // Add shared parameters.
777   if (done[sharedClause] && clauseSegments[pos[sharedClause]] &&
778       failed(parser.resolveOperands(shareds, sharedTypes, shareds[0].location,
779                                     result.operands)))
780     return failure();
781 
782   // Add copyin parameters.
783   if (done[copyinClause] && clauseSegments[pos[copyinClause]] &&
784       failed(parser.resolveOperands(copyins, copyinTypes, copyins[0].location,
785                                     result.operands)))
786     return failure();
787 
788   // Add allocate parameters.
789   if (done[allocateClause] && clauseSegments[pos[allocateClause]] &&
790       failed(parser.resolveOperands(allocates, allocateTypes,
791                                     allocates[0].location, result.operands)))
792     return failure();
793 
794   // Add allocator parameters.
795   if (done[allocateClause] && clauseSegments[pos[allocateClause] + 1] &&
796       failed(parser.resolveOperands(allocators, allocatorTypes,
797                                     allocators[0].location, result.operands)))
798     return failure();
799 
800   // Add reduction parameters and symbols
801   if (done[reductionClause] && clauseSegments[pos[reductionClause]]) {
802     if (failed(parser.resolveOperands(reductionVars, reductionVarTypes,
803                                       parser.getNameLoc(), result.operands)))
804       return failure();
805 
806     SmallVector<Attribute> reductions(reductionSymbols.begin(),
807                                       reductionSymbols.end());
808     result.addAttribute("reductions",
809                         parser.getBuilder().getArrayAttr(reductions));
810   }
811 
812   // Add linear parameters
813   if (done[linearClause] && clauseSegments[pos[linearClause]]) {
814     auto linearStepType = parser.getBuilder().getI32Type();
815     SmallVector<Type> linearStepTypes(linearSteps.size(), linearStepType);
816     if (failed(parser.resolveOperands(linears, linearTypes, linears[0].location,
817                                       result.operands)) ||
818         failed(parser.resolveOperands(linearSteps, linearStepTypes,
819                                       linearSteps[0].location,
820                                       result.operands)))
821       return failure();
822   }
823 
824   // Add schedule parameters
825   if (done[scheduleClause] && !schedule.empty()) {
826     schedule[0] = llvm::toUpper(schedule[0]);
827     auto attr = parser.getBuilder().getStringAttr(schedule);
828     result.addAttribute("schedule_val", attr);
829     if (modifiers.size() > 0) {
830       auto mod = parser.getBuilder().getStringAttr(modifiers[0]);
831       result.addAttribute("schedule_modifier", mod);
832     }
833     if (scheduleChunkSize) {
834       auto chunkSizeType = parser.getBuilder().getI32Type();
835       parser.resolveOperand(*scheduleChunkSize, chunkSizeType, result.operands);
836     }
837   }
838 
839   segments.insert(segments.end(), clauseSegments.begin(), clauseSegments.end());
840 
841   return success();
842 }
843 
844 /// Parses a parallel operation.
845 ///
846 /// operation ::= `omp.parallel` clause-list
847 /// clause-list ::= clause | clause clause-list
848 /// clause ::= if | num-threads | private | firstprivate | shared | copyin |
849 ///            allocate | default | proc-bind
850 ///
851 static ParseResult parseParallelOp(OpAsmParser &parser,
852                                    OperationState &result) {
853   SmallVector<ClauseType> clauses = {
854       ifClause,           numThreadsClause, privateClause,
855       firstprivateClause, sharedClause,     copyinClause,
856       allocateClause,     defaultClause,    procBindClause};
857 
858   SmallVector<int> segments;
859 
860   if (failed(parseClauses(parser, result, clauses, segments)))
861     return failure();
862 
863   result.addAttribute("operand_segment_sizes",
864                       parser.getBuilder().getI32VectorAttr(segments));
865 
866   Region *body = result.addRegion();
867   SmallVector<OpAsmParser::OperandType> regionArgs;
868   SmallVector<Type> regionArgTypes;
869   if (parser.parseRegion(*body, regionArgs, regionArgTypes))
870     return failure();
871   return success();
872 }
873 
874 /// Parses an OpenMP Workshare Loop operation
875 ///
876 /// wsloop ::= `omp.wsloop` loop-control clause-list
877 /// loop-control ::= `(` ssa-id-list `)` `:` type `=`  loop-bounds
878 /// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` steps
879 /// steps := `step` `(`ssa-id-list`)`
880 /// clause-list ::= clause clause-list | empty
881 /// clause ::= private | firstprivate | lastprivate | linear | schedule |
882 //             collapse | nowait | ordered | order | inclusive | reduction
883 static ParseResult parseWsLoopOp(OpAsmParser &parser, OperationState &result) {
884 
885   // Parse an opening `(` followed by induction variables followed by `)`
886   SmallVector<OpAsmParser::OperandType> ivs;
887   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
888                                      OpAsmParser::Delimiter::Paren))
889     return failure();
890 
891   int numIVs = static_cast<int>(ivs.size());
892   Type loopVarType;
893   if (parser.parseColonType(loopVarType))
894     return failure();
895 
896   // Parse loop bounds.
897   SmallVector<OpAsmParser::OperandType> lower;
898   if (parser.parseEqual() ||
899       parser.parseOperandList(lower, numIVs, OpAsmParser::Delimiter::Paren) ||
900       parser.resolveOperands(lower, loopVarType, result.operands))
901     return failure();
902 
903   SmallVector<OpAsmParser::OperandType> upper;
904   if (parser.parseKeyword("to") ||
905       parser.parseOperandList(upper, numIVs, OpAsmParser::Delimiter::Paren) ||
906       parser.resolveOperands(upper, loopVarType, result.operands))
907     return failure();
908 
909   // Parse step values.
910   SmallVector<OpAsmParser::OperandType> steps;
911   if (parser.parseKeyword("step") ||
912       parser.parseOperandList(steps, numIVs, OpAsmParser::Delimiter::Paren) ||
913       parser.resolveOperands(steps, loopVarType, result.operands))
914     return failure();
915 
916   SmallVector<ClauseType> clauses = {
917       privateClause,   firstprivateClause, lastprivateClause, linearClause,
918       reductionClause, collapseClause,     orderClause,       orderedClause,
919       nowaitClause,    scheduleClause};
920   SmallVector<int> segments{numIVs, numIVs, numIVs};
921   if (failed(parseClauses(parser, result, clauses, segments)))
922     return failure();
923 
924   result.addAttribute("operand_segment_sizes",
925                       parser.getBuilder().getI32VectorAttr(segments));
926 
927   // Now parse the body.
928   Region *body = result.addRegion();
929   SmallVector<Type> ivTypes(numIVs, loopVarType);
930   SmallVector<OpAsmParser::OperandType> blockArgs(ivs);
931   if (parser.parseRegion(*body, blockArgs, ivTypes))
932     return failure();
933   return success();
934 }
935 
936 static void printWsLoopOp(OpAsmPrinter &p, WsLoopOp op) {
937   auto args = op.getRegion().front().getArguments();
938   p << " (" << args << ") : " << args[0].getType() << " = (" << op.lowerBound()
939     << ") to (" << op.upperBound() << ") step (" << op.step() << ") ";
940 
941   printDataVars(p, op.private_vars(), "private");
942   printDataVars(p, op.firstprivate_vars(), "firstprivate");
943   printDataVars(p, op.lastprivate_vars(), "lastprivate");
944 
945   if (op.linear_vars().size()) {
946     p << "linear";
947     printLinearClause(p, op.linear_vars(), op.linear_step_vars());
948   }
949 
950   if (auto sched = op.schedule_val()) {
951     p << "schedule";
952     printScheduleClause(p, sched.getValue(), op.schedule_modifier(),
953                         op.schedule_chunk_var());
954   }
955 
956   if (auto collapse = op.collapse_val())
957     p << "collapse(" << collapse << ") ";
958 
959   if (op.nowait())
960     p << "nowait ";
961 
962   if (auto ordered = op.ordered_val())
963     p << "ordered(" << ordered << ") ";
964 
965   if (!op.reduction_vars().empty()) {
966     p << "reduction(";
967     printReductionVarList(p, op.reductions(), op.reduction_vars());
968   }
969 
970   if (op.inclusive()) {
971     p << "inclusive ";
972   }
973 
974   p.printRegion(op.region(), /*printEntryBlockArgs=*/false);
975 }
976 
977 //===----------------------------------------------------------------------===//
978 // ReductionOp
979 //===----------------------------------------------------------------------===//
980 
981 static ParseResult parseAtomicReductionRegion(OpAsmParser &parser,
982                                               Region &region) {
983   if (parser.parseOptionalKeyword("atomic"))
984     return success();
985   return parser.parseRegion(region);
986 }
987 
988 static void printAtomicReductionRegion(OpAsmPrinter &printer,
989                                        ReductionDeclareOp op, Region &region) {
990   if (region.empty())
991     return;
992   printer << "atomic ";
993   printer.printRegion(region);
994 }
995 
996 static LogicalResult verifyReductionDeclareOp(ReductionDeclareOp op) {
997   if (op.initializerRegion().empty())
998     return op.emitOpError() << "expects non-empty initializer region";
999   Block &initializerEntryBlock = op.initializerRegion().front();
1000   if (initializerEntryBlock.getNumArguments() != 1 ||
1001       initializerEntryBlock.getArgument(0).getType() != op.type()) {
1002     return op.emitOpError() << "expects initializer region with one argument "
1003                                "of the reduction type";
1004   }
1005 
1006   for (YieldOp yieldOp : op.initializerRegion().getOps<YieldOp>()) {
1007     if (yieldOp.results().size() != 1 ||
1008         yieldOp.results().getTypes()[0] != op.type())
1009       return op.emitOpError() << "expects initializer region to yield a value "
1010                                  "of the reduction type";
1011   }
1012 
1013   if (op.reductionRegion().empty())
1014     return op.emitOpError() << "expects non-empty reduction region";
1015   Block &reductionEntryBlock = op.reductionRegion().front();
1016   if (reductionEntryBlock.getNumArguments() != 2 ||
1017       reductionEntryBlock.getArgumentTypes()[0] !=
1018           reductionEntryBlock.getArgumentTypes()[1] ||
1019       reductionEntryBlock.getArgumentTypes()[0] != op.type())
1020     return op.emitOpError() << "expects reduction region with two arguments of "
1021                                "the reduction type";
1022   for (YieldOp yieldOp : op.reductionRegion().getOps<YieldOp>()) {
1023     if (yieldOp.results().size() != 1 ||
1024         yieldOp.results().getTypes()[0] != op.type())
1025       return op.emitOpError() << "expects reduction region to yield a value "
1026                                  "of the reduction type";
1027   }
1028 
1029   if (op.atomicReductionRegion().empty())
1030     return success();
1031 
1032   Block &atomicReductionEntryBlock = op.atomicReductionRegion().front();
1033   if (atomicReductionEntryBlock.getNumArguments() != 2 ||
1034       atomicReductionEntryBlock.getArgumentTypes()[0] !=
1035           atomicReductionEntryBlock.getArgumentTypes()[1])
1036     return op.emitOpError() << "expects atomic reduction region with two "
1037                                "arguments of the same type";
1038   auto ptrType = atomicReductionEntryBlock.getArgumentTypes()[0]
1039                      .dyn_cast<PointerLikeType>();
1040   if (!ptrType || ptrType.getElementType() != op.type())
1041     return op.emitOpError() << "expects atomic reduction region arguments to "
1042                                "be accumulators containing the reduction type";
1043   return success();
1044 }
1045 
1046 static LogicalResult verifyReductionOp(ReductionOp op) {
1047   // TODO: generalize this to an op interface when there is more than one op
1048   // that supports reductions.
1049   auto container = op->getParentOfType<WsLoopOp>();
1050   for (unsigned i = 0, e = container.getNumReductionVars(); i < e; ++i)
1051     if (container.reduction_vars()[i] == op.accumulator())
1052       return success();
1053 
1054   return op.emitOpError() << "the accumulator is not used by the parent";
1055 }
1056 
1057 //===----------------------------------------------------------------------===//
1058 // WsLoopOp
1059 //===----------------------------------------------------------------------===//
1060 
1061 void WsLoopOp::build(OpBuilder &builder, OperationState &state,
1062                      ValueRange lowerBound, ValueRange upperBound,
1063                      ValueRange step, ArrayRef<NamedAttribute> attributes) {
1064   build(builder, state, TypeRange(), lowerBound, upperBound, step,
1065         /*private_vars=*/ValueRange(),
1066         /*firstprivate_vars=*/ValueRange(), /*lastprivate_vars=*/ValueRange(),
1067         /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(),
1068         /*reduction_vars=*/ValueRange(), /*schedule_val=*/nullptr,
1069         /*schedule_chunk_var=*/nullptr, /*collapse_val=*/nullptr,
1070         /*nowait=*/nullptr, /*ordered_val=*/nullptr, /*order_val=*/nullptr,
1071         /*inclusive=*/nullptr, /*buildBody=*/false);
1072   state.addAttributes(attributes);
1073 }
1074 
1075 void WsLoopOp::build(OpBuilder &, OperationState &state, TypeRange resultTypes,
1076                      ValueRange operands, ArrayRef<NamedAttribute> attributes) {
1077   state.addOperands(operands);
1078   state.addAttributes(attributes);
1079   (void)state.addRegion();
1080   assert(resultTypes.empty() && "mismatched number of return types");
1081   state.addTypes(resultTypes);
1082 }
1083 
1084 void WsLoopOp::build(OpBuilder &builder, OperationState &result,
1085                      TypeRange typeRange, ValueRange lowerBounds,
1086                      ValueRange upperBounds, ValueRange steps,
1087                      ValueRange privateVars, ValueRange firstprivateVars,
1088                      ValueRange lastprivateVars, ValueRange linearVars,
1089                      ValueRange linearStepVars, ValueRange reductionVars,
1090                      StringAttr scheduleVal, Value scheduleChunkVar,
1091                      IntegerAttr collapseVal, UnitAttr nowait,
1092                      IntegerAttr orderedVal, StringAttr orderVal,
1093                      UnitAttr inclusive, bool buildBody) {
1094   result.addOperands(lowerBounds);
1095   result.addOperands(upperBounds);
1096   result.addOperands(steps);
1097   result.addOperands(privateVars);
1098   result.addOperands(firstprivateVars);
1099   result.addOperands(linearVars);
1100   result.addOperands(linearStepVars);
1101   if (scheduleChunkVar)
1102     result.addOperands(scheduleChunkVar);
1103 
1104   if (scheduleVal)
1105     result.addAttribute("schedule_val", scheduleVal);
1106   if (collapseVal)
1107     result.addAttribute("collapse_val", collapseVal);
1108   if (nowait)
1109     result.addAttribute("nowait", nowait);
1110   if (orderedVal)
1111     result.addAttribute("ordered_val", orderedVal);
1112   if (orderVal)
1113     result.addAttribute("order", orderVal);
1114   if (inclusive)
1115     result.addAttribute("inclusive", inclusive);
1116   result.addAttribute(
1117       WsLoopOp::getOperandSegmentSizeAttr(),
1118       builder.getI32VectorAttr(
1119           {static_cast<int32_t>(lowerBounds.size()),
1120            static_cast<int32_t>(upperBounds.size()),
1121            static_cast<int32_t>(steps.size()),
1122            static_cast<int32_t>(privateVars.size()),
1123            static_cast<int32_t>(firstprivateVars.size()),
1124            static_cast<int32_t>(lastprivateVars.size()),
1125            static_cast<int32_t>(linearVars.size()),
1126            static_cast<int32_t>(linearStepVars.size()),
1127            static_cast<int32_t>(reductionVars.size()),
1128            static_cast<int32_t>(scheduleChunkVar != nullptr ? 1 : 0)}));
1129 
1130   Region *bodyRegion = result.addRegion();
1131   if (buildBody) {
1132     OpBuilder::InsertionGuard guard(builder);
1133     unsigned numIVs = steps.size();
1134     SmallVector<Type, 8> argTypes(numIVs, steps.getType().front());
1135     builder.createBlock(bodyRegion, {}, argTypes);
1136   }
1137 }
1138 
1139 static LogicalResult verifyWsLoopOp(WsLoopOp op) {
1140   return verifyReductionVarList(op, op.reductions(), op.reduction_vars());
1141 }
1142 
1143 //===----------------------------------------------------------------------===//
1144 // Verifier for critical construct (2.17.1)
1145 //===----------------------------------------------------------------------===//
1146 
1147 static LogicalResult verifyCriticalDeclareOp(CriticalDeclareOp op) {
1148   return verifySynchronizationHint(op, op.hint());
1149 }
1150 
1151 static LogicalResult verifyCriticalOp(CriticalOp op) {
1152 
1153   if (op.nameAttr()) {
1154     auto symbolRef = op.nameAttr().cast<SymbolRefAttr>();
1155     auto decl =
1156         SymbolTable::lookupNearestSymbolFrom<CriticalDeclareOp>(op, symbolRef);
1157     if (!decl) {
1158       return op.emitOpError() << "expected symbol reference " << symbolRef
1159                               << " to point to a critical declaration";
1160     }
1161   }
1162 
1163   return success();
1164 }
1165 
1166 //===----------------------------------------------------------------------===//
1167 // Verifier for ordered construct
1168 //===----------------------------------------------------------------------===//
1169 
1170 static LogicalResult verifyOrderedOp(OrderedOp op) {
1171   auto container = op->getParentOfType<WsLoopOp>();
1172   if (!container || !container.ordered_valAttr() ||
1173       container.ordered_valAttr().getInt() == 0)
1174     return op.emitOpError() << "ordered depend directive must be closely "
1175                             << "nested inside a worksharing-loop with ordered "
1176                             << "clause with parameter present";
1177 
1178   if (container.ordered_valAttr().getInt() !=
1179       (int64_t)op.num_loops_val().getValue())
1180     return op.emitOpError() << "number of variables in depend clause does not "
1181                             << "match number of iteration variables in the "
1182                             << "doacross loop";
1183 
1184   return success();
1185 }
1186 
1187 static LogicalResult verifyOrderedRegionOp(OrderedRegionOp op) {
1188   // TODO: The code generation for ordered simd directive is not supported yet.
1189   if (op.simd())
1190     return failure();
1191 
1192   if (auto container = op->getParentOfType<WsLoopOp>()) {
1193     if (!container.ordered_valAttr() ||
1194         container.ordered_valAttr().getInt() != 0)
1195       return op.emitOpError() << "ordered region must be closely nested inside "
1196                               << "a worksharing-loop region with an ordered "
1197                               << "clause without parameter present";
1198   }
1199 
1200   return success();
1201 }
1202 
1203 //===----------------------------------------------------------------------===//
1204 // AtomicReadOp
1205 //===----------------------------------------------------------------------===//
1206 
1207 /// Parser for AtomicReadOp
1208 ///
1209 /// operation ::= `omp.atomic.read` atomic-clause-list address `->` result-type
1210 /// address ::= operand `:` type
1211 static ParseResult parseAtomicReadOp(OpAsmParser &parser,
1212                                      OperationState &result) {
1213   OpAsmParser::OperandType address;
1214   Type addressType;
1215   SmallVector<ClauseType> clauses = {memoryOrderClause, hintClause};
1216   SmallVector<int> segments;
1217 
1218   if (parser.parseOperand(address) ||
1219       parseClauses(parser, result, clauses, segments) ||
1220       parser.parseColonType(addressType) ||
1221       parser.resolveOperand(address, addressType, result.operands))
1222     return failure();
1223 
1224   SmallVector<Type> resultType;
1225   if (parser.parseArrowTypeList(resultType))
1226     return failure();
1227   result.addTypes(resultType);
1228   return success();
1229 }
1230 
1231 /// Printer for AtomicReadOp
1232 static void printAtomicReadOp(OpAsmPrinter &p, AtomicReadOp op) {
1233   p << " " << op.address() << " ";
1234   if (op.memory_order())
1235     p << "memory_order(" << op.memory_order().getValue() << ") ";
1236   if (op.hintAttr())
1237     printSynchronizationHint(p << " ", op, op.hintAttr());
1238   p << ": " << op.address().getType() << " -> " << op.getType();
1239   return;
1240 }
1241 
1242 /// Verifier for AtomicReadOp
1243 static LogicalResult verifyAtomicReadOp(AtomicReadOp op) {
1244   if (op.memory_order()) {
1245     StringRef memOrder = op.memory_order().getValue();
1246     if (memOrder.equals("acq_rel") || memOrder.equals("release"))
1247       return op.emitError(
1248           "memory-order must not be acq_rel or release for atomic reads");
1249   }
1250   return verifySynchronizationHint(op, op.hint());
1251 }
1252 
1253 //===----------------------------------------------------------------------===//
1254 // AtomicWriteOp
1255 //===----------------------------------------------------------------------===//
1256 
1257 /// Parser for AtomicWriteOp
1258 ///
1259 /// operation ::= `omp.atomic.write` atomic-clause-list operands
1260 /// operands ::= address `,` value
1261 /// address ::= operand `:` type
1262 /// value ::= operand `:` type
1263 static ParseResult parseAtomicWriteOp(OpAsmParser &parser,
1264                                       OperationState &result) {
1265   OpAsmParser::OperandType address, value;
1266   Type addrType, valueType;
1267   SmallVector<ClauseType> clauses = {memoryOrderClause, hintClause};
1268   SmallVector<int> segments;
1269 
1270   if (parser.parseOperand(address) || parser.parseComma() ||
1271       parser.parseOperand(value) ||
1272       parseClauses(parser, result, clauses, segments) ||
1273       parser.parseColonType(addrType) || parser.parseComma() ||
1274       parser.parseType(valueType) ||
1275       parser.resolveOperand(address, addrType, result.operands) ||
1276       parser.resolveOperand(value, valueType, result.operands))
1277     return failure();
1278   return success();
1279 }
1280 
1281 /// Printer for AtomicWriteOp
1282 static void printAtomicWriteOp(OpAsmPrinter &p, AtomicWriteOp op) {
1283   p << " " << op.address() << ", " << op.value() << " ";
1284   if (op.memory_order())
1285     p << "memory_order(" << op.memory_order() << ") ";
1286   if (op.hintAttr())
1287     printSynchronizationHint(p, op, op.hintAttr());
1288   p << ": " << op.address().getType() << ", " << op.value().getType();
1289   return;
1290 }
1291 
1292 /// Verifier for AtomicWriteOp
1293 static LogicalResult verifyAtomicWriteOp(AtomicWriteOp op) {
1294   if (op.memory_order()) {
1295     StringRef memoryOrder = op.memory_order().getValue();
1296     if (memoryOrder.equals("acq_rel") || memoryOrder.equals("acquire"))
1297       return op.emitError(
1298           "memory-order must not be acq_rel or acquire for atomic writes");
1299   }
1300   return verifySynchronizationHint(op, op.hint());
1301 }
1302 
1303 #define GET_OP_CLASSES
1304 #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
1305