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