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   if (failed(parser.parseOptionalKeyword("hint"))) {
396     hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), 0);
397     return success();
398   }
399 
400   if (failed(parser.parseLParen()))
401     return failure();
402   StringRef hintKeyword;
403   int64_t hint = 0;
404   do {
405     if (failed(parser.parseKeyword(&hintKeyword)))
406       return failure();
407     if (hintKeyword == "uncontended")
408       hint |= 1;
409     else if (hintKeyword == "contended")
410       hint |= 2;
411     else if (hintKeyword == "nonspeculative")
412       hint |= 4;
413     else if (hintKeyword == "speculative")
414       hint |= 8;
415     else
416       return parser.emitError(parser.getCurrentLocation())
417              << hintKeyword << " is not a valid hint";
418   } while (succeeded(parser.parseOptionalComma()));
419   if (failed(parser.parseRParen()))
420     return failure();
421   hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), hint);
422   return success();
423 }
424 
425 /// Prints a Synchronization Hint clause
426 static void printSynchronizationHint(OpAsmPrinter &p, Operation *op,
427                                      IntegerAttr hintAttr) {
428   int64_t hint = hintAttr.getInt();
429 
430   if (hint == 0)
431     return;
432 
433   // Helper function to get n-th bit from the right end of `value`
434   auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
435 
436   bool uncontended = bitn(hint, 0);
437   bool contended = bitn(hint, 1);
438   bool nonspeculative = bitn(hint, 2);
439   bool speculative = bitn(hint, 3);
440 
441   SmallVector<StringRef> hints;
442   if (uncontended)
443     hints.push_back("uncontended");
444   if (contended)
445     hints.push_back("contended");
446   if (nonspeculative)
447     hints.push_back("nonspeculative");
448   if (speculative)
449     hints.push_back("speculative");
450 
451   p << "hint(";
452   llvm::interleaveComma(hints, p);
453   p << ")";
454 }
455 
456 /// Verifies a synchronization hint clause
457 static LogicalResult verifySynchronizationHint(Operation *op, int32_t hint) {
458 
459   // Helper function to get n-th bit from the right end of `value`
460   auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
461 
462   bool uncontended = bitn(hint, 0);
463   bool contended = bitn(hint, 1);
464   bool nonspeculative = bitn(hint, 2);
465   bool speculative = bitn(hint, 3);
466 
467   if (uncontended && contended)
468     return op->emitOpError() << "the hints omp_sync_hint_uncontended and "
469                                 "omp_sync_hint_contended cannot be combined";
470   if (nonspeculative && speculative)
471     return op->emitOpError() << "the hints omp_sync_hint_nonspeculative and "
472                                 "omp_sync_hint_speculative cannot be combined.";
473   return success();
474 }
475 
476 enum ClauseType {
477   ifClause,
478   numThreadsClause,
479   privateClause,
480   firstprivateClause,
481   lastprivateClause,
482   sharedClause,
483   copyinClause,
484   allocateClause,
485   defaultClause,
486   procBindClause,
487   reductionClause,
488   nowaitClause,
489   linearClause,
490   scheduleClause,
491   collapseClause,
492   orderClause,
493   orderedClause,
494   inclusiveClause,
495   COUNT
496 };
497 
498 //===----------------------------------------------------------------------===//
499 // Parser for Clause List
500 //===----------------------------------------------------------------------===//
501 
502 /// Parse a list of clauses. The clauses can appear in any order, but their
503 /// operand segment indices are in the same order that they are passed in the
504 /// `clauses` list. The operand segments are added over the prevSegments
505 
506 /// clause-list ::= clause clause-list | empty
507 /// clause ::= if | num-threads | private | firstprivate | lastprivate |
508 ///            shared | copyin | allocate | default | proc-bind | reduction |
509 ///            nowait | linear | schedule | collapse | order | ordered |
510 ///            inclusive
511 /// if ::= `if` `(` ssa-id-and-type `)`
512 /// num-threads ::= `num_threads` `(` ssa-id-and-type `)`
513 /// private ::= `private` operand-and-type-list
514 /// firstprivate ::= `firstprivate` operand-and-type-list
515 /// lastprivate ::= `lastprivate` operand-and-type-list
516 /// shared ::= `shared` operand-and-type-list
517 /// copyin ::= `copyin` operand-and-type-list
518 /// allocate ::= `allocate` `(` allocate-operand-list `)`
519 /// default ::= `default` `(` (`private` | `firstprivate` | `shared` | `none`)
520 /// proc-bind ::= `proc_bind` `(` (`master` | `close` | `spread`) `)`
521 /// reduction ::= `reduction` `(` reduction-entry-list `)`
522 /// nowait ::= `nowait`
523 /// linear ::= `linear` `(` linear-list `)`
524 /// schedule ::= `schedule` `(` sched-list `)`
525 /// collapse ::= `collapse` `(` ssa-id-and-type `)`
526 /// order ::= `order` `(` `concurrent` `)`
527 /// ordered ::= `ordered` `(` ssa-id-and-type `)`
528 /// inclusive ::= `inclusive`
529 ///
530 /// Note that each clause can only appear once in the clase-list.
531 static ParseResult parseClauses(OpAsmParser &parser, OperationState &result,
532                                 SmallVectorImpl<ClauseType> &clauses,
533                                 SmallVectorImpl<int> &segments) {
534 
535   // Check done[clause] to see if it has been parsed already
536   llvm::BitVector done(ClauseType::COUNT, false);
537 
538   // See pos[clause] to get position of clause in operand segments
539   SmallVector<int> pos(ClauseType::COUNT, -1);
540 
541   // Stores the last parsed clause keyword
542   StringRef clauseKeyword;
543   StringRef opName = result.name.getStringRef();
544 
545   // Containers for storing operands, types and attributes for various clauses
546   std::pair<OpAsmParser::OperandType, Type> ifCond;
547   std::pair<OpAsmParser::OperandType, Type> numThreads;
548 
549   SmallVector<OpAsmParser::OperandType> privates, firstprivates, lastprivates,
550       shareds, copyins;
551   SmallVector<Type> privateTypes, firstprivateTypes, lastprivateTypes,
552       sharedTypes, copyinTypes;
553 
554   SmallVector<OpAsmParser::OperandType> allocates, allocators;
555   SmallVector<Type> allocateTypes, allocatorTypes;
556 
557   SmallVector<SymbolRefAttr> reductionSymbols;
558   SmallVector<OpAsmParser::OperandType> reductionVars;
559   SmallVector<Type> reductionVarTypes;
560 
561   SmallVector<OpAsmParser::OperandType> linears;
562   SmallVector<Type> linearTypes;
563   SmallVector<OpAsmParser::OperandType> linearSteps;
564 
565   SmallString<8> schedule;
566   SmallVector<SmallString<12>> modifiers;
567   Optional<OpAsmParser::OperandType> scheduleChunkSize;
568 
569   // Compute the position of clauses in operand segments
570   int currPos = 0;
571   for (ClauseType clause : clauses) {
572 
573     // Skip the following clauses - they do not take any position in operand
574     // segments
575     if (clause == defaultClause || clause == procBindClause ||
576         clause == nowaitClause || clause == collapseClause ||
577         clause == orderClause || clause == orderedClause ||
578         clause == inclusiveClause)
579       continue;
580 
581     pos[clause] = currPos++;
582 
583     // For the following clauses, two positions are reserved in the operand
584     // segments
585     if (clause == allocateClause || clause == linearClause)
586       currPos++;
587   }
588 
589   SmallVector<int> clauseSegments(currPos);
590 
591   // Helper function to check if a clause is allowed/repeated or not
592   auto checkAllowed = [&](ClauseType clause,
593                           bool allowRepeat = false) -> ParseResult {
594     if (!llvm::is_contained(clauses, clause))
595       return parser.emitError(parser.getCurrentLocation())
596              << clauseKeyword << "is not a valid clause for the " << opName
597              << " operation";
598     if (done[clause] && !allowRepeat)
599       return parser.emitError(parser.getCurrentLocation())
600              << "at most one " << clauseKeyword << " clause can appear on the "
601              << opName << " operation";
602     done[clause] = true;
603     return success();
604   };
605 
606   while (succeeded(parser.parseOptionalKeyword(&clauseKeyword))) {
607     if (clauseKeyword == "if") {
608       if (checkAllowed(ifClause) || parser.parseLParen() ||
609           parser.parseOperand(ifCond.first) ||
610           parser.parseColonType(ifCond.second) || parser.parseRParen())
611         return failure();
612       clauseSegments[pos[ifClause]] = 1;
613     } else if (clauseKeyword == "num_threads") {
614       if (checkAllowed(numThreadsClause) || parser.parseLParen() ||
615           parser.parseOperand(numThreads.first) ||
616           parser.parseColonType(numThreads.second) || parser.parseRParen())
617         return failure();
618       clauseSegments[pos[numThreadsClause]] = 1;
619     } else if (clauseKeyword == "private") {
620       if (checkAllowed(privateClause) ||
621           parseOperandAndTypeList(parser, privates, privateTypes))
622         return failure();
623       clauseSegments[pos[privateClause]] = privates.size();
624     } else if (clauseKeyword == "firstprivate") {
625       if (checkAllowed(firstprivateClause) ||
626           parseOperandAndTypeList(parser, firstprivates, firstprivateTypes))
627         return failure();
628       clauseSegments[pos[firstprivateClause]] = firstprivates.size();
629     } else if (clauseKeyword == "lastprivate") {
630       if (checkAllowed(lastprivateClause) ||
631           parseOperandAndTypeList(parser, lastprivates, lastprivateTypes))
632         return failure();
633       clauseSegments[pos[lastprivateClause]] = lastprivates.size();
634     } else if (clauseKeyword == "shared") {
635       if (checkAllowed(sharedClause) ||
636           parseOperandAndTypeList(parser, shareds, sharedTypes))
637         return failure();
638       clauseSegments[pos[sharedClause]] = shareds.size();
639     } else if (clauseKeyword == "copyin") {
640       if (checkAllowed(copyinClause) ||
641           parseOperandAndTypeList(parser, copyins, copyinTypes))
642         return failure();
643       clauseSegments[pos[copyinClause]] = copyins.size();
644     } else if (clauseKeyword == "allocate") {
645       if (checkAllowed(allocateClause) ||
646           parseAllocateAndAllocator(parser, allocates, allocateTypes,
647                                     allocators, allocatorTypes))
648         return failure();
649       clauseSegments[pos[allocateClause]] = allocates.size();
650       clauseSegments[pos[allocateClause] + 1] = allocators.size();
651     } else if (clauseKeyword == "default") {
652       StringRef defval;
653       if (checkAllowed(defaultClause) || parser.parseLParen() ||
654           parser.parseKeyword(&defval) || parser.parseRParen())
655         return failure();
656       // The def prefix is required for the attribute as "private" is a keyword
657       // in C++.
658       auto attr = parser.getBuilder().getStringAttr("def" + defval);
659       result.addAttribute("default_val", attr);
660     } else if (clauseKeyword == "proc_bind") {
661       StringRef bind;
662       if (checkAllowed(procBindClause) || parser.parseLParen() ||
663           parser.parseKeyword(&bind) || parser.parseRParen())
664         return failure();
665       auto attr = parser.getBuilder().getStringAttr(bind);
666       result.addAttribute("proc_bind_val", attr);
667     } else if (clauseKeyword == "reduction") {
668       if (checkAllowed(reductionClause) ||
669           parseReductionVarList(parser, reductionSymbols, reductionVars,
670                                 reductionVarTypes))
671         return failure();
672       clauseSegments[pos[reductionClause]] = reductionVars.size();
673     } else if (clauseKeyword == "nowait") {
674       if (checkAllowed(nowaitClause))
675         return failure();
676       auto attr = UnitAttr::get(parser.getBuilder().getContext());
677       result.addAttribute("nowait", attr);
678     } else if (clauseKeyword == "linear") {
679       if (checkAllowed(linearClause) ||
680           parseLinearClause(parser, linears, linearTypes, linearSteps))
681         return failure();
682       clauseSegments[pos[linearClause]] = linears.size();
683       clauseSegments[pos[linearClause] + 1] = linearSteps.size();
684     } else if (clauseKeyword == "schedule") {
685       if (checkAllowed(scheduleClause) ||
686           parseScheduleClause(parser, schedule, modifiers, scheduleChunkSize))
687         return failure();
688       if (scheduleChunkSize) {
689         clauseSegments[pos[scheduleClause]] = 1;
690       }
691     } else if (clauseKeyword == "collapse") {
692       auto type = parser.getBuilder().getI64Type();
693       mlir::IntegerAttr attr;
694       if (checkAllowed(collapseClause) || parser.parseLParen() ||
695           parser.parseAttribute(attr, type) || parser.parseRParen())
696         return failure();
697       result.addAttribute("collapse_val", attr);
698     } else if (clauseKeyword == "ordered") {
699       mlir::IntegerAttr attr;
700       if (checkAllowed(orderedClause))
701         return failure();
702       if (succeeded(parser.parseOptionalLParen())) {
703         auto type = parser.getBuilder().getI64Type();
704         if (parser.parseAttribute(attr, type) || parser.parseRParen())
705           return failure();
706       } else {
707         // Use 0 to represent no ordered parameter was specified
708         attr = parser.getBuilder().getI64IntegerAttr(0);
709       }
710       result.addAttribute("ordered_val", attr);
711     } else if (clauseKeyword == "order") {
712       StringRef order;
713       if (checkAllowed(orderClause) || parser.parseLParen() ||
714           parser.parseKeyword(&order) || parser.parseRParen())
715         return failure();
716       auto attr = parser.getBuilder().getStringAttr(order);
717       result.addAttribute("order", attr);
718     } else if (clauseKeyword == "inclusive") {
719       if (checkAllowed(inclusiveClause))
720         return failure();
721       auto attr = UnitAttr::get(parser.getBuilder().getContext());
722       result.addAttribute("inclusive", attr);
723     } else {
724       return parser.emitError(parser.getNameLoc())
725              << clauseKeyword << " is not a valid clause";
726     }
727   }
728 
729   // Add if parameter.
730   if (done[ifClause] && clauseSegments[pos[ifClause]] &&
731       failed(
732           parser.resolveOperand(ifCond.first, ifCond.second, result.operands)))
733     return failure();
734 
735   // Add num_threads parameter.
736   if (done[numThreadsClause] && clauseSegments[pos[numThreadsClause]] &&
737       failed(parser.resolveOperand(numThreads.first, numThreads.second,
738                                    result.operands)))
739     return failure();
740 
741   // Add private parameters.
742   if (done[privateClause] && clauseSegments[pos[privateClause]] &&
743       failed(parser.resolveOperands(privates, privateTypes,
744                                     privates[0].location, result.operands)))
745     return failure();
746 
747   // Add firstprivate parameters.
748   if (done[firstprivateClause] && clauseSegments[pos[firstprivateClause]] &&
749       failed(parser.resolveOperands(firstprivates, firstprivateTypes,
750                                     firstprivates[0].location,
751                                     result.operands)))
752     return failure();
753 
754   // Add lastprivate parameters.
755   if (done[lastprivateClause] && clauseSegments[pos[lastprivateClause]] &&
756       failed(parser.resolveOperands(lastprivates, lastprivateTypes,
757                                     lastprivates[0].location, result.operands)))
758     return failure();
759 
760   // Add shared parameters.
761   if (done[sharedClause] && clauseSegments[pos[sharedClause]] &&
762       failed(parser.resolveOperands(shareds, sharedTypes, shareds[0].location,
763                                     result.operands)))
764     return failure();
765 
766   // Add copyin parameters.
767   if (done[copyinClause] && clauseSegments[pos[copyinClause]] &&
768       failed(parser.resolveOperands(copyins, copyinTypes, copyins[0].location,
769                                     result.operands)))
770     return failure();
771 
772   // Add allocate parameters.
773   if (done[allocateClause] && clauseSegments[pos[allocateClause]] &&
774       failed(parser.resolveOperands(allocates, allocateTypes,
775                                     allocates[0].location, result.operands)))
776     return failure();
777 
778   // Add allocator parameters.
779   if (done[allocateClause] && clauseSegments[pos[allocateClause] + 1] &&
780       failed(parser.resolveOperands(allocators, allocatorTypes,
781                                     allocators[0].location, result.operands)))
782     return failure();
783 
784   // Add reduction parameters and symbols
785   if (done[reductionClause] && clauseSegments[pos[reductionClause]]) {
786     if (failed(parser.resolveOperands(reductionVars, reductionVarTypes,
787                                       parser.getNameLoc(), result.operands)))
788       return failure();
789 
790     SmallVector<Attribute> reductions(reductionSymbols.begin(),
791                                       reductionSymbols.end());
792     result.addAttribute("reductions",
793                         parser.getBuilder().getArrayAttr(reductions));
794   }
795 
796   // Add linear parameters
797   if (done[linearClause] && clauseSegments[pos[linearClause]]) {
798     auto linearStepType = parser.getBuilder().getI32Type();
799     SmallVector<Type> linearStepTypes(linearSteps.size(), linearStepType);
800     if (failed(parser.resolveOperands(linears, linearTypes, linears[0].location,
801                                       result.operands)) ||
802         failed(parser.resolveOperands(linearSteps, linearStepTypes,
803                                       linearSteps[0].location,
804                                       result.operands)))
805       return failure();
806   }
807 
808   // Add schedule parameters
809   if (done[scheduleClause] && !schedule.empty()) {
810     schedule[0] = llvm::toUpper(schedule[0]);
811     auto attr = parser.getBuilder().getStringAttr(schedule);
812     result.addAttribute("schedule_val", attr);
813     if (modifiers.size() > 0) {
814       auto mod = parser.getBuilder().getStringAttr(modifiers[0]);
815       result.addAttribute("schedule_modifier", mod);
816     }
817     if (scheduleChunkSize) {
818       auto chunkSizeType = parser.getBuilder().getI32Type();
819       parser.resolveOperand(*scheduleChunkSize, chunkSizeType, result.operands);
820     }
821   }
822 
823   segments.insert(segments.end(), clauseSegments.begin(), clauseSegments.end());
824 
825   return success();
826 }
827 
828 /// Parses a parallel operation.
829 ///
830 /// operation ::= `omp.parallel` clause-list
831 /// clause-list ::= clause | clause clause-list
832 /// clause ::= if | num-threads | private | firstprivate | shared | copyin |
833 ///            allocate | default | proc-bind
834 ///
835 static ParseResult parseParallelOp(OpAsmParser &parser,
836                                    OperationState &result) {
837   SmallVector<ClauseType> clauses = {
838       ifClause,           numThreadsClause, privateClause,
839       firstprivateClause, sharedClause,     copyinClause,
840       allocateClause,     defaultClause,    procBindClause};
841 
842   SmallVector<int> segments;
843 
844   if (failed(parseClauses(parser, result, clauses, segments)))
845     return failure();
846 
847   result.addAttribute("operand_segment_sizes",
848                       parser.getBuilder().getI32VectorAttr(segments));
849 
850   Region *body = result.addRegion();
851   SmallVector<OpAsmParser::OperandType> regionArgs;
852   SmallVector<Type> regionArgTypes;
853   if (parser.parseRegion(*body, regionArgs, regionArgTypes))
854     return failure();
855   return success();
856 }
857 
858 /// Parses an OpenMP Workshare Loop operation
859 ///
860 /// wsloop ::= `omp.wsloop` loop-control clause-list
861 /// loop-control ::= `(` ssa-id-list `)` `:` type `=`  loop-bounds
862 /// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` steps
863 /// steps := `step` `(`ssa-id-list`)`
864 /// clause-list ::= clause clause-list | empty
865 /// clause ::= private | firstprivate | lastprivate | linear | schedule |
866 //             collapse | nowait | ordered | order | inclusive | reduction
867 static ParseResult parseWsLoopOp(OpAsmParser &parser, OperationState &result) {
868 
869   // Parse an opening `(` followed by induction variables followed by `)`
870   SmallVector<OpAsmParser::OperandType> ivs;
871   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
872                                      OpAsmParser::Delimiter::Paren))
873     return failure();
874 
875   int numIVs = static_cast<int>(ivs.size());
876   Type loopVarType;
877   if (parser.parseColonType(loopVarType))
878     return failure();
879 
880   // Parse loop bounds.
881   SmallVector<OpAsmParser::OperandType> lower;
882   if (parser.parseEqual() ||
883       parser.parseOperandList(lower, numIVs, OpAsmParser::Delimiter::Paren) ||
884       parser.resolveOperands(lower, loopVarType, result.operands))
885     return failure();
886 
887   SmallVector<OpAsmParser::OperandType> upper;
888   if (parser.parseKeyword("to") ||
889       parser.parseOperandList(upper, numIVs, OpAsmParser::Delimiter::Paren) ||
890       parser.resolveOperands(upper, loopVarType, result.operands))
891     return failure();
892 
893   // Parse step values.
894   SmallVector<OpAsmParser::OperandType> steps;
895   if (parser.parseKeyword("step") ||
896       parser.parseOperandList(steps, numIVs, OpAsmParser::Delimiter::Paren) ||
897       parser.resolveOperands(steps, loopVarType, result.operands))
898     return failure();
899 
900   SmallVector<ClauseType> clauses = {
901       privateClause,   firstprivateClause, lastprivateClause, linearClause,
902       reductionClause, collapseClause,     orderClause,       orderedClause,
903       nowaitClause,    scheduleClause};
904   SmallVector<int> segments{numIVs, numIVs, numIVs};
905   if (failed(parseClauses(parser, result, clauses, segments)))
906     return failure();
907 
908   result.addAttribute("operand_segment_sizes",
909                       parser.getBuilder().getI32VectorAttr(segments));
910 
911   // Now parse the body.
912   Region *body = result.addRegion();
913   SmallVector<Type> ivTypes(numIVs, loopVarType);
914   SmallVector<OpAsmParser::OperandType> blockArgs(ivs);
915   if (parser.parseRegion(*body, blockArgs, ivTypes))
916     return failure();
917   return success();
918 }
919 
920 static void printWsLoopOp(OpAsmPrinter &p, WsLoopOp op) {
921   auto args = op.getRegion().front().getArguments();
922   p << " (" << args << ") : " << args[0].getType() << " = (" << op.lowerBound()
923     << ") to (" << op.upperBound() << ") step (" << op.step() << ") ";
924 
925   printDataVars(p, op.private_vars(), "private");
926   printDataVars(p, op.firstprivate_vars(), "firstprivate");
927   printDataVars(p, op.lastprivate_vars(), "lastprivate");
928 
929   if (op.linear_vars().size()) {
930     p << "linear";
931     printLinearClause(p, op.linear_vars(), op.linear_step_vars());
932   }
933 
934   if (auto sched = op.schedule_val()) {
935     p << "schedule";
936     printScheduleClause(p, sched.getValue(), op.schedule_modifier(),
937                         op.schedule_chunk_var());
938   }
939 
940   if (auto collapse = op.collapse_val())
941     p << "collapse(" << collapse << ") ";
942 
943   if (op.nowait())
944     p << "nowait ";
945 
946   if (auto ordered = op.ordered_val())
947     p << "ordered(" << ordered << ") ";
948 
949   if (!op.reduction_vars().empty()) {
950     p << "reduction(";
951     printReductionVarList(p, op.reductions(), op.reduction_vars());
952   }
953 
954   if (op.inclusive()) {
955     p << "inclusive ";
956   }
957 
958   p.printRegion(op.region(), /*printEntryBlockArgs=*/false);
959 }
960 
961 //===----------------------------------------------------------------------===//
962 // ReductionOp
963 //===----------------------------------------------------------------------===//
964 
965 static ParseResult parseAtomicReductionRegion(OpAsmParser &parser,
966                                               Region &region) {
967   if (parser.parseOptionalKeyword("atomic"))
968     return success();
969   return parser.parseRegion(region);
970 }
971 
972 static void printAtomicReductionRegion(OpAsmPrinter &printer,
973                                        ReductionDeclareOp op, Region &region) {
974   if (region.empty())
975     return;
976   printer << "atomic ";
977   printer.printRegion(region);
978 }
979 
980 static LogicalResult verifyReductionDeclareOp(ReductionDeclareOp op) {
981   if (op.initializerRegion().empty())
982     return op.emitOpError() << "expects non-empty initializer region";
983   Block &initializerEntryBlock = op.initializerRegion().front();
984   if (initializerEntryBlock.getNumArguments() != 1 ||
985       initializerEntryBlock.getArgument(0).getType() != op.type()) {
986     return op.emitOpError() << "expects initializer region with one argument "
987                                "of the reduction type";
988   }
989 
990   for (YieldOp yieldOp : op.initializerRegion().getOps<YieldOp>()) {
991     if (yieldOp.results().size() != 1 ||
992         yieldOp.results().getTypes()[0] != op.type())
993       return op.emitOpError() << "expects initializer region to yield a value "
994                                  "of the reduction type";
995   }
996 
997   if (op.reductionRegion().empty())
998     return op.emitOpError() << "expects non-empty reduction region";
999   Block &reductionEntryBlock = op.reductionRegion().front();
1000   if (reductionEntryBlock.getNumArguments() != 2 ||
1001       reductionEntryBlock.getArgumentTypes()[0] !=
1002           reductionEntryBlock.getArgumentTypes()[1] ||
1003       reductionEntryBlock.getArgumentTypes()[0] != op.type())
1004     return op.emitOpError() << "expects reduction region with two arguments of "
1005                                "the reduction type";
1006   for (YieldOp yieldOp : op.reductionRegion().getOps<YieldOp>()) {
1007     if (yieldOp.results().size() != 1 ||
1008         yieldOp.results().getTypes()[0] != op.type())
1009       return op.emitOpError() << "expects reduction region to yield a value "
1010                                  "of the reduction type";
1011   }
1012 
1013   if (op.atomicReductionRegion().empty())
1014     return success();
1015 
1016   Block &atomicReductionEntryBlock = op.atomicReductionRegion().front();
1017   if (atomicReductionEntryBlock.getNumArguments() != 2 ||
1018       atomicReductionEntryBlock.getArgumentTypes()[0] !=
1019           atomicReductionEntryBlock.getArgumentTypes()[1])
1020     return op.emitOpError() << "expects atomic reduction region with two "
1021                                "arguments of the same type";
1022   auto ptrType = atomicReductionEntryBlock.getArgumentTypes()[0]
1023                      .dyn_cast<PointerLikeType>();
1024   if (!ptrType || ptrType.getElementType() != op.type())
1025     return op.emitOpError() << "expects atomic reduction region arguments to "
1026                                "be accumulators containing the reduction type";
1027   return success();
1028 }
1029 
1030 static LogicalResult verifyReductionOp(ReductionOp op) {
1031   // TODO: generalize this to an op interface when there is more than one op
1032   // that supports reductions.
1033   auto container = op->getParentOfType<WsLoopOp>();
1034   for (unsigned i = 0, e = container.getNumReductionVars(); i < e; ++i)
1035     if (container.reduction_vars()[i] == op.accumulator())
1036       return success();
1037 
1038   return op.emitOpError() << "the accumulator is not used by the parent";
1039 }
1040 
1041 //===----------------------------------------------------------------------===//
1042 // WsLoopOp
1043 //===----------------------------------------------------------------------===//
1044 
1045 void WsLoopOp::build(OpBuilder &builder, OperationState &state,
1046                      ValueRange lowerBound, ValueRange upperBound,
1047                      ValueRange step, ArrayRef<NamedAttribute> attributes) {
1048   build(builder, state, TypeRange(), lowerBound, upperBound, step,
1049         /*private_vars=*/ValueRange(),
1050         /*firstprivate_vars=*/ValueRange(), /*lastprivate_vars=*/ValueRange(),
1051         /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(),
1052         /*reduction_vars=*/ValueRange(), /*schedule_val=*/nullptr,
1053         /*schedule_chunk_var=*/nullptr, /*collapse_val=*/nullptr,
1054         /*nowait=*/nullptr, /*ordered_val=*/nullptr, /*order_val=*/nullptr,
1055         /*inclusive=*/nullptr, /*buildBody=*/false);
1056   state.addAttributes(attributes);
1057 }
1058 
1059 void WsLoopOp::build(OpBuilder &, OperationState &state, TypeRange resultTypes,
1060                      ValueRange operands, ArrayRef<NamedAttribute> attributes) {
1061   state.addOperands(operands);
1062   state.addAttributes(attributes);
1063   (void)state.addRegion();
1064   assert(resultTypes.empty() && "mismatched number of return types");
1065   state.addTypes(resultTypes);
1066 }
1067 
1068 void WsLoopOp::build(OpBuilder &builder, OperationState &result,
1069                      TypeRange typeRange, ValueRange lowerBounds,
1070                      ValueRange upperBounds, ValueRange steps,
1071                      ValueRange privateVars, ValueRange firstprivateVars,
1072                      ValueRange lastprivateVars, ValueRange linearVars,
1073                      ValueRange linearStepVars, ValueRange reductionVars,
1074                      StringAttr scheduleVal, Value scheduleChunkVar,
1075                      IntegerAttr collapseVal, UnitAttr nowait,
1076                      IntegerAttr orderedVal, StringAttr orderVal,
1077                      UnitAttr inclusive, bool buildBody) {
1078   result.addOperands(lowerBounds);
1079   result.addOperands(upperBounds);
1080   result.addOperands(steps);
1081   result.addOperands(privateVars);
1082   result.addOperands(firstprivateVars);
1083   result.addOperands(linearVars);
1084   result.addOperands(linearStepVars);
1085   if (scheduleChunkVar)
1086     result.addOperands(scheduleChunkVar);
1087 
1088   if (scheduleVal)
1089     result.addAttribute("schedule_val", scheduleVal);
1090   if (collapseVal)
1091     result.addAttribute("collapse_val", collapseVal);
1092   if (nowait)
1093     result.addAttribute("nowait", nowait);
1094   if (orderedVal)
1095     result.addAttribute("ordered_val", orderedVal);
1096   if (orderVal)
1097     result.addAttribute("order", orderVal);
1098   if (inclusive)
1099     result.addAttribute("inclusive", inclusive);
1100   result.addAttribute(
1101       WsLoopOp::getOperandSegmentSizeAttr(),
1102       builder.getI32VectorAttr(
1103           {static_cast<int32_t>(lowerBounds.size()),
1104            static_cast<int32_t>(upperBounds.size()),
1105            static_cast<int32_t>(steps.size()),
1106            static_cast<int32_t>(privateVars.size()),
1107            static_cast<int32_t>(firstprivateVars.size()),
1108            static_cast<int32_t>(lastprivateVars.size()),
1109            static_cast<int32_t>(linearVars.size()),
1110            static_cast<int32_t>(linearStepVars.size()),
1111            static_cast<int32_t>(reductionVars.size()),
1112            static_cast<int32_t>(scheduleChunkVar != nullptr ? 1 : 0)}));
1113 
1114   Region *bodyRegion = result.addRegion();
1115   if (buildBody) {
1116     OpBuilder::InsertionGuard guard(builder);
1117     unsigned numIVs = steps.size();
1118     SmallVector<Type, 8> argTypes(numIVs, steps.getType().front());
1119     builder.createBlock(bodyRegion, {}, argTypes);
1120   }
1121 }
1122 
1123 static LogicalResult verifyWsLoopOp(WsLoopOp op) {
1124   return verifyReductionVarList(op, op.reductions(), op.reduction_vars());
1125 }
1126 
1127 //===----------------------------------------------------------------------===//
1128 // Verifier for critical construct (2.17.1)
1129 //===----------------------------------------------------------------------===//
1130 
1131 static LogicalResult verifyCriticalDeclareOp(CriticalDeclareOp op) {
1132   return verifySynchronizationHint(op, op.hint());
1133 }
1134 
1135 static LogicalResult verifyCriticalOp(CriticalOp op) {
1136 
1137   if (op.nameAttr()) {
1138     auto symbolRef = op.nameAttr().cast<SymbolRefAttr>();
1139     auto decl =
1140         SymbolTable::lookupNearestSymbolFrom<CriticalDeclareOp>(op, symbolRef);
1141     if (!decl) {
1142       return op.emitOpError() << "expected symbol reference " << symbolRef
1143                               << " to point to a critical declaration";
1144     }
1145   }
1146 
1147   return success();
1148 }
1149 
1150 //===----------------------------------------------------------------------===//
1151 // Verifier for ordered construct
1152 //===----------------------------------------------------------------------===//
1153 
1154 static LogicalResult verifyOrderedOp(OrderedOp op) {
1155   auto container = op->getParentOfType<WsLoopOp>();
1156   if (!container || !container.ordered_valAttr() ||
1157       container.ordered_valAttr().getInt() == 0)
1158     return op.emitOpError() << "ordered depend directive must be closely "
1159                             << "nested inside a worksharing-loop with ordered "
1160                             << "clause with parameter present";
1161 
1162   if (container.ordered_valAttr().getInt() !=
1163       (int64_t)op.num_loops_val().getValue())
1164     return op.emitOpError() << "number of variables in depend clause does not "
1165                             << "match number of iteration variables in the "
1166                             << "doacross loop";
1167 
1168   return success();
1169 }
1170 
1171 static LogicalResult verifyOrderedRegionOp(OrderedRegionOp op) {
1172   // TODO: The code generation for ordered simd directive is not supported yet.
1173   if (op.simd())
1174     return failure();
1175 
1176   if (auto container = op->getParentOfType<WsLoopOp>()) {
1177     if (!container.ordered_valAttr() ||
1178         container.ordered_valAttr().getInt() != 0)
1179       return op.emitOpError() << "ordered region must be closely nested inside "
1180                               << "a worksharing-loop region with an ordered "
1181                               << "clause without parameter present";
1182   }
1183 
1184   return success();
1185 }
1186 
1187 #define GET_OP_CLASSES
1188 #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
1189