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::OperandType> &operandsAllocate,
87     SmallVectorImpl<Type> &typesAllocate,
88     SmallVectorImpl<OpAsmParser::OperandType> &operandsAllocator,
89     SmallVectorImpl<Type> &typesAllocator) {
90 
91   return parser.parseCommaSeparatedList([&]() -> ParseResult {
92     OpAsmParser::OperandType 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::OperandType> &vars,
181                   SmallVectorImpl<Type> &types,
182                   SmallVectorImpl<OpAsmParser::OperandType> &stepVars) {
183   if (parser.parseLParen())
184     return failure();
185 
186   do {
187     OpAsmParser::OperandType var;
188     Type type;
189     OpAsmParser::OperandType 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::OperandType> &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::OperandType{};
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 parseReductionVarList(
330     OpAsmParser &parser, SmallVectorImpl<OpAsmParser::OperandType> &operands,
331     SmallVectorImpl<Type> &types, ArrayAttr &redcuctionSymbols) {
332   SmallVector<SymbolRefAttr> reductionVec;
333   do {
334     if (parser.parseAttribute(reductionVec.emplace_back()) ||
335         parser.parseArrow() || parser.parseOperand(operands.emplace_back()) ||
336         parser.parseColonType(types.emplace_back()))
337       return failure();
338   } while (succeeded(parser.parseOptionalComma()));
339   SmallVector<Attribute> reductions(reductionVec.begin(), reductionVec.end());
340   redcuctionSymbols = ArrayAttr::get(parser.getContext(), reductions);
341   return success();
342 }
343 
344 /// Print Reduction clause
345 static void printReductionVarList(OpAsmPrinter &p, Operation *op,
346                                   OperandRange reductionVars,
347                                   TypeRange reductionTypes,
348                                   Optional<ArrayAttr> reductions) {
349   for (unsigned i = 0, e = reductions->size(); i < e; ++i) {
350     if (i != 0)
351       p << ", ";
352     p << (*reductions)[i] << " -> " << reductionVars[i] << " : "
353       << reductionVars[i].getType();
354   }
355 }
356 
357 /// Verifies Reduction Clause
358 static LogicalResult verifyReductionVarList(Operation *op,
359                                             Optional<ArrayAttr> reductions,
360                                             OperandRange reductionVars) {
361   if (!reductionVars.empty()) {
362     if (!reductions || reductions->size() != reductionVars.size())
363       return op->emitOpError()
364              << "expected as many reduction symbol references "
365                 "as reduction variables";
366   } else {
367     if (reductions)
368       return op->emitOpError() << "unexpected reduction symbol references";
369     return success();
370   }
371 
372   // TODO: The followings should be done in
373   // SymbolUserOpInterface::verifySymbolUses.
374   DenseSet<Value> accumulators;
375   for (auto args : llvm::zip(reductionVars, *reductions)) {
376     Value accum = std::get<0>(args);
377 
378     if (!accumulators.insert(accum).second)
379       return op->emitOpError() << "accumulator variable used more than once";
380 
381     Type varType = accum.getType().cast<PointerLikeType>();
382     auto symbolRef = std::get<1>(args).cast<SymbolRefAttr>();
383     auto decl =
384         SymbolTable::lookupNearestSymbolFrom<ReductionDeclareOp>(op, symbolRef);
385     if (!decl)
386       return op->emitOpError() << "expected symbol reference " << symbolRef
387                                << " to point to a reduction declaration";
388 
389     if (decl.getAccumulatorType() && decl.getAccumulatorType() != varType)
390       return op->emitOpError()
391              << "expected accumulator (" << varType
392              << ") to be the same type as reduction declaration ("
393              << decl.getAccumulatorType() << ")";
394   }
395 
396   return success();
397 }
398 
399 //===----------------------------------------------------------------------===//
400 // Parser, printer and verifier for Synchronization Hint (2.17.12)
401 //===----------------------------------------------------------------------===//
402 
403 /// Parses a Synchronization Hint clause. The value of hint is an integer
404 /// which is a combination of different hints from `omp_sync_hint_t`.
405 ///
406 /// hint-clause = `hint` `(` hint-value `)`
407 static ParseResult parseSynchronizationHint(OpAsmParser &parser,
408                                             IntegerAttr &hintAttr) {
409   StringRef hintKeyword;
410   int64_t hint = 0;
411   do {
412     if (failed(parser.parseKeyword(&hintKeyword)))
413       return failure();
414     if (hintKeyword == "uncontended")
415       hint |= 1;
416     else if (hintKeyword == "contended")
417       hint |= 2;
418     else if (hintKeyword == "nonspeculative")
419       hint |= 4;
420     else if (hintKeyword == "speculative")
421       hint |= 8;
422     else
423       return parser.emitError(parser.getCurrentLocation())
424              << hintKeyword << " is not a valid hint";
425   } while (succeeded(parser.parseOptionalComma()));
426   hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), hint);
427   return success();
428 }
429 
430 /// Prints a Synchronization Hint clause
431 static void printSynchronizationHint(OpAsmPrinter &p, Operation *op,
432                                      IntegerAttr hintAttr) {
433   int64_t hint = hintAttr.getInt();
434 
435   if (hint == 0)
436     return;
437 
438   // Helper function to get n-th bit from the right end of `value`
439   auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
440 
441   bool uncontended = bitn(hint, 0);
442   bool contended = bitn(hint, 1);
443   bool nonspeculative = bitn(hint, 2);
444   bool speculative = bitn(hint, 3);
445 
446   SmallVector<StringRef> hints;
447   if (uncontended)
448     hints.push_back("uncontended");
449   if (contended)
450     hints.push_back("contended");
451   if (nonspeculative)
452     hints.push_back("nonspeculative");
453   if (speculative)
454     hints.push_back("speculative");
455 
456   llvm::interleaveComma(hints, p);
457 }
458 
459 /// Verifies a synchronization hint clause
460 static LogicalResult verifySynchronizationHint(Operation *op, uint64_t hint) {
461 
462   // Helper function to get n-th bit from the right end of `value`
463   auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
464 
465   bool uncontended = bitn(hint, 0);
466   bool contended = bitn(hint, 1);
467   bool nonspeculative = bitn(hint, 2);
468   bool speculative = bitn(hint, 3);
469 
470   if (uncontended && contended)
471     return op->emitOpError() << "the hints omp_sync_hint_uncontended and "
472                                 "omp_sync_hint_contended cannot be combined";
473   if (nonspeculative && speculative)
474     return op->emitOpError() << "the hints omp_sync_hint_nonspeculative and "
475                                 "omp_sync_hint_speculative cannot be combined.";
476   return success();
477 }
478 
479 enum ClauseType {
480   allocateClause,
481   reductionClause,
482   nowaitClause,
483   linearClause,
484   scheduleClause,
485   collapseClause,
486   orderClause,
487   orderedClause,
488   COUNT
489 };
490 
491 //===----------------------------------------------------------------------===//
492 // Parser for Clause List
493 //===----------------------------------------------------------------------===//
494 
495 /// Parse a list of clauses. The clauses can appear in any order, but their
496 /// operand segment indices are in the same order that they are passed in the
497 /// `clauses` list. The operand segments are added over the prevSegments
498 
499 /// clause-list ::= clause clause-list | empty
500 /// clause ::= allocate | reduction | nowait | linear | schedule | collapse
501 ///          | order | ordered
502 /// allocate ::= `allocate` `(` allocate-operand-list `)`
503 /// reduction ::= `reduction` `(` reduction-entry-list `)`
504 /// nowait ::= `nowait`
505 /// linear ::= `linear` `(` linear-list `)`
506 /// schedule ::= `schedule` `(` sched-list `)`
507 /// collapse ::= `collapse` `(` ssa-id-and-type `)`
508 /// order ::= `order` `(` `concurrent` `)`
509 /// ordered ::= `ordered` `(` ssa-id-and-type `)`
510 ///
511 /// Note that each clause can only appear once in the clase-list.
512 static ParseResult parseClauses(OpAsmParser &parser, OperationState &result,
513                                 SmallVectorImpl<ClauseType> &clauses,
514                                 SmallVectorImpl<int> &segments) {
515 
516   // Check done[clause] to see if it has been parsed already
517   BitVector done(ClauseType::COUNT, false);
518 
519   // See pos[clause] to get position of clause in operand segments
520   SmallVector<int> pos(ClauseType::COUNT, -1);
521 
522   // Stores the last parsed clause keyword
523   StringRef clauseKeyword;
524   StringRef opName = result.name.getStringRef();
525 
526   // Containers for storing operands, types and attributes for various clauses
527   SmallVector<OpAsmParser::OperandType> allocates, allocators;
528   SmallVector<Type> allocateTypes, allocatorTypes;
529 
530   ArrayAttr reductions;
531   SmallVector<OpAsmParser::OperandType> reductionVars;
532   SmallVector<Type> reductionVarTypes;
533 
534   SmallVector<OpAsmParser::OperandType> linears;
535   SmallVector<Type> linearTypes;
536   SmallVector<OpAsmParser::OperandType> linearSteps;
537 
538   SmallString<8> schedule;
539   SmallVector<SmallString<12>> modifiers;
540   Optional<OpAsmParser::OperandType> scheduleChunkSize;
541   Type scheduleChunkType;
542 
543   // Compute the position of clauses in operand segments
544   int currPos = 0;
545   for (ClauseType clause : clauses) {
546 
547     // Skip the following clauses - they do not take any position in operand
548     // segments
549     if (clause == nowaitClause || clause == collapseClause ||
550         clause == orderClause || clause == orderedClause)
551       continue;
552 
553     pos[clause] = currPos++;
554 
555     // For the following clauses, two positions are reserved in the operand
556     // segments
557     if (clause == allocateClause || clause == linearClause)
558       currPos++;
559   }
560 
561   SmallVector<int> clauseSegments(currPos);
562 
563   // Helper function to check if a clause is allowed/repeated or not
564   auto checkAllowed = [&](ClauseType clause) -> ParseResult {
565     if (!llvm::is_contained(clauses, clause))
566       return parser.emitError(parser.getCurrentLocation())
567              << clauseKeyword << " is not a valid clause for the " << opName
568              << " operation";
569     if (done[clause])
570       return parser.emitError(parser.getCurrentLocation())
571              << "at most one " << clauseKeyword << " clause can appear on the "
572              << opName << " operation";
573     done[clause] = true;
574     return success();
575   };
576 
577   while (succeeded(parser.parseOptionalKeyword(&clauseKeyword))) {
578     if (clauseKeyword == "allocate") {
579       if (checkAllowed(allocateClause) || parser.parseLParen() ||
580           parseAllocateAndAllocator(parser, allocates, allocateTypes,
581                                     allocators, allocatorTypes) ||
582           parser.parseRParen())
583         return failure();
584       clauseSegments[pos[allocateClause]] = allocates.size();
585       clauseSegments[pos[allocateClause] + 1] = allocators.size();
586     } else if (clauseKeyword == "reduction") {
587       if (checkAllowed(reductionClause) || parser.parseLParen() ||
588           parseReductionVarList(parser, reductionVars, reductionVarTypes,
589                                 reductions) ||
590           parser.parseRParen())
591         return failure();
592       clauseSegments[pos[reductionClause]] = reductionVars.size();
593     } else if (clauseKeyword == "nowait") {
594       if (checkAllowed(nowaitClause))
595         return failure();
596       auto attr = UnitAttr::get(parser.getBuilder().getContext());
597       result.addAttribute("nowait", attr);
598     } else if (clauseKeyword == "linear") {
599       if (checkAllowed(linearClause) ||
600           parseLinearClause(parser, linears, linearTypes, linearSteps))
601         return failure();
602       clauseSegments[pos[linearClause]] = linears.size();
603       clauseSegments[pos[linearClause] + 1] = linearSteps.size();
604     } else if (clauseKeyword == "schedule") {
605       if (checkAllowed(scheduleClause) ||
606           parseScheduleClause(parser, schedule, modifiers, scheduleChunkSize,
607                               scheduleChunkType))
608         return failure();
609       if (scheduleChunkSize) {
610         clauseSegments[pos[scheduleClause]] = 1;
611       }
612     } else if (clauseKeyword == "collapse") {
613       auto type = parser.getBuilder().getI64Type();
614       mlir::IntegerAttr attr;
615       if (checkAllowed(collapseClause) || parser.parseLParen() ||
616           parser.parseAttribute(attr, type) || parser.parseRParen())
617         return failure();
618       result.addAttribute("collapse_val", attr);
619     } else if (clauseKeyword == "ordered") {
620       mlir::IntegerAttr attr;
621       if (checkAllowed(orderedClause))
622         return failure();
623       if (succeeded(parser.parseOptionalLParen())) {
624         auto type = parser.getBuilder().getI64Type();
625         if (parser.parseAttribute(attr, type) || parser.parseRParen())
626           return failure();
627       } else {
628         // Use 0 to represent no ordered parameter was specified
629         attr = parser.getBuilder().getI64IntegerAttr(0);
630       }
631       result.addAttribute("ordered_val", attr);
632     } else if (clauseKeyword == "order") {
633       ClauseOrderKindAttr order;
634       if (checkAllowed(orderClause) || parser.parseLParen() ||
635           parseClauseAttr<ClauseOrderKindAttr>(parser, order) ||
636           parser.parseRParen())
637         return failure();
638       result.addAttribute("order_val", order);
639     } else {
640       return parser.emitError(parser.getNameLoc())
641              << clauseKeyword << " is not a valid clause";
642     }
643   }
644 
645   // Add allocate parameters.
646   if (done[allocateClause] && clauseSegments[pos[allocateClause]] &&
647       failed(parser.resolveOperands(allocates, allocateTypes,
648                                     allocates[0].location, result.operands)))
649     return failure();
650 
651   // Add allocator parameters.
652   if (done[allocateClause] && clauseSegments[pos[allocateClause] + 1] &&
653       failed(parser.resolveOperands(allocators, allocatorTypes,
654                                     allocators[0].location, result.operands)))
655     return failure();
656 
657   // Add reduction parameters and symbols
658   if (done[reductionClause] && clauseSegments[pos[reductionClause]]) {
659     if (failed(parser.resolveOperands(reductionVars, reductionVarTypes,
660                                       parser.getNameLoc(), result.operands)))
661       return failure();
662     result.addAttribute("reductions", reductions);
663   }
664 
665   // Add linear parameters
666   if (done[linearClause] && clauseSegments[pos[linearClause]]) {
667     auto linearStepType = parser.getBuilder().getI32Type();
668     SmallVector<Type> linearStepTypes(linearSteps.size(), linearStepType);
669     if (failed(parser.resolveOperands(linears, linearTypes, linears[0].location,
670                                       result.operands)) ||
671         failed(parser.resolveOperands(linearSteps, linearStepTypes,
672                                       linearSteps[0].location,
673                                       result.operands)))
674       return failure();
675   }
676 
677   // Add schedule parameters
678   if (done[scheduleClause] && !schedule.empty()) {
679     if (Optional<ClauseScheduleKind> sched =
680             symbolizeClauseScheduleKind(schedule)) {
681       auto attr = ClauseScheduleKindAttr::get(parser.getContext(), *sched);
682       result.addAttribute("schedule_val", attr);
683     } else {
684       return parser.emitError(parser.getCurrentLocation(),
685                               "invalid schedule kind");
686     }
687     if (!modifiers.empty()) {
688       SMLoc loc = parser.getCurrentLocation();
689       if (Optional<ScheduleModifier> mod =
690               symbolizeScheduleModifier(modifiers[0])) {
691         result.addAttribute(
692             "schedule_modifier",
693             ScheduleModifierAttr::get(parser.getContext(), *mod));
694       } else {
695         return parser.emitError(loc, "invalid schedule modifier");
696       }
697       // Only SIMD attribute is allowed here!
698       if (modifiers.size() > 1) {
699         assert(symbolizeScheduleModifier(modifiers[1]) ==
700                ScheduleModifier::simd);
701         auto attr = UnitAttr::get(parser.getBuilder().getContext());
702         result.addAttribute("simd_modifier", attr);
703       }
704     }
705     if (scheduleChunkSize)
706       parser.resolveOperand(*scheduleChunkSize, scheduleChunkType,
707                             result.operands);
708   }
709 
710   segments.insert(segments.end(), clauseSegments.begin(), clauseSegments.end());
711 
712   return success();
713 }
714 
715 //===----------------------------------------------------------------------===//
716 // Verifier for SectionsOp
717 //===----------------------------------------------------------------------===//
718 
719 LogicalResult SectionsOp::verify() {
720   if (allocate_vars().size() != allocators_vars().size())
721     return emitError(
722         "expected equal sizes for allocate and allocator variables");
723 
724   return verifyReductionVarList(*this, reductions(), reduction_vars());
725 }
726 
727 LogicalResult SectionsOp::verifyRegions() {
728   for (auto &inst : *region().begin()) {
729     if (!(isa<SectionOp>(inst) || isa<TerminatorOp>(inst))) {
730       return emitOpError()
731              << "expected omp.section op or terminator op inside region";
732     }
733   }
734 
735   return success();
736 }
737 
738 /// Parses an OpenMP Workshare Loop operation
739 ///
740 /// wsloop ::= `omp.wsloop` loop-control clause-list
741 /// loop-control ::= `(` ssa-id-list `)` `:` type `=`  loop-bounds
742 /// loop-bounds := `(` ssa-id-list `)` to `(` ssa-id-list `)` inclusive? steps
743 /// steps := `step` `(`ssa-id-list`)`
744 /// clause-list ::= clause clause-list | empty
745 /// clause ::= linear | schedule | collapse | nowait | ordered | order
746 ///          | reduction
747 ParseResult WsLoopOp::parse(OpAsmParser &parser, OperationState &result) {
748   // Parse an opening `(` followed by induction variables followed by `)`
749   SmallVector<OpAsmParser::OperandType> ivs;
750   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
751                                      OpAsmParser::Delimiter::Paren))
752     return failure();
753 
754   int numIVs = static_cast<int>(ivs.size());
755   Type loopVarType;
756   if (parser.parseColonType(loopVarType))
757     return failure();
758 
759   // Parse loop bounds.
760   SmallVector<OpAsmParser::OperandType> lower;
761   if (parser.parseEqual() ||
762       parser.parseOperandList(lower, numIVs, OpAsmParser::Delimiter::Paren) ||
763       parser.resolveOperands(lower, loopVarType, result.operands))
764     return failure();
765 
766   SmallVector<OpAsmParser::OperandType> upper;
767   if (parser.parseKeyword("to") ||
768       parser.parseOperandList(upper, numIVs, OpAsmParser::Delimiter::Paren) ||
769       parser.resolveOperands(upper, loopVarType, result.operands))
770     return failure();
771 
772   if (succeeded(parser.parseOptionalKeyword("inclusive"))) {
773     auto attr = UnitAttr::get(parser.getBuilder().getContext());
774     result.addAttribute("inclusive", attr);
775   }
776 
777   // Parse step values.
778   SmallVector<OpAsmParser::OperandType> steps;
779   if (parser.parseKeyword("step") ||
780       parser.parseOperandList(steps, numIVs, OpAsmParser::Delimiter::Paren) ||
781       parser.resolveOperands(steps, loopVarType, result.operands))
782     return failure();
783 
784   SmallVector<ClauseType> clauses = {
785       linearClause,  reductionClause, collapseClause, orderClause,
786       orderedClause, nowaitClause,    scheduleClause};
787   SmallVector<int> segments{numIVs, numIVs, numIVs};
788   if (failed(parseClauses(parser, result, clauses, segments)))
789     return failure();
790 
791   result.addAttribute("operand_segment_sizes",
792                       parser.getBuilder().getI32VectorAttr(segments));
793 
794   // Now parse the body.
795   Region *body = result.addRegion();
796   SmallVector<Type> ivTypes(numIVs, loopVarType);
797   SmallVector<OpAsmParser::OperandType> blockArgs(ivs);
798   if (parser.parseRegion(*body, blockArgs, ivTypes))
799     return failure();
800   return success();
801 }
802 
803 void WsLoopOp::print(OpAsmPrinter &p) {
804   auto args = getRegion().front().getArguments();
805   p << " (" << args << ") : " << args[0].getType() << " = (" << lowerBound()
806     << ") to (" << upperBound() << ") ";
807   if (inclusive()) {
808     p << "inclusive ";
809   }
810   p << "step (" << step() << ") ";
811 
812   if (!linear_vars().empty())
813     printLinearClause(p, linear_vars(), linear_step_vars());
814 
815   if (auto sched = schedule_val())
816     printScheduleClause(p, sched.getValue(), schedule_modifier(),
817                         simd_modifier(), schedule_chunk_var());
818 
819   if (auto collapse = collapse_val())
820     p << "collapse(" << collapse << ") ";
821 
822   if (nowait())
823     p << "nowait ";
824 
825   if (auto ordered = ordered_val())
826     p << "ordered(" << ordered << ") ";
827 
828   if (auto order = order_val())
829     p << "order(" << stringifyClauseOrderKind(*order) << ") ";
830 
831   if (!reduction_vars().empty()) {
832     printReductionVarList(p << "reduction(", *this, reduction_vars(),
833                           reduction_vars().getTypes(), reductions());
834     p << ")";
835   }
836 
837   p << ' ';
838   p.printRegion(region(), /*printEntryBlockArgs=*/false);
839 }
840 
841 //===----------------------------------------------------------------------===//
842 // ReductionOp
843 //===----------------------------------------------------------------------===//
844 
845 static ParseResult parseAtomicReductionRegion(OpAsmParser &parser,
846                                               Region &region) {
847   if (parser.parseOptionalKeyword("atomic"))
848     return success();
849   return parser.parseRegion(region);
850 }
851 
852 static void printAtomicReductionRegion(OpAsmPrinter &printer,
853                                        ReductionDeclareOp op, Region &region) {
854   if (region.empty())
855     return;
856   printer << "atomic ";
857   printer.printRegion(region);
858 }
859 
860 LogicalResult ReductionDeclareOp::verifyRegions() {
861   if (initializerRegion().empty())
862     return emitOpError() << "expects non-empty initializer region";
863   Block &initializerEntryBlock = initializerRegion().front();
864   if (initializerEntryBlock.getNumArguments() != 1 ||
865       initializerEntryBlock.getArgument(0).getType() != type()) {
866     return emitOpError() << "expects initializer region with one argument "
867                             "of the reduction type";
868   }
869 
870   for (YieldOp yieldOp : initializerRegion().getOps<YieldOp>()) {
871     if (yieldOp.results().size() != 1 ||
872         yieldOp.results().getTypes()[0] != type())
873       return emitOpError() << "expects initializer region to yield a value "
874                               "of the reduction type";
875   }
876 
877   if (reductionRegion().empty())
878     return emitOpError() << "expects non-empty reduction region";
879   Block &reductionEntryBlock = reductionRegion().front();
880   if (reductionEntryBlock.getNumArguments() != 2 ||
881       reductionEntryBlock.getArgumentTypes()[0] !=
882           reductionEntryBlock.getArgumentTypes()[1] ||
883       reductionEntryBlock.getArgumentTypes()[0] != type())
884     return emitOpError() << "expects reduction region with two arguments of "
885                             "the reduction type";
886   for (YieldOp yieldOp : reductionRegion().getOps<YieldOp>()) {
887     if (yieldOp.results().size() != 1 ||
888         yieldOp.results().getTypes()[0] != type())
889       return emitOpError() << "expects reduction region to yield a value "
890                               "of the reduction type";
891   }
892 
893   if (atomicReductionRegion().empty())
894     return success();
895 
896   Block &atomicReductionEntryBlock = atomicReductionRegion().front();
897   if (atomicReductionEntryBlock.getNumArguments() != 2 ||
898       atomicReductionEntryBlock.getArgumentTypes()[0] !=
899           atomicReductionEntryBlock.getArgumentTypes()[1])
900     return emitOpError() << "expects atomic reduction region with two "
901                             "arguments of the same type";
902   auto ptrType = atomicReductionEntryBlock.getArgumentTypes()[0]
903                      .dyn_cast<PointerLikeType>();
904   if (!ptrType || ptrType.getElementType() != type())
905     return emitOpError() << "expects atomic reduction region arguments to "
906                             "be accumulators containing the reduction type";
907   return success();
908 }
909 
910 LogicalResult ReductionOp::verify() {
911   // TODO: generalize this to an op interface when there is more than one op
912   // that supports reductions.
913   auto container = (*this)->getParentOfType<WsLoopOp>();
914   for (unsigned i = 0, e = container.getNumReductionVars(); i < e; ++i)
915     if (container.reduction_vars()[i] == accumulator())
916       return success();
917 
918   return emitOpError() << "the accumulator is not used by the parent";
919 }
920 
921 //===----------------------------------------------------------------------===//
922 // WsLoopOp
923 //===----------------------------------------------------------------------===//
924 
925 void WsLoopOp::build(OpBuilder &builder, OperationState &state,
926                      ValueRange lowerBound, ValueRange upperBound,
927                      ValueRange step, ArrayRef<NamedAttribute> attributes) {
928   build(builder, state, lowerBound, upperBound, step,
929         /*linear_vars=*/ValueRange(),
930         /*linear_step_vars=*/ValueRange(), /*reduction_vars=*/ValueRange(),
931         /*reductions=*/nullptr, /*schedule_val=*/nullptr,
932         /*schedule_chunk_var=*/nullptr, /*schedule_modifier=*/nullptr,
933         /*simd_modifier=*/false, /*collapse_val=*/nullptr, /*nowait=*/false,
934         /*ordered_val=*/nullptr, /*order_val=*/nullptr, /*inclusive=*/false);
935   state.addAttributes(attributes);
936 }
937 
938 LogicalResult WsLoopOp::verify() {
939   return verifyReductionVarList(*this, reductions(), reduction_vars());
940 }
941 
942 //===----------------------------------------------------------------------===//
943 // Verifier for critical construct (2.17.1)
944 //===----------------------------------------------------------------------===//
945 
946 LogicalResult CriticalDeclareOp::verify() {
947   return verifySynchronizationHint(*this, hint_val());
948 }
949 
950 LogicalResult
951 CriticalOp::verifySymbolUses(SymbolTableCollection &symbol_table) {
952   if (nameAttr()) {
953     SymbolRefAttr symbolRef = nameAttr();
954     auto decl = symbol_table.lookupNearestSymbolFrom<CriticalDeclareOp>(
955         *this, symbolRef);
956     if (!decl) {
957       return emitOpError() << "expected symbol reference " << symbolRef
958                            << " to point to a critical declaration";
959     }
960   }
961 
962   return success();
963 }
964 
965 //===----------------------------------------------------------------------===//
966 // Verifier for ordered construct
967 //===----------------------------------------------------------------------===//
968 
969 LogicalResult OrderedOp::verify() {
970   auto container = (*this)->getParentOfType<WsLoopOp>();
971   if (!container || !container.ordered_valAttr() ||
972       container.ordered_valAttr().getInt() == 0)
973     return emitOpError() << "ordered depend directive must be closely "
974                          << "nested inside a worksharing-loop with ordered "
975                          << "clause with parameter present";
976 
977   if (container.ordered_valAttr().getInt() !=
978       (int64_t)num_loops_val().getValue())
979     return emitOpError() << "number of variables in depend clause does not "
980                          << "match number of iteration variables in the "
981                          << "doacross loop";
982 
983   return success();
984 }
985 
986 LogicalResult OrderedRegionOp::verify() {
987   // TODO: The code generation for ordered simd directive is not supported yet.
988   if (simd())
989     return failure();
990 
991   if (auto container = (*this)->getParentOfType<WsLoopOp>()) {
992     if (!container.ordered_valAttr() ||
993         container.ordered_valAttr().getInt() != 0)
994       return emitOpError() << "ordered region must be closely nested inside "
995                            << "a worksharing-loop region with an ordered "
996                            << "clause without parameter present";
997   }
998 
999   return success();
1000 }
1001 
1002 //===----------------------------------------------------------------------===//
1003 // Verifier for AtomicReadOp
1004 //===----------------------------------------------------------------------===//
1005 
1006 LogicalResult AtomicReadOp::verify() {
1007   if (auto mo = memory_order_val()) {
1008     if (*mo == ClauseMemoryOrderKind::Acq_rel ||
1009         *mo == ClauseMemoryOrderKind::Release) {
1010       return emitError(
1011           "memory-order must not be acq_rel or release for atomic reads");
1012     }
1013   }
1014   if (x() == v())
1015     return emitError(
1016         "read and write must not be to the same location for atomic reads");
1017   return verifySynchronizationHint(*this, hint_val());
1018 }
1019 
1020 //===----------------------------------------------------------------------===//
1021 // Verifier for AtomicWriteOp
1022 //===----------------------------------------------------------------------===//
1023 
1024 LogicalResult AtomicWriteOp::verify() {
1025   if (auto mo = memory_order_val()) {
1026     if (*mo == ClauseMemoryOrderKind::Acq_rel ||
1027         *mo == ClauseMemoryOrderKind::Acquire) {
1028       return emitError(
1029           "memory-order must not be acq_rel or acquire for atomic writes");
1030     }
1031   }
1032   return verifySynchronizationHint(*this, hint_val());
1033 }
1034 
1035 //===----------------------------------------------------------------------===//
1036 // Verifier for AtomicUpdateOp
1037 //===----------------------------------------------------------------------===//
1038 
1039 LogicalResult AtomicUpdateOp::verify() {
1040   if (auto mo = memory_order_val()) {
1041     if (*mo == ClauseMemoryOrderKind::Acq_rel ||
1042         *mo == ClauseMemoryOrderKind::Acquire) {
1043       return emitError(
1044           "memory-order must not be acq_rel or acquire for atomic updates");
1045     }
1046   }
1047 
1048   if (x().getType().cast<PointerLikeType>().getElementType() !=
1049       region().getArgument(0).getType()) {
1050     return emitError("the type of the operand must be a pointer type whose "
1051                      "element type is the same as that of the region argument");
1052   }
1053 
1054   return success();
1055 }
1056 
1057 LogicalResult AtomicUpdateOp::verifyRegions() {
1058   if (region().getNumArguments() != 1)
1059     return emitError("the region must accept exactly one argument");
1060 
1061   if (region().front().getOperations().size() < 2)
1062     return emitError() << "the update region must have at least two operations "
1063                           "(binop and terminator)";
1064 
1065   YieldOp yieldOp = *region().getOps<YieldOp>().begin();
1066 
1067   if (yieldOp.results().size() != 1)
1068     return emitError("only updated value must be returned");
1069   if (yieldOp.results().front().getType() != region().getArgument(0).getType())
1070     return emitError("input and yielded value must have the same type");
1071   return success();
1072 }
1073 
1074 //===----------------------------------------------------------------------===//
1075 // Verifier for AtomicCaptureOp
1076 //===----------------------------------------------------------------------===//
1077 
1078 LogicalResult AtomicCaptureOp::verifyRegions() {
1079   Block::OpListType &ops = region().front().getOperations();
1080   if (ops.size() != 3)
1081     return emitError()
1082            << "expected three operations in omp.atomic.capture region (one "
1083               "terminator, and two atomic ops)";
1084   auto &firstOp = ops.front();
1085   auto &secondOp = *ops.getNextNode(firstOp);
1086   auto firstReadStmt = dyn_cast<AtomicReadOp>(firstOp);
1087   auto firstUpdateStmt = dyn_cast<AtomicUpdateOp>(firstOp);
1088   auto secondReadStmt = dyn_cast<AtomicReadOp>(secondOp);
1089   auto secondUpdateStmt = dyn_cast<AtomicUpdateOp>(secondOp);
1090   auto secondWriteStmt = dyn_cast<AtomicWriteOp>(secondOp);
1091 
1092   if (!((firstUpdateStmt && secondReadStmt) ||
1093         (firstReadStmt && secondUpdateStmt) ||
1094         (firstReadStmt && secondWriteStmt)))
1095     return ops.front().emitError()
1096            << "invalid sequence of operations in the capture region";
1097   if (firstUpdateStmt && secondReadStmt &&
1098       firstUpdateStmt.x() != secondReadStmt.x())
1099     return firstUpdateStmt.emitError()
1100            << "updated variable in omp.atomic.update must be captured in "
1101               "second operation";
1102   if (firstReadStmt && secondUpdateStmt &&
1103       firstReadStmt.x() != secondUpdateStmt.x())
1104     return firstReadStmt.emitError()
1105            << "captured variable in omp.atomic.read must be updated in second "
1106               "operation";
1107   if (firstReadStmt && secondWriteStmt &&
1108       firstReadStmt.x() != secondWriteStmt.address())
1109     return firstReadStmt.emitError()
1110            << "captured variable in omp.atomic.read must be updated in "
1111               "second operation";
1112   return success();
1113 }
1114 
1115 #define GET_ATTRDEF_CLASSES
1116 #include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"
1117 
1118 #define GET_OP_CLASSES
1119 #include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
1120