MLIR 24.0.0git
OpenMPDialect.cpp
Go to the documentation of this file.
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
18#include "mlir/IR/Attributes.h"
21#include "mlir/IR/Matchers.h"
24#include "mlir/IR/SymbolTable.h"
27
28#include "llvm/ADT/ArrayRef.h"
29#include "llvm/ADT/PostOrderIterator.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/STLForwardCompat.h"
32#include "llvm/ADT/SmallString.h"
33#include "llvm/ADT/StringExtras.h"
34#include "llvm/ADT/StringRef.h"
35#include "llvm/ADT/TypeSwitch.h"
36#include "llvm/ADT/bit.h"
37#include "llvm/Support/InterleavedRange.h"
38#include <cstddef>
39#include <iterator>
40#include <optional>
41#include <variant>
42
43#include "mlir/Dialect/OpenMP/OpenMPOpsDialect.cpp.inc"
44#include "mlir/Dialect/OpenMP/OpenMPOpsEnums.cpp.inc"
45#include "mlir/Dialect/OpenMP/OpenMPOpsInterfaces.cpp.inc"
46#include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.cpp.inc"
47
48using namespace mlir;
49using namespace mlir::omp;
50
53 return attrs.empty() ? nullptr : ArrayAttr::get(context, attrs);
54}
55
58 return boolArray.empty() ? nullptr : DenseBoolArrayAttr::get(ctx, boolArray);
59}
60
63 return intArray.empty() ? nullptr : DenseI64ArrayAttr::get(ctx, intArray);
64}
65
66namespace {
67struct MemRefPointerLikeModel
68 : public PointerLikeType::ExternalModel<MemRefPointerLikeModel,
69 MemRefType> {
70 Type getElementType(Type pointer) const {
71 return llvm::cast<MemRefType>(pointer).getElementType();
72 }
73};
74
75struct LLVMPointerPointerLikeModel
76 : public PointerLikeType::ExternalModel<LLVMPointerPointerLikeModel,
77 LLVM::LLVMPointerType> {
78 Type getElementType(Type pointer) const { return Type(); }
79};
80} // namespace
81
82/// Generate a name of a canonical loop nest of the format
83/// `<prefix>(_r<idx>_s<idx>)*`. Hereby, `_r<idx>` identifies the region
84/// argument index of an operation that has multiple regions, if the operation
85/// has multiple regions.
86/// `_s<idx>` identifies the position of an operation within a region, where
87/// only operations that may potentially contain loops ("container operations"
88/// i.e. have region arguments) are counted. Again, it is omitted if there is
89/// only one such operation in a region. If there are canonical loops nested
90/// inside each other, also may also use the format `_d<num>` where <num> is the
91/// nesting depth of the loop.
92///
93/// The generated name is a best-effort to make canonical loop unique within an
94/// SSA namespace. This also means that regions with IsolatedFromAbove property
95/// do not consider any parents or siblings.
96static std::string generateLoopNestingName(StringRef prefix,
97 CanonicalLoopOp op) {
98 struct Component {
99 /// If true, this component describes a region operand of an operation (the
100 /// operand's owner) If false, this component describes an operation located
101 /// in a parent region
102 bool isRegionArgOfOp;
103 bool skip = false;
104 bool isUnique = false;
105
106 size_t idx;
107 Operation *op;
108 Region *parentRegion;
109 size_t loopDepth;
110
111 Operation *&getOwnerOp() {
112 assert(isRegionArgOfOp && "Must describe a region operand");
113 return op;
114 }
115 size_t &getArgIdx() {
116 assert(isRegionArgOfOp && "Must describe a region operand");
117 return idx;
118 }
119
120 Operation *&getContainerOp() {
121 assert(!isRegionArgOfOp && "Must describe a operation of a region");
122 return op;
123 }
124 size_t &getOpPos() {
125 assert(!isRegionArgOfOp && "Must describe a operation of a region");
126 return idx;
127 }
128 bool isLoopOp() const {
129 assert(!isRegionArgOfOp && "Must describe a operation of a region");
130 return isa<CanonicalLoopOp>(op);
131 }
132 Region *&getParentRegion() {
133 assert(!isRegionArgOfOp && "Must describe a operation of a region");
134 return parentRegion;
135 }
136 size_t &getLoopDepth() {
137 assert(!isRegionArgOfOp && "Must describe a operation of a region");
138 return loopDepth;
139 }
140
141 void skipIf(bool v = true) { skip = skip || v; }
142 };
143
144 // List of ancestors, from inner to outer.
145 // Alternates between
146 // * region argument of an operation
147 // * operation within a region
148 SmallVector<Component> components;
149
150 // Gather a list of parent regions and operations, and the position within
151 // their parent
152 Operation *o = op.getOperation();
153 while (o) {
154 // Operation within a region
155 Region *r = o->getParentRegion();
156 if (!r)
157 break;
158
159 llvm::ReversePostOrderTraversal<Block *> traversal(&r->getBlocks().front());
160 size_t idx = 0;
161 bool found = false;
162 size_t sequentialIdx = -1;
163 bool isOnlyContainerOp = true;
164 for (Block *b : traversal) {
165 for (Operation &op : *b) {
166 if (&op == o && !found) {
167 sequentialIdx = idx;
168 found = true;
169 }
170 if (op.getNumRegions()) {
171 idx += 1;
172 if (idx > 1)
173 isOnlyContainerOp = false;
174 }
175 if (found && !isOnlyContainerOp)
176 break;
177 }
178 }
179
180 Component &containerOpInRegion = components.emplace_back();
181 containerOpInRegion.isRegionArgOfOp = false;
182 containerOpInRegion.isUnique = isOnlyContainerOp;
183 containerOpInRegion.getContainerOp() = o;
184 containerOpInRegion.getOpPos() = sequentialIdx;
185 containerOpInRegion.getParentRegion() = r;
186
187 Operation *parent = r->getParentOp();
188
189 // Region argument of an operation
190 Component &regionArgOfOperation = components.emplace_back();
191 regionArgOfOperation.isRegionArgOfOp = true;
192 regionArgOfOperation.isUnique = true;
193 regionArgOfOperation.getArgIdx() = 0;
194 regionArgOfOperation.getOwnerOp() = parent;
195
196 // The IsolatedFromAbove trait of the parent operation implies that each
197 // individual region argument has its own separate namespace, so no
198 // ambiguity.
199 if (!parent || parent->hasTrait<mlir::OpTrait::IsIsolatedFromAbove>())
200 break;
201
202 // Component only needed if operation has multiple region operands. Region
203 // arguments may be optional, but we currently do not consider this.
204 if (parent->getRegions().size() > 1) {
205 auto getRegionIndex = [](Operation *o, Region *r) {
206 for (auto [idx, region] : llvm::enumerate(o->getRegions())) {
207 if (&region == r)
208 return idx;
209 }
210 llvm_unreachable("Region not child of its parent operation");
211 };
212 regionArgOfOperation.isUnique = false;
213 regionArgOfOperation.getArgIdx() = getRegionIndex(parent, r);
214 }
215
216 // next parent
217 o = parent;
218 }
219
220 // Determine whether a region-argument component is not needed
221 for (Component &c : components)
222 c.skipIf(c.isRegionArgOfOp && c.isUnique);
223
224 // Find runs of nested loops and determine each loop's depth in the loop nest
225 size_t numSurroundingLoops = 0;
226 for (Component &c : llvm::reverse(components)) {
227 if (c.skip)
228 continue;
229
230 // non-skipped multi-argument operands interrupt the loop nest
231 if (c.isRegionArgOfOp) {
232 numSurroundingLoops = 0;
233 continue;
234 }
235
236 // Multiple loops in a region means each of them is the outermost loop of a
237 // new loop nest
238 if (!c.isUnique)
239 numSurroundingLoops = 0;
240
241 c.getLoopDepth() = numSurroundingLoops;
242
243 // Next loop is surrounded by one more loop
244 if (isa<CanonicalLoopOp>(c.getContainerOp()))
245 numSurroundingLoops += 1;
246 }
247
248 // In loop nests, skip all but the innermost loop that contains the depth
249 // number
250 bool isLoopNest = false;
251 for (Component &c : components) {
252 if (c.skip || c.isRegionArgOfOp)
253 continue;
254
255 if (!isLoopNest && c.getLoopDepth() >= 1) {
256 // Innermost loop of a loop nest of at least two loops
257 isLoopNest = true;
258 } else if (isLoopNest) {
259 // Non-innermost loop of a loop nest
260 c.skipIf(c.isUnique);
261
262 // If there is no surrounding loop left, this must have been the outermost
263 // loop; leave loop-nest mode for the next iteration
264 if (c.getLoopDepth() == 0)
265 isLoopNest = false;
266 }
267 }
268
269 // Skip non-loop unambiguous regions (but they should interrupt loop nests, so
270 // we mark them as skipped only after computing loop nests)
271 for (Component &c : components)
272 c.skipIf(!c.isRegionArgOfOp && c.isUnique &&
273 !isa<CanonicalLoopOp>(c.getContainerOp()));
274
275 // Components can be skipped if they are already disambiguated by their parent
276 // (or does not have a parent)
277 bool newRegion = true;
278 for (Component &c : llvm::reverse(components)) {
279 c.skipIf(newRegion && c.isUnique);
280
281 // non-skipped components disambiguate unique children
282 if (!c.skip)
283 newRegion = true;
284
285 // ...except canonical loops that need a suffix for each nest
286 if (!c.isRegionArgOfOp && c.getContainerOp())
287 newRegion = false;
288 }
289
290 // Compile the nesting name string
291 SmallString<64> Name{prefix};
292 llvm::raw_svector_ostream NameOS(Name);
293 for (auto &c : llvm::reverse(components)) {
294 if (c.skip)
295 continue;
296
297 if (c.isRegionArgOfOp)
298 NameOS << "_r" << c.getArgIdx();
299 else if (c.getLoopDepth() >= 1)
300 NameOS << "_d" << c.getLoopDepth();
301 else
302 NameOS << "_s" << c.getOpPos();
303 }
304
305 return NameOS.str().str();
306}
307
308void OpenMPDialect::initialize() {
309 addOperations<
310#define GET_OP_LIST
311#include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
312 >();
313 addAttributes<
314#define GET_ATTRDEF_LIST
315#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"
316 >();
317 addTypes<
318#define GET_TYPEDEF_LIST
319#include "mlir/Dialect/OpenMP/OpenMPOpsTypes.cpp.inc"
320 >();
321
322 declarePromisedInterface<ConvertToLLVMPatternInterface, OpenMPDialect>();
323
324 MemRefType::attachInterface<MemRefPointerLikeModel>(*getContext());
325 LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(
326 *getContext());
327
328 // Attach default offload module interface to module op to access
329 // offload functionality through
330 mlir::ModuleOp::attachInterface<mlir::omp::OffloadModuleDefaultModel>(
331 *getContext());
332
333 // Attach default declare target interfaces to operations which can be marked
334 // as declare target (Global Operations and Functions/Subroutines in dialects
335 // that Fortran (or other languages that lower to MLIR) translates too
336 mlir::LLVM::GlobalOp::attachInterface<
338 *getContext());
339 mlir::LLVM::LLVMFuncOp::attachInterface<
341 *getContext());
342 mlir::func::FuncOp::attachInterface<
344}
345
346//===----------------------------------------------------------------------===//
347// Parser and printer for Allocate Clause
348//===----------------------------------------------------------------------===//
349
350/// Parse an allocate clause with allocators and a list of operands with types.
351///
352/// allocate-operand-list :: = allocate-operand |
353/// allocator-operand `,` allocate-operand-list
354/// allocate-operand :: = ssa-id-and-type -> ssa-id-and-type
355/// ssa-id-and-type ::= ssa-id `:` type
356static ParseResult parseAllocateAndAllocator(
357 OpAsmParser &parser,
359 SmallVectorImpl<Type> &allocateTypes,
361 SmallVectorImpl<Type> &allocatorTypes) {
362
363 return parser.parseCommaSeparatedList([&]() {
365 Type type;
366 if (parser.parseOperand(operand) || parser.parseColonType(type))
367 return failure();
368 allocatorVars.push_back(operand);
369 allocatorTypes.push_back(type);
370 if (parser.parseArrow())
371 return failure();
372 if (parser.parseOperand(operand) || parser.parseColonType(type))
373 return failure();
374
375 allocateVars.push_back(operand);
376 allocateTypes.push_back(type);
377 return success();
378 });
379}
380
381/// Print allocate clause
383 OperandRange allocateVars,
384 TypeRange allocateTypes,
385 OperandRange allocatorVars,
386 TypeRange allocatorTypes) {
387 for (unsigned i = 0; i < allocateVars.size(); ++i) {
388 std::string separator = i == allocateVars.size() - 1 ? "" : ", ";
389 p << allocatorVars[i] << " : " << allocatorTypes[i] << " -> ";
390 p << allocateVars[i] << " : " << allocateTypes[i] << separator;
391 }
392}
393
394//===----------------------------------------------------------------------===//
395// Parser and printer for a clause attribute (StringEnumAttr)
396//===----------------------------------------------------------------------===//
397
398template <typename ClauseAttr>
399static ParseResult parseClauseAttr(AsmParser &parser, ClauseAttr &attr) {
400 using ClauseT = decltype(std::declval<ClauseAttr>().getValue());
401 StringRef enumStr;
402 SMLoc loc = parser.getCurrentLocation();
403 if (parser.parseKeyword(&enumStr))
404 return failure();
405 if (std::optional<ClauseT> enumValue = symbolizeEnum<ClauseT>(enumStr)) {
406 attr = ClauseAttr::get(parser.getContext(), *enumValue);
407 return success();
408 }
409 return parser.emitError(loc, "invalid clause value: '") << enumStr << "'";
410}
411
412template <typename ClauseAttr>
413static void printClauseAttr(OpAsmPrinter &p, Operation *op, ClauseAttr attr) {
414 p << stringifyEnum(attr.getValue());
415}
416
417//===----------------------------------------------------------------------===//
418// Parser and printer for Linear Clause
419//===----------------------------------------------------------------------===//
420
421/// linear ::= `linear` `(` linear-list `)`
422/// linear-list := linear-val | linear-val linear-list
423/// linear-val := ssa-id-and-type `=` ssa-id-and-type
424/// | `val` `(` ssa-id-and-type `=` ssa-id-and-type `)`
425/// | `ref` `(` ssa-id-and-type `=` ssa-id-and-type `)`
426/// | `uval` `(` ssa-id-and-type `=` ssa-id-and-type `)`
427static ParseResult parseLinearClause(
428 OpAsmParser &parser,
430 SmallVectorImpl<Type> &linearTypes,
432 SmallVectorImpl<Type> &linearStepTypes, ArrayAttr &linearModifiers) {
433 SmallVector<Attribute> modifiers;
434 auto result = parser.parseCommaSeparatedList([&]() {
436 Type type, stepType;
438
439 std::optional<omp::LinearModifier> linearModifier;
440 if (succeeded(parser.parseOptionalKeyword("val"))) {
441 linearModifier = omp::LinearModifier::val;
442 } else if (succeeded(parser.parseOptionalKeyword("ref"))) {
443 linearModifier = omp::LinearModifier::ref;
444 } else if (succeeded(parser.parseOptionalKeyword("uval"))) {
445 linearModifier = omp::LinearModifier::uval;
446 }
447
448 bool hasLinearModifierParens = linearModifier.has_value();
449 if (hasLinearModifierParens && parser.parseLParen())
450 return failure();
451
452 if (parser.parseOperand(var) || parser.parseColonType(type) ||
453 parser.parseEqual() || parser.parseOperand(stepVar) ||
454 parser.parseColonType(stepType))
455 return failure();
456
457 if (hasLinearModifierParens && parser.parseRParen())
458 return failure();
459
460 linearVars.push_back(var);
461 linearTypes.push_back(type);
462 linearStepVars.push_back(stepVar);
463 linearStepTypes.push_back(stepType);
464 if (linearModifier) {
465 modifiers.push_back(
466 omp::LinearModifierAttr::get(parser.getContext(), *linearModifier));
467 } else {
468 modifiers.push_back(UnitAttr::get(parser.getContext()));
469 }
470 return success();
471 });
472 if (failed(result))
473 return failure();
474 linearModifiers = ArrayAttr::get(parser.getContext(), modifiers);
475 return success();
476}
477
478/// Print Linear Clause
480 ValueRange linearVars, TypeRange linearTypes,
481 ValueRange linearStepVars, TypeRange stepVarTypes,
482 ArrayAttr linearModifiers) {
483 size_t linearVarsSize = linearVars.size();
484 for (unsigned i = 0; i < linearVarsSize; ++i) {
485 if (i != 0)
486 p << ", ";
487 // Print modifier keyword wrapper if present.
488 Attribute modAttr = linearModifiers ? linearModifiers[i] : nullptr;
489 auto mod = modAttr ? dyn_cast<omp::LinearModifierAttr>(modAttr) : nullptr;
490 if (mod) {
491 p << omp::stringifyLinearModifier(mod.getValue()) << "(";
492 }
493 p << linearVars[i] << " : " << linearTypes[i];
494 p << " = " << linearStepVars[i] << " : " << stepVarTypes[i];
495 if (mod)
496 p << ")";
497 }
498}
499
500//===----------------------------------------------------------------------===//
501// Verifier for Linear modifier
502//===----------------------------------------------------------------------===//
503
504/// OpenMP 5.2, Section 5.4.6: "A linear-modifier may be specified as ref or
505/// uval only on a declare simd directive."
506/// Also verifies that modifier count matches variable count.
507static LogicalResult
508verifyLinearModifiers(Operation *op, std::optional<ArrayAttr> linearModifiers,
509 OperandRange linearVars, bool isDeclareSimd = false) {
510 if (!linearModifiers)
511 return success();
512 if (linearModifiers->size() != linearVars.size())
513 return op->emitOpError()
514 << "expected as many linear modifiers as linear variables";
515 if (!isDeclareSimd) {
516 for (Attribute attr : *linearModifiers) {
517 if (!attr)
518 continue;
519 auto modAttr = dyn_cast<omp::LinearModifierAttr>(attr);
520 if (!modAttr)
521 continue;
522 omp::LinearModifier mod = modAttr.getValue();
523 if (mod == omp::LinearModifier::ref || mod == omp::LinearModifier::uval)
524 return op->emitOpError()
525 << "linear modifier '" << omp::stringifyLinearModifier(mod)
526 << "' may only be specified on a declare simd directive";
527 }
528 }
529 return success();
530}
531
532//===----------------------------------------------------------------------===//
533// Verifier for Nontemporal Clause
534//===----------------------------------------------------------------------===//
535
536static LogicalResult verifyNontemporalClause(Operation *op,
537 OperandRange nontemporalVars) {
538
539 // Check if each var is unique - OpenMP 5.0 -> 2.9.3.1 section
540 DenseSet<Value> nontemporalItems;
541 for (const auto &it : nontemporalVars)
542 if (!nontemporalItems.insert(it).second)
543 return op->emitOpError() << "nontemporal variable used more than once";
544
545 return success();
546}
547
548//===----------------------------------------------------------------------===//
549// Parser, verifier and printer for Aligned Clause
550//===----------------------------------------------------------------------===//
551static LogicalResult verifyAlignedClause(Operation *op,
552 std::optional<ArrayAttr> alignments,
553 OperandRange alignedVars) {
554 // Check if number of alignment values equals to number of aligned variables
555 if (!alignedVars.empty()) {
556 if (!alignments || alignments->size() != alignedVars.size())
557 return op->emitOpError()
558 << "expected as many alignment values as aligned variables";
559 } else {
560 if (alignments)
561 return op->emitOpError() << "unexpected alignment values attribute";
562 return success();
563 }
564
565 // Check if each var is aligned only once - OpenMP 4.5 -> 2.8.1 section
566 DenseSet<Value> alignedItems;
567 for (auto it : alignedVars)
568 if (!alignedItems.insert(it).second)
569 return op->emitOpError() << "aligned variable used more than once";
570
571 if (!alignments)
572 return success();
573
574 // Check if all alignment values are positive - OpenMP 4.5 -> 2.8.1 section
575 for (unsigned i = 0; i < (*alignments).size(); ++i) {
576 if (auto intAttr = llvm::dyn_cast<IntegerAttr>((*alignments)[i])) {
577 if (intAttr.getValue().sle(0))
578 return op->emitOpError() << "alignment should be greater than 0";
579 } else {
580 return op->emitOpError() << "expected integer alignment";
581 }
582 }
583
584 return success();
585}
586
587/// aligned ::= `aligned` `(` aligned-list `)`
588/// aligned-list := aligned-val | aligned-val aligned-list
589/// aligned-val := ssa-id-and-type `->` alignment
590static ParseResult
593 SmallVectorImpl<Type> &alignedTypes,
594 ArrayAttr &alignmentsAttr) {
595 SmallVector<Attribute> alignmentVec;
596 if (failed(parser.parseCommaSeparatedList([&]() {
597 if (parser.parseOperand(alignedVars.emplace_back()) ||
598 parser.parseColonType(alignedTypes.emplace_back()) ||
599 parser.parseArrow() ||
600 parser.parseAttribute(alignmentVec.emplace_back())) {
601 return failure();
602 }
603 return success();
604 })))
605 return failure();
606 SmallVector<Attribute> alignments(alignmentVec.begin(), alignmentVec.end());
607 alignmentsAttr = ArrayAttr::get(parser.getContext(), alignments);
608 return success();
609}
610
611/// Print Aligned Clause
613 ValueRange alignedVars, TypeRange alignedTypes,
614 std::optional<ArrayAttr> alignments) {
615 for (unsigned i = 0; i < alignedVars.size(); ++i) {
616 if (i != 0)
617 p << ", ";
618 p << alignedVars[i] << " : " << alignedVars[i].getType();
619 p << " -> " << (*alignments)[i];
620 }
621}
622
623//===----------------------------------------------------------------------===//
624// Parser, printer and verifier for Schedule Clause
625//===----------------------------------------------------------------------===//
626
627static ParseResult
629 SmallVectorImpl<SmallString<12>> &modifiers) {
630 if (modifiers.size() > 2)
631 return parser.emitError(parser.getNameLoc()) << " unexpected modifier(s)";
632 for (const auto &mod : modifiers) {
633 // Translate the string. If it has no value, then it was not a valid
634 // modifier!
635 auto symbol = symbolizeScheduleModifier(mod);
636 if (!symbol)
637 return parser.emitError(parser.getNameLoc())
638 << " unknown modifier type: " << mod;
639 }
640
641 // If we have one modifier that is "simd", then stick a "none" modiifer in
642 // index 0.
643 if (modifiers.size() == 1) {
644 if (symbolizeScheduleModifier(modifiers[0]) == ScheduleModifier::simd) {
645 modifiers.push_back(modifiers[0]);
646 modifiers[0] = stringifyScheduleModifier(ScheduleModifier::none);
647 }
648 } else if (modifiers.size() == 2) {
649 // If there are two modifier:
650 // First modifier should not be simd, second one should be simd
651 if (symbolizeScheduleModifier(modifiers[0]) == ScheduleModifier::simd ||
652 symbolizeScheduleModifier(modifiers[1]) != ScheduleModifier::simd)
653 return parser.emitError(parser.getNameLoc())
654 << " incorrect modifier order";
655 }
656 return success();
657}
658
659/// schedule ::= `schedule` `(` sched-list `)`
660/// sched-list ::= sched-val | sched-val sched-list |
661/// sched-val `,` sched-modifier
662/// sched-val ::= sched-with-chunk | sched-wo-chunk
663/// sched-with-chunk ::= sched-with-chunk-types (`=` ssa-id-and-type)?
664/// sched-with-chunk-types ::= `static` | `dynamic` | `guided`
665/// sched-wo-chunk ::= `auto` | `runtime`
666/// sched-modifier ::= sched-mod-val | sched-mod-val `,` sched-mod-val
667/// sched-mod-val ::= `monotonic` | `nonmonotonic` | `simd` | `none`
668static ParseResult
669parseScheduleClause(OpAsmParser &parser, ClauseScheduleKindAttr &scheduleAttr,
670 ScheduleModifierAttr &scheduleMod, UnitAttr &scheduleSimd,
671 std::optional<OpAsmParser::UnresolvedOperand> &chunkSize,
672 Type &chunkType) {
673 StringRef keyword;
674 if (parser.parseKeyword(&keyword))
675 return failure();
676 std::optional<mlir::omp::ClauseScheduleKind> schedule =
677 symbolizeClauseScheduleKind(keyword);
678 if (!schedule)
679 return parser.emitError(parser.getNameLoc()) << " expected schedule kind";
680
681 scheduleAttr = ClauseScheduleKindAttr::get(parser.getContext(), *schedule);
682 switch (*schedule) {
683 case ClauseScheduleKind::Static:
684 case ClauseScheduleKind::Dynamic:
685 case ClauseScheduleKind::Guided:
686 if (succeeded(parser.parseOptionalEqual())) {
687 chunkSize = OpAsmParser::UnresolvedOperand{};
688 if (parser.parseOperand(*chunkSize) || parser.parseColonType(chunkType))
689 return failure();
690 } else {
691 chunkSize = std::nullopt;
692 }
693 break;
694 case ClauseScheduleKind::Auto:
695 case ClauseScheduleKind::Runtime:
696 case ClauseScheduleKind::Distribute:
697 chunkSize = std::nullopt;
698 }
699
700 // If there is a comma, we have one or more modifiers..
702 while (succeeded(parser.parseOptionalComma())) {
703 StringRef mod;
704 if (parser.parseKeyword(&mod))
705 return failure();
706 modifiers.push_back(mod);
707 }
708
709 if (verifyScheduleModifiers(parser, modifiers))
710 return failure();
711
712 if (!modifiers.empty()) {
713 SMLoc loc = parser.getCurrentLocation();
714 if (std::optional<ScheduleModifier> mod =
715 symbolizeScheduleModifier(modifiers[0])) {
716 scheduleMod = ScheduleModifierAttr::get(parser.getContext(), *mod);
717 } else {
718 return parser.emitError(loc, "invalid schedule modifier");
719 }
720 // Only SIMD attribute is allowed here!
721 if (modifiers.size() > 1) {
722 assert(symbolizeScheduleModifier(modifiers[1]) == ScheduleModifier::simd);
723 scheduleSimd = UnitAttr::get(parser.getBuilder().getContext());
724 }
725 }
726
727 return success();
728}
729
730/// Print schedule clause
732 ClauseScheduleKindAttr scheduleKind,
733 ScheduleModifierAttr scheduleMod,
734 UnitAttr scheduleSimd, Value scheduleChunk,
735 Type scheduleChunkType) {
736 p << stringifyClauseScheduleKind(scheduleKind.getValue());
737 if (scheduleChunk)
738 p << " = " << scheduleChunk << " : " << scheduleChunk.getType();
739 if (scheduleMod)
740 p << ", " << stringifyScheduleModifier(scheduleMod.getValue());
741 if (scheduleSimd)
742 p << ", simd";
743}
744
745//===----------------------------------------------------------------------===//
746// Parser and printer for Order Clause
747//===----------------------------------------------------------------------===//
748
749// order ::= `order` `(` [order-modifier ':'] concurrent `)`
750// order-modifier ::= reproducible | unconstrained
751static ParseResult parseOrderClause(OpAsmParser &parser,
752 ClauseOrderKindAttr &order,
753 OrderModifierAttr &orderMod) {
754 StringRef enumStr;
755 SMLoc loc = parser.getCurrentLocation();
756 if (parser.parseKeyword(&enumStr))
757 return failure();
758 if (std::optional<OrderModifier> enumValue =
759 symbolizeOrderModifier(enumStr)) {
760 orderMod = OrderModifierAttr::get(parser.getContext(), *enumValue);
761 if (parser.parseOptionalColon())
762 return failure();
763 loc = parser.getCurrentLocation();
764 if (parser.parseKeyword(&enumStr))
765 return failure();
766 }
767 if (std::optional<ClauseOrderKind> enumValue =
768 symbolizeClauseOrderKind(enumStr)) {
769 order = ClauseOrderKindAttr::get(parser.getContext(), *enumValue);
770 return success();
771 }
772 return parser.emitError(loc, "invalid clause value: '") << enumStr << "'";
773}
774
776 ClauseOrderKindAttr order,
777 OrderModifierAttr orderMod) {
778 if (orderMod)
779 p << stringifyOrderModifier(orderMod.getValue()) << ":";
780 if (order)
781 p << stringifyClauseOrderKind(order.getValue());
782}
783
784template <typename ClauseTypeAttr, typename ClauseType>
785static ParseResult
786parseGranularityClause(OpAsmParser &parser, ClauseTypeAttr &prescriptiveness,
787 std::optional<OpAsmParser::UnresolvedOperand> &operand,
788 Type &operandType,
789 std::optional<ClauseType> (*symbolizeClause)(StringRef),
790 StringRef clauseName) {
791 StringRef enumStr;
792 if (succeeded(parser.parseOptionalKeyword(&enumStr))) {
793 if (std::optional<ClauseType> enumValue = symbolizeClause(enumStr)) {
794 prescriptiveness = ClauseTypeAttr::get(parser.getContext(), *enumValue);
795 if (parser.parseComma())
796 return failure();
797 } else {
798 return parser.emitError(parser.getCurrentLocation())
799 << "invalid " << clauseName << " modifier : '" << enumStr << "'";
800 ;
801 }
802 }
803
805 if (succeeded(parser.parseOperand(var))) {
806 operand = var;
807 } else {
808 return parser.emitError(parser.getCurrentLocation())
809 << "expected " << clauseName << " operand";
810 }
811
812 if (operand.has_value()) {
813 if (parser.parseColonType(operandType))
814 return failure();
815 }
816
817 return success();
818}
819
820template <typename ClauseTypeAttr, typename ClauseType>
821static void
823 ClauseTypeAttr prescriptiveness, Value operand,
824 mlir::Type operandType,
825 StringRef (*stringifyClauseType)(ClauseType)) {
826
827 if (prescriptiveness)
828 p << stringifyClauseType(prescriptiveness.getValue()) << ", ";
829
830 if (operand)
831 p << operand << ": " << operandType;
832}
833
834//===----------------------------------------------------------------------===//
835// Parser and printer for grainsize Clause
836//===----------------------------------------------------------------------===//
837
838// grainsize ::= `grainsize` `(` [strict ':'] grain-size `)`
839static ParseResult
840parseGrainsizeClause(OpAsmParser &parser, ClauseGrainsizeTypeAttr &grainsizeMod,
841 std::optional<OpAsmParser::UnresolvedOperand> &grainsize,
842 Type &grainsizeType) {
844 parser, grainsizeMod, grainsize, grainsizeType,
845 &symbolizeClauseGrainsizeType, "grainsize");
846}
847
849 ClauseGrainsizeTypeAttr grainsizeMod,
850 Value grainsize, mlir::Type grainsizeType) {
852 p, op, grainsizeMod, grainsize, grainsizeType,
853 &stringifyClauseGrainsizeType);
854}
855
856//===----------------------------------------------------------------------===//
857// Parser and printer for num_tasks Clause
858//===----------------------------------------------------------------------===//
859
860// numtask ::= `num_tasks` `(` [strict ':'] num-tasks `)`
861static ParseResult
862parseNumTasksClause(OpAsmParser &parser, ClauseNumTasksTypeAttr &numTasksMod,
863 std::optional<OpAsmParser::UnresolvedOperand> &numTasks,
864 Type &numTasksType) {
866 parser, numTasksMod, numTasks, numTasksType, &symbolizeClauseNumTasksType,
867 "num_tasks");
868}
869
871 ClauseNumTasksTypeAttr numTasksMod,
872 Value numTasks, mlir::Type numTasksType) {
874 p, op, numTasksMod, numTasks, numTasksType, &stringifyClauseNumTasksType);
875}
876
877//===----------------------------------------------------------------------===//
878// Parser and printer for Heap Alloc Clause
879//===----------------------------------------------------------------------===//
880
881/// operation ::= $in_type ( `(` $typeparams `)` )? ( `,` $shape )?
882static ParseResult parseHeapAllocClause(
883 OpAsmParser &parser, TypeAttr &inTypeAttr,
885 SmallVectorImpl<Type> &typeparamsTypes,
887 SmallVectorImpl<Type> &shapeTypes) {
888 mlir::Type inType;
889 if (parser.parseType(inType))
890 return mlir::failure();
891 inTypeAttr = TypeAttr::get(inType);
892
893 if (!parser.parseOptionalLParen()) {
894 // parse the LEN params of the derived type. (<params> : <types>)
895 if (parser.parseOperandList(typeparams, OpAsmParser::Delimiter::None) ||
896 parser.parseColonTypeList(typeparamsTypes) || parser.parseRParen())
897 return failure();
898 }
899
900 if (!parser.parseOptionalComma()) {
901 // parse size to scale by, vector of n dimensions of type index
903 return failure();
904
905 // TODO: This overrides the actual types of the operands, which might cause
906 // issues when they don't match. At the moment this is done in place of
907 // making the corresponding operand type `Variadic<Index>` because index
908 // types are lowered to I64 prior to LLVM IR translation.
909 shapeTypes.append(shape.size(), IndexType::get(parser.getContext()));
910 }
911
912 return success();
913}
914
916 TypeAttr inType, ValueRange typeparams,
917 TypeRange typeparamsTypes, ValueRange shape,
918 TypeRange shapeTypes) {
919 p << inType;
920 if (!typeparams.empty()) {
921 p << '(' << typeparams << " : " << typeparamsTypes << ')';
922 }
923 for (auto sh : shape) {
924 p << ", ";
925 p.printOperand(sh);
926 }
927}
928
929//===----------------------------------------------------------------------===//
930// Parser, printer and verify for dyn_groupprivate Clause
931//===----------------------------------------------------------------------===//
932
933static LogicalResult
934verifyDynGroupprivateClause(Operation *op, AccessGroupModifierAttr accessGroup,
935 FallbackModifierAttr fallback,
936 Value dynGroupprivateSize) {
937 if (!dynGroupprivateSize && (accessGroup || fallback))
938 return op->emitOpError("dyn_groupprivate modifiers require a size operand");
939
940 return success();
941}
942
943static ParseResult parseDynGroupprivateClause(
944 OpAsmParser &parser, AccessGroupModifierAttr &accessGroupAttr,
945 FallbackModifierAttr &fallbackAttr,
946 std::optional<OpAsmParser::UnresolvedOperand> &dynGroupprivateSize,
947 Type &sizeType) {
948
949 bool parsedAccessGroup = false;
950 bool parsedFallback = false;
951 bool parsedSize = false;
952
953 return parser.parseCommaSeparatedList([&]() -> ParseResult {
954 // Parse AccessGroupModifier.
955 if (succeeded(parser.parseOptionalKeyword("cgroup"))) {
956 if (parsedAccessGroup)
957 return parser.emitError(parser.getCurrentLocation(),
958 "duplicate access group modifier");
959 accessGroupAttr = AccessGroupModifierAttr::get(
960 parser.getContext(), AccessGroupModifier::cgroup);
961 parsedAccessGroup = true;
962 return success();
963 }
964 // Parse FallbackModifier.
965 if (succeeded(parser.parseOptionalKeyword("fallback"))) {
966 if (parsedFallback)
967 return parser.emitError(parser.getCurrentLocation(),
968 "duplicate fallback modifier");
969 if (parser.parseLParen())
970 return parser.emitError(parser.getCurrentLocation(),
971 "expected '(' after 'fallback'");
972 llvm::StringRef fbKind;
973 if (parser.parseKeyword(&fbKind))
974 return parser.emitError(
975 parser.getCurrentLocation(),
976 "expected fallback modifier (abort/null/default_mem)");
977 std::optional<FallbackModifier> fbEnum;
978 if (fbKind == "abort")
979 fbEnum = FallbackModifier::abort;
980 else if (fbKind == "null")
981 fbEnum = FallbackModifier::null;
982 else if (fbKind == "default_mem")
983 fbEnum = FallbackModifier::default_mem;
984 else
985 return parser.emitError(parser.getCurrentLocation(),
986 "invalid fallback modifier '" + fbKind + "'");
987 fallbackAttr = FallbackModifierAttr::get(parser.getContext(), *fbEnum);
988 if (parser.parseRParen())
989 return parser.emitError(parser.getCurrentLocation(),
990 "expected ')' after fallback modifier");
991 parsedFallback = true;
992 return success();
993 }
994 // Parse size operand.
996 if (succeeded(parser.parseOperand(operand))) {
997 if (parsedSize)
998 return parser.emitError(parser.getCurrentLocation(),
999 "duplicate size operand");
1000 dynGroupprivateSize = operand;
1001 parsedSize = true;
1002 if (failed(parser.parseColon()) || failed(parser.parseType(sizeType)))
1003 return parser.emitError(parser.getCurrentLocation(),
1004 "expected ':' and type after size operand");
1005 return success();
1006 }
1007 return parser.emitError(parser.getCurrentLocation(),
1008 "expected dyn_groupprivate_size operand");
1009 });
1010}
1011
1013 AccessGroupModifierAttr modifierFirst,
1014 FallbackModifierAttr modifierSecond,
1015 Value dynGroupprivateSize,
1016 Type sizeType) {
1017
1018 bool needsComma = false;
1019
1020 if (modifierFirst) {
1021 printer << modifierFirst.getValue();
1022 needsComma = true;
1023 }
1024
1025 if (modifierSecond) {
1026 if (needsComma)
1027 printer << ", ";
1028 printer << "fallback(";
1029 printer << modifierSecond.getValue();
1030 printer << ")";
1031 needsComma = true;
1032 }
1033
1034 if (dynGroupprivateSize) {
1035 if (needsComma)
1036 printer << ", ";
1037 printer << dynGroupprivateSize << " : " << sizeType;
1038 }
1039}
1040
1041//===----------------------------------------------------------------------===//
1042// Parser and printer for in_reduction Clause
1043//===----------------------------------------------------------------------===//
1044
1045/// Parses an `in_reduction` clause for an operation that does not give its
1046/// list items entry block arguments (e.g. `omp.target`). The expected format is
1047/// a comma-separated list of `[byref] @sym %var` followed by `: types`.
1048static ParseResult parseInReductionClause(
1049 OpAsmParser &parser,
1051 SmallVectorImpl<Type> &inReductionTypes,
1052 DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms) {
1054 SmallVector<bool> isByRefVec;
1055
1056 if (parser.parseCommaSeparatedList([&]() {
1057 isByRefVec.push_back(parser.parseOptionalKeyword("byref").succeeded());
1058 if (parser.parseAttribute(symbolVec.emplace_back()) ||
1059 parser.parseOperand(inReductionVars.emplace_back()))
1060 return failure();
1061 return success();
1062 }))
1063 return failure();
1064
1065 if (parser.parseColon())
1066 return failure();
1067
1068 if (parser.parseCommaSeparatedList(
1069 [&]() { return parser.parseType(inReductionTypes.emplace_back()); }))
1070 return failure();
1071
1072 if (inReductionVars.size() != inReductionTypes.size())
1073 return failure();
1074
1075 inReductionByref = makeDenseBoolArrayAttr(parser.getContext(), isByRefVec);
1076 SmallVector<Attribute> symbolAttrs(symbolVec.begin(), symbolVec.end());
1077 inReductionSyms = ArrayAttr::get(parser.getContext(), symbolAttrs);
1078 return success();
1079}
1080
1081/// Prints an `in_reduction` clause for an operation that does not give its list
1082/// items entry block arguments (e.g. `omp.target`). Mirrors
1083/// `parseInReductionClause`.
1085 ValueRange inReductionVars,
1086 TypeRange inReductionTypes,
1087 DenseBoolArrayAttr inReductionByref,
1088 ArrayAttr inReductionSyms) {
1089 MLIRContext *ctx = op->getContext();
1090
1091 ArrayAttr syms = inReductionSyms;
1092 if (!syms) {
1093 SmallVector<Attribute> values(inReductionVars.size(), nullptr);
1094 syms = ArrayAttr::get(ctx, values);
1095 }
1096
1097 DenseBoolArrayAttr byref = inReductionByref;
1098 if (!byref) {
1099 SmallVector<bool> values(inReductionVars.size(), false);
1100 byref = DenseBoolArrayAttr::get(ctx, values);
1101 }
1102
1103 llvm::interleaveComma(
1104 llvm::zip_equal(inReductionVars, syms.getValue(), byref.asArrayRef()), p,
1105 [&p](auto t) {
1106 auto [var, sym, isByRef] = t;
1107 if (isByRef)
1108 p << "byref ";
1109 if (sym)
1110 p << sym << " ";
1111 p << var;
1112 });
1113 p << " : ";
1114 llvm::interleaveComma(inReductionTypes, p);
1115}
1116
1117//===----------------------------------------------------------------------===//
1118// Parsers for operations including clauses that define entry block arguments.
1119//===----------------------------------------------------------------------===//
1120
1121namespace {
1122struct MapParseArgs {
1123 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars;
1124 SmallVectorImpl<Type> &types;
1125 MapParseArgs(SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars,
1126 SmallVectorImpl<Type> &types)
1127 : vars(vars), types(types) {}
1128};
1129struct PrivateParseArgs {
1130 llvm::SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars;
1131 llvm::SmallVectorImpl<Type> &types;
1132 ArrayAttr &syms;
1133 UnitAttr &needsBarrier;
1134 DenseI64ArrayAttr *mapIndices;
1135 PrivateParseArgs(SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars,
1136 SmallVectorImpl<Type> &types, ArrayAttr &syms,
1137 UnitAttr &needsBarrier,
1138 DenseI64ArrayAttr *mapIndices = nullptr)
1139 : vars(vars), types(types), syms(syms), needsBarrier(needsBarrier),
1140 mapIndices(mapIndices) {}
1141};
1142
1143struct ReductionParseArgs {
1144 SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars;
1145 SmallVectorImpl<Type> &types;
1146 DenseBoolArrayAttr &byref;
1147 ArrayAttr &syms;
1148 ReductionModifierAttr *modifier;
1149 ReductionParseArgs(SmallVectorImpl<OpAsmParser::UnresolvedOperand> &vars,
1150 SmallVectorImpl<Type> &types, DenseBoolArrayAttr &byref,
1151 ArrayAttr &syms, ReductionModifierAttr *mod = nullptr)
1152 : vars(vars), types(types), byref(byref), syms(syms), modifier(mod) {}
1153};
1154
1155struct AllRegionParseArgs {
1156 std::optional<MapParseArgs> hasDeviceAddrArgs;
1157 std::optional<MapParseArgs> hostEvalArgs;
1158 std::optional<ReductionParseArgs> inReductionArgs;
1159 std::optional<MapParseArgs> mapArgs;
1160 std::optional<PrivateParseArgs> privateArgs;
1161 std::optional<ReductionParseArgs> reductionArgs;
1162 std::optional<ReductionParseArgs> taskReductionArgs;
1163 std::optional<MapParseArgs> useDeviceAddrArgs;
1164 std::optional<MapParseArgs> useDevicePtrArgs;
1165};
1166} // namespace
1167
1168static inline constexpr StringRef getPrivateNeedsBarrierSpelling() {
1169 return "private_barrier";
1170}
1171
1172static ParseResult parseClauseWithRegionArgs(
1173 OpAsmParser &parser,
1175 SmallVectorImpl<Type> &types,
1176 SmallVectorImpl<OpAsmParser::Argument> &regionPrivateArgs,
1177 ArrayAttr *symbols = nullptr, DenseI64ArrayAttr *mapIndices = nullptr,
1178 DenseBoolArrayAttr *byref = nullptr,
1179 ReductionModifierAttr *modifier = nullptr,
1180 UnitAttr *needsBarrier = nullptr) {
1182 SmallVector<int64_t> mapIndicesVec;
1183 SmallVector<bool> isByRefVec;
1184 unsigned regionArgOffset = regionPrivateArgs.size();
1185
1186 if (parser.parseLParen())
1187 return failure();
1188
1189 if (modifier && succeeded(parser.parseOptionalKeyword("mod"))) {
1190 StringRef enumStr;
1191 if (parser.parseColon() || parser.parseKeyword(&enumStr) ||
1192 parser.parseComma())
1193 return failure();
1194 std::optional<ReductionModifier> enumValue =
1195 symbolizeReductionModifier(enumStr);
1196 if (!enumValue.has_value())
1197 return failure();
1198 *modifier = ReductionModifierAttr::get(parser.getContext(), *enumValue);
1199 if (!*modifier)
1200 return failure();
1201 }
1202
1203 if (parser.parseCommaSeparatedList([&]() {
1204 if (byref)
1205 isByRefVec.push_back(
1206 parser.parseOptionalKeyword("byref").succeeded());
1207
1208 if (symbols && parser.parseAttribute(symbolVec.emplace_back()))
1209 return failure();
1210
1211 if (parser.parseOperand(operands.emplace_back()) ||
1212 parser.parseArrow() ||
1213 parser.parseArgument(regionPrivateArgs.emplace_back()))
1214 return failure();
1215
1216 if (mapIndices) {
1217 if (parser.parseOptionalLSquare().succeeded()) {
1218 if (parser.parseKeyword("map_idx") || parser.parseEqual() ||
1219 parser.parseInteger(mapIndicesVec.emplace_back()) ||
1220 parser.parseRSquare())
1221 return failure();
1222 } else {
1223 mapIndicesVec.push_back(-1);
1224 }
1225 }
1226
1227 return success();
1228 }))
1229 return failure();
1230
1231 if (parser.parseColon())
1232 return failure();
1233
1234 if (parser.parseCommaSeparatedList([&]() {
1235 if (parser.parseType(types.emplace_back()))
1236 return failure();
1237
1238 return success();
1239 }))
1240 return failure();
1241
1242 if (operands.size() != types.size())
1243 return failure();
1244
1245 if (parser.parseRParen())
1246 return failure();
1247
1248 if (needsBarrier) {
1250 .succeeded())
1251 *needsBarrier = mlir::UnitAttr::get(parser.getContext());
1252 }
1253
1254 auto *argsBegin = regionPrivateArgs.begin();
1255 MutableArrayRef argsSubrange(argsBegin + regionArgOffset,
1256 argsBegin + regionArgOffset + types.size());
1257 for (auto [prv, type] : llvm::zip_equal(argsSubrange, types)) {
1258 prv.type = type;
1259 }
1260
1261 if (symbols) {
1262 SmallVector<Attribute> symbolAttrs(symbolVec.begin(), symbolVec.end());
1263 *symbols = ArrayAttr::get(parser.getContext(), symbolAttrs);
1264 }
1265
1266 if (!mapIndicesVec.empty())
1267 *mapIndices =
1268 mlir::DenseI64ArrayAttr::get(parser.getContext(), mapIndicesVec);
1269
1270 if (byref)
1271 *byref = makeDenseBoolArrayAttr(parser.getContext(), isByRefVec);
1272
1273 return success();
1274}
1275
1276static ParseResult parseBlockArgClause(
1277 OpAsmParser &parser,
1279 StringRef keyword, std::optional<MapParseArgs> mapArgs) {
1280 if (succeeded(parser.parseOptionalKeyword(keyword))) {
1281 if (!mapArgs)
1282 return failure();
1283
1284 if (failed(parseClauseWithRegionArgs(parser, mapArgs->vars, mapArgs->types,
1285 entryBlockArgs)))
1286 return failure();
1287 }
1288 return success();
1289}
1290
1291static ParseResult parseBlockArgClause(
1292 OpAsmParser &parser,
1294 StringRef keyword, std::optional<PrivateParseArgs> privateArgs) {
1295 if (succeeded(parser.parseOptionalKeyword(keyword))) {
1296 if (!privateArgs)
1297 return failure();
1298
1299 if (failed(parseClauseWithRegionArgs(
1300 parser, privateArgs->vars, privateArgs->types, entryBlockArgs,
1301 &privateArgs->syms, privateArgs->mapIndices, /*byref=*/nullptr,
1302 /*modifier=*/nullptr, &privateArgs->needsBarrier)))
1303 return failure();
1304 }
1305 return success();
1306}
1307
1308static ParseResult parseBlockArgClause(
1309 OpAsmParser &parser,
1311 StringRef keyword, std::optional<ReductionParseArgs> reductionArgs) {
1312 if (succeeded(parser.parseOptionalKeyword(keyword))) {
1313 if (!reductionArgs)
1314 return failure();
1315 if (failed(parseClauseWithRegionArgs(
1316 parser, reductionArgs->vars, reductionArgs->types, entryBlockArgs,
1317 &reductionArgs->syms, /*mapIndices=*/nullptr, &reductionArgs->byref,
1318 reductionArgs->modifier)))
1319 return failure();
1320 }
1321 return success();
1322}
1323
1324static ParseResult parseBlockArgRegion(OpAsmParser &parser, Region &region,
1325 AllRegionParseArgs args) {
1327
1328 if (failed(parseBlockArgClause(parser, entryBlockArgs, "has_device_addr",
1329 args.hasDeviceAddrArgs)))
1330 return parser.emitError(parser.getCurrentLocation())
1331 << "invalid `has_device_addr` format";
1332
1333 if (failed(parseBlockArgClause(parser, entryBlockArgs, "host_eval",
1334 args.hostEvalArgs)))
1335 return parser.emitError(parser.getCurrentLocation())
1336 << "invalid `host_eval` format";
1337
1338 if (failed(parseBlockArgClause(parser, entryBlockArgs, "in_reduction",
1339 args.inReductionArgs)))
1340 return parser.emitError(parser.getCurrentLocation())
1341 << "invalid `in_reduction` format";
1342
1343 if (failed(parseBlockArgClause(parser, entryBlockArgs, "map_entries",
1344 args.mapArgs)))
1345 return parser.emitError(parser.getCurrentLocation())
1346 << "invalid `map_entries` format";
1347
1348 if (failed(parseBlockArgClause(parser, entryBlockArgs, "private",
1349 args.privateArgs)))
1350 return parser.emitError(parser.getCurrentLocation())
1351 << "invalid `private` format";
1352
1353 if (failed(parseBlockArgClause(parser, entryBlockArgs, "reduction",
1354 args.reductionArgs)))
1355 return parser.emitError(parser.getCurrentLocation())
1356 << "invalid `reduction` format";
1357
1358 if (failed(parseBlockArgClause(parser, entryBlockArgs, "task_reduction",
1359 args.taskReductionArgs)))
1360 return parser.emitError(parser.getCurrentLocation())
1361 << "invalid `task_reduction` format";
1362
1363 if (failed(parseBlockArgClause(parser, entryBlockArgs, "use_device_addr",
1364 args.useDeviceAddrArgs)))
1365 return parser.emitError(parser.getCurrentLocation())
1366 << "invalid `use_device_addr` format";
1367
1368 if (failed(parseBlockArgClause(parser, entryBlockArgs, "use_device_ptr",
1369 args.useDevicePtrArgs)))
1370 return parser.emitError(parser.getCurrentLocation())
1371 << "invalid `use_device_addr` format";
1372
1373 return parser.parseRegion(region, entryBlockArgs);
1374}
1375
1376// These parseXyz functions correspond to the custom<Xyz> definitions
1377// in the .td file(s).
1378static ParseResult parseTargetOpRegion(
1379 OpAsmParser &parser, Region &region,
1381 SmallVectorImpl<Type> &hasDeviceAddrTypes,
1383 SmallVectorImpl<Type> &hostEvalTypes,
1385 SmallVectorImpl<Type> &mapTypes,
1387 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,
1388 UnitAttr &privateNeedsBarrier, DenseI64ArrayAttr &privateMaps) {
1389 AllRegionParseArgs args;
1390 args.hasDeviceAddrArgs.emplace(hasDeviceAddrVars, hasDeviceAddrTypes);
1391 args.hostEvalArgs.emplace(hostEvalVars, hostEvalTypes);
1392 args.mapArgs.emplace(mapVars, mapTypes);
1393 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1394 privateNeedsBarrier, &privateMaps);
1395 return parseBlockArgRegion(parser, region, args);
1396}
1397
1399 OpAsmParser &parser, Region &region,
1401 SmallVectorImpl<Type> &inReductionTypes,
1402 DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms,
1404 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,
1405 UnitAttr &privateNeedsBarrier) {
1406 AllRegionParseArgs args;
1407 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,
1408 inReductionByref, inReductionSyms);
1409 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1410 privateNeedsBarrier);
1411 return parseBlockArgRegion(parser, region, args);
1412}
1413
1415 OpAsmParser &parser, Region &region,
1417 SmallVectorImpl<Type> &inReductionTypes,
1418 DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms,
1420 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,
1421 UnitAttr &privateNeedsBarrier, ReductionModifierAttr &reductionMod,
1423 SmallVectorImpl<Type> &reductionTypes, DenseBoolArrayAttr &reductionByref,
1424 ArrayAttr &reductionSyms) {
1425 AllRegionParseArgs args;
1426 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,
1427 inReductionByref, inReductionSyms);
1428 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1429 privateNeedsBarrier);
1430 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,
1431 reductionSyms, &reductionMod);
1432 return parseBlockArgRegion(parser, region, args);
1433}
1434
1435static ParseResult parsePrivateRegion(
1436 OpAsmParser &parser, Region &region,
1438 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,
1439 UnitAttr &privateNeedsBarrier) {
1440 AllRegionParseArgs args;
1441 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1442 privateNeedsBarrier);
1443 return parseBlockArgRegion(parser, region, args);
1444}
1445
1447 OpAsmParser &parser, Region &region,
1449 llvm::SmallVectorImpl<Type> &privateTypes, ArrayAttr &privateSyms,
1450 UnitAttr &privateNeedsBarrier, ReductionModifierAttr &reductionMod,
1452 SmallVectorImpl<Type> &reductionTypes, DenseBoolArrayAttr &reductionByref,
1453 ArrayAttr &reductionSyms) {
1454 AllRegionParseArgs args;
1455 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1456 privateNeedsBarrier);
1457 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,
1458 reductionSyms, &reductionMod);
1459 return parseBlockArgRegion(parser, region, args);
1460}
1461
1462static ParseResult parseTaskReductionRegion(
1463 OpAsmParser &parser, Region &region,
1465 SmallVectorImpl<Type> &taskReductionTypes,
1466 DenseBoolArrayAttr &taskReductionByref, ArrayAttr &taskReductionSyms) {
1467 AllRegionParseArgs args;
1468 args.taskReductionArgs.emplace(taskReductionVars, taskReductionTypes,
1469 taskReductionByref, taskReductionSyms);
1470 return parseBlockArgRegion(parser, region, args);
1471}
1472
1474 OpAsmParser &parser, Region &region,
1476 SmallVectorImpl<Type> &useDeviceAddrTypes,
1478 SmallVectorImpl<Type> &useDevicePtrTypes) {
1479 AllRegionParseArgs args;
1480 args.useDeviceAddrArgs.emplace(useDeviceAddrVars, useDeviceAddrTypes);
1481 args.useDevicePtrArgs.emplace(useDevicePtrVars, useDevicePtrTypes);
1482 return parseBlockArgRegion(parser, region, args);
1483}
1484
1485//===----------------------------------------------------------------------===//
1486// Printers for operations including clauses that define entry block arguments.
1487//===----------------------------------------------------------------------===//
1488
1489namespace {
1490struct MapPrintArgs {
1491 ValueRange vars;
1492 TypeRange types;
1493 MapPrintArgs(ValueRange vars, TypeRange types) : vars(vars), types(types) {}
1494};
1495struct PrivatePrintArgs {
1496 ValueRange vars;
1497 TypeRange types;
1498 ArrayAttr syms;
1499 UnitAttr needsBarrier;
1500 DenseI64ArrayAttr mapIndices;
1501 PrivatePrintArgs(ValueRange vars, TypeRange types, ArrayAttr syms,
1502 UnitAttr needsBarrier, DenseI64ArrayAttr mapIndices)
1503 : vars(vars), types(types), syms(syms), needsBarrier(needsBarrier),
1504 mapIndices(mapIndices) {}
1505};
1506struct ReductionPrintArgs {
1507 ValueRange vars;
1508 TypeRange types;
1509 DenseBoolArrayAttr byref;
1510 ArrayAttr syms;
1511 ReductionModifierAttr modifier;
1512 ReductionPrintArgs(ValueRange vars, TypeRange types, DenseBoolArrayAttr byref,
1513 ArrayAttr syms, ReductionModifierAttr mod = nullptr)
1514 : vars(vars), types(types), byref(byref), syms(syms), modifier(mod) {}
1515};
1516struct AllRegionPrintArgs {
1517 std::optional<MapPrintArgs> hasDeviceAddrArgs;
1518 std::optional<MapPrintArgs> hostEvalArgs;
1519 std::optional<ReductionPrintArgs> inReductionArgs;
1520 std::optional<MapPrintArgs> mapArgs;
1521 std::optional<PrivatePrintArgs> privateArgs;
1522 std::optional<ReductionPrintArgs> reductionArgs;
1523 std::optional<ReductionPrintArgs> taskReductionArgs;
1524 std::optional<MapPrintArgs> useDeviceAddrArgs;
1525 std::optional<MapPrintArgs> useDevicePtrArgs;
1526};
1527} // namespace
1528
1530 OpAsmPrinter &p, MLIRContext *ctx, StringRef clauseName,
1531 ValueRange argsSubrange, ValueRange operands, TypeRange types,
1532 ArrayAttr symbols = nullptr, DenseI64ArrayAttr mapIndices = nullptr,
1533 DenseBoolArrayAttr byref = nullptr,
1534 ReductionModifierAttr modifier = nullptr, UnitAttr needsBarrier = nullptr) {
1535 if (argsSubrange.empty())
1536 return;
1537
1538 p << clauseName << "(";
1539
1540 if (modifier)
1541 p << "mod: " << stringifyReductionModifier(modifier.getValue()) << ", ";
1542
1543 if (!symbols) {
1544 llvm::SmallVector<Attribute> values(operands.size(), nullptr);
1545 symbols = ArrayAttr::get(ctx, values);
1546 }
1547
1548 if (!mapIndices) {
1549 llvm::SmallVector<int64_t> values(operands.size(), -1);
1550 mapIndices = DenseI64ArrayAttr::get(ctx, values);
1551 }
1552
1553 if (!byref) {
1554 mlir::SmallVector<bool> values(operands.size(), false);
1555 byref = DenseBoolArrayAttr::get(ctx, values);
1556 }
1557
1558 llvm::interleaveComma(llvm::zip_equal(operands, argsSubrange, symbols,
1559 mapIndices.asArrayRef(),
1560 byref.asArrayRef()),
1561 p, [&p](auto t) {
1562 auto [op, arg, sym, map, isByRef] = t;
1563 if (isByRef)
1564 p << "byref ";
1565 if (sym)
1566 p << sym << " ";
1567
1568 p << op << " -> " << arg;
1569
1570 if (map != -1)
1571 p << " [map_idx=" << map << "]";
1572 });
1573 p << " : ";
1574 llvm::interleaveComma(types, p);
1575 p << ") ";
1576
1577 if (needsBarrier)
1578 p << getPrivateNeedsBarrierSpelling() << " ";
1579}
1580
1582 StringRef clauseName, ValueRange argsSubrange,
1583 std::optional<MapPrintArgs> mapArgs) {
1584 if (mapArgs)
1585 printClauseWithRegionArgs(p, ctx, clauseName, argsSubrange, mapArgs->vars,
1586 mapArgs->types);
1587}
1588
1590 StringRef clauseName, ValueRange argsSubrange,
1591 std::optional<PrivatePrintArgs> privateArgs) {
1592 if (privateArgs)
1594 p, ctx, clauseName, argsSubrange, privateArgs->vars, privateArgs->types,
1595 privateArgs->syms, privateArgs->mapIndices, /*byref=*/nullptr,
1596 /*modifier=*/nullptr, privateArgs->needsBarrier);
1597}
1598
1599static void
1600printBlockArgClause(OpAsmPrinter &p, MLIRContext *ctx, StringRef clauseName,
1601 ValueRange argsSubrange,
1602 std::optional<ReductionPrintArgs> reductionArgs) {
1603 if (reductionArgs)
1604 printClauseWithRegionArgs(p, ctx, clauseName, argsSubrange,
1605 reductionArgs->vars, reductionArgs->types,
1606 reductionArgs->syms, /*mapIndices=*/nullptr,
1607 reductionArgs->byref, reductionArgs->modifier);
1608}
1609
1611 const AllRegionPrintArgs &args) {
1612 auto iface = llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op);
1613 MLIRContext *ctx = op->getContext();
1614
1615 printBlockArgClause(p, ctx, "has_device_addr",
1616 iface.getHasDeviceAddrBlockArgs(),
1617 args.hasDeviceAddrArgs);
1618 printBlockArgClause(p, ctx, "host_eval", iface.getHostEvalBlockArgs(),
1619 args.hostEvalArgs);
1620 printBlockArgClause(p, ctx, "in_reduction", iface.getInReductionBlockArgs(),
1621 args.inReductionArgs);
1622 printBlockArgClause(p, ctx, "map_entries", iface.getMapBlockArgs(),
1623 args.mapArgs);
1624 printBlockArgClause(p, ctx, "private", iface.getPrivateBlockArgs(),
1625 args.privateArgs);
1626 printBlockArgClause(p, ctx, "reduction", iface.getReductionBlockArgs(),
1627 args.reductionArgs);
1628 printBlockArgClause(p, ctx, "task_reduction",
1629 iface.getTaskReductionBlockArgs(),
1630 args.taskReductionArgs);
1631 printBlockArgClause(p, ctx, "use_device_addr",
1632 iface.getUseDeviceAddrBlockArgs(),
1633 args.useDeviceAddrArgs);
1634 printBlockArgClause(p, ctx, "use_device_ptr",
1635 iface.getUseDevicePtrBlockArgs(), args.useDevicePtrArgs);
1636
1637 p.printRegion(region, /*printEntryBlockArgs=*/false);
1638}
1639
1640// These parseXyz functions correspond to the custom<Xyz> definitions
1641// in the .td file(s).
1643 ValueRange hasDeviceAddrVars,
1644 TypeRange hasDeviceAddrTypes,
1645 ValueRange hostEvalVars,
1646 TypeRange hostEvalTypes, ValueRange mapVars,
1647 TypeRange mapTypes, ValueRange privateVars,
1648 TypeRange privateTypes, ArrayAttr privateSyms,
1649 UnitAttr privateNeedsBarrier,
1650 DenseI64ArrayAttr privateMaps) {
1651 AllRegionPrintArgs args;
1652 args.hasDeviceAddrArgs.emplace(hasDeviceAddrVars, hasDeviceAddrTypes);
1653 args.hostEvalArgs.emplace(hostEvalVars, hostEvalTypes);
1654 args.mapArgs.emplace(mapVars, mapTypes);
1655 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1656 privateNeedsBarrier, privateMaps);
1657 printBlockArgRegion(p, op, region, args);
1658}
1659
1661 OpAsmPrinter &p, Operation *op, Region &region, ValueRange inReductionVars,
1662 TypeRange inReductionTypes, DenseBoolArrayAttr inReductionByref,
1663 ArrayAttr inReductionSyms, ValueRange privateVars, TypeRange privateTypes,
1664 ArrayAttr privateSyms, UnitAttr privateNeedsBarrier) {
1665 AllRegionPrintArgs args;
1666 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,
1667 inReductionByref, inReductionSyms);
1668 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1669 privateNeedsBarrier,
1670 /*mapIndices=*/nullptr);
1671 printBlockArgRegion(p, op, region, args);
1672}
1673
1675 OpAsmPrinter &p, Operation *op, Region &region, ValueRange inReductionVars,
1676 TypeRange inReductionTypes, DenseBoolArrayAttr inReductionByref,
1677 ArrayAttr inReductionSyms, ValueRange privateVars, TypeRange privateTypes,
1678 ArrayAttr privateSyms, UnitAttr privateNeedsBarrier,
1679 ReductionModifierAttr reductionMod, ValueRange reductionVars,
1680 TypeRange reductionTypes, DenseBoolArrayAttr reductionByref,
1681 ArrayAttr reductionSyms) {
1682 AllRegionPrintArgs args;
1683 args.inReductionArgs.emplace(inReductionVars, inReductionTypes,
1684 inReductionByref, inReductionSyms);
1685 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1686 privateNeedsBarrier,
1687 /*mapIndices=*/nullptr);
1688 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,
1689 reductionSyms, reductionMod);
1690 printBlockArgRegion(p, op, region, args);
1691}
1692
1694 ValueRange privateVars, TypeRange privateTypes,
1695 ArrayAttr privateSyms,
1696 UnitAttr privateNeedsBarrier) {
1697 AllRegionPrintArgs args;
1698 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1699 privateNeedsBarrier,
1700 /*mapIndices=*/nullptr);
1701 printBlockArgRegion(p, op, region, args);
1702}
1703
1705 OpAsmPrinter &p, Operation *op, Region &region, ValueRange privateVars,
1706 TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier,
1707 ReductionModifierAttr reductionMod, ValueRange reductionVars,
1708 TypeRange reductionTypes, DenseBoolArrayAttr reductionByref,
1709 ArrayAttr reductionSyms) {
1710 AllRegionPrintArgs args;
1711 args.privateArgs.emplace(privateVars, privateTypes, privateSyms,
1712 privateNeedsBarrier,
1713 /*mapIndices=*/nullptr);
1714 args.reductionArgs.emplace(reductionVars, reductionTypes, reductionByref,
1715 reductionSyms, reductionMod);
1716 printBlockArgRegion(p, op, region, args);
1717}
1718
1720 Region &region,
1721 ValueRange taskReductionVars,
1722 TypeRange taskReductionTypes,
1723 DenseBoolArrayAttr taskReductionByref,
1724 ArrayAttr taskReductionSyms) {
1725 AllRegionPrintArgs args;
1726 args.taskReductionArgs.emplace(taskReductionVars, taskReductionTypes,
1727 taskReductionByref, taskReductionSyms);
1728 printBlockArgRegion(p, op, region, args);
1729}
1730
1732 Region &region,
1733 ValueRange useDeviceAddrVars,
1734 TypeRange useDeviceAddrTypes,
1735 ValueRange useDevicePtrVars,
1736 TypeRange useDevicePtrTypes) {
1737 AllRegionPrintArgs args;
1738 args.useDeviceAddrArgs.emplace(useDeviceAddrVars, useDeviceAddrTypes);
1739 args.useDevicePtrArgs.emplace(useDevicePtrVars, useDevicePtrTypes);
1740 printBlockArgRegion(p, op, region, args);
1741}
1742
1743template <typename ParsePrefixFn>
1744static ParseResult parseSplitIteratedList(
1745 OpAsmParser &parser,
1747 SmallVectorImpl<Type> &iteratedTypes,
1749 SmallVectorImpl<Type> &plainTypes, ParsePrefixFn &&parsePrefix) {
1750
1751 return parser.parseCommaSeparatedList([&]() -> ParseResult {
1752 if (failed(parsePrefix()))
1753 return failure();
1754
1756 Type ty;
1757 if (parser.parseOperand(v) || parser.parseColonType(ty))
1758 return failure();
1759
1760 if (llvm::isa<mlir::omp::IteratedType>(ty)) {
1761 iteratedVars.push_back(v);
1762 iteratedTypes.push_back(ty);
1763 } else {
1764 plainVars.push_back(v);
1765 plainTypes.push_back(ty);
1766 }
1767 return success();
1768 });
1769}
1770
1771template <typename PrintPrefixFn>
1773 TypeRange iteratedTypes,
1774 ValueRange plainVars, TypeRange plainTypes,
1775 PrintPrefixFn &&printPrefixForPlain,
1776 PrintPrefixFn &&printPrefixForIterated) {
1777
1778 bool first = true;
1779 auto emit = [&](Value v, Type t, auto &&printPrefix) {
1780 if (!first)
1781 p << ", ";
1782 printPrefix(v, t);
1783 p << v << " : " << t;
1784 first = false;
1785 };
1786
1787 for (unsigned i = 0; i < iteratedVars.size(); ++i)
1788 emit(iteratedVars[i], iteratedTypes[i], printPrefixForIterated);
1789 for (unsigned i = 0; i < plainVars.size(); ++i)
1790 emit(plainVars[i], plainTypes[i], printPrefixForPlain);
1791}
1792
1793/// Verifies Reduction Clause
1794static LogicalResult
1795verifyReductionVarList(Operation *op, std::optional<ArrayAttr> reductionSyms,
1796 OperandRange reductionVars,
1797 std::optional<ArrayRef<bool>> reductionByref) {
1798 if (!reductionVars.empty()) {
1799 if (!reductionSyms || reductionSyms->size() != reductionVars.size())
1800 return op->emitOpError()
1801 << "expected as many reduction symbol references "
1802 "as reduction variables";
1803 if (reductionByref && reductionByref->size() != reductionVars.size())
1804 return op->emitError() << "expected as many reduction variable by "
1805 "reference attributes as reduction variables";
1806 } else {
1807 if (reductionSyms)
1808 return op->emitOpError() << "unexpected reduction symbol references";
1809 return success();
1810 }
1811
1812 // TODO: The followings should be done in
1813 // SymbolUserOpInterface::verifySymbolUses.
1814 DenseSet<Value> accumulators;
1815 for (auto args : llvm::zip(reductionVars, *reductionSyms)) {
1816 Value accum = std::get<0>(args);
1817
1818 if (!accumulators.insert(accum).second)
1819 return op->emitOpError() << "accumulator variable used more than once";
1820
1821 Type varType = accum.getType();
1822 auto symbolRef = llvm::cast<SymbolRefAttr>(std::get<1>(args));
1823 auto decl =
1825 if (!decl)
1826 return op->emitOpError() << "expected symbol reference " << symbolRef
1827 << " to point to a reduction declaration";
1828
1829 if (decl.getAccumulatorType() && decl.getAccumulatorType() != varType)
1830 return op->emitOpError()
1831 << "expected accumulator (" << varType
1832 << ") to be the same type as reduction declaration ("
1833 << decl.getAccumulatorType() << ")";
1834 }
1835
1836 return success();
1837}
1838
1839//===----------------------------------------------------------------------===//
1840// Parser, printer and verifier for Copyprivate
1841//===----------------------------------------------------------------------===//
1842
1843/// copyprivate-entry-list ::= copyprivate-entry
1844/// | copyprivate-entry-list `,` copyprivate-entry
1845/// copyprivate-entry ::= ssa-id `->` symbol-ref `:` type
1846static ParseResult parseCopyprivate(
1847 OpAsmParser &parser,
1849 SmallVectorImpl<Type> &copyprivateTypes, ArrayAttr &copyprivateSyms) {
1851 if (failed(parser.parseCommaSeparatedList([&]() {
1852 if (parser.parseOperand(copyprivateVars.emplace_back()) ||
1853 parser.parseArrow() ||
1854 parser.parseAttribute(symsVec.emplace_back()) ||
1855 parser.parseColonType(copyprivateTypes.emplace_back()))
1856 return failure();
1857 return success();
1858 })))
1859 return failure();
1860 SmallVector<Attribute> syms(symsVec.begin(), symsVec.end());
1861 copyprivateSyms = ArrayAttr::get(parser.getContext(), syms);
1862 return success();
1863}
1864
1865/// Print Copyprivate clause
1867 OperandRange copyprivateVars,
1868 TypeRange copyprivateTypes,
1869 std::optional<ArrayAttr> copyprivateSyms) {
1870 if (!copyprivateSyms.has_value())
1871 return;
1872 llvm::interleaveComma(
1873 llvm::zip(copyprivateVars, *copyprivateSyms, copyprivateTypes), p,
1874 [&](const auto &args) {
1875 p << std::get<0>(args) << " -> " << std::get<1>(args) << " : "
1876 << std::get<2>(args);
1877 });
1878}
1879
1880/// Verifies CopyPrivate Clause
1881static LogicalResult
1883 std::optional<ArrayAttr> copyprivateSyms) {
1884 size_t copyprivateSymsSize =
1885 copyprivateSyms.has_value() ? copyprivateSyms->size() : 0;
1886 if (copyprivateSymsSize != copyprivateVars.size())
1887 return op->emitOpError() << "inconsistent number of copyprivate vars (= "
1888 << copyprivateVars.size()
1889 << ") and functions (= " << copyprivateSymsSize
1890 << "), both must be equal";
1891 if (!copyprivateSyms.has_value())
1892 return success();
1893
1894 for (auto copyprivateVarAndSym :
1895 llvm::zip(copyprivateVars, *copyprivateSyms)) {
1896 auto symbolRef =
1897 llvm::cast<SymbolRefAttr>(std::get<1>(copyprivateVarAndSym));
1898 std::optional<std::variant<mlir::func::FuncOp, mlir::LLVM::LLVMFuncOp>>
1899 funcOp;
1900 if (mlir::func::FuncOp mlirFuncOp =
1902 symbolRef))
1903 funcOp = mlirFuncOp;
1904 else if (mlir::LLVM::LLVMFuncOp llvmFuncOp =
1906 op, symbolRef))
1907 funcOp = llvmFuncOp;
1908
1909 auto getNumArguments = [&] {
1910 return std::visit([](auto &f) { return f.getNumArguments(); }, *funcOp);
1911 };
1912
1913 auto getArgumentType = [&](unsigned i) {
1914 return std::visit([i](auto &f) { return f.getArgumentTypes()[i]; },
1915 *funcOp);
1916 };
1917
1918 if (!funcOp)
1919 return op->emitOpError() << "expected symbol reference " << symbolRef
1920 << " to point to a copy function";
1921
1922 if (getNumArguments() != 2)
1923 return op->emitOpError()
1924 << "expected copy function " << symbolRef << " to have 2 operands";
1925
1926 Type argTy = getArgumentType(0);
1927 if (argTy != getArgumentType(1))
1928 return op->emitOpError() << "expected copy function " << symbolRef
1929 << " arguments to have the same type";
1930
1931 Type varType = std::get<0>(copyprivateVarAndSym).getType();
1932 if (argTy != varType)
1933 return op->emitOpError()
1934 << "expected copy function arguments' type (" << argTy
1935 << ") to be the same as copyprivate variable's type (" << varType
1936 << ")";
1937 }
1938
1939 return success();
1940}
1941
1942//===----------------------------------------------------------------------===//
1943// Parser, printer and verifier for DependVarList
1944//===----------------------------------------------------------------------===//
1945
1946/// depend-entry-list ::= depend-entry
1947/// | depend-entry-list `,` depend-entry
1948/// depend-entry ::= depend-kind `->` ssa-id `:` type
1949/// | depend-kind `->` ssa-id `:` iterated-type
1950static ParseResult parseDependVarList(
1951 OpAsmParser &parser,
1953 SmallVectorImpl<Type> &dependTypes, ArrayAttr &dependKinds,
1955 SmallVectorImpl<Type> &iteratedTypes, ArrayAttr &iteratedKinds) {
1958 if (failed(parser.parseCommaSeparatedList([&]() {
1959 StringRef keyword;
1960 OpAsmParser::UnresolvedOperand operand;
1961 Type ty;
1962 if (parser.parseKeyword(&keyword) || parser.parseArrow() ||
1963 parser.parseOperand(operand) || parser.parseColonType(ty))
1964 return failure();
1965 std::optional<ClauseTaskDepend> keywordDepend =
1966 symbolizeClauseTaskDepend(keyword);
1967 if (!keywordDepend)
1968 return failure();
1969 auto kindAttr =
1970 ClauseTaskDependAttr::get(parser.getContext(), *keywordDepend);
1971 if (llvm::isa<mlir::omp::IteratedType>(ty)) {
1972 iteratedVars.push_back(operand);
1973 iteratedTypes.push_back(ty);
1974 iterKindsVec.push_back(kindAttr);
1975 } else {
1976 dependVars.push_back(operand);
1977 dependTypes.push_back(ty);
1978 kindsVec.push_back(kindAttr);
1979 }
1980 return success();
1981 })))
1982 return failure();
1983 SmallVector<Attribute> kinds(kindsVec.begin(), kindsVec.end());
1984 dependKinds = ArrayAttr::get(parser.getContext(), kinds);
1985 SmallVector<Attribute> iterKinds(iterKindsVec.begin(), iterKindsVec.end());
1986 iteratedKinds = ArrayAttr::get(parser.getContext(), iterKinds);
1987 return success();
1988}
1989
1990/// Print Depend clause
1992 OperandRange dependVars, TypeRange dependTypes,
1993 std::optional<ArrayAttr> dependKinds,
1994 OperandRange iteratedVars,
1995 TypeRange iteratedTypes,
1996 std::optional<ArrayAttr> iteratedKinds) {
1997 bool first = true;
1998 auto printEntries = [&](OperandRange vars, TypeRange types,
1999 std::optional<ArrayAttr> kinds) {
2000 for (unsigned i = 0, e = vars.size(); i < e; ++i) {
2001 if (!first)
2002 p << ", ";
2003 p << stringifyClauseTaskDepend(
2004 llvm::cast<mlir::omp::ClauseTaskDependAttr>((*kinds)[i])
2005 .getValue())
2006 << " -> " << vars[i] << " : " << types[i];
2007 first = false;
2008 }
2009 };
2010 printEntries(dependVars, dependTypes, dependKinds);
2011 printEntries(iteratedVars, iteratedTypes, iteratedKinds);
2012}
2013
2014/// Verifies Depend clause
2015static LogicalResult verifyDependVarList(Operation *op,
2016 std::optional<ArrayAttr> dependKinds,
2017 OperandRange dependVars,
2018 std::optional<ArrayAttr> iteratedKinds,
2019 OperandRange iteratedVars) {
2020 if (!dependVars.empty()) {
2021 if (!dependKinds || dependKinds->size() != dependVars.size())
2022 return op->emitOpError() << "expected as many depend values"
2023 " as depend variables";
2024 } else {
2025 if (dependKinds && !dependKinds->empty())
2026 return op->emitOpError() << "unexpected depend values";
2027 }
2028
2029 if (!iteratedVars.empty()) {
2030 if (!iteratedKinds || iteratedKinds->size() != iteratedVars.size())
2031 return op->emitOpError() << "expected as many depend iterated values"
2032 " as depend iterated variables";
2033 } else {
2034 if (iteratedKinds && !iteratedKinds->empty())
2035 return op->emitOpError() << "unexpected depend iterated values";
2036 }
2037
2038 return success();
2039}
2040
2041//===----------------------------------------------------------------------===//
2042// Parser, printer and verifier for Synchronization Hint (2.17.12)
2043//===----------------------------------------------------------------------===//
2044
2045/// Parses a Synchronization Hint clause. The value of hint is an integer
2046/// which is a combination of different hints from `omp_sync_hint_t`.
2047///
2048/// hint-clause = `hint` `(` hint-value `)`
2049static ParseResult parseSynchronizationHint(OpAsmParser &parser,
2050 IntegerAttr &hintAttr) {
2051 StringRef hintKeyword;
2052 int64_t hint = 0;
2053 if (succeeded(parser.parseOptionalKeyword("none"))) {
2054 hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), 0);
2055 return success();
2056 }
2057 auto parseKeyword = [&]() -> ParseResult {
2058 if (failed(parser.parseKeyword(&hintKeyword)))
2059 return failure();
2060 if (hintKeyword == "uncontended")
2061 hint |= 1;
2062 else if (hintKeyword == "contended")
2063 hint |= 2;
2064 else if (hintKeyword == "nonspeculative")
2065 hint |= 4;
2066 else if (hintKeyword == "speculative")
2067 hint |= 8;
2068 else
2069 return parser.emitError(parser.getCurrentLocation())
2070 << hintKeyword << " is not a valid hint";
2071 return success();
2072 };
2073 if (parser.parseCommaSeparatedList(parseKeyword))
2074 return failure();
2075 hintAttr = IntegerAttr::get(parser.getBuilder().getI64Type(), hint);
2076 return success();
2077}
2078
2079/// Prints a Synchronization Hint clause
2081 IntegerAttr hintAttr) {
2082 int64_t hint = hintAttr.getInt();
2083
2084 if (hint == 0) {
2085 p << "none";
2086 return;
2087 }
2088
2089 // Helper function to get n-th bit from the right end of `value`
2090 auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
2091
2092 bool uncontended = bitn(hint, 0);
2093 bool contended = bitn(hint, 1);
2094 bool nonspeculative = bitn(hint, 2);
2095 bool speculative = bitn(hint, 3);
2096
2098 if (uncontended)
2099 hints.push_back("uncontended");
2100 if (contended)
2101 hints.push_back("contended");
2102 if (nonspeculative)
2103 hints.push_back("nonspeculative");
2104 if (speculative)
2105 hints.push_back("speculative");
2106
2107 llvm::interleaveComma(hints, p);
2108}
2109
2110/// Verifies a synchronization hint clause
2111static LogicalResult verifySynchronizationHint(Operation *op, uint64_t hint) {
2112
2113 // Helper function to get n-th bit from the right end of `value`
2114 auto bitn = [](int value, int n) -> bool { return value & (1 << n); };
2115
2116 bool uncontended = bitn(hint, 0);
2117 bool contended = bitn(hint, 1);
2118 bool nonspeculative = bitn(hint, 2);
2119 bool speculative = bitn(hint, 3);
2120
2121 if (uncontended && contended)
2122 return op->emitOpError() << "the hints omp_sync_hint_uncontended and "
2123 "omp_sync_hint_contended cannot be combined";
2124 if (nonspeculative && speculative)
2125 return op->emitOpError() << "the hints omp_sync_hint_nonspeculative and "
2126 "omp_sync_hint_speculative cannot be combined.";
2127 return success();
2128}
2129
2130//===----------------------------------------------------------------------===//
2131// Parser, printer and verifier for Target
2132//===----------------------------------------------------------------------===//
2133
2134// Helper function to get bitwise AND of `value` and 'flag' then return it as a
2135// boolean
2136static bool mapTypeToBool(ClauseMapFlags value, ClauseMapFlags flag) {
2137 return (value & flag) == flag;
2138}
2139
2140/// Parses a map_entries map type from a string format back into its numeric
2141/// value.
2142///
2143/// map-clause = `map_clauses ( ( `(` `always, `? `implicit, `? `ompx_hold, `?
2144/// `close, `? `present, `? ( `to` | `from` | `delete` `)` )+ `)` )
2145static ParseResult parseMapClause(OpAsmParser &parser,
2146 ClauseMapFlagsAttr &mapType) {
2147 ClauseMapFlags mapTypeBits = ClauseMapFlags::none;
2148 // This simply verifies the correct keyword is read in, the
2149 // keyword itself is stored inside of the operation
2150 auto parseTypeAndMod = [&]() -> ParseResult {
2151 StringRef mapTypeMod;
2152 if (parser.parseKeyword(&mapTypeMod))
2153 return failure();
2154
2155 if (mapTypeMod == "always")
2156 mapTypeBits |= ClauseMapFlags::always;
2157
2158 if (mapTypeMod == "implicit")
2159 mapTypeBits |= ClauseMapFlags::implicit;
2160
2161 if (mapTypeMod == "ompx_hold")
2162 mapTypeBits |= ClauseMapFlags::ompx_hold;
2163
2164 if (mapTypeMod == "close")
2165 mapTypeBits |= ClauseMapFlags::close;
2166
2167 if (mapTypeMod == "present")
2168 mapTypeBits |= ClauseMapFlags::present;
2169
2170 if (mapTypeMod == "to")
2171 mapTypeBits |= ClauseMapFlags::to;
2172
2173 if (mapTypeMod == "from")
2174 mapTypeBits |= ClauseMapFlags::from;
2175
2176 if (mapTypeMod == "tofrom")
2177 mapTypeBits |= ClauseMapFlags::to | ClauseMapFlags::from;
2178
2179 if (mapTypeMod == "delete")
2180 mapTypeBits |= ClauseMapFlags::del;
2181
2182 if (mapTypeMod == "storage")
2183 mapTypeBits |= ClauseMapFlags::storage;
2184
2185 if (mapTypeMod == "return_param")
2186 mapTypeBits |= ClauseMapFlags::return_param;
2187
2188 if (mapTypeMod == "private")
2189 mapTypeBits |= ClauseMapFlags::priv;
2190
2191 if (mapTypeMod == "literal")
2192 mapTypeBits |= ClauseMapFlags::literal;
2193
2194 if (mapTypeMod == "attach")
2195 mapTypeBits |= ClauseMapFlags::attach;
2196
2197 if (mapTypeMod == "attach_always")
2198 mapTypeBits |= ClauseMapFlags::attach_always;
2199
2200 if (mapTypeMod == "attach_never")
2201 mapTypeBits |= ClauseMapFlags::attach_never;
2202
2203 if (mapTypeMod == "attach_auto")
2204 mapTypeBits |= ClauseMapFlags::attach_auto;
2205
2206 if (mapTypeMod == "ref_ptr")
2207 mapTypeBits |= ClauseMapFlags::ref_ptr;
2208
2209 if (mapTypeMod == "ref_ptee")
2210 mapTypeBits |= ClauseMapFlags::ref_ptee;
2211
2212 if (mapTypeMod == "is_device_ptr")
2213 mapTypeBits |= ClauseMapFlags::is_device_ptr;
2214
2215 return success();
2216 };
2217
2218 if (parser.parseCommaSeparatedList(parseTypeAndMod))
2219 return failure();
2220
2221 mapType =
2222 parser.getBuilder().getAttr<mlir::omp::ClauseMapFlagsAttr>(mapTypeBits);
2223
2224 return success();
2225}
2226
2227/// Prints a map_entries map type from its numeric value out into its string
2228/// format.
2229static void printMapClause(OpAsmPrinter &p, Operation *op,
2230 ClauseMapFlagsAttr mapType) {
2232 ClauseMapFlags mapFlags = mapType.getValue();
2233
2234 // handling of always, close, present placed at the beginning of the string
2235 // to aid readability
2236 if (mapTypeToBool(mapFlags, ClauseMapFlags::always))
2237 mapTypeStrs.push_back("always");
2238 if (mapTypeToBool(mapFlags, ClauseMapFlags::implicit))
2239 mapTypeStrs.push_back("implicit");
2240 if (mapTypeToBool(mapFlags, ClauseMapFlags::ompx_hold))
2241 mapTypeStrs.push_back("ompx_hold");
2242 if (mapTypeToBool(mapFlags, ClauseMapFlags::close))
2243 mapTypeStrs.push_back("close");
2244 if (mapTypeToBool(mapFlags, ClauseMapFlags::present))
2245 mapTypeStrs.push_back("present");
2246
2247 // special handling of to/from/tofrom/delete and release/alloc, release +
2248 // alloc are the abscense of one of the other flags, whereas tofrom requires
2249 // both the to and from flag to be set.
2250 bool to = mapTypeToBool(mapFlags, ClauseMapFlags::to);
2251 bool from = mapTypeToBool(mapFlags, ClauseMapFlags::from);
2252
2253 if (to && from)
2254 mapTypeStrs.push_back("tofrom");
2255 else if (from)
2256 mapTypeStrs.push_back("from");
2257 else if (to)
2258 mapTypeStrs.push_back("to");
2259
2260 if (mapTypeToBool(mapFlags, ClauseMapFlags::del))
2261 mapTypeStrs.push_back("delete");
2262 if (mapTypeToBool(mapFlags, ClauseMapFlags::return_param))
2263 mapTypeStrs.push_back("return_param");
2264 if (mapTypeToBool(mapFlags, ClauseMapFlags::storage))
2265 mapTypeStrs.push_back("storage");
2266 if (mapTypeToBool(mapFlags, ClauseMapFlags::priv))
2267 mapTypeStrs.push_back("private");
2268 if (mapTypeToBool(mapFlags, ClauseMapFlags::literal))
2269 mapTypeStrs.push_back("literal");
2270 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach))
2271 mapTypeStrs.push_back("attach");
2272 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_always))
2273 mapTypeStrs.push_back("attach_always");
2274 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_never))
2275 mapTypeStrs.push_back("attach_never");
2276 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_auto))
2277 mapTypeStrs.push_back("attach_auto");
2278 if (mapTypeToBool(mapFlags, ClauseMapFlags::ref_ptr))
2279 mapTypeStrs.push_back("ref_ptr");
2280 if (mapTypeToBool(mapFlags, ClauseMapFlags::ref_ptee))
2281 mapTypeStrs.push_back("ref_ptee");
2282 if (mapTypeToBool(mapFlags, ClauseMapFlags::is_device_ptr))
2283 mapTypeStrs.push_back("is_device_ptr");
2284 if (mapFlags == ClauseMapFlags::none)
2285 mapTypeStrs.push_back("none");
2286
2287 for (unsigned int i = 0; i < mapTypeStrs.size(); ++i) {
2288 p << mapTypeStrs[i];
2289 if (i + 1 < mapTypeStrs.size()) {
2290 p << ", ";
2291 }
2292 }
2293}
2294
2295static ParseResult parseMembersIndex(OpAsmParser &parser,
2296 ArrayAttr &membersIdx) {
2297 SmallVector<Attribute> values, memberIdxs;
2298
2299 auto parseIndices = [&]() -> ParseResult {
2300 int64_t value;
2301 if (parser.parseInteger(value))
2302 return failure();
2303 values.push_back(IntegerAttr::get(parser.getBuilder().getIntegerType(64),
2304 APInt(64, value, /*isSigned=*/false)));
2305 return success();
2306 };
2307
2308 do {
2309 if (failed(parser.parseLSquare()))
2310 return failure();
2311
2312 if (parser.parseCommaSeparatedList(parseIndices))
2313 return failure();
2314
2315 if (failed(parser.parseRSquare()))
2316 return failure();
2317
2318 memberIdxs.push_back(ArrayAttr::get(parser.getContext(), values));
2319 values.clear();
2320 } while (succeeded(parser.parseOptionalComma()));
2321
2322 if (!memberIdxs.empty())
2323 membersIdx = ArrayAttr::get(parser.getContext(), memberIdxs);
2324
2325 return success();
2326}
2327
2328static void printMembersIndex(OpAsmPrinter &p, MapInfoOp op,
2329 ArrayAttr membersIdx) {
2330 if (!membersIdx)
2331 return;
2332
2333 llvm::interleaveComma(membersIdx, p, [&p](Attribute v) {
2334 p << "[";
2335 auto memberIdx = cast<ArrayAttr>(v);
2336 llvm::interleaveComma(memberIdx.getValue(), p, [&p](Attribute v2) {
2337 p << cast<IntegerAttr>(v2).getInt();
2338 });
2339 p << "]";
2340 });
2341}
2342
2344 VariableCaptureKindAttr mapCaptureType) {
2345 std::string typeCapStr;
2346 llvm::raw_string_ostream typeCap(typeCapStr);
2347 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::ByRef)
2348 typeCap << "ByRef";
2349 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::ByCopy)
2350 typeCap << "ByCopy";
2351 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::VLAType)
2352 typeCap << "VLAType";
2353 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::This)
2354 typeCap << "This";
2355 p << typeCapStr;
2356}
2357
2358static ParseResult parseCaptureType(OpAsmParser &parser,
2359 VariableCaptureKindAttr &mapCaptureType) {
2360 StringRef mapCaptureKey;
2361 if (parser.parseKeyword(&mapCaptureKey))
2362 return failure();
2363
2364 if (mapCaptureKey == "This")
2365 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2366 parser.getContext(), mlir::omp::VariableCaptureKind::This);
2367 if (mapCaptureKey == "ByRef")
2368 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2369 parser.getContext(), mlir::omp::VariableCaptureKind::ByRef);
2370 if (mapCaptureKey == "ByCopy")
2371 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2372 parser.getContext(), mlir::omp::VariableCaptureKind::ByCopy);
2373 if (mapCaptureKey == "VLAType")
2374 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2375 parser.getContext(), mlir::omp::VariableCaptureKind::VLAType);
2376
2377 return success();
2378}
2379
2380static LogicalResult verifyMapInfoForMapClause(
2381 Operation *op, mlir::omp::MapInfoOp mapInfoOp,
2384 &updateFromVars) {
2385 mlir::omp::ClauseMapFlags mapTypeBits = mapInfoOp.getMapType();
2386
2387 bool to = mapTypeToBool(mapTypeBits, ClauseMapFlags::to);
2388 bool from = mapTypeToBool(mapTypeBits, ClauseMapFlags::from);
2389 bool del = mapTypeToBool(mapTypeBits, ClauseMapFlags::del);
2390
2391 bool always = mapTypeToBool(mapTypeBits, ClauseMapFlags::always);
2392 bool close = mapTypeToBool(mapTypeBits, ClauseMapFlags::close);
2393 bool implicit = mapTypeToBool(mapTypeBits, ClauseMapFlags::implicit);
2394 bool attach = mapTypeToBool(mapTypeBits, ClauseMapFlags::attach);
2395
2396 if ((isa<TargetDataOp>(op) || isa<TargetOp>(op)) && del)
2397 return emitError(op->getLoc(),
2398 "to, from, tofrom and alloc map types are permitted");
2399
2400 if (isa<TargetEnterDataOp>(op) && (from || del))
2401 return emitError(op->getLoc(), "to and alloc map types are permitted");
2402
2403 if (isa<TargetExitDataOp>(op) && to)
2404 return emitError(op->getLoc(),
2405 "from, release and delete map types are permitted");
2406
2407 if (isa<TargetUpdateOp>(op)) {
2408 if (del) {
2409 return emitError(op->getLoc(),
2410 "at least one of to or from map types must be "
2411 "specified, other map types are not permitted");
2412 }
2413
2414 if (!to && !from && !attach) {
2415 return emitError(op->getLoc(),
2416 "at least one of to or from or attach map types must be "
2417 "specified, other map types are not permitted");
2418 }
2419
2420 auto updateVar = mapInfoOp.getVarPtr();
2421
2422 if ((to && from) || (to && updateFromVars.contains(updateVar)) ||
2423 (from && updateToVars.contains(updateVar))) {
2424 return emitError(
2425 op->getLoc(),
2426 "either to or from map types can be specified, not both");
2427 }
2428
2429 if (always || close || implicit) {
2430 return emitError(
2431 op->getLoc(),
2432 "present, mapper and iterator map type modifiers are permitted");
2433 }
2434
2435 // It's possible we have an attach map, in which case if there is no to
2436 // or from tied to it, we skip insertion.
2437 if (to || from) {
2438 to ? updateToVars.insert(updateVar) : updateFromVars.insert(updateVar);
2439 }
2440 }
2441
2442 if ((mapInfoOp.getVarPtrPtr() && !mapInfoOp.getVarPtrPtrType()) ||
2443 (!mapInfoOp.getVarPtrPtr() && mapInfoOp.getVarPtrPtrType())) {
2444 return emitError(op->getLoc(),
2445 "if varPtrPtr or varPtrPtrType is specified, then both "
2446 "must be present");
2447 }
2448
2449 return success();
2450}
2451
2452static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars,
2453 OperandRange mapIterated) {
2456
2457 for (auto mapOp : mapVars) {
2458 if (!mapOp.getDefiningOp())
2459 return emitError(op->getLoc(), "missing map operation");
2460
2461 if (auto mapInfoOp = mapOp.getDefiningOp<mlir::omp::MapInfoOp>()) {
2462 if (failed(verifyMapInfoForMapClause(op, mapInfoOp, updateToVars,
2463 updateFromVars)))
2464 return failure();
2465 } else if (!isa<DeclareMapperInfoOp>(op)) {
2466 return emitError(op->getLoc(),
2467 "map argument is not a map entry operation");
2468 }
2469 }
2470
2471 // Verify iterated map entries.
2472 for (auto iterVal : mapIterated) {
2473 auto iterOp = iterVal.getDefiningOp<mlir::omp::IteratorOp>();
2474 if (!iterOp)
2475 return op->emitOpError() << "'map_iterated' arguments must be defined by "
2476 "'omp.iterator' ops";
2477
2478 // Check that the iterator body yields a value defined by omp.map.info.
2479 auto yieldOp =
2480 cast<mlir::omp::YieldOp>(iterOp.getRegion().front().getTerminator());
2481 auto yieldedMapInfo =
2482 yieldOp.getResults()[0].getDefiningOp<mlir::omp::MapInfoOp>();
2483 if (!yieldedMapInfo)
2484 return op->emitOpError() << "'map_iterated' iterator body must yield "
2485 "a value defined by 'omp.map.info'";
2486
2487 if (failed(verifyMapInfoForMapClause(op, yieldedMapInfo, updateToVars,
2488 updateFromVars)))
2489 return failure();
2490 }
2491
2492 return success();
2493}
2494
2495template <typename OpType>
2496static LogicalResult verifyPrivateVarList(OpType &op);
2497
2498static LogicalResult verifyPrivateVarsMapping(TargetOp targetOp) {
2499 std::optional<DenseI64ArrayAttr> privateMapIndices =
2500 targetOp.getPrivateMapsAttr();
2501
2502 // None of the private operands are mapped.
2503 if (!privateMapIndices.has_value() || !privateMapIndices.value())
2504 return success();
2505
2506 OperandRange privateVars = targetOp.getPrivateVars();
2507
2508 if (privateMapIndices.value().size() !=
2509 static_cast<int64_t>(privateVars.size()))
2510 return emitError(targetOp.getLoc(), "sizes of `private` operand range and "
2511 "`private_maps` attribute mismatch");
2512
2513 return success();
2514}
2515
2516//===----------------------------------------------------------------------===//
2517// MapInfoOp
2518//===----------------------------------------------------------------------===//
2519
2520static LogicalResult verifyMapInfoDefinedArgs(Operation *op,
2521 StringRef clauseName,
2522 OperandRange vars) {
2523 for (Value var : vars)
2524 if (!llvm::isa_and_present<MapInfoOp>(var.getDefiningOp()))
2525 return op->emitOpError()
2526 << "'" << clauseName
2527 << "' arguments must be defined by 'omp.map.info' ops";
2528 return success();
2529}
2530
2531LogicalResult MapInfoOp::verify() {
2532 if (getMapperId() &&
2534 *this, getMapperIdAttr())) {
2535 return emitError("invalid mapper id");
2536 }
2537
2538 if (failed(verifyMapInfoDefinedArgs(*this, "members", getMembers())))
2539 return failure();
2540
2541 return success();
2542}
2543
2544//===----------------------------------------------------------------------===//
2545// TargetDataOp
2546//===----------------------------------------------------------------------===//
2547
2548void TargetDataOp::build(OpBuilder &builder, OperationState &state,
2549 const TargetDataOperands &clauses) {
2550 TargetDataOp::build(builder, state, clauses.device, clauses.ifExpr,
2551 clauses.mapVars, clauses.mapIterated,
2552 clauses.useDeviceAddrVars, clauses.useDevicePtrVars);
2553}
2554
2555LogicalResult TargetDataOp::verify() {
2556 if (getMapVars().empty() && getMapIterated().empty() &&
2557 getUseDevicePtrVars().empty() && getUseDeviceAddrVars().empty()) {
2558 return ::emitError(this->getLoc(),
2559 "At least one of map, use_device_ptr_vars, or "
2560 "use_device_addr_vars operand must be present");
2561 }
2562
2563 if (failed(verifyMapInfoDefinedArgs(*this, "use_device_ptr",
2564 getUseDevicePtrVars())))
2565 return failure();
2566
2567 if (failed(verifyMapInfoDefinedArgs(*this, "use_device_addr",
2568 getUseDeviceAddrVars())))
2569 return failure();
2570
2571 return verifyMapClause(*this, getMapVars(), getMapIterated());
2572}
2573
2574//===----------------------------------------------------------------------===//
2575// TargetEnterDataOp
2576//===----------------------------------------------------------------------===//
2577
2578void TargetEnterDataOp::build(
2579 OpBuilder &builder, OperationState &state,
2580 const TargetEnterExitUpdateDataOperands &clauses) {
2581 MLIRContext *ctx = builder.getContext();
2582 TargetEnterDataOp::build(
2583 builder, state, makeArrayAttr(ctx, clauses.dependKinds),
2584 clauses.dependVars, makeArrayAttr(ctx, clauses.dependIteratedKinds),
2585 clauses.dependIterated, clauses.device, clauses.ifExpr, clauses.mapVars,
2586 clauses.mapIterated, clauses.nowait);
2587}
2588
2589LogicalResult TargetEnterDataOp::verify() {
2590 LogicalResult verifyDependVars =
2591 verifyDependVarList(*this, getDependKinds(), getDependVars(),
2592 getDependIteratedKinds(), getDependIterated());
2593 return failed(verifyDependVars)
2594 ? verifyDependVars
2595 : verifyMapClause(*this, getMapVars(), getMapIterated());
2596}
2597
2598//===----------------------------------------------------------------------===//
2599// TargetExitDataOp
2600//===----------------------------------------------------------------------===//
2601
2602void TargetExitDataOp::build(OpBuilder &builder, OperationState &state,
2603 const TargetEnterExitUpdateDataOperands &clauses) {
2604 MLIRContext *ctx = builder.getContext();
2605 TargetExitDataOp::build(
2606 builder, state, makeArrayAttr(ctx, clauses.dependKinds),
2607 clauses.dependVars, makeArrayAttr(ctx, clauses.dependIteratedKinds),
2608 clauses.dependIterated, clauses.device, clauses.ifExpr, clauses.mapVars,
2609 clauses.mapIterated, clauses.nowait);
2610}
2611
2612LogicalResult TargetExitDataOp::verify() {
2613 LogicalResult verifyDependVars =
2614 verifyDependVarList(*this, getDependKinds(), getDependVars(),
2615 getDependIteratedKinds(), getDependIterated());
2616 return failed(verifyDependVars)
2617 ? verifyDependVars
2618 : verifyMapClause(*this, getMapVars(), getMapIterated());
2619}
2620
2621//===----------------------------------------------------------------------===//
2622// TargetUpdateOp
2623//===----------------------------------------------------------------------===//
2624
2625void TargetUpdateOp::build(OpBuilder &builder, OperationState &state,
2626 const TargetEnterExitUpdateDataOperands &clauses) {
2627 MLIRContext *ctx = builder.getContext();
2628 TargetUpdateOp::build(builder, state, makeArrayAttr(ctx, clauses.dependKinds),
2629 clauses.dependVars,
2630 makeArrayAttr(ctx, clauses.dependIteratedKinds),
2631 clauses.dependIterated, clauses.device, clauses.ifExpr,
2632 clauses.mapVars, clauses.mapIterated, clauses.nowait);
2633}
2634
2635LogicalResult TargetUpdateOp::verify() {
2636 LogicalResult verifyDependVars =
2637 verifyDependVarList(*this, getDependKinds(), getDependVars(),
2638 getDependIteratedKinds(), getDependIterated());
2639 return failed(verifyDependVars)
2640 ? verifyDependVars
2641 : verifyMapClause(*this, getMapVars(), getMapIterated());
2642}
2643
2644//===----------------------------------------------------------------------===//
2645// TargetOp
2646//===----------------------------------------------------------------------===//
2647
2648void TargetOp::build(OpBuilder &builder, OperationState &state,
2649 const TargetExtOperands &clauses) {
2650 MLIRContext *ctx = builder.getContext();
2651 // TODO Store clauses in op: allocateVars, allocatorVars.
2652 TargetOp::build(
2653 builder, state, /*allocate_vars=*/{}, /*allocator_vars=*/{},
2654 makeArrayAttr(ctx, clauses.dependKinds), clauses.dependVars,
2655 makeArrayAttr(ctx, clauses.dependIteratedKinds), clauses.dependIterated,
2656 clauses.device, clauses.dynGroupprivateAccessGroup,
2657 clauses.dynGroupprivateFallback, clauses.dynGroupprivateSize,
2658 clauses.hasDeviceAddrVars, clauses.hostEvalVars, clauses.ifExpr,
2659 clauses.inReductionVars,
2660 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),
2661 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.isDevicePtrVars,
2662 clauses.mapVars, clauses.mapIterated, clauses.nowait, clauses.privateVars,
2663 makeArrayAttr(ctx, clauses.privateSyms), clauses.privateNeedsBarrier,
2664 clauses.threadLimitVars, /*private_maps=*/nullptr, clauses.kernelType);
2665}
2666
2667bool TargetOp::hasHostEvalTripCount() {
2668 TargetExecMode mode = getKernelType();
2669 if (mode == TargetExecMode::spmd || mode == TargetExecMode::spmd_no_loop)
2670 return true;
2671
2672 if (mode == TargetExecMode::bare)
2673 return false;
2674
2675 // If it represents a `target teams distribute` construct, also evaluate the
2676 // `distribute` trip count on the host.
2677 Operation *capturedOp =
2678 cast<ComposableOpInterface>(getOperation()).findCapturedOp();
2679 if (auto loopNestOp = dyn_cast_if_present<LoopNestOp>(capturedOp)) {
2681 loopNestOp.gatherWrappers(loopWrappers);
2682
2683 LoopWrapperInterface *innermostWrapper = loopWrappers.begin();
2684 if (isa<SimdOp>(innermostWrapper))
2685 innermostWrapper = std::next(innermostWrapper);
2686
2687 auto numWrappers = std::distance(innermostWrapper, loopWrappers.end());
2688 if (numWrappers != 1)
2689 return false;
2690
2691 if (!isa<DistributeOp>(innermostWrapper))
2692 return false;
2693
2694 Operation *parentOp = innermostWrapper->getOperation()->getParentOp();
2695 if (isa_and_present<TeamsOp>(parentOp) &&
2696 parentOp->getParentOp() == getOperation())
2697 return true;
2698 }
2699
2700 return false;
2701}
2702
2703/// An `omp.target` `in_reduction` operand is captured by a `map_entries` entry
2704/// when the entry's `MapInfoOp` var_ptr is the same SSA value, or another
2705/// result of the same defining op. At this stage, exact identity can only be
2706/// required for block arguments, which have no defining op. Flang emits
2707/// `hlfir.declare` #0 for the `in_reduction` operand and #1 for the map
2708/// `var_ptr`; these collapse to the same value after lowering, but that cannot
2709/// be enforced here.
2710static bool targetInReductionCapturedBy(Value inReductionVar, Value mapVarPtr) {
2711 if (mapVarPtr == inReductionVar)
2712 return true;
2713 Operation *def = inReductionVar.getDefiningOp();
2714 return def && mapVarPtr.getDefiningOp() == def;
2715}
2716
2717LogicalResult TargetOp::verify() {
2718 if (getKernelType() == TargetExecMode::bare && !isCombined())
2719 return emitOpError() << "bare kernel requires 'omp.combined'";
2720
2721 if (failed(verifyDependVarList(*this, getDependKinds(), getDependVars(),
2722 getDependIteratedKinds(),
2723 getDependIterated())))
2724 return failure();
2725
2726 if (failed(verifyMapInfoDefinedArgs(*this, "has_device_addr",
2727 getHasDeviceAddrVars())))
2728 return failure();
2729
2730 if (failed(verifyMapClause(*this, getMapVars(), getMapIterated())))
2731 return failure();
2732
2734 *this, getDynGroupprivateAccessGroupAttr(),
2735 getDynGroupprivateFallbackAttr(), getDynGroupprivateSize())))
2736 return failure();
2737
2738 if (failed(verifyPrivateVarList(*this)))
2739 return failure();
2740
2741 if (failed(verifyReductionVarList(*this, getInReductionSyms(),
2742 getInReductionVars(),
2743 getInReductionByref())))
2744 return failure();
2745
2746 // An `in_reduction` operand on `omp.target` has no dedicated entry block
2747 // argument; inside the region it is accessed through the block argument of a
2748 // matching `map_entries` entry, and the host rewrites that map argument to
2749 // the reduction-private storage. Require every `in_reduction` operand to be
2750 // captured by at least one `map_entries` entry.
2751 for (Value inReductionVar : getInReductionVars()) {
2752 bool captured = false;
2753 for (Value mapVar : getMapVars()) {
2754 auto mapInfo = mapVar.getDefiningOp<MapInfoOp>();
2755 if (targetInReductionCapturedBy(inReductionVar, mapInfo.getVarPtr())) {
2756 captured = true;
2757 break;
2758 }
2759 }
2760 if (!captured)
2761 return emitOpError() << "in_reduction variable must be captured by a "
2762 "matching map_entries entry";
2763 }
2764
2765 return verifyPrivateVarsMapping(*this);
2766}
2767
2768LogicalResult TargetOp::verifyRegions() {
2769 auto teamsOps = getOps<TeamsOp>();
2770 auto numNestedTeams = std::distance(teamsOps.begin(), teamsOps.end());
2771 if (numNestedTeams > 1)
2772 return emitError("target containing multiple 'omp.teams' nested ops");
2773
2774 if (numNestedTeams == 0) {
2775 switch (getKernelType()) {
2776 case TargetExecMode::bare:
2777 return emitOpError()
2778 << "bare kernel must contain a nested 'omp.teams' operation";
2779 case TargetExecMode::spmd_no_loop:
2780 return emitOpError() << "spmd_no_loop kernel must contain a nested "
2781 "'omp.teams' operation";
2782 default:
2783 break;
2784 }
2785 }
2786
2787 Operation *capturedOp =
2788 cast<ComposableOpInterface>(getOperation()).findCapturedOp();
2789 if ((getKernelType() == TargetExecMode::spmd ||
2790 getKernelType() == TargetExecMode::spmd_no_loop) &&
2791 !isa_and_present<LoopNestOp>(capturedOp))
2792 return emitOpError()
2793 << "SPMD kernel must capture an 'omp.loop_nest' operation";
2794
2795 bool isTargetDevice = false;
2796 if (auto offloadMod = (*this)->getParentOfType<OffloadModuleInterface>())
2797 if (offloadMod.getIsTargetDevice())
2798 isTargetDevice = true;
2799
2800 // Check that host_eval values are only used in legal ways.
2801 llvm::ArrayRef<BlockArgument> hostEvalBlockArgs =
2802 cast<BlockArgOpenMPOpInterface>(getOperation()).getHostEvalBlockArgs();
2803
2804 bool hostEvalTripCount = hasHostEvalTripCount();
2805 for (Value hostEvalArg : hostEvalBlockArgs) {
2806 for (Operation *user : hostEvalArg.getUsers()) {
2807 if (auto teamsOp = dyn_cast<TeamsOp>(user)) {
2808 // Check if used in num_teams_lower or any of num_teams_upper_vars
2809 if (hostEvalArg == teamsOp.getNumTeamsLower() ||
2810 llvm::is_contained(teamsOp.getNumTeamsUpperVars(), hostEvalArg) ||
2811 llvm::is_contained(teamsOp.getThreadLimitVars(), hostEvalArg))
2812 continue;
2813
2814 return emitOpError() << "host_eval argument only legal as 'num_teams' "
2815 "and 'thread_limit' in 'omp.teams'";
2816 }
2817 if (auto parallelOp = dyn_cast<ParallelOp>(user)) {
2818 if (llvm::is_contained(parallelOp.getNumThreadsVars(), hostEvalArg))
2819 continue;
2820
2821 return emitOpError()
2822 << "host_eval argument only legal as 'num_threads' in "
2823 "'omp.parallel'";
2824 }
2825 if (auto loopNestOp = dyn_cast<LoopNestOp>(user)) {
2826 if (hostEvalTripCount &&
2827 (llvm::is_contained(loopNestOp.getLoopLowerBounds(), hostEvalArg) ||
2828 llvm::is_contained(loopNestOp.getLoopUpperBounds(), hostEvalArg) ||
2829 llvm::is_contained(loopNestOp.getLoopSteps(), hostEvalArg)))
2830 continue;
2831
2832 return emitOpError() << "host_eval argument only legal as loop bounds "
2833 "and steps in 'omp.loop_nest' when trip count "
2834 "must be evaluated in the host";
2835 }
2836
2837 return emitOpError() << "host_eval argument illegal use in '"
2838 << user->getName() << "' operation";
2839 }
2840 }
2841
2842 if (hostEvalTripCount && !isTargetDevice) {
2843 auto loopOp = cast<LoopNestOp>(capturedOp);
2844 for (auto arg : llvm::concat<Value>(loopOp.getLoopLowerBounds(),
2845 loopOp.getLoopUpperBounds(),
2846 loopOp.getLoopSteps())) {
2847 if (!llvm::is_contained(hostEvalBlockArgs, arg))
2848 return emitOpError() << "nested 'omp.loop_nest' bounds expected to "
2849 "be host-evaluated";
2850 }
2851 }
2852
2853 return success();
2854}
2855
2856//===----------------------------------------------------------------------===//
2857// ParallelOp
2858//===----------------------------------------------------------------------===//
2859
2860void ParallelOp::build(OpBuilder &builder, OperationState &state,
2861 ArrayRef<NamedAttribute> attributes) {
2862 ParallelOp::build(builder, state, /*allocate_vars=*/ValueRange(),
2863 /*allocator_vars=*/ValueRange(), /*if_expr=*/nullptr,
2864 /*num_threads_vars=*/ValueRange(),
2865 /*private_vars=*/ValueRange(),
2866 /*private_syms=*/nullptr, /*private_needs_barrier=*/nullptr,
2867 /*proc_bind_kind=*/nullptr,
2868 /*reduction_mod =*/nullptr, /*reduction_vars=*/ValueRange(),
2869 /*reduction_byref=*/nullptr, /*reduction_syms=*/nullptr);
2870 state.addAttributes(attributes);
2871}
2872
2873void ParallelOp::build(OpBuilder &builder, OperationState &state,
2874 const ParallelOperands &clauses) {
2875 MLIRContext *ctx = builder.getContext();
2876 ParallelOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
2877 clauses.ifExpr, clauses.numThreadsVars, clauses.privateVars,
2878 makeArrayAttr(ctx, clauses.privateSyms),
2879 clauses.privateNeedsBarrier, clauses.procBindKind,
2880 clauses.reductionMod, clauses.reductionVars,
2881 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
2882 makeArrayAttr(ctx, clauses.reductionSyms));
2883}
2884
2885template <typename OpType>
2886static LogicalResult verifyPrivateVarList(OpType &op) {
2887 auto privateVars = op.getPrivateVars();
2888 auto privateSyms = op.getPrivateSymsAttr();
2889
2890 if (privateVars.empty() && (privateSyms == nullptr || privateSyms.empty()))
2891 return success();
2892
2893 auto numPrivateVars = privateVars.size();
2894 auto numPrivateSyms = (privateSyms == nullptr) ? 0 : privateSyms.size();
2895
2896 if (numPrivateVars != numPrivateSyms)
2897 return op.emitError() << "inconsistent number of private variables and "
2898 "privatizer op symbols, private vars: "
2899 << numPrivateVars
2900 << " vs. privatizer op symbols: " << numPrivateSyms;
2901
2902 for (auto privateVarInfo : llvm::zip_equal(privateVars, privateSyms)) {
2903 Type varType = std::get<0>(privateVarInfo).getType();
2904 SymbolRefAttr privateSym = cast<SymbolRefAttr>(std::get<1>(privateVarInfo));
2905 PrivateClauseOp privatizerOp =
2907
2908 if (privatizerOp == nullptr)
2909 return op.emitError() << "failed to lookup privatizer op with symbol: '"
2910 << privateSym << "'";
2911
2912 Type privatizerType = privatizerOp.getArgType();
2913
2914 if (privatizerType && (varType != privatizerType))
2915 return op.emitError()
2916 << "type mismatch between a "
2917 << (privatizerOp.getDataSharingType() ==
2918 DataSharingClauseType::Private
2919 ? "private"
2920 : "firstprivate")
2921 << " variable and its privatizer op, var type: " << varType
2922 << " vs. privatizer op type: " << privatizerType;
2923 }
2924
2925 return success();
2926}
2927
2928LogicalResult ParallelOp::verify() {
2929 if (getAllocateVars().size() != getAllocatorVars().size())
2930 return emitError(
2931 "expected equal sizes for allocate and allocator variables");
2932
2933 if (failed(verifyPrivateVarList(*this)))
2934 return failure();
2935
2936 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
2937 getReductionByref());
2938}
2939
2940LogicalResult ParallelOp::verifyRegions() {
2941 auto distChildOps = getOps<DistributeOp>();
2942 int numDistChildOps = std::distance(distChildOps.begin(), distChildOps.end());
2943 if (numDistChildOps > 1)
2944 return emitError()
2945 << "multiple 'omp.distribute' nested inside of 'omp.parallel'";
2946
2947 if (numDistChildOps == 1) {
2948 if (!isComposite())
2949 return emitError()
2950 << "'omp.composite' attribute missing from composite operation";
2951
2952 auto *ompDialect = getContext()->getLoadedDialect<OpenMPDialect>();
2953 Operation &distributeOp = **distChildOps.begin();
2954 for (Operation &childOp : getOps()) {
2955 if (&childOp == &distributeOp || ompDialect != childOp.getDialect())
2956 continue;
2957
2958 if (!childOp.hasTrait<OpTrait::IsTerminator>())
2959 return emitError() << "unexpected OpenMP operation inside of composite "
2960 "'omp.parallel': "
2961 << childOp.getName();
2962 }
2963 } else if (isComposite()) {
2964 return emitError()
2965 << "'omp.composite' attribute present in non-composite operation";
2966 }
2967 return success();
2968}
2969
2970//===----------------------------------------------------------------------===//
2971// TeamsOp
2972//===----------------------------------------------------------------------===//
2973
2975 while ((op = op->getParentOp()))
2976 if (isa<OpenMPDialect>(op->getDialect()))
2977 return false;
2978 return true;
2979}
2980
2981void TeamsOp::build(OpBuilder &builder, OperationState &state,
2982 const TeamsOperands &clauses) {
2983 MLIRContext *ctx = builder.getContext();
2984 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier
2985 TeamsOp::build(
2986 builder, state, clauses.allocateVars, clauses.allocatorVars,
2987 clauses.dynGroupprivateAccessGroup, clauses.dynGroupprivateFallback,
2988 clauses.dynGroupprivateSize, clauses.ifExpr, clauses.numTeamsLower,
2989 clauses.numTeamsUpperVars, /*private_vars=*/{}, /*private_syms=*/nullptr,
2990 /*private_needs_barrier=*/nullptr, clauses.reductionMod,
2991 clauses.reductionVars,
2992 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
2993 makeArrayAttr(ctx, clauses.reductionSyms), clauses.threadLimitVars);
2994}
2995
2996// Verify num_teams clause
2997static LogicalResult verifyNumTeamsClause(Operation *op, Value numTeamsLower,
2998 OperandRange numTeamsUpperVars) {
2999 // If lower is specified, upper must have exactly one value
3000 if (numTeamsLower) {
3001 if (numTeamsUpperVars.size() != 1)
3002 return op->emitError(
3003 "expected exactly one num_teams upper bound when lower bound is "
3004 "specified");
3005 if (numTeamsLower.getType() != numTeamsUpperVars[0].getType())
3006 return op->emitError(
3007 "expected num_teams upper bound and lower bound to be "
3008 "the same type");
3009 }
3010
3011 return success();
3012}
3013
3014LogicalResult TeamsOp::verify() {
3015 // Check parent region
3016 // TODO If nested inside of a target region, also check that it does not
3017 // contain any statements, declarations or directives other than this
3018 // omp.teams construct. The issue is how to support the initialization of
3019 // this operation's own arguments (allow SSA values across omp.target?).
3020 Operation *op = getOperation();
3021 auto parentTarget = llvm::dyn_cast_if_present<TargetOp>(op->getParentOp());
3022 if (!parentTarget && !opInGlobalImplicitParallelRegion(op))
3023 return emitError("expected to be nested inside of omp.target or not nested "
3024 "in any OpenMP dialect operations");
3025
3026 // Check for num_teams clause restrictions
3027 if (failed(verifyNumTeamsClause(op, this->getNumTeamsLower(),
3028 this->getNumTeamsUpperVars())))
3029 return failure();
3030
3031 if (parentTarget &&
3032 parentTarget.getKernelType() == TargetExecMode::spmd_no_loop &&
3033 (getNumTeamsLower() || !getNumTeamsUpperVars().empty()))
3034 return emitOpError() << "'num_teams' not allowed in SPMD-no-loop kernels";
3035
3036 // Check for allocate clause restrictions
3037 if (getAllocateVars().size() != getAllocatorVars().size())
3038 return emitError(
3039 "expected equal sizes for allocate and allocator variables");
3040
3042 op, getDynGroupprivateAccessGroupAttr(),
3043 getDynGroupprivateFallbackAttr(), getDynGroupprivateSize())))
3044 return failure();
3045
3046 if (failed(verifyPrivateVarList(*this)))
3047 return failure();
3048
3049 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3050 getReductionByref());
3051}
3052
3053//===----------------------------------------------------------------------===//
3054// SectionOp
3055//===----------------------------------------------------------------------===//
3056
3057OperandRange SectionOp::getPrivateVars() {
3058 return getParentOp().getPrivateVars();
3059}
3060
3061OperandRange SectionOp::getReductionVars() {
3062 return getParentOp().getReductionVars();
3063}
3064
3065//===----------------------------------------------------------------------===//
3066// SectionsOp
3067//===----------------------------------------------------------------------===//
3068
3069void SectionsOp::build(OpBuilder &builder, OperationState &state,
3070 const SectionsOperands &clauses) {
3071 MLIRContext *ctx = builder.getContext();
3072 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier
3073 SectionsOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
3074 clauses.nowait, /*private_vars=*/{},
3075 /*private_syms=*/nullptr, /*private_needs_barrier=*/nullptr,
3076 clauses.reductionMod, clauses.reductionVars,
3077 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3078 makeArrayAttr(ctx, clauses.reductionSyms));
3079}
3080
3081LogicalResult SectionsOp::verify() {
3082 if (isCombined())
3083 return emitOpError() << "cannot be a non-innermost combined construct leaf";
3084
3085 if (getAllocateVars().size() != getAllocatorVars().size())
3086 return emitError(
3087 "expected equal sizes for allocate and allocator variables");
3088
3089 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3090 getReductionByref());
3091}
3092
3093LogicalResult SectionsOp::verifyRegions() {
3094 for (auto &inst : *getRegion().begin()) {
3095 if (!(isa<SectionOp>(inst) || isa<TerminatorOp>(inst))) {
3096 return emitOpError()
3097 << "expected omp.section op or terminator op inside region";
3098 }
3099 }
3100
3101 return success();
3102}
3103
3104//===----------------------------------------------------------------------===//
3105// ScopeOp
3106//===----------------------------------------------------------------------===//
3107
3108void ScopeOp::build(OpBuilder &builder, OperationState &state,
3109 const ScopeOperands &clauses) {
3110 MLIRContext *ctx = builder.getContext();
3111 ScopeOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
3112 clauses.nowait, clauses.privateVars,
3113 makeArrayAttr(ctx, clauses.privateSyms),
3114 clauses.privateNeedsBarrier, clauses.reductionMod,
3115 clauses.reductionVars,
3116 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3117 makeArrayAttr(ctx, clauses.reductionSyms));
3118}
3119
3120LogicalResult ScopeOp::verify() {
3121 if (getAllocateVars().size() != getAllocatorVars().size())
3122 return emitError(
3123 "expected equal sizes for allocate and allocator variables");
3124
3125 if (failed(verifyPrivateVarList(*this)))
3126 return failure();
3127
3128 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3129 getReductionByref());
3130}
3131
3132//===----------------------------------------------------------------------===//
3133// SingleOp
3134//===----------------------------------------------------------------------===//
3135
3136void SingleOp::build(OpBuilder &builder, OperationState &state,
3137 const SingleOperands &clauses) {
3138 MLIRContext *ctx = builder.getContext();
3139 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier
3140 SingleOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
3141 clauses.copyprivateVars,
3142 makeArrayAttr(ctx, clauses.copyprivateSyms), clauses.nowait,
3143 /*private_vars=*/{}, /*private_syms=*/nullptr,
3144 /*private_needs_barrier=*/nullptr);
3145}
3146
3147LogicalResult SingleOp::verify() {
3148 // Check for allocate clause restrictions
3149 if (getAllocateVars().size() != getAllocatorVars().size())
3150 return emitError(
3151 "expected equal sizes for allocate and allocator variables");
3152
3153 return verifyCopyprivateVarList(*this, getCopyprivateVars(),
3154 getCopyprivateSyms());
3155}
3156
3157//===----------------------------------------------------------------------===//
3158// WorkshareOp
3159//===----------------------------------------------------------------------===//
3160
3161void WorkshareOp::build(OpBuilder &builder, OperationState &state,
3162 const WorkshareOperands &clauses) {
3163 WorkshareOp::build(builder, state, clauses.nowait);
3164}
3165
3166LogicalResult WorkshareOp::verify() {
3167 if (isCombined())
3168 return emitOpError() << "cannot be a non-innermost combined construct leaf";
3169
3170 return success();
3171}
3172
3173//===----------------------------------------------------------------------===//
3174// WorkshareLoopWrapperOp
3175//===----------------------------------------------------------------------===//
3176
3177LogicalResult WorkshareLoopWrapperOp::verifyRegions() {
3178 if (isa_and_nonnull<LoopWrapperInterface>((*this)->getParentOp()) ||
3179 getNestedWrapper())
3180 return emitOpError() << "expected to be a standalone loop wrapper";
3181
3182 return success();
3183}
3184
3185//===----------------------------------------------------------------------===//
3186// LoopWrapperInterface
3187//===----------------------------------------------------------------------===//
3188
3189LogicalResult LoopWrapperInterface::verifyImpl() {
3190 Operation *op = this->getOperation();
3191 if (!op->hasTrait<OpTrait::NoTerminator>() ||
3193 return emitOpError() << "loop wrapper must also have the `NoTerminator` "
3194 "and `SingleBlock` traits";
3195
3196 if (op->getNumRegions() != 1)
3197 return emitOpError() << "loop wrapper does not contain exactly one region";
3198
3199 Region &region = op->getRegion(0);
3200 if (range_size(region.getOps()) != 1)
3201 return emitOpError()
3202 << "loop wrapper does not contain exactly one nested op";
3203
3204 Operation &firstOp = *region.op_begin();
3205 if (!isa<LoopNestOp, LoopWrapperInterface>(firstOp))
3206 return emitOpError() << "nested in loop wrapper is not another loop "
3207 "wrapper or `omp.loop_nest`";
3208
3209 return success();
3210}
3211
3212//===----------------------------------------------------------------------===//
3213// ComposableOpInterface
3214//===----------------------------------------------------------------------===//
3215
3216Operation *ComposableOpInterface::findCapturedOp() {
3217 Operation *op = this->getOperation();
3218
3219 // Handle the composite case by returning the wrapped omp.loop_nest.
3220 if (auto wrapperOp = dyn_cast<LoopWrapperInterface>(op))
3221 return wrapperOp.getWrappedLoop();
3222
3223 // Do not look further if this op is not combined with any of its children.
3224 // Need to check for composite for the omp.parallel case, which is not a loop
3225 // wrapper itself.
3226 if (!isCombined() && !isComposite())
3227 return op;
3228
3229 Region &region = op->getRegion(0);
3230 for (Operation &nestedOp : region.getOps()) {
3231 if (auto wrapperOp = dyn_cast<LoopWrapperInterface>(&nestedOp))
3232 return wrapperOp.getWrappedLoop();
3233
3234 if (auto composableOp = dyn_cast<ComposableOpInterface>(&nestedOp))
3235 return composableOp.findCapturedOp();
3236 }
3237
3238 // This can only be reached if the op has an omp.combined attribute but the
3239 // corresponding nested composable op has been deleted. In that case, it's
3240 // correct to return this operation.
3241 return op;
3242}
3243
3244LogicalResult ComposableOpInterface::verifyImpl() {
3245 Operation *op = this->getOperation();
3246
3247 if (op->getNumRegions() != 1)
3248 return emitOpError() << "composable ops must have a single region";
3249
3250 if (isComposite() && !isa<LoopWrapperInterface, ParallelOp>(op))
3251 return emitOpError() << "non-loop wrapper cannot be composite";
3252
3253 // If combined, must have exactly one eligible nested op (composable or loop
3254 // wrapper).
3255 if (isCombined()) {
3256 Operation *nestedOp = nullptr;
3257 auto count = llvm::count_if(
3258 op->getRegion(0).getOps(), [&nestedOp](mlir::Operation &op) {
3259 if (isa<ComposableOpInterface, LoopWrapperInterface>(op)) {
3260 nestedOp = &op;
3261 return true;
3262 }
3263 return false;
3264 });
3265
3266 // Make an exception for ops marked as omp.combined with no eligible nested
3267 // ops: this situation should be disallowed, but it can be reached if an
3268 // MLIR optimization pass find that the child operation has no side effects
3269 // (many ComposableOpInterface ops have RecursiveMemoryEffects), so it gets
3270 // deleted without updating the parent's attribute.
3271 //
3272 // Since there's a well defined way of handling that situation (treat it as
3273 // non-combined), we relax the requirement here. Ensuring the parent is
3274 // updated every time a pass that can potentially remove a child composable
3275 // op runs is less preferable as a solution.
3276 if (count == 0)
3277 return success();
3278
3279 if (count > 1)
3280 return emitOpError()
3281 << "multiple eligible child ops found in combined op";
3282
3283 // This operation cannot be combined if its captured nested op can be
3284 // executed more than once (i.e. its block's successors can reach it) or if
3285 // it's not guaranteed to be executed before all exits of the region (i.e.
3286 // it doesn't dominate all blocks with no successors reachable from the
3287 // entry block).
3288 DominanceInfo domInfo;
3289 Block *parentBlock = nestedOp->getBlock();
3290
3291 for (Block *successor : parentBlock->getSuccessors())
3292 if (successor->isReachable(parentBlock))
3293 return emitOpError() << "nested combined child op is part of a loop";
3294
3295 for (Block &block : op->getRegion(0))
3296 if (domInfo.isReachableFromEntry(&block) && block.hasNoSuccessors() &&
3297 !domInfo.dominates(parentBlock, &block))
3298 return emitOpError()
3299 << "nested combined child op doesn't unconditionally execute";
3300 }
3301 return success();
3302}
3303
3304//===----------------------------------------------------------------------===//
3305// LoopOp
3306//===----------------------------------------------------------------------===//
3307
3308void LoopOp::build(OpBuilder &builder, OperationState &state,
3309 const LoopOperands &clauses) {
3310 MLIRContext *ctx = builder.getContext();
3311
3312 LoopOp::build(builder, state, clauses.bindKind, clauses.privateVars,
3313 makeArrayAttr(ctx, clauses.privateSyms),
3314 clauses.privateNeedsBarrier, clauses.order, clauses.orderMod,
3315 clauses.reductionMod, clauses.reductionVars,
3316 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3317 makeArrayAttr(ctx, clauses.reductionSyms));
3318}
3319
3320LogicalResult LoopOp::verify() {
3321 if (failed(verifyPrivateVarList(*this)))
3322 return failure();
3323
3324 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3325 getReductionByref());
3326}
3327
3328LogicalResult LoopOp::verifyRegions() {
3329 if (llvm::isa_and_nonnull<LoopWrapperInterface>((*this)->getParentOp()) ||
3330 getNestedWrapper())
3331 return emitOpError() << "expected to be a standalone loop wrapper";
3332
3333 return success();
3334}
3335
3336//===----------------------------------------------------------------------===//
3337// WsloopOp
3338//===----------------------------------------------------------------------===//
3339
3340void WsloopOp::build(OpBuilder &builder, OperationState &state,
3341 ArrayRef<NamedAttribute> attributes) {
3342 build(builder, state, /*allocate_vars=*/{}, /*allocator_vars=*/{},
3343 /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(),
3344 /*linear_var_types*/ nullptr, /*linear_modifiers=*/nullptr,
3345 /*nowait=*/false, /*order=*/nullptr, /*order_mod=*/nullptr,
3346 /*ordered=*/nullptr, /*private_vars=*/{}, /*private_syms=*/nullptr,
3347 /*private_needs_barrier=*/false,
3348 /*reduction_mod=*/nullptr, /*reduction_vars=*/ValueRange(),
3349 /*reduction_byref=*/nullptr,
3350 /*reduction_syms=*/nullptr, /*schedule_kind=*/nullptr,
3351 /*schedule_chunk=*/nullptr, /*schedule_mod=*/nullptr,
3352 /*schedule_simd=*/false);
3353 state.addAttributes(attributes);
3354}
3355
3356void WsloopOp::build(OpBuilder &builder, OperationState &state,
3357 const WsloopOperands &clauses) {
3358 MLIRContext *ctx = builder.getContext();
3359 // TODO: Store clauses in op: allocateVars, allocatorVars
3360 WsloopOp::build(
3361 builder, state,
3362 /*allocate_vars=*/{}, /*allocator_vars=*/{}, clauses.linearVars,
3363 clauses.linearStepVars, clauses.linearVarTypes, clauses.linearModifiers,
3364 clauses.nowait, clauses.order, clauses.orderMod, clauses.ordered,
3365 clauses.privateVars, makeArrayAttr(ctx, clauses.privateSyms),
3366 clauses.privateNeedsBarrier, clauses.reductionMod, clauses.reductionVars,
3367 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3368 makeArrayAttr(ctx, clauses.reductionSyms), clauses.scheduleKind,
3369 clauses.scheduleChunk, clauses.scheduleMod, clauses.scheduleSimd);
3370}
3371
3372LogicalResult WsloopOp::verify() {
3373 if (failed(
3374 verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars())))
3375 return failure();
3376 if (getLinearVars().size() &&
3377 getLinearVarTypes().value().size() != getLinearVars().size())
3378 return emitError() << "Ill-formed type attributes for linear variables";
3379
3380 if (failed(verifyPrivateVarList(*this)))
3381 return failure();
3382
3383 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3384 getReductionByref());
3385}
3386
3387LogicalResult WsloopOp::verifyRegions() {
3388 bool isCompositeChildLeaf =
3389 llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
3390
3391 if (LoopWrapperInterface nested = getNestedWrapper()) {
3392 if (!isComposite())
3393 return emitError()
3394 << "'omp.composite' attribute missing from composite wrapper";
3395
3396 // Check for the allowed leaf constructs that may appear in a composite
3397 // construct directly after DO/FOR.
3398 if (!isa<SimdOp>(nested))
3399 return emitError() << "only supported nested wrapper is 'omp.simd'";
3400
3401 } else if (isComposite() && !isCompositeChildLeaf) {
3402 return emitError()
3403 << "'omp.composite' attribute present in non-composite wrapper";
3404 } else if (!isComposite() && isCompositeChildLeaf) {
3405 return emitError()
3406 << "'omp.composite' attribute missing from composite wrapper";
3407 }
3408
3409 return success();
3410}
3411
3412//===----------------------------------------------------------------------===//
3413// Simd construct [2.9.3.1]
3414//===----------------------------------------------------------------------===//
3415
3416void SimdOp::build(OpBuilder &builder, OperationState &state,
3417 const SimdOperands &clauses) {
3418 MLIRContext *ctx = builder.getContext();
3419 SimdOp::build(builder, state, clauses.alignedVars,
3420 makeArrayAttr(ctx, clauses.alignments), clauses.ifExpr,
3421 clauses.linearVars, clauses.linearStepVars,
3422 clauses.linearVarTypes, clauses.linearModifiers,
3423 clauses.nontemporalVars, clauses.order, clauses.orderMod,
3424 clauses.privateVars, makeArrayAttr(ctx, clauses.privateSyms),
3425 clauses.privateNeedsBarrier, clauses.reductionMod,
3426 clauses.reductionVars,
3427 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3428 makeArrayAttr(ctx, clauses.reductionSyms), clauses.safelen,
3429 clauses.simdlen);
3430}
3431
3432LogicalResult SimdOp::verify() {
3433 if (getSimdlen().has_value() && getSafelen().has_value() &&
3434 getSimdlen().value() > getSafelen().value())
3435 return emitOpError()
3436 << "simdlen clause and safelen clause are both present, but the "
3437 "simdlen value is not less than or equal to safelen value";
3438
3439 if (verifyAlignedClause(*this, getAlignments(), getAlignedVars()).failed())
3440 return failure();
3441
3442 if (verifyNontemporalClause(*this, getNontemporalVars()).failed())
3443 return failure();
3444
3445 if (failed(
3446 verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars())))
3447 return failure();
3448
3449 bool isCompositeChildLeaf =
3450 llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
3451
3452 if (!isComposite() && isCompositeChildLeaf)
3453 return emitError()
3454 << "'omp.composite' attribute missing from composite wrapper";
3455
3456 if (isComposite() && !isCompositeChildLeaf)
3457 return emitError()
3458 << "'omp.composite' attribute present in non-composite wrapper";
3459
3460 // Firstprivate is not allowed for SIMD in the standard. Check that none of
3461 // the private decls are for firstprivate.
3462 std::optional<ArrayAttr> privateSyms = getPrivateSyms();
3463 if (privateSyms) {
3464 for (const Attribute &sym : *privateSyms) {
3465 auto symRef = cast<SymbolRefAttr>(sym);
3466 omp::PrivateClauseOp privatizer =
3468 getOperation(), symRef);
3469 if (!privatizer)
3470 return emitError() << "Cannot find privatizer '" << symRef << "'";
3471 if (privatizer.getDataSharingType() ==
3472 DataSharingClauseType::FirstPrivate)
3473 return emitError() << "FIRSTPRIVATE cannot be used with SIMD";
3474 }
3475 }
3476
3477 if (failed(verifyPrivateVarList(*this)))
3478 return failure();
3479
3480 if (getLinearVars().size() &&
3481 getLinearVarTypes().value().size() != getLinearVars().size())
3482 return emitError() << "Ill-formed type attributes for linear variables";
3483 return success();
3484}
3485
3486LogicalResult SimdOp::verifyRegions() {
3487 if (getNestedWrapper())
3488 return emitOpError() << "must wrap an 'omp.loop_nest' directly";
3489
3490 return success();
3491}
3492
3493//===----------------------------------------------------------------------===//
3494// Distribute construct [2.9.4.1]
3495//===----------------------------------------------------------------------===//
3496
3497void DistributeOp::build(OpBuilder &builder, OperationState &state,
3498 const DistributeOperands &clauses) {
3499 DistributeOp::build(builder, state, clauses.allocateVars,
3500 clauses.allocatorVars, clauses.distScheduleStatic,
3501 clauses.distScheduleChunkSize, clauses.order,
3502 clauses.orderMod, clauses.privateVars,
3503 makeArrayAttr(builder.getContext(), clauses.privateSyms),
3504 clauses.privateNeedsBarrier);
3505}
3506
3507LogicalResult DistributeOp::verify() {
3508 if (this->getDistScheduleChunkSize() && !this->getDistScheduleStatic())
3509 return emitOpError() << "chunk size set without "
3510 "dist_schedule_static being present";
3511
3512 if (getAllocateVars().size() != getAllocatorVars().size())
3513 return emitError(
3514 "expected equal sizes for allocate and allocator variables");
3515
3516 if (failed(verifyPrivateVarList(*this)))
3517 return failure();
3518
3519 return success();
3520}
3521
3522LogicalResult DistributeOp::verifyRegions() {
3523 if (LoopWrapperInterface nested = getNestedWrapper()) {
3524 if (!isComposite())
3525 return emitError()
3526 << "'omp.composite' attribute missing from composite wrapper";
3527 // Check for the allowed leaf constructs that may appear in a composite
3528 // construct directly after DISTRIBUTE.
3529 if (isa<WsloopOp>(nested)) {
3530 Operation *parentOp = (*this)->getParentOp();
3531 if (!llvm::dyn_cast_if_present<ParallelOp>(parentOp) ||
3532 !cast<ComposableOpInterface>(parentOp).isComposite()) {
3533 return emitError() << "an 'omp.wsloop' nested wrapper is only allowed "
3534 "when a composite 'omp.parallel' is the direct "
3535 "parent";
3536 }
3537 } else if (!isa<SimdOp>(nested))
3538 return emitError() << "only supported nested wrappers are 'omp.simd' and "
3539 "'omp.wsloop'";
3540 } else if (isComposite()) {
3541 return emitError()
3542 << "'omp.composite' attribute present in non-composite wrapper";
3543 }
3544
3545 return success();
3546}
3547
3548//===----------------------------------------------------------------------===//
3549// DeclareMapperOp / DeclareMapperInfoOp
3550//===----------------------------------------------------------------------===//
3551
3552void DeclareMapperInfoOp::build(OpBuilder &builder, OperationState &state,
3553 const DeclareMapperInfoOperands &clauses) {
3554 DeclareMapperInfoOp::build(builder, state, clauses.mapVars,
3555 clauses.mapIterated);
3556}
3557
3558LogicalResult DeclareMapperInfoOp::verify() {
3559 return verifyMapClause(*this, getMapVars(), getMapIterated());
3560}
3561
3562LogicalResult DeclareMapperOp::verifyRegions() {
3563 if (!llvm::isa_and_present<DeclareMapperInfoOp>(
3564 getRegion().getBlocks().front().getTerminator()))
3565 return emitOpError() << "expected terminator to be a DeclareMapperInfoOp";
3566
3567 return success();
3568}
3569
3570//===----------------------------------------------------------------------===//
3571// DeclareReductionOp
3572//===----------------------------------------------------------------------===//
3573
3574LogicalResult DeclareReductionOp::verifyRegions() {
3575 if (!getAllocRegion().empty()) {
3576 for (YieldOp yieldOp : getAllocRegion().getOps<YieldOp>()) {
3577 if (yieldOp.getResults().size() != 1 ||
3578 yieldOp.getResults().getTypes()[0] != getType())
3579 return emitOpError() << "expects alloc region to yield a value "
3580 "of the reduction type";
3581 }
3582 }
3583
3584 if (getInitializerRegion().empty())
3585 return emitOpError() << "expects non-empty initializer region";
3586 Block &initializerEntryBlock = getInitializerRegion().front();
3587
3588 if (initializerEntryBlock.getNumArguments() == 1) {
3589 if (!getAllocRegion().empty())
3590 return emitOpError() << "expects two arguments to the initializer region "
3591 "when an allocation region is used";
3592 } else if (initializerEntryBlock.getNumArguments() == 2) {
3593 if (getAllocRegion().empty())
3594 return emitOpError() << "expects one argument to the initializer region "
3595 "when no allocation region is used";
3596 } else {
3597 return emitOpError()
3598 << "expects one or two arguments to the initializer region";
3599 }
3600
3601 for (mlir::Value arg : initializerEntryBlock.getArguments())
3602 if (arg.getType() != getType())
3603 return emitOpError() << "expects initializer region argument to match "
3604 "the reduction type";
3605
3606 for (YieldOp yieldOp : getInitializerRegion().getOps<YieldOp>()) {
3607 if (yieldOp.getResults().size() != 1 ||
3608 yieldOp.getResults().getTypes()[0] != getType())
3609 return emitOpError() << "expects initializer region to yield a value "
3610 "of the reduction type";
3611 }
3612
3613 if (getReductionRegion().empty())
3614 return emitOpError() << "expects non-empty reduction region";
3615 Block &reductionEntryBlock = getReductionRegion().front();
3616 if (reductionEntryBlock.getNumArguments() != 2 ||
3617 reductionEntryBlock.getArgumentTypes()[0] !=
3618 reductionEntryBlock.getArgumentTypes()[1] ||
3619 reductionEntryBlock.getArgumentTypes()[0] != getType())
3620 return emitOpError() << "expects reduction region with two arguments of "
3621 "the reduction type";
3622 for (YieldOp yieldOp : getReductionRegion().getOps<YieldOp>()) {
3623 if (yieldOp.getResults().size() != 1 ||
3624 yieldOp.getResults().getTypes()[0] != getType())
3625 return emitOpError() << "expects reduction region to yield a value "
3626 "of the reduction type";
3627 }
3628
3629 if (!getAtomicReductionRegion().empty()) {
3630 Block &atomicReductionEntryBlock = getAtomicReductionRegion().front();
3631 if (atomicReductionEntryBlock.getNumArguments() != 2 ||
3632 atomicReductionEntryBlock.getArgumentTypes()[0] !=
3633 atomicReductionEntryBlock.getArgumentTypes()[1])
3634 return emitOpError() << "expects atomic reduction region with two "
3635 "arguments of the same type";
3636 auto ptrType = llvm::dyn_cast<PointerLikeType>(
3637 atomicReductionEntryBlock.getArgumentTypes()[0]);
3638 if (!ptrType ||
3639 (ptrType.getElementType() && ptrType.getElementType() != getType()))
3640 return emitOpError() << "expects atomic reduction region arguments to "
3641 "be accumulators containing the reduction type";
3642 }
3643
3644 if (getCleanupRegion().empty())
3645 return success();
3646 Block &cleanupEntryBlock = getCleanupRegion().front();
3647 if (cleanupEntryBlock.getNumArguments() != 1 ||
3648 cleanupEntryBlock.getArgument(0).getType() != getType())
3649 return emitOpError() << "expects cleanup region with one argument "
3650 "of the reduction type";
3651
3652 return success();
3653}
3654
3655//===----------------------------------------------------------------------===//
3656// TaskOp
3657//===----------------------------------------------------------------------===//
3658
3659void TaskOp::build(OpBuilder &builder, OperationState &state,
3660 const TaskOperands &clauses) {
3661 MLIRContext *ctx = builder.getContext();
3662 TaskOp::build(
3663 builder, state, clauses.iterated, clauses.affinityVars,
3664 clauses.allocateVars, clauses.allocatorVars,
3665 makeArrayAttr(ctx, clauses.dependKinds), clauses.dependVars,
3666 makeArrayAttr(ctx, clauses.dependIteratedKinds), clauses.dependIterated,
3667 clauses.final, clauses.ifExpr, clauses.inReductionVars,
3668 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),
3669 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.mergeable,
3670 clauses.priority, /*private_vars=*/clauses.privateVars,
3671 /*private_syms=*/makeArrayAttr(ctx, clauses.privateSyms),
3672 clauses.privateNeedsBarrier, clauses.untied, clauses.eventHandle);
3673}
3674
3675LogicalResult TaskOp::verify() {
3676 LogicalResult verifyDependVars =
3677 verifyDependVarList(*this, getDependKinds(), getDependVars(),
3678 getDependIteratedKinds(), getDependIterated());
3679 if (failed(verifyDependVars))
3680 return verifyDependVars;
3681
3682 if (failed(verifyPrivateVarList(*this)))
3683 return failure();
3684
3685 return verifyReductionVarList(*this, getInReductionSyms(),
3686 getInReductionVars(), getInReductionByref());
3687}
3688
3689//===----------------------------------------------------------------------===//
3690// TaskgroupOp
3691//===----------------------------------------------------------------------===//
3692
3693void TaskgroupOp::build(OpBuilder &builder, OperationState &state,
3694 const TaskgroupOperands &clauses) {
3695 MLIRContext *ctx = builder.getContext();
3696 TaskgroupOp::build(builder, state, clauses.allocateVars,
3697 clauses.allocatorVars, clauses.taskReductionVars,
3698 makeDenseBoolArrayAttr(ctx, clauses.taskReductionByref),
3699 makeArrayAttr(ctx, clauses.taskReductionSyms));
3700}
3701
3702LogicalResult TaskgroupOp::verify() {
3703 return verifyReductionVarList(*this, getTaskReductionSyms(),
3704 getTaskReductionVars(),
3705 getTaskReductionByref());
3706}
3707
3708//===----------------------------------------------------------------------===//
3709// TaskloopContextOp
3710//===----------------------------------------------------------------------===//
3711
3712void TaskloopContextOp::build(OpBuilder &builder, OperationState &state,
3713 const TaskloopContextOperands &clauses) {
3714 MLIRContext *ctx = builder.getContext();
3715 TaskloopContextOp::build(
3716 builder, state, clauses.allocateVars, clauses.allocatorVars,
3717 clauses.final, clauses.grainsizeMod, clauses.grainsize, clauses.ifExpr,
3718 clauses.inReductionVars,
3719 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),
3720 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.mergeable,
3721 clauses.nogroup, clauses.numTasksMod, clauses.numTasks, clauses.priority,
3722 /*private_vars=*/clauses.privateVars,
3723 /*private_syms=*/makeArrayAttr(ctx, clauses.privateSyms),
3724 clauses.privateNeedsBarrier, clauses.reductionMod, clauses.reductionVars,
3725 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3726 makeArrayAttr(ctx, clauses.reductionSyms), clauses.untied);
3727 state.addAttribute("omp.combined", UnitAttr::get(ctx));
3728}
3729
3730TaskloopWrapperOp TaskloopContextOp::getLoopOp() {
3731 return cast<TaskloopWrapperOp>(
3732 *llvm::find_if(getRegion().front(), [](mlir::Operation &op) {
3733 return isa<TaskloopWrapperOp>(op);
3734 }));
3735}
3736
3737LogicalResult TaskloopContextOp::verify() {
3738 if (getAllocateVars().size() != getAllocatorVars().size())
3739 return emitError(
3740 "expected equal sizes for allocate and allocator variables");
3741
3742 if (failed(verifyPrivateVarList(*this)))
3743 return failure();
3744
3745 if (failed(verifyReductionVarList(*this, getReductionSyms(),
3746 getReductionVars(), getReductionByref())) ||
3747 failed(verifyReductionVarList(*this, getInReductionSyms(),
3748 getInReductionVars(),
3749 getInReductionByref())))
3750 return failure();
3751
3752 if (!getReductionVars().empty() && getNogroup())
3753 return emitError("if a reduction clause is present on the taskloop "
3754 "directive, the nogroup clause must not be specified");
3755 for (auto var : getReductionVars()) {
3756 if (llvm::is_contained(getInReductionVars(), var))
3757 return emitError("the same list item cannot appear in both a reduction "
3758 "and an in_reduction clause");
3759 }
3760
3761 if (getGrainsize() && getNumTasks()) {
3762 return emitError(
3763 "the grainsize clause and num_tasks clause are mutually exclusive and "
3764 "may not appear on the same taskloop directive");
3765 }
3766
3767 // Without this restriction, any compound construct including `taskloop` would
3768 // fail to correctly identify the whole chain of operations (see
3769 // ComposableOpInterface::findCapturedOp()), as well as failing to do so even
3770 // for standalone `taskloop` constructs.
3771 if (!isCombined())
3772 return emitOpError("must always contain the 'omp.combined' attribute");
3773
3774 return success();
3775}
3776
3777LogicalResult TaskloopContextOp::verifyRegions() {
3778 Region &region = getRegion();
3779 auto loopWrapperIt = llvm::find_if(region.front(), [](mlir::Operation &op) {
3780 return isa<TaskloopWrapperOp>(op);
3781 });
3782 if (loopWrapperIt == region.front().end())
3783 return emitOpError()
3784 << "expected a TaskloopWrapperOp directly nested in the region";
3785
3786 auto loopWrapperOp = cast<TaskloopWrapperOp>(*loopWrapperIt);
3787 auto loopNestOp = dyn_cast<LoopNestOp>(loopWrapperOp.getWrappedLoop());
3788 // This will fail the verifier for TaskloopWrapperOp and print an error
3789 // message there.
3790 if (!loopNestOp)
3791 return failure();
3792
3793 std::function<bool(Value)> isValidBoundValue = [&](Value value) -> bool {
3794 Region *valueRegion = value.getParentRegion();
3795 // A loop bound value defined outside of the taskloop context region is
3796 // valid. A region is considered an ancestor of itself.
3797 if (!region.isAncestor(valueRegion))
3798 return true;
3799
3800 Operation *defOp = value.getDefiningOp();
3801 if (!defOp || defOp->getNumRegions() != 0 || !isPure(defOp))
3802 return false;
3803
3804 return llvm::all_of(defOp->getOperands(), isValidBoundValue);
3805 };
3806 auto hasUnsupportedTaskloopLocalBound = [&](OperandRange range) -> bool {
3807 return llvm::any_of(range,
3808 [&](Value value) { return !isValidBoundValue(value); });
3809 };
3810
3811 if (hasUnsupportedTaskloopLocalBound(loopNestOp.getLoopLowerBounds()) ||
3812 hasUnsupportedTaskloopLocalBound(loopNestOp.getLoopUpperBounds()) ||
3813 hasUnsupportedTaskloopLocalBound(loopNestOp.getLoopSteps())) {
3814 return emitOpError()
3815 << "expects loop bounds and steps to be defined outside of the "
3816 "taskloop.context region or by pure, regionless operations "
3817 "that do not depend on block arguments";
3818 }
3819
3820 return success();
3821}
3822
3823//===----------------------------------------------------------------------===//
3824// TaskloopWrapperOp
3825//===----------------------------------------------------------------------===//
3826
3827void TaskloopWrapperOp::build(OpBuilder &builder, OperationState &state,
3828 const TaskloopWrapperOperands &clauses) {
3829 TaskloopWrapperOp::build(builder, state);
3830}
3831
3832TaskloopContextOp TaskloopWrapperOp::getTaskloopContext() {
3833 return dyn_cast<TaskloopContextOp>(getOperation()->getParentOp());
3834}
3835
3836LogicalResult TaskloopWrapperOp::verify() {
3837 TaskloopContextOp context = getTaskloopContext();
3838 if (!context)
3839 return emitOpError() << "expected to be nested in a taskloop context op";
3840 return success();
3841}
3842
3843LogicalResult TaskloopWrapperOp::verifyRegions() {
3844 if (LoopWrapperInterface nested = getNestedWrapper()) {
3845 if (!isComposite())
3846 return emitError()
3847 << "'omp.composite' attribute missing from composite wrapper";
3848
3849 // Check for the allowed leaf constructs that may appear in a composite
3850 // construct directly after TASKLOOP.
3851 if (!isa<SimdOp>(nested))
3852 return emitError() << "only supported nested wrapper is 'omp.simd'";
3853 } else if (isComposite()) {
3854 return emitError()
3855 << "'omp.composite' attribute present in non-composite wrapper";
3856 }
3857
3858 return success();
3859}
3860
3861//===----------------------------------------------------------------------===//
3862// LoopNestOp
3863//===----------------------------------------------------------------------===//
3864
3865ParseResult LoopNestOp::parse(OpAsmParser &parser, OperationState &result) {
3866 // Parse an opening `(` followed by induction variables followed by `)`
3869 Type loopVarType;
3871 parser.parseColonType(loopVarType) ||
3872 // Parse loop bounds.
3873 parser.parseEqual() ||
3874 parser.parseOperandList(lbs, ivs.size(), OpAsmParser::Delimiter::Paren) ||
3875 parser.parseKeyword("to") ||
3876 parser.parseOperandList(ubs, ivs.size(), OpAsmParser::Delimiter::Paren))
3877 return failure();
3878
3879 for (auto &iv : ivs)
3880 iv.type = loopVarType;
3881
3882 auto *ctx = parser.getBuilder().getContext();
3883 // Parse "inclusive" flag.
3884 if (succeeded(parser.parseOptionalKeyword("inclusive")))
3885 result.addAttribute("loop_inclusive", UnitAttr::get(ctx));
3886
3887 // Parse step values.
3889 if (parser.parseKeyword("step") ||
3890 parser.parseOperandList(steps, ivs.size(), OpAsmParser::Delimiter::Paren))
3891 return failure();
3892
3893 // Parse collapse
3894 int64_t value = 0;
3895 if (!parser.parseOptionalKeyword("collapse") &&
3896 (parser.parseLParen() || parser.parseInteger(value) ||
3897 parser.parseRParen()))
3898 return failure();
3899 if (value > 1)
3900 result.addAttribute(
3901 "collapse_num_loops",
3902 IntegerAttr::get(parser.getBuilder().getI64Type(), value));
3903
3904 // Parse tiles
3906 auto parseTiles = [&]() -> ParseResult {
3907 int64_t tile;
3908 if (parser.parseInteger(tile))
3909 return failure();
3910 tiles.push_back(tile);
3911 return success();
3912 };
3913
3914 if (!parser.parseOptionalKeyword("tiles") &&
3915 (parser.parseLParen() || parser.parseCommaSeparatedList(parseTiles) ||
3916 parser.parseRParen()))
3917 return failure();
3918
3919 if (tiles.size() > 0)
3920 result.addAttribute("tile_sizes", DenseI64ArrayAttr::get(ctx, tiles));
3921
3922 // Parse the body.
3923 Region *region = result.addRegion();
3924 if (parser.parseRegion(*region, ivs))
3925 return failure();
3926
3927 // Resolve operands.
3928 if (parser.resolveOperands(lbs, loopVarType, result.operands) ||
3929 parser.resolveOperands(ubs, loopVarType, result.operands) ||
3930 parser.resolveOperands(steps, loopVarType, result.operands))
3931 return failure();
3932
3933 // Parse the optional attribute list.
3934 return parser.parseOptionalAttrDict(result.attributes);
3935}
3936
3937void LoopNestOp::print(OpAsmPrinter &p) {
3938 Region &region = getRegion();
3939 auto args = region.getArguments();
3940 p << " (" << args << ") : " << args[0].getType() << " = ("
3941 << getLoopLowerBounds() << ") to (" << getLoopUpperBounds() << ") ";
3942 if (getLoopInclusive())
3943 p << "inclusive ";
3944 p << "step (" << getLoopSteps() << ") ";
3945 if (int64_t numCollapse = getCollapseNumLoops())
3946 if (numCollapse > 1)
3947 p << "collapse(" << numCollapse << ") ";
3948
3949 if (const auto tiles = getTileSizes())
3950 p << "tiles(" << tiles.value() << ") ";
3951
3952 p.printRegion(region, /*printEntryBlockArgs=*/false);
3953}
3954
3955void LoopNestOp::build(OpBuilder &builder, OperationState &state,
3956 const LoopNestOperands &clauses) {
3957 MLIRContext *ctx = builder.getContext();
3958 LoopNestOp::build(builder, state, clauses.collapseNumLoops,
3959 clauses.loopLowerBounds, clauses.loopUpperBounds,
3960 clauses.loopSteps, clauses.loopInclusive,
3961 makeDenseI64ArrayAttr(ctx, clauses.tileSizes));
3962}
3963
3964LogicalResult LoopNestOp::verify() {
3965 if (getLoopLowerBounds().empty())
3966 return emitOpError() << "must represent at least one loop";
3967
3968 if (getLoopLowerBounds().size() != getIVs().size())
3969 return emitOpError() << "number of range arguments and IVs do not match";
3970
3971 for (auto [lb, iv] : llvm::zip_equal(getLoopLowerBounds(), getIVs())) {
3972 if (lb.getType() != iv.getType())
3973 return emitOpError()
3974 << "range argument type does not match corresponding IV type";
3975 }
3976
3977 uint64_t numIVs = getIVs().size();
3978
3979 if (const auto &numCollapse = getCollapseNumLoops())
3980 if (numCollapse > numIVs)
3981 return emitOpError()
3982 << "collapse value is larger than the number of loops";
3983
3984 if (const auto &tiles = getTileSizes())
3985 if (tiles.value().size() > numIVs)
3986 return emitOpError() << "too few canonical loops for tile dimensions";
3987
3988 if (!llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp()))
3989 return emitOpError() << "expects parent op to be a loop wrapper";
3990
3991 return success();
3992}
3993
3994void LoopNestOp::gatherWrappers(
3996 Operation *parent = (*this)->getParentOp();
3997 while (auto wrapper =
3998 llvm::dyn_cast_if_present<LoopWrapperInterface>(parent)) {
3999 wrappers.push_back(wrapper);
4000 parent = parent->getParentOp();
4001 }
4002}
4003
4004//===----------------------------------------------------------------------===//
4005// OpenMP canonical loop handling
4006//===----------------------------------------------------------------------===//
4007
4008std::tuple<NewCliOp, OpOperand *, OpOperand *>
4009mlir::omp ::decodeCli(Value cli) {
4010
4011 // Defining a CLI for a generated loop is optional; if there is none then
4012 // there is no followup-tranformation
4013 if (!cli)
4014 return {{}, nullptr, nullptr};
4015
4016 assert(cli.getType() == CanonicalLoopInfoType::get(cli.getContext()) &&
4017 "Unexpected type of cli");
4018
4019 NewCliOp create = cast<NewCliOp>(cli.getDefiningOp());
4020 OpOperand *gen = nullptr;
4021 OpOperand *cons = nullptr;
4022 for (OpOperand &use : cli.getUses()) {
4023 auto op = cast<LoopTransformationInterface>(use.getOwner());
4024
4025 unsigned opnum = use.getOperandNumber();
4026 if (op.isGeneratee(opnum)) {
4027 assert(!gen && "Each CLI may have at most one def");
4028 gen = &use;
4029 } else if (op.isApplyee(opnum)) {
4030 assert(!cons && "Each CLI may have at most one consumer");
4031 cons = &use;
4032 } else {
4033 llvm_unreachable("Unexpected operand for a CLI");
4034 }
4035 }
4036
4037 return {create, gen, cons};
4038}
4039
4040ClauseProcBindKind
4041mlir::omp::convertProcBindKind(llvm::omp::ProcBindKind kind) {
4042 switch (kind) {
4043 case llvm::omp::ProcBindKind::OMP_PROC_BIND_close:
4044 return ClauseProcBindKind::Close;
4045 case llvm::omp::ProcBindKind::OMP_PROC_BIND_master:
4046 return ClauseProcBindKind::Master;
4047 case llvm::omp::ProcBindKind::OMP_PROC_BIND_primary:
4048 return ClauseProcBindKind::Primary;
4049 case llvm::omp::ProcBindKind::OMP_PROC_BIND_spread:
4050 return ClauseProcBindKind::Spread;
4051 case llvm::omp::ProcBindKind::OMP_PROC_BIND_default:
4052 case llvm::omp::ProcBindKind::OMP_PROC_BIND_unknown:
4053 break;
4054 }
4055 llvm_unreachable("unexpected proc-bind kind");
4056}
4057
4058void NewCliOp::build(::mlir::OpBuilder &odsBuilder,
4059 ::mlir::OperationState &odsState) {
4060 odsState.addTypes(CanonicalLoopInfoType::get(odsBuilder.getContext()));
4061}
4062
4063void NewCliOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4064 Value result = getResult();
4065 auto [newCli, gen, cons] = decodeCli(result);
4066
4067 // Structured binding `gen` cannot be captured in lambdas before C++20
4068 OpOperand *generator = gen;
4069
4070 // Derive the CLI variable name from its generator:
4071 // * "canonloop" for omp.canonical_loop
4072 // * custom name for loop transformation generatees
4073 // * "cli" as fallback if no generator
4074 // * "_r<idx>" suffix for nested loops, where <idx> is the sequential order
4075 // at that level
4076 // * "_s<idx>" suffix for operations with multiple regions, where <idx> is
4077 // the index of that region
4078 std::string cliName{"cli"};
4079 if (gen) {
4080 cliName =
4082 .Case([&](CanonicalLoopOp op) {
4083 return generateLoopNestingName("canonloop", op);
4084 })
4085 .Case([&](UnrollHeuristicOp op) -> std::string {
4086 llvm_unreachable("heuristic unrolling does not generate a loop");
4087 })
4088 .Case([&](FuseOp op) -> std::string {
4089 unsigned opnum = generator->getOperandNumber();
4090 // The position of the first loop to be fused is the same position
4091 // as the resulting fused loop
4092 if (op.getFirst().has_value() && opnum != op.getFirst().value())
4093 return "canonloop_fuse";
4094 else
4095 return "fused";
4096 })
4097 .Case([&](TileOp op) -> std::string {
4098 auto [generateesFirst, generateesCount] =
4099 op.getGenerateesODSOperandIndexAndLength();
4100 unsigned firstGrid = generateesFirst;
4101 unsigned firstIntratile = generateesFirst + generateesCount / 2;
4102 unsigned end = generateesFirst + generateesCount;
4103 unsigned opnum = generator->getOperandNumber();
4104 // In the OpenMP apply and looprange clauses, indices are 1-based
4105 if (firstGrid <= opnum && opnum < firstIntratile) {
4106 unsigned gridnum = opnum - firstGrid + 1;
4107 return ("grid" + Twine(gridnum)).str();
4108 }
4109 if (firstIntratile <= opnum && opnum < end) {
4110 unsigned intratilenum = opnum - firstIntratile + 1;
4111 return ("intratile" + Twine(intratilenum)).str();
4112 }
4113 llvm_unreachable("Unexpected generatee argument");
4114 })
4115 .DefaultUnreachable("TODO: Custom name for this operation");
4116 }
4117
4118 setNameFn(result, cliName);
4119}
4120
4121LogicalResult NewCliOp::verify() {
4122 Value cli = getResult();
4123
4124 assert(cli.getType() == CanonicalLoopInfoType::get(cli.getContext()) &&
4125 "Unexpected type of cli");
4126
4127 // Check that the CLI is used in at most generator and one consumer
4128 OpOperand *gen = nullptr;
4129 OpOperand *cons = nullptr;
4130 for (mlir::OpOperand &use : cli.getUses()) {
4131 auto op = cast<mlir::omp::LoopTransformationInterface>(use.getOwner());
4132
4133 unsigned opnum = use.getOperandNumber();
4134 if (op.isGeneratee(opnum)) {
4135 if (gen) {
4136 InFlightDiagnostic error =
4137 emitOpError("CLI must have at most one generator");
4138 error.attachNote(gen->getOwner()->getLoc())
4139 .append("first generator here:");
4140 error.attachNote(use.getOwner()->getLoc())
4141 .append("second generator here:");
4142 return error;
4143 }
4144
4145 gen = &use;
4146 } else if (op.isApplyee(opnum)) {
4147 if (cons) {
4148 InFlightDiagnostic error =
4149 emitOpError("CLI must have at most one consumer");
4150 error.attachNote(cons->getOwner()->getLoc())
4151 .append("first consumer here:")
4152 .appendOp(*cons->getOwner(),
4153 OpPrintingFlags().printGenericOpForm());
4154 error.attachNote(use.getOwner()->getLoc())
4155 .append("second consumer here:")
4156 .appendOp(*use.getOwner(), OpPrintingFlags().printGenericOpForm());
4157 return error;
4158 }
4159
4160 cons = &use;
4161 } else {
4162 llvm_unreachable("Unexpected operand for a CLI");
4163 }
4164 }
4165
4166 // If the CLI is source of a transformation, it must have a generator
4167 if (cons && !gen) {
4168 InFlightDiagnostic error = emitOpError("CLI has no generator");
4169 error.attachNote(cons->getOwner()->getLoc())
4170 .append("see consumer here: ")
4171 .appendOp(*cons->getOwner(), OpPrintingFlags().printGenericOpForm());
4172 return error;
4173 }
4174
4175 return success();
4176}
4177
4178void CanonicalLoopOp::build(OpBuilder &odsBuilder, OperationState &odsState,
4179 Value tripCount) {
4180 odsState.addOperands(tripCount);
4181 odsState.addOperands(Value());
4182 (void)odsState.addRegion();
4183}
4184
4185void CanonicalLoopOp::build(OpBuilder &odsBuilder, OperationState &odsState,
4186 Value tripCount, ::mlir::Value cli) {
4187 odsState.addOperands(tripCount);
4188 odsState.addOperands(cli);
4189 (void)odsState.addRegion();
4190}
4191
4192void CanonicalLoopOp::getAsmBlockNames(OpAsmSetBlockNameFn setNameFn) {
4193 setNameFn(&getRegion().front(), "body_entry");
4194}
4195
4196void CanonicalLoopOp::getAsmBlockArgumentNames(Region &region,
4197 OpAsmSetValueNameFn setNameFn) {
4198 std::string ivName = generateLoopNestingName("iv", *this);
4199 setNameFn(region.getArgument(0), ivName);
4200}
4201
4202void CanonicalLoopOp::print(OpAsmPrinter &p) {
4203 if (getCli())
4204 p << '(' << getCli() << ')';
4205 p << ' ' << getInductionVar() << " : " << getInductionVar().getType()
4206 << " in range(" << getTripCount() << ") ";
4207
4208 p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,
4209 /*printBlockTerminators=*/true);
4210
4211 p.printOptionalAttrDict((*this)->getAttrs());
4212}
4213
4214mlir::ParseResult CanonicalLoopOp::parse(::mlir::OpAsmParser &parser,
4216 CanonicalLoopInfoType cliType =
4217 CanonicalLoopInfoType::get(parser.getContext());
4218
4219 // Parse (optional) omp.cli identifier
4221 SmallVector<mlir::Value, 1> cliOperand;
4222 if (!parser.parseOptionalLParen()) {
4223 if (parser.parseOperand(cli) ||
4224 parser.resolveOperand(cli, cliType, cliOperand) || parser.parseRParen())
4225 return failure();
4226 }
4227
4228 // We derive the type of tripCount from inductionVariable. MLIR requires the
4229 // type of tripCount to be known when calling resolveOperand so we have parse
4230 // the type before processing the inductionVariable.
4231 OpAsmParser::Argument inductionVariable;
4233 if (parser.parseArgument(inductionVariable, /*allowType*/ true) ||
4234 parser.parseKeyword("in") || parser.parseKeyword("range") ||
4235 parser.parseLParen() || parser.parseOperand(tripcount) ||
4236 parser.parseRParen() ||
4237 parser.resolveOperand(tripcount, inductionVariable.type, result.operands))
4238 return failure();
4239
4240 // Parse the loop body.
4241 Region *region = result.addRegion();
4242 if (parser.parseRegion(*region, {inductionVariable}))
4243 return failure();
4244
4245 // We parsed the cli operand forst, but because it is optional, it must be
4246 // last in the operand list.
4247 result.operands.append(cliOperand);
4248
4249 // Parse the optional attribute list.
4250 if (parser.parseOptionalAttrDict(result.attributes))
4251 return failure();
4252
4253 return mlir::success();
4254}
4255
4256LogicalResult CanonicalLoopOp::verify() {
4257 // The region's entry must accept the induction variable
4258 // It can also be empty if just created
4259 if (!getRegion().empty()) {
4260 Region &region = getRegion();
4261 if (region.getNumArguments() != 1)
4262 return emitOpError(
4263 "Canonical loop region must have exactly one argument");
4264
4265 if (getInductionVar().getType() != getTripCount().getType())
4266 return emitOpError(
4267 "Region argument must be the same type as the trip count");
4268 }
4269
4270 return success();
4271}
4272
4273Value CanonicalLoopOp::getInductionVar() { return getRegion().getArgument(0); }
4274
4275std::pair<unsigned, unsigned>
4276CanonicalLoopOp::getApplyeesODSOperandIndexAndLength() {
4277 // No applyees
4278 return {0, 0};
4279}
4280
4281std::pair<unsigned, unsigned>
4282CanonicalLoopOp::getGenerateesODSOperandIndexAndLength() {
4283 return getODSOperandIndexAndLength(odsIndex_cli);
4284}
4285
4286//===----------------------------------------------------------------------===//
4287// UnrollHeuristicOp
4288//===----------------------------------------------------------------------===//
4289
4290void UnrollHeuristicOp::build(::mlir::OpBuilder &odsBuilder,
4291 ::mlir::OperationState &odsState,
4292 ::mlir::Value cli) {
4293 odsState.addOperands(cli);
4294}
4295
4296void UnrollHeuristicOp::print(OpAsmPrinter &p) {
4297 p << '(' << getApplyee() << ')';
4298
4299 p.printOptionalAttrDict((*this)->getAttrs());
4300}
4301
4302mlir::ParseResult UnrollHeuristicOp::parse(::mlir::OpAsmParser &parser,
4304 auto cliType = CanonicalLoopInfoType::get(parser.getContext());
4305
4306 if (parser.parseLParen())
4307 return failure();
4308
4310 if (parser.parseOperand(applyee) ||
4311 parser.resolveOperand(applyee, cliType, result.operands))
4312 return failure();
4313
4314 if (parser.parseRParen())
4315 return failure();
4316
4317 // Optional output loop (full unrolling has none)
4318 if (!parser.parseOptionalArrow()) {
4319 if (parser.parseLParen() || parser.parseRParen())
4320 return failure();
4321 }
4322
4323 // Parse the optional attribute list.
4324 if (parser.parseOptionalAttrDict(result.attributes))
4325 return failure();
4326
4327 return mlir::success();
4328}
4329
4330std::pair<unsigned, unsigned>
4331UnrollHeuristicOp ::getApplyeesODSOperandIndexAndLength() {
4332 return getODSOperandIndexAndLength(odsIndex_applyee);
4333}
4334
4335std::pair<unsigned, unsigned>
4336UnrollHeuristicOp::getGenerateesODSOperandIndexAndLength() {
4337 return {0, 0};
4338}
4339
4340//===----------------------------------------------------------------------===//
4341// UnrollPartialOp
4342//===----------------------------------------------------------------------===//
4343
4344void UnrollPartialOp::build(::mlir::OpBuilder &odsBuilder,
4346 uint64_t unrollFactor) {
4347 odsState.addOperands(cli);
4348 Properties &props = odsState.getOrAddProperties<Properties>();
4349 props.unroll_factor = odsBuilder.getI64IntegerAttr(unrollFactor);
4350}
4351
4352void UnrollPartialOp::print(OpAsmPrinter &p) {
4353 p << '(' << getApplyee() << ')';
4354
4355 p.printOptionalAttrDict((*this)->getAttrs());
4356}
4357
4358mlir::ParseResult UnrollPartialOp::parse(::mlir::OpAsmParser &parser,
4360 auto cliType = CanonicalLoopInfoType::get(parser.getContext());
4361
4362 if (parser.parseLParen())
4363 return failure();
4364
4366 if (parser.parseOperand(applyee) ||
4367 parser.resolveOperand(applyee, cliType, result.operands))
4368 return failure();
4369
4370 if (parser.parseRParen())
4371 return failure();
4372
4373 // The unroll factor is carried by the `unroll_factor` attribute.
4374 if (parser.parseOptionalAttrDict(result.attributes))
4375 return failure();
4376
4377 return mlir::success();
4378}
4379
4380std::pair<unsigned, unsigned>
4381UnrollPartialOp::getApplyeesODSOperandIndexAndLength() {
4382 return getODSOperandIndexAndLength(odsIndex_applyee);
4383}
4384
4385std::pair<unsigned, unsigned>
4386UnrollPartialOp::getGenerateesODSOperandIndexAndLength() {
4387 return {0, 0};
4388}
4389
4390//===----------------------------------------------------------------------===//
4391// TileOp
4392//===----------------------------------------------------------------------===//
4393
4394static void printLoopTransformClis(OpAsmPrinter &p, TileOp op,
4395 OperandRange generatees,
4396 OperandRange applyees) {
4397 if (!generatees.empty())
4398 p << '(' << llvm::interleaved(generatees) << ')';
4399
4400 if (!applyees.empty())
4401 p << " <- (" << llvm::interleaved(applyees) << ')';
4402}
4403
4404static ParseResult parseLoopTransformClis(
4405 OpAsmParser &parser,
4408 if (parser.parseOptionalLess()) {
4409 // Syntax 1: generatees present
4410
4411 if (parser.parseOperandList(generateesOperands,
4413 return failure();
4414
4415 if (parser.parseLess())
4416 return failure();
4417 } else {
4418 // Syntax 2: generatees omitted
4419 }
4420
4421 // Parse `<-` (`<` has already been parsed)
4422 if (parser.parseMinus())
4423 return failure();
4424
4425 if (parser.parseOperandList(applyeesOperands,
4427 return failure();
4428
4429 return success();
4430}
4431
4432/// Check properties of the loop nest consisting of the transformation's
4433/// applyees:
4434/// 1. They are nested inside each other
4435/// 2. They are perfectly nested
4436/// (no code with side-effects in-between the loops)
4437/// 3. They are rectangular
4438/// (loop bounds are invariant in respect to the outer loops)
4439///
4440/// TODO: Generalize for LoopTransformationInterface.
4441static LogicalResult checkApplyeesNesting(TileOp op) {
4442 // Collect the loops from the nest
4443 bool isOnlyCanonLoops = true;
4445 for (Value applyee : op.getApplyees()) {
4446 auto [create, gen, cons] = decodeCli(applyee);
4447
4448 if (!gen)
4449 return op.emitOpError() << "applyee CLI has no generator";
4450
4451 auto loop = dyn_cast_or_null<CanonicalLoopOp>(gen->getOwner());
4452 canonLoops.push_back(loop);
4453 if (!loop)
4454 isOnlyCanonLoops = false;
4455 }
4456
4457 // FIXME: We currently can only verify non-rectangularity and perfect nest of
4458 // omp.canonical_loop.
4459 if (!isOnlyCanonLoops)
4460 return success();
4461
4462 DenseSet<Value> parentIVs;
4463 for (auto i : llvm::seq<int>(1, canonLoops.size())) {
4464 auto parentLoop = canonLoops[i - 1];
4465 auto loop = canonLoops[i];
4466
4467 if (parentLoop.getOperation() != loop.getOperation()->getParentOp())
4468 return op.emitOpError()
4469 << "tiled loop nest must be nested within each other";
4470
4471 parentIVs.insert(parentLoop.getInductionVar());
4472
4473 // Canonical loop must be perfectly nested, i.e. the body of the parent must
4474 // only contain the omp.canonical_loop of the nested loops, and
4475 // omp.terminator
4476 bool isPerfectlyNested = [&]() {
4477 auto &parentBody = parentLoop.getRegion();
4478 if (!parentBody.hasOneBlock())
4479 return false;
4480 auto &parentBlock = parentBody.getBlocks().front();
4481
4482 auto nestedLoopIt = parentBlock.begin();
4483 if (nestedLoopIt == parentBlock.end() ||
4484 (&*nestedLoopIt != loop.getOperation()))
4485 return false;
4486
4487 auto termIt = std::next(nestedLoopIt);
4488 if (termIt == parentBlock.end() || !isa<TerminatorOp>(termIt))
4489 return false;
4490
4491 if (std::next(termIt) != parentBlock.end())
4492 return false;
4493
4494 return true;
4495 }();
4496 if (!isPerfectlyNested)
4497 return op.emitOpError() << "tiled loop nest must be perfectly nested";
4498
4499 if (parentIVs.contains(loop.getTripCount()))
4500 return op.emitOpError() << "tiled loop nest must be rectangular";
4501 }
4502
4503 // TODO: The tile sizes must be computed before the loop, but checking this
4504 // requires dominance analysis. For instance:
4505 //
4506 // %canonloop = omp.new_cli
4507 // omp.canonical_loop(%canonloop) %iv : i32 in range(%tc) {
4508 // // write to %x
4509 // omp.terminator
4510 // }
4511 // %ts = llvm.load %x
4512 // omp.tile <- (%canonloop) sizes(%ts : i32)
4513
4514 return success();
4515}
4516
4517LogicalResult TileOp::verify() {
4518 if (getApplyees().empty())
4519 return emitOpError() << "must apply to at least one loop";
4520
4521 if (getSizes().size() != getApplyees().size())
4522 return emitOpError() << "there must be one tile size for each applyee";
4523
4524 if (!getGeneratees().empty() &&
4525 2 * getSizes().size() != getGeneratees().size())
4526 return emitOpError()
4527 << "expecting two times the number of generatees than applyees";
4528
4529 return checkApplyeesNesting(*this);
4530}
4531
4532std::pair<unsigned, unsigned> TileOp ::getApplyeesODSOperandIndexAndLength() {
4533 return getODSOperandIndexAndLength(odsIndex_applyees);
4534}
4535
4536std::pair<unsigned, unsigned> TileOp::getGenerateesODSOperandIndexAndLength() {
4537 return getODSOperandIndexAndLength(odsIndex_generatees);
4538}
4539
4540//===----------------------------------------------------------------------===//
4541// FuseOp
4542//===----------------------------------------------------------------------===//
4543
4544static void printLoopTransformClis(OpAsmPrinter &p, FuseOp op,
4545 OperandRange generatees,
4546 OperandRange applyees) {
4547 if (!generatees.empty())
4548 p << '(' << llvm::interleaved(generatees) << ')';
4549
4550 if (!applyees.empty())
4551 p << " <- (" << llvm::interleaved(applyees) << ')';
4552}
4553
4554LogicalResult FuseOp::verify() {
4555 if (getApplyees().size() < 2)
4556 return emitOpError() << "must apply to at least two loops";
4557
4558 if (getFirst().has_value() && getCount().has_value()) {
4559 int64_t first = getFirst().value();
4560 int64_t count = getCount().value();
4561 if ((unsigned)(first + count - 1) > getApplyees().size())
4562 return emitOpError() << "the numbers of applyees must be at least first "
4563 "minus one plus count attributes";
4564 if (!getGeneratees().empty() &&
4565 getGeneratees().size() != getApplyees().size() + 1 - count)
4566 return emitOpError() << "the number of generatees must be the number of "
4567 "aplyees plus one minus count";
4568
4569 } else {
4570 if (!getGeneratees().empty() && getGeneratees().size() != 1)
4571 return emitOpError()
4572 << "in a complete fuse the number of generatees must be exactly 1";
4573 }
4574 for (auto &&applyee : getApplyees()) {
4575 auto [create, gen, cons] = decodeCli(applyee);
4576
4577 if (!gen)
4578 return emitOpError() << "applyee CLI has no generator";
4579 auto loop = dyn_cast_or_null<CanonicalLoopOp>(gen->getOwner());
4580 if (!loop)
4581 return emitOpError()
4582 << "currently only supports omp.canonical_loop as applyee";
4583 }
4584 return success();
4585}
4586std::pair<unsigned, unsigned> FuseOp::getApplyeesODSOperandIndexAndLength() {
4587 return getODSOperandIndexAndLength(odsIndex_applyees);
4588}
4589
4590std::pair<unsigned, unsigned> FuseOp::getGenerateesODSOperandIndexAndLength() {
4591 return getODSOperandIndexAndLength(odsIndex_generatees);
4592}
4593
4594//===----------------------------------------------------------------------===//
4595// Critical construct (2.17.1)
4596//===----------------------------------------------------------------------===//
4597
4598void CriticalDeclareOp::build(OpBuilder &builder, OperationState &state,
4599 const CriticalDeclareOperands &clauses) {
4600 CriticalDeclareOp::build(builder, state, clauses.symName, clauses.hint);
4601}
4602
4603LogicalResult CriticalDeclareOp::verify() {
4604 return verifySynchronizationHint(*this, getHint());
4605}
4606
4607LogicalResult CriticalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
4608 if (getNameAttr()) {
4609 SymbolRefAttr symbolRef = getNameAttr();
4610 auto decl = symbolTable.lookupNearestSymbolFrom<CriticalDeclareOp>(
4611 *this, symbolRef);
4612 if (!decl) {
4613 return emitOpError() << "expected symbol reference " << symbolRef
4614 << " to point to a critical declaration";
4615 }
4616 }
4617
4618 return success();
4619}
4620
4621//===----------------------------------------------------------------------===//
4622// Ordered construct
4623//===----------------------------------------------------------------------===//
4624
4625static LogicalResult verifyOrderedParent(Operation &op) {
4626 bool hasRegion = op.getNumRegions() > 0;
4627 auto loopOp = op.getParentOfType<LoopNestOp>();
4628 if (!loopOp) {
4629 if (hasRegion)
4630 return success();
4631
4632 // TODO: Consider if this needs to be the case only for the standalone
4633 // variant of the ordered construct.
4634 return op.emitOpError() << "must be nested inside of a loop";
4635 }
4636
4637 Operation *wrapper = loopOp->getParentOp();
4638 if (auto wsloopOp = dyn_cast<WsloopOp>(wrapper)) {
4639 IntegerAttr orderedAttr = wsloopOp.getOrderedAttr();
4640 if (!orderedAttr)
4641 return op.emitOpError() << "the enclosing worksharing-loop region must "
4642 "have an ordered clause";
4643
4644 if (hasRegion && orderedAttr.getInt() != 0)
4645 return op.emitOpError() << "the enclosing loop's ordered clause must not "
4646 "have a parameter present";
4647
4648 if (!hasRegion && orderedAttr.getInt() == 0)
4649 return op.emitOpError() << "the enclosing loop's ordered clause must "
4650 "have a parameter present";
4651 } else if (!isa<SimdOp>(wrapper)) {
4652 return op.emitOpError() << "must be nested inside of a worksharing, simd "
4653 "or worksharing simd loop";
4654 }
4655 return success();
4656}
4657
4658void OrderedOp::build(OpBuilder &builder, OperationState &state,
4659 const OrderedOperands &clauses) {
4660 OrderedOp::build(builder, state, clauses.doacrossDependType,
4661 clauses.doacrossNumLoops, clauses.doacrossDependVars);
4662}
4663
4664LogicalResult OrderedOp::verify() {
4665 if (failed(verifyOrderedParent(**this)))
4666 return failure();
4667
4668 auto wrapper = (*this)->getParentOfType<WsloopOp>();
4669 if (!wrapper || *wrapper.getOrdered() != *getDoacrossNumLoops())
4670 return emitOpError() << "number of variables in depend clause does not "
4671 << "match number of iteration variables in the "
4672 << "doacross loop";
4673
4674 return success();
4675}
4676
4677void OrderedRegionOp::build(OpBuilder &builder, OperationState &state,
4678 const OrderedRegionOperands &clauses) {
4679 OrderedRegionOp::build(builder, state, clauses.parLevelSimd);
4680}
4681
4682LogicalResult OrderedRegionOp::verify() { return verifyOrderedParent(**this); }
4683
4684//===----------------------------------------------------------------------===//
4685// TaskwaitOp
4686//===----------------------------------------------------------------------===//
4687
4688void TaskwaitOp::build(OpBuilder &builder, OperationState &state,
4689 const TaskwaitOperands &clauses) {
4690 // TODO Store clauses in op: depend_iterated_kinds, depend_iterated, nowait.
4691 MLIRContext *ctx = builder.getContext();
4692 TaskwaitOp::build(
4693 builder, state,
4694 /*depend_kinds=*/makeArrayAttr(ctx, clauses.dependKinds),
4695 /*depend_vars=*/clauses.dependVars,
4696 /*depend_iterated_kinds=*/makeArrayAttr(ctx, clauses.dependIteratedKinds),
4697 /*depend_iterated=*/ValueRange(clauses.dependIterated),
4698 /*nowait=*/nullptr);
4699}
4700
4701//===----------------------------------------------------------------------===//
4702// Verifier for AtomicReadOp
4703//===----------------------------------------------------------------------===//
4704
4705LogicalResult AtomicReadOp::verify() {
4706 if (verifyCommon().failed())
4707 return mlir::failure();
4708
4709 int64_t version = 50;
4710 if (auto moduleOp = getOperation()->getParentOfType<ModuleOp>())
4711 if (Attribute verAttr = moduleOp->getAttr("omp.version"))
4712 version = llvm::cast<VersionAttr>(verAttr).getVersion();
4713
4714 if (auto mo = getMemoryOrder()) {
4715 if (*mo == ClauseMemoryOrderKind::Release) {
4716 return emitError("memory-order must not be release for atomic reads");
4717 }
4718 if (*mo == ClauseMemoryOrderKind::Acq_rel) {
4719 // acq_rel is prohibited on read only in OpenMP 5.0; allowed in 5.1+.
4720 if (version < 51)
4721 return emitError("memory-order must not be acq_rel for atomic reads");
4722 }
4723 }
4724 return verifySynchronizationHint(*this, getHint());
4725}
4726
4727//===----------------------------------------------------------------------===//
4728// Verifier for AtomicWriteOp
4729//===----------------------------------------------------------------------===//
4730
4731LogicalResult AtomicWriteOp::verify() {
4732 if (verifyCommon().failed())
4733 return mlir::failure();
4734
4735 int64_t version = 50;
4736 if (auto moduleOp = getOperation()->getParentOfType<ModuleOp>())
4737 if (Attribute verAttr = moduleOp->getAttr("omp.version"))
4738 version = llvm::cast<VersionAttr>(verAttr).getVersion();
4739
4740 if (auto mo = getMemoryOrder()) {
4741 if (*mo == ClauseMemoryOrderKind::Acquire) {
4742 return emitError("memory-order must not be acquire for atomic writes");
4743 }
4744 if (*mo == ClauseMemoryOrderKind::Acq_rel) {
4745 // acq_rel is prohibited on write only in OpenMP 5.0; allowed in 5.1+.
4746 if (version < 51)
4747 return emitError("memory-order must not be acq_rel for atomic writes");
4748 }
4749 }
4750 return verifySynchronizationHint(*this, getHint());
4751}
4752
4753//===----------------------------------------------------------------------===//
4754// Verifier for AtomicUpdateOp
4755//===----------------------------------------------------------------------===//
4756
4757LogicalResult AtomicUpdateOp::canonicalize(AtomicUpdateOp op,
4758 PatternRewriter &rewriter) {
4759 if (op.isNoOp()) {
4760 rewriter.eraseOp(op);
4761 return success();
4762 }
4763 if (Value writeVal = op.getWriteOpVal()) {
4764 rewriter.replaceOpWithNewOp<AtomicWriteOp>(
4765 op, op.getX(), writeVal, op.getHintAttr(), op.getMemoryOrderAttr());
4766 return success();
4767 }
4768 return failure();
4769}
4770
4771LogicalResult AtomicUpdateOp::verify() {
4772 if (verifyCommon().failed())
4773 return mlir::failure();
4774
4775 int64_t version = 50;
4776 if (auto moduleOp = getOperation()->getParentOfType<ModuleOp>())
4777 if (Attribute verAttr = moduleOp->getAttr("omp.version"))
4778 version = llvm::cast<VersionAttr>(verAttr).getVersion();
4779
4780 if (auto mo = getMemoryOrder()) {
4781 if (*mo == ClauseMemoryOrderKind::Acq_rel ||
4782 *mo == ClauseMemoryOrderKind::Acquire) {
4783 // This restriction applies only to OpenMP 5.0; removed in 5.1.
4784 if (version < 51)
4785 return emitError(
4786 "memory-order must not be acq_rel or acquire for atomic updates");
4787 }
4788 }
4789
4790 return verifySynchronizationHint(*this, getHint());
4791}
4792
4793LogicalResult AtomicUpdateOp::verifyRegions() { return verifyRegionsCommon(); }
4794
4795//===----------------------------------------------------------------------===//
4796// Verifier for AtomicCaptureOp
4797//===----------------------------------------------------------------------===//
4798
4799AtomicReadOp AtomicCaptureOp::getAtomicReadOp() {
4800 if (auto op = dyn_cast<AtomicReadOp>(getFirstOp()))
4801 return op;
4802 return dyn_cast<AtomicReadOp>(getSecondOp());
4803}
4804
4805AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() {
4806 if (auto op = dyn_cast<AtomicWriteOp>(getFirstOp()))
4807 return op;
4808 return dyn_cast<AtomicWriteOp>(getSecondOp());
4809}
4810
4811AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {
4812 if (auto op = dyn_cast<AtomicUpdateOp>(getFirstOp()))
4813 return op;
4814 return dyn_cast<AtomicUpdateOp>(getSecondOp());
4815}
4816
4817LogicalResult AtomicCaptureOp::verify() {
4818 return verifySynchronizationHint(*this, getHint());
4819}
4820
4821LogicalResult AtomicCaptureOp::verifyRegions() {
4822 if (verifyRegionsCommon().failed())
4823 return mlir::failure();
4824
4825 if (getFirstOp()->getAttr("hint") || getSecondOp()->getAttr("hint"))
4826 return emitOpError(
4827 "operations inside capture region must not have hint clause");
4828
4829 if (getFirstOp()->getAttr("memory_order") ||
4830 getSecondOp()->getAttr("memory_order"))
4831 return emitOpError(
4832 "operations inside capture region must not have memory_order clause");
4833 return success();
4834}
4835
4836//===----------------------------------------------------------------------===//
4837// AtomicCompareOp
4838//===----------------------------------------------------------------------===//
4839
4840LogicalResult AtomicCompareOp::verify() {
4841 if (verifyCommon().failed())
4842 return mlir::failure();
4843 return verifySynchronizationHint(*this, getHint());
4844}
4845
4846LogicalResult AtomicCompareOp::verifyRegions() {
4847 if (verifyRegionsCommon().failed())
4848 return mlir::failure();
4849
4850 if (verifyOperator().failed())
4851 return mlir::failure();
4852
4853 Block &block = getRegion().front();
4854
4855 Operation *terminator = block.getTerminator();
4856 if (!terminator || !isa<YieldOp>(terminator))
4857 return emitOpError("region must be terminated with omp.yield");
4858
4859 return success();
4860}
4861
4862//===----------------------------------------------------------------------===//
4863// CancelOp
4864//===----------------------------------------------------------------------===//
4865
4866void CancelOp::build(OpBuilder &builder, OperationState &state,
4867 const CancelOperands &clauses) {
4868 CancelOp::build(builder, state, clauses.cancelDirective, clauses.ifExpr);
4869}
4870
4872 Operation *parent = thisOp->getParentOp();
4873 while (parent) {
4874 if (parent->getDialect() == thisOp->getDialect())
4875 return parent;
4876 parent = parent->getParentOp();
4877 }
4878 return nullptr;
4879}
4880
4881LogicalResult CancelOp::verify() {
4882 ClauseCancellationConstructType cct = getCancelDirective();
4883 // The next OpenMP operation in the chain of parents
4884 Operation *structuralParent = getParentInSameDialect((*this).getOperation());
4885 if (!structuralParent)
4886 return emitOpError() << "Orphaned cancel construct";
4887
4888 if ((cct == ClauseCancellationConstructType::Parallel) &&
4889 !mlir::isa<ParallelOp>(structuralParent)) {
4890 return emitOpError() << "cancel parallel must appear "
4891 << "inside a parallel region";
4892 }
4893 if (cct == ClauseCancellationConstructType::Loop) {
4894 // structural parent will be omp.loop_nest, directly nested inside
4895 // omp.wsloop
4896 auto wsloopOp = mlir::dyn_cast<WsloopOp>(structuralParent->getParentOp());
4897
4898 if (!wsloopOp) {
4899 return emitOpError()
4900 << "cancel loop must appear inside a worksharing-loop region";
4901 }
4902 if (wsloopOp.getNowaitAttr()) {
4903 return emitError() << "A worksharing construct that is canceled "
4904 << "must not have a nowait clause";
4905 }
4906 if (wsloopOp.getOrderedAttr()) {
4907 return emitError() << "A worksharing construct that is canceled "
4908 << "must not have an ordered clause";
4909 }
4910
4911 } else if (cct == ClauseCancellationConstructType::Sections) {
4912 // structural parent will be an omp.section, directly nested inside
4913 // omp.sections
4914 auto sectionsOp =
4915 mlir::dyn_cast<SectionsOp>(structuralParent->getParentOp());
4916 if (!sectionsOp) {
4917 return emitOpError() << "cancel sections must appear "
4918 << "inside a sections region";
4919 }
4920 if (sectionsOp.getNowait()) {
4921 return emitError() << "A sections construct that is canceled "
4922 << "must not have a nowait clause";
4923 }
4924 }
4925 if ((cct == ClauseCancellationConstructType::Taskgroup) &&
4926 (!mlir::isa<omp::TaskOp>(structuralParent) &&
4927 !mlir::isa<omp::TaskloopWrapperOp>(structuralParent->getParentOp()))) {
4928 return emitOpError() << "cancel taskgroup must appear "
4929 << "inside a task region";
4930 }
4931 return success();
4932}
4933
4934//===----------------------------------------------------------------------===//
4935// CancellationPointOp
4936//===----------------------------------------------------------------------===//
4937
4938void CancellationPointOp::build(OpBuilder &builder, OperationState &state,
4939 const CancellationPointOperands &clauses) {
4940 CancellationPointOp::build(builder, state, clauses.cancelDirective);
4941}
4942
4943LogicalResult CancellationPointOp::verify() {
4944 ClauseCancellationConstructType cct = getCancelDirective();
4945 // The next OpenMP operation in the chain of parents
4946 Operation *structuralParent = getParentInSameDialect((*this).getOperation());
4947 if (!structuralParent)
4948 return emitOpError() << "Orphaned cancellation point";
4949
4950 if ((cct == ClauseCancellationConstructType::Parallel) &&
4951 !mlir::isa<ParallelOp>(structuralParent)) {
4952 return emitOpError() << "cancellation point parallel must appear "
4953 << "inside a parallel region";
4954 }
4955 // Strucutal parent here will be an omp.loop_nest. Get the parent of that to
4956 // find the wsloop
4957 if ((cct == ClauseCancellationConstructType::Loop) &&
4958 !mlir::isa<WsloopOp>(structuralParent->getParentOp())) {
4959 return emitOpError() << "cancellation point loop must appear "
4960 << "inside a worksharing-loop region";
4961 }
4962 if ((cct == ClauseCancellationConstructType::Sections) &&
4963 !mlir::isa<omp::SectionOp>(structuralParent)) {
4964 return emitOpError() << "cancellation point sections must appear "
4965 << "inside a sections region";
4966 }
4967 if ((cct == ClauseCancellationConstructType::Taskgroup) &&
4968 (!mlir::isa<omp::TaskOp>(structuralParent) &&
4969 !mlir::isa<omp::TaskloopWrapperOp>(structuralParent->getParentOp()))) {
4970 return emitOpError() << "cancellation point taskgroup must appear "
4971 << "inside a task region";
4972 }
4973 return success();
4974}
4975
4976//===----------------------------------------------------------------------===//
4977// MapBoundsOp
4978//===----------------------------------------------------------------------===//
4979
4980LogicalResult MapBoundsOp::verify() {
4981 auto extent = getExtent();
4982 auto upperbound = getUpperBound();
4983 if (!extent && !upperbound)
4984 return emitError("expected extent or upperbound.");
4985 return success();
4986}
4987
4988void PrivateClauseOp::build(OpBuilder &odsBuilder, OperationState &odsState,
4989 TypeRange /*result_types*/, StringAttr symName,
4990 TypeAttr type) {
4991 PrivateClauseOp::build(
4992 odsBuilder, odsState, symName, type,
4993 DataSharingClauseTypeAttr::get(odsBuilder.getContext(),
4994 DataSharingClauseType::Private));
4995}
4996
4997LogicalResult PrivateClauseOp::verifyRegions() {
4998 Type argType = getArgType();
4999 auto verifyTerminator = [&](Operation *terminator,
5000 bool yieldsValue) -> LogicalResult {
5001 if (!terminator->getBlock()->getSuccessors().empty())
5002 return success();
5003
5004 if (!llvm::isa<YieldOp>(terminator))
5005 return mlir::emitError(terminator->getLoc())
5006 << "expected exit block terminator to be an `omp.yield` op.";
5007
5008 YieldOp yieldOp = llvm::cast<YieldOp>(terminator);
5009 TypeRange yieldedTypes = yieldOp.getResults().getTypes();
5010
5011 if (!yieldsValue) {
5012 if (yieldedTypes.empty())
5013 return success();
5014
5015 return mlir::emitError(terminator->getLoc())
5016 << "Did not expect any values to be yielded.";
5017 }
5018
5019 if (yieldedTypes.size() == 1 && yieldedTypes.front() == argType)
5020 return success();
5021
5022 auto error = mlir::emitError(yieldOp.getLoc())
5023 << "Invalid yielded value. Expected type: " << argType
5024 << ", got: ";
5025
5026 if (yieldedTypes.empty())
5027 error << "None";
5028 else
5029 error << yieldedTypes;
5030
5031 return error;
5032 };
5033
5034 auto verifyRegion = [&](Region &region, unsigned expectedNumArgs,
5035 StringRef regionName,
5036 bool yieldsValue) -> LogicalResult {
5037 assert(!region.empty());
5038
5039 if (region.getNumArguments() != expectedNumArgs)
5040 return mlir::emitError(region.getLoc())
5041 << "`" << regionName << "`: " << "expected " << expectedNumArgs
5042 << " region arguments, got: " << region.getNumArguments();
5043
5044 for (Block &block : region) {
5045 // MLIR will verify the absence of the terminator for us.
5046 if (!block.mightHaveTerminator())
5047 continue;
5048
5049 if (failed(verifyTerminator(block.getTerminator(), yieldsValue)))
5050 return failure();
5051 }
5052
5053 return success();
5054 };
5055
5056 // Ensure all of the region arguments have the same type
5057 for (Region *region : getRegions())
5058 for (Type ty : region->getArgumentTypes())
5059 if (ty != argType)
5060 return emitError() << "Region argument type mismatch: got " << ty
5061 << " expected " << argType << ".";
5062
5063 mlir::Region &initRegion = getInitRegion();
5064 if (!initRegion.empty() &&
5065 failed(verifyRegion(getInitRegion(), /*expectedNumArgs=*/2, "init",
5066 /*yieldsValue=*/true)))
5067 return failure();
5068
5069 DataSharingClauseType dsType = getDataSharingType();
5070
5071 if (dsType == DataSharingClauseType::Private && !getCopyRegion().empty())
5072 return emitError("`private` clauses do not require a `copy` region.");
5073
5074 if (dsType == DataSharingClauseType::FirstPrivate && getCopyRegion().empty())
5075 return emitError(
5076 "`firstprivate` clauses require at least a `copy` region.");
5077
5078 if (dsType == DataSharingClauseType::FirstPrivate &&
5079 failed(verifyRegion(getCopyRegion(), /*expectedNumArgs=*/2, "copy",
5080 /*yieldsValue=*/true)))
5081 return failure();
5082
5083 if (!getDeallocRegion().empty() &&
5084 failed(verifyRegion(getDeallocRegion(), /*expectedNumArgs=*/1, "dealloc",
5085 /*yieldsValue=*/false)))
5086 return failure();
5087
5088 return success();
5089}
5090
5091//===----------------------------------------------------------------------===//
5092// Spec 5.2: Masked construct (10.5)
5093//===----------------------------------------------------------------------===//
5094
5095void MaskedOp::build(OpBuilder &builder, OperationState &state,
5096 const MaskedOperands &clauses) {
5097 MaskedOp::build(builder, state, clauses.filteredThreadId);
5098}
5099
5100//===----------------------------------------------------------------------===//
5101// Spec 5.2: Scan construct (5.6)
5102//===----------------------------------------------------------------------===//
5103
5104void ScanOp::build(OpBuilder &builder, OperationState &state,
5105 const ScanOperands &clauses) {
5106 ScanOp::build(builder, state, clauses.inclusiveVars, clauses.exclusiveVars);
5107}
5108
5109LogicalResult ScanOp::verify() {
5110 if (hasExclusiveVars() == hasInclusiveVars())
5111 return emitError(
5112 "Exactly one of EXCLUSIVE or INCLUSIVE clause is expected");
5113 if (WsloopOp parentWsLoopOp = (*this)->getParentOfType<WsloopOp>()) {
5114 if (parentWsLoopOp.getReductionModAttr() &&
5115 parentWsLoopOp.getReductionModAttr().getValue() ==
5116 ReductionModifier::inscan)
5117 return success();
5118 }
5119 if (SimdOp parentSimdOp = (*this)->getParentOfType<SimdOp>()) {
5120 if (parentSimdOp.getReductionModAttr() &&
5121 parentSimdOp.getReductionModAttr().getValue() ==
5122 ReductionModifier::inscan)
5123 return success();
5124 }
5125 return emitError("SCAN directive needs to be enclosed within a parent "
5126 "worksharing loop construct or SIMD construct with INSCAN "
5127 "reduction modifier");
5128}
5129
5130/// Verifies align clause in allocate directive
5131LogicalResult verifyAlignment(Operation &op,
5132 std::optional<uint64_t> alignment) {
5133 if (alignment.has_value()) {
5134 if ((alignment.value() != 0) && !llvm::has_single_bit(alignment.value()))
5135 return op.emitError()
5136 << "ALIGN value : " << alignment.value() << " must be power of 2";
5137 }
5138 return success();
5139}
5140
5141LogicalResult AllocateDirOp::verify() {
5142 return verifyAlignment(*getOperation(), getAlign());
5143}
5144
5145//===----------------------------------------------------------------------===//
5146// AllocSharedMemOp
5147//===----------------------------------------------------------------------===//
5148
5149LogicalResult AllocSharedMemOp::verify() {
5150 return verifyAlignment(*getOperation(), getMemAlignment());
5151}
5152
5153//===----------------------------------------------------------------------===//
5154// FreeSharedMemOp
5155//===----------------------------------------------------------------------===//
5156
5157LogicalResult FreeSharedMemOp::verify() {
5158 return verifyAlignment(*getOperation(), getMemAlignment());
5159}
5160
5161//===----------------------------------------------------------------------===//
5162// WorkdistributeOp
5163//===----------------------------------------------------------------------===//
5164
5165LogicalResult WorkdistributeOp::verify() {
5166 if (isCombined())
5167 return emitOpError() << "cannot be a non-innermost combined construct leaf";
5168
5169 // Check that region exists and is not empty
5170 Region &region = getRegion();
5171 if (region.empty())
5172 return emitOpError("region cannot be empty");
5173 // Verify single entry point.
5174 Block &entryBlock = region.front();
5175 if (entryBlock.empty())
5176 return emitOpError("region must contain a structured block");
5177 // Verify single exit point.
5178 bool hasTerminator = false;
5179 for (Block &block : region) {
5180 if (isa<TerminatorOp>(block.back())) {
5181 if (hasTerminator) {
5182 return emitOpError("region must have exactly one terminator");
5183 }
5184 hasTerminator = true;
5185 }
5186 }
5187 if (!hasTerminator) {
5188 return emitOpError("region must be terminated with omp.terminator");
5189 }
5190 auto walkResult = region.walk([&](Operation *op) -> WalkResult {
5191 // No implicit barrier at end
5192 if (isa<BarrierOp>(op)) {
5193 return emitOpError(
5194 "explicit barriers are not allowed in workdistribute region");
5195 }
5196 // Check for invalid nested constructs
5197 if (isa<ParallelOp>(op)) {
5198 return emitOpError(
5199 "nested parallel constructs not allowed in workdistribute");
5200 }
5201 if (isa<TeamsOp>(op)) {
5202 return emitOpError(
5203 "nested teams constructs not allowed in workdistribute");
5204 }
5205 return WalkResult::advance();
5206 });
5207 if (walkResult.wasInterrupted())
5208 return failure();
5209
5210 Operation *parentOp = (*this)->getParentOp();
5211 if (!llvm::dyn_cast<TeamsOp>(parentOp))
5212 return emitOpError("workdistribute must be nested under teams");
5213 return success();
5214}
5215
5216//===----------------------------------------------------------------------===//
5217// Declare simd [7.7]
5218//===----------------------------------------------------------------------===//
5219
5220LogicalResult DeclareSimdOp::verify() {
5221 // Must be nested inside a function-like op
5222 auto func =
5223 dyn_cast_if_present<mlir::FunctionOpInterface>((*this)->getParentOp());
5224 if (!func)
5225 return emitOpError() << "must be nested inside a function";
5226
5227 if (getInbranch() && getNotinbranch())
5228 return emitOpError("cannot have both 'inbranch' and 'notinbranch'");
5229
5230 if (failed(verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars(),
5231 /*isDeclareSimd=*/true)))
5232 return failure();
5233
5234 return verifyAlignedClause(*this, getAlignments(), getAlignedVars());
5235}
5236
5237void DeclareSimdOp::build(OpBuilder &odsBuilder, OperationState &odsState,
5238 const DeclareSimdOperands &clauses) {
5239 MLIRContext *ctx = odsBuilder.getContext();
5240 DeclareSimdOp::build(odsBuilder, odsState, clauses.alignedVars,
5241 makeArrayAttr(ctx, clauses.alignments), clauses.inbranch,
5242 clauses.linearVars, clauses.linearStepVars,
5243 clauses.linearVarTypes, clauses.linearModifiers,
5244 clauses.notinbranch, clauses.simdlen,
5245 clauses.uniformVars);
5246}
5247
5248//===----------------------------------------------------------------------===//
5249// Parser and printer for Uniform Clause
5250//===----------------------------------------------------------------------===//
5251
5252/// uniform ::= `uniform` `(` uniform-list `)`
5253/// uniform-list := uniform-val (`,` uniform-val)*
5254/// uniform-val := ssa-id `:` type
5255static ParseResult
5258 SmallVectorImpl<Type> &uniformTypes) {
5259 return parser.parseCommaSeparatedList([&]() -> mlir::ParseResult {
5260 if (parser.parseOperand(uniformVars.emplace_back()) ||
5261 parser.parseColonType(uniformTypes.emplace_back()))
5262 return mlir::failure();
5263 return mlir::success();
5264 });
5265}
5266
5267/// Print Uniform Clauses
5269 ValueRange uniformVars, TypeRange uniformTypes) {
5270 for (unsigned i = 0; i < uniformVars.size(); ++i) {
5271 if (i != 0)
5272 p << ", ";
5273 p << uniformVars[i] << " : " << uniformTypes[i];
5274 }
5275}
5276
5277//===----------------------------------------------------------------------===//
5278// Parser and printer for Affinity Clause
5279//===----------------------------------------------------------------------===//
5280
5281static ParseResult parseAffinityClause(
5282 OpAsmParser &parser,
5285 SmallVectorImpl<Type> &iteratedTypes,
5286 SmallVectorImpl<Type> &affinityVarTypes) {
5287 if (failed(parseSplitIteratedList(
5288 parser, iterated, iteratedTypes, affinityVars, affinityVarTypes,
5289 /*parsePrefix=*/[&]() -> ParseResult { return success(); })))
5290 return failure();
5291 return success();
5292}
5293
5295 ValueRange iterated, ValueRange affinityVars,
5296 TypeRange iteratedTypes,
5297 TypeRange affinityVarTypes) {
5298 auto nop = [&](Value, Type) {};
5299 printSplitIteratedList(p, iterated, iteratedTypes, affinityVars,
5300 affinityVarTypes,
5301 /*plain prefix*/ nop,
5302 /*iterated prefix*/ nop);
5303}
5304
5305//===----------------------------------------------------------------------===//
5306// Parser, printer, and verifier for Iterator modifier
5307//===----------------------------------------------------------------------===//
5308
5309static ParseResult
5314 SmallVectorImpl<Type> &lbTypes,
5315 SmallVectorImpl<Type> &ubTypes,
5316 SmallVectorImpl<Type> &stepTypes) {
5317
5318 llvm::SMLoc ivLoc = parser.getCurrentLocation();
5320
5321 // Parse induction variables: %i : i32, %j : i32
5322 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
5323 OpAsmParser::Argument &arg = ivArgs.emplace_back();
5324 if (parser.parseArgument(arg))
5325 return failure();
5326
5327 // Optional type, default to Index if not provided
5328 if (succeeded(parser.parseOptionalColon())) {
5329 if (parser.parseType(arg.type))
5330 return failure();
5331 } else {
5332 arg.type = parser.getBuilder().getIndexType();
5333 }
5334 return success();
5335 }))
5336 return failure();
5337
5338 // ) = (
5339 if (parser.parseRParen() || parser.parseEqual() || parser.parseLParen())
5340 return failure();
5341
5342 // Parse Ranges: (%lb to %ub step %st, ...)
5343 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
5344 OpAsmParser::UnresolvedOperand lb, ub, st;
5345 if (parser.parseOperand(lb) || parser.parseKeyword("to") ||
5346 parser.parseOperand(ub) || parser.parseKeyword("step") ||
5347 parser.parseOperand(st))
5348 return failure();
5349
5350 lbs.push_back(lb);
5351 ubs.push_back(ub);
5352 steps.push_back(st);
5353 return success();
5354 }))
5355 return failure();
5356
5357 if (parser.parseRParen())
5358 return failure();
5359
5360 if (ivArgs.size() != lbs.size())
5361 return parser.emitError(ivLoc)
5362 << "mismatch: " << ivArgs.size() << " variables but " << lbs.size()
5363 << " ranges";
5364
5365 for (auto &arg : ivArgs) {
5366 lbTypes.push_back(arg.type);
5367 ubTypes.push_back(arg.type);
5368 stepTypes.push_back(arg.type);
5369 }
5370
5371 return parser.parseRegion(region, ivArgs);
5372}
5373
5375 ValueRange lbs, ValueRange ubs,
5377 TypeRange) {
5378 Block &entry = region.front();
5379
5380 for (unsigned i = 0, e = entry.getNumArguments(); i < e; ++i) {
5381 if (i != 0)
5382 p << ", ";
5383 p.printRegionArgument(entry.getArgument(i));
5384 }
5385 p << ") = (";
5386
5387 // (%lb0 to %ub0 step %step0, %lb1 to %ub1 step %step1, ...)
5388 for (unsigned i = 0, e = lbs.size(); i < e; ++i) {
5389 if (i)
5390 p << ", ";
5391 p << lbs[i] << " to " << ubs[i] << " step " << steps[i];
5392 }
5393 p << ") ";
5394
5395 p.printRegion(region, /*printEntryBlockArgs=*/false,
5396 /*printBlockTerminators=*/true);
5397}
5398
5399LogicalResult IteratorOp::verify() {
5400 auto iteratedTy = llvm::dyn_cast<omp::IteratedType>(getIterated().getType());
5401 if (!iteratedTy)
5402 return emitOpError() << "result must be omp.iterated<entry_ty>";
5403
5404 for (auto [lb, ub, step] : llvm::zip_equal(
5405 getLoopLowerBounds(), getLoopUpperBounds(), getLoopSteps())) {
5406 if (matchPattern(step, m_Zero()))
5407 return emitOpError() << "loop step must not be zero";
5408
5409 IntegerAttr lbAttr;
5410 IntegerAttr ubAttr;
5411 IntegerAttr stepAttr;
5412 if (!matchPattern(lb, m_Constant(&lbAttr)) ||
5413 !matchPattern(ub, m_Constant(&ubAttr)) ||
5414 !matchPattern(step, m_Constant(&stepAttr)))
5415 continue;
5416
5417 const APInt &lbVal = lbAttr.getValue();
5418 const APInt &ubVal = ubAttr.getValue();
5419 const APInt &stepVal = stepAttr.getValue();
5420 if (stepVal.isStrictlyPositive() && lbVal.sgt(ubVal))
5421 return emitOpError() << "positive loop step requires lower bound to be "
5422 "less than or equal to upper bound";
5423 if (stepVal.isNegative() && lbVal.slt(ubVal))
5424 return emitOpError() << "negative loop step requires lower bound to be "
5425 "greater than or equal to upper bound";
5426 }
5427
5428 Block &b = getRegion().front();
5429 auto yield = llvm::dyn_cast<omp::YieldOp>(b.getTerminator());
5430
5431 if (!yield)
5432 return emitOpError() << "region must be terminated by omp.yield";
5433
5434 if (yield.getNumOperands() != 1)
5435 return emitOpError()
5436 << "omp.yield in omp.iterator region must yield exactly one value";
5437
5438 mlir::Type yieldedTy = yield.getOperand(0).getType();
5439 mlir::Type elemTy = iteratedTy.getElementType();
5440
5441 if (yieldedTy != elemTy)
5442 return emitOpError() << "omp.iterated element type (" << elemTy
5443 << ") does not match omp.yield operand type ("
5444 << yieldedTy << ")";
5445
5446 return success();
5447}
5448
5449//===----------------------------------------------------------------------===//
5450// GroupprivateOp
5451//===----------------------------------------------------------------------===//
5452
5453LogicalResult
5454GroupprivateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
5455 auto *symbol = symbolTable.lookupNearestSymbolFrom(*this, getSymNameAttr());
5456 if (!symbol)
5457 return emitOpError() << "expected symbol reference '" << getSymName()
5458 << "' to point to a global variable";
5459
5460 if (isa<FunctionOpInterface>(symbol))
5461 return emitOpError() << "expected symbol reference '" << getSymName()
5462 << "' to point to a global variable, not a function";
5463
5464 return success();
5465}
5466
5467#define GET_ATTRDEF_CLASSES
5468#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"
5469
5470#define GET_OP_CLASSES
5471#include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
5472
5473#define GET_TYPEDEF_CLASSES
5474#include "mlir/Dialect/OpenMP/OpenMPOpsTypes.cpp.inc"
return success()
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static std::optional< int64_t > getUpperBound(Value iv)
Gets the constant upper bound on an affine.for iv.
static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region, const Twine &name)
Definition EmitC.cpp:1533
static Type getElementType(Type type)
Determine the element type of type.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
b getContext())
static const mlir::GenInfo * generator
static LogicalResult verifyNontemporalClause(Operation *op, OperandRange nontemporalVars)
static DenseI64ArrayAttr makeDenseI64ArrayAttr(MLIRContext *ctx, const ArrayRef< int64_t > intArray)
static void printDependVarList(OpAsmPrinter &p, Operation *op, OperandRange dependVars, TypeRange dependTypes, std::optional< ArrayAttr > dependKinds, OperandRange iteratedVars, TypeRange iteratedTypes, std::optional< ArrayAttr > iteratedKinds)
Print Depend clause.
static ParseResult parseTargetOpRegion(OpAsmParser &parser, Region &region, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &hasDeviceAddrVars, SmallVectorImpl< Type > &hasDeviceAddrTypes, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &hostEvalVars, SmallVectorImpl< Type > &hostEvalTypes, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &mapVars, SmallVectorImpl< Type > &mapTypes, llvm::SmallVectorImpl< OpAsmParser::UnresolvedOperand > &privateVars, llvm::SmallVectorImpl< Type > &privateTypes, ArrayAttr &privateSyms, UnitAttr &privateNeedsBarrier, DenseI64ArrayAttr &privateMaps)
static constexpr StringRef getPrivateNeedsBarrierSpelling()
static void printHeapAllocClause(OpAsmPrinter &p, Operation *op, TypeAttr inType, ValueRange typeparams, TypeRange typeparamsTypes, ValueRange shape, TypeRange shapeTypes)
static LogicalResult verifyReductionVarList(Operation *op, std::optional< ArrayAttr > reductionSyms, OperandRange reductionVars, std::optional< ArrayRef< bool > > reductionByref)
Verifies Reduction Clause.
static ParseResult parseLinearClause(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &linearVars, SmallVectorImpl< Type > &linearTypes, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &linearStepVars, SmallVectorImpl< Type > &linearStepTypes, ArrayAttr &linearModifiers)
linear ::= linear ( linear-list ) linear-list := linear-val | linear-val linear-list linear-val := ss...
static ParseResult parseInReductionPrivateRegion(OpAsmParser &parser, Region &region, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &inReductionVars, SmallVectorImpl< Type > &inReductionTypes, DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms, llvm::SmallVectorImpl< OpAsmParser::UnresolvedOperand > &privateVars, llvm::SmallVectorImpl< Type > &privateTypes, ArrayAttr &privateSyms, UnitAttr &privateNeedsBarrier)
static ArrayAttr makeArrayAttr(MLIRContext *context, llvm::ArrayRef< Attribute > attrs)
static ParseResult parseClauseAttr(AsmParser &parser, ClauseAttr &attr)
static void printDynGroupprivateClause(OpAsmPrinter &printer, Operation *op, AccessGroupModifierAttr modifierFirst, FallbackModifierAttr modifierSecond, Value dynGroupprivateSize, Type sizeType)
static void printAllocateAndAllocator(OpAsmPrinter &p, Operation *op, OperandRange allocateVars, TypeRange allocateTypes, OperandRange allocatorVars, TypeRange allocatorTypes)
Print allocate clause.
static DenseBoolArrayAttr makeDenseBoolArrayAttr(MLIRContext *ctx, const ArrayRef< bool > boolArray)
static std::string generateLoopNestingName(StringRef prefix, CanonicalLoopOp op)
Generate a name of a canonical loop nest of the format <prefix>(_r<idx>_s<idx>)*.
static ParseResult parseAffinityClause(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &iterated, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &affinityVars, SmallVectorImpl< Type > &iteratedTypes, SmallVectorImpl< Type > &affinityVarTypes)
static void printClauseWithRegionArgs(OpAsmPrinter &p, MLIRContext *ctx, StringRef clauseName, ValueRange argsSubrange, ValueRange operands, TypeRange types, ArrayAttr symbols=nullptr, DenseI64ArrayAttr mapIndices=nullptr, DenseBoolArrayAttr byref=nullptr, ReductionModifierAttr modifier=nullptr, UnitAttr needsBarrier=nullptr)
static void printSplitIteratedList(OpAsmPrinter &p, ValueRange iteratedVars, TypeRange iteratedTypes, ValueRange plainVars, TypeRange plainTypes, PrintPrefixFn &&printPrefixForPlain, PrintPrefixFn &&printPrefixForIterated)
static LogicalResult verifyDependVarList(Operation *op, std::optional< ArrayAttr > dependKinds, OperandRange dependVars, std::optional< ArrayAttr > iteratedKinds, OperandRange iteratedVars)
Verifies Depend clause.
static void printBlockArgClause(OpAsmPrinter &p, MLIRContext *ctx, StringRef clauseName, ValueRange argsSubrange, std::optional< MapPrintArgs > mapArgs)
static void printAffinityClause(OpAsmPrinter &p, Operation *op, ValueRange iterated, ValueRange affinityVars, TypeRange iteratedTypes, TypeRange affinityVarTypes)
static void printBlockArgRegion(OpAsmPrinter &p, Operation *op, Region &region, const AllRegionPrintArgs &args)
static ParseResult parseGranularityClause(OpAsmParser &parser, ClauseTypeAttr &prescriptiveness, std::optional< OpAsmParser::UnresolvedOperand > &operand, Type &operandType, std::optional< ClauseType >(*symbolizeClause)(StringRef), StringRef clauseName)
static void printIteratorHeader(OpAsmPrinter &p, Operation *op, Region &region, ValueRange lbs, ValueRange ubs, ValueRange steps, TypeRange, TypeRange, TypeRange)
static ParseResult parseHeapAllocClause(OpAsmParser &parser, TypeAttr &inTypeAttr, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &typeparams, SmallVectorImpl< Type > &typeparamsTypes, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &shape, SmallVectorImpl< Type > &shapeTypes)
operation ::= $in_type ( ( $typeparams ) )? ( , $shape )?
static void printInReductionClause(OpAsmPrinter &p, Operation *op, ValueRange inReductionVars, TypeRange inReductionTypes, DenseBoolArrayAttr inReductionByref, ArrayAttr inReductionSyms)
Prints an in_reduction clause for an operation that does not give its list items entry block argument...
static ParseResult parseIteratorHeader(OpAsmParser &parser, Region &region, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &lbs, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &ubs, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &steps, SmallVectorImpl< Type > &lbTypes, SmallVectorImpl< Type > &ubTypes, SmallVectorImpl< Type > &stepTypes)
static ParseResult parseBlockArgRegion(OpAsmParser &parser, Region &region, AllRegionParseArgs args)
static ParseResult parseLoopTransformClis(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &generateesOperands, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &applyeesOperands)
static ParseResult parseSynchronizationHint(OpAsmParser &parser, IntegerAttr &hintAttr)
Parses a Synchronization Hint clause.
static void printScheduleClause(OpAsmPrinter &p, Operation *op, ClauseScheduleKindAttr scheduleKind, ScheduleModifierAttr scheduleMod, UnitAttr scheduleSimd, Value scheduleChunk, Type scheduleChunkType)
Print schedule clause.
static void printCopyprivate(OpAsmPrinter &p, Operation *op, OperandRange copyprivateVars, TypeRange copyprivateTypes, std::optional< ArrayAttr > copyprivateSyms)
Print Copyprivate clause.
static ParseResult parseOrderClause(OpAsmParser &parser, ClauseOrderKindAttr &order, OrderModifierAttr &orderMod)
static bool mapTypeToBool(ClauseMapFlags value, ClauseMapFlags flag)
static void printAlignedClause(OpAsmPrinter &p, Operation *op, ValueRange alignedVars, TypeRange alignedTypes, std::optional< ArrayAttr > alignments)
Print Aligned Clause.
static bool targetInReductionCapturedBy(Value inReductionVar, Value mapVarPtr)
An omp.target in_reduction operand is captured by a map_entries entry when the entry's MapInfoOp var_...
static LogicalResult verifySynchronizationHint(Operation *op, uint64_t hint)
Verifies a synchronization hint clause.
static ParseResult parseUseDeviceAddrUseDevicePtrRegion(OpAsmParser &parser, Region &region, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &useDeviceAddrVars, SmallVectorImpl< Type > &useDeviceAddrTypes, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &useDevicePtrVars, SmallVectorImpl< Type > &useDevicePtrTypes)
static ParseResult parseUniformClause(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &uniformVars, SmallVectorImpl< Type > &uniformTypes)
uniform ::= uniform ( uniform-list ) uniform-list := uniform-val (, uniform-val)* uniform-val := ssa-...
static void printInReductionPrivateReductionRegion(OpAsmPrinter &p, Operation *op, Region &region, ValueRange inReductionVars, TypeRange inReductionTypes, DenseBoolArrayAttr inReductionByref, ArrayAttr inReductionSyms, ValueRange privateVars, TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier, ReductionModifierAttr reductionMod, ValueRange reductionVars, TypeRange reductionTypes, DenseBoolArrayAttr reductionByref, ArrayAttr reductionSyms)
static void printInReductionPrivateRegion(OpAsmPrinter &p, Operation *op, Region &region, ValueRange inReductionVars, TypeRange inReductionTypes, DenseBoolArrayAttr inReductionByref, ArrayAttr inReductionSyms, ValueRange privateVars, TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier)
static void printSynchronizationHint(OpAsmPrinter &p, Operation *op, IntegerAttr hintAttr)
Prints a Synchronization Hint clause.
static void printGranularityClause(OpAsmPrinter &p, Operation *op, ClauseTypeAttr prescriptiveness, Value operand, mlir::Type operandType, StringRef(*stringifyClauseType)(ClauseType))
static ParseResult parseDependVarList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &dependVars, SmallVectorImpl< Type > &dependTypes, ArrayAttr &dependKinds, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &iteratedVars, SmallVectorImpl< Type > &iteratedTypes, ArrayAttr &iteratedKinds)
depend-entry-list ::= depend-entry | depend-entry-list , depend-entry depend-entry ::= depend-kind ->...
static Operation * getParentInSameDialect(Operation *thisOp)
static void printUniformClause(OpAsmPrinter &p, Operation *op, ValueRange uniformVars, TypeRange uniformTypes)
Print Uniform Clauses.
static LogicalResult verifyCopyprivateVarList(Operation *op, OperandRange copyprivateVars, std::optional< ArrayAttr > copyprivateSyms)
Verifies CopyPrivate Clause.
static LogicalResult verifyAlignedClause(Operation *op, std::optional< ArrayAttr > alignments, OperandRange alignedVars)
static ParseResult parsePrivateRegion(OpAsmParser &parser, Region &region, llvm::SmallVectorImpl< OpAsmParser::UnresolvedOperand > &privateVars, llvm::SmallVectorImpl< Type > &privateTypes, ArrayAttr &privateSyms, UnitAttr &privateNeedsBarrier)
static void printNumTasksClause(OpAsmPrinter &p, Operation *op, ClauseNumTasksTypeAttr numTasksMod, Value numTasks, mlir::Type numTasksType)
static void printLoopTransformClis(OpAsmPrinter &p, TileOp op, OperandRange generatees, OperandRange applyees)
static ParseResult parseDynGroupprivateClause(OpAsmParser &parser, AccessGroupModifierAttr &accessGroupAttr, FallbackModifierAttr &fallbackAttr, std::optional< OpAsmParser::UnresolvedOperand > &dynGroupprivateSize, Type &sizeType)
static void printPrivateRegion(OpAsmPrinter &p, Operation *op, Region &region, ValueRange privateVars, TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier)
static void printPrivateReductionRegion(OpAsmPrinter &p, Operation *op, Region &region, ValueRange privateVars, TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier, ReductionModifierAttr reductionMod, ValueRange reductionVars, TypeRange reductionTypes, DenseBoolArrayAttr reductionByref, ArrayAttr reductionSyms)
static ParseResult parseSplitIteratedList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &iteratedVars, SmallVectorImpl< Type > &iteratedTypes, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &plainVars, SmallVectorImpl< Type > &plainTypes, ParsePrefixFn &&parsePrefix)
static void printTaskReductionRegion(OpAsmPrinter &p, Operation *op, Region &region, ValueRange taskReductionVars, TypeRange taskReductionTypes, DenseBoolArrayAttr taskReductionByref, ArrayAttr taskReductionSyms)
static LogicalResult verifyMapInfoForMapClause(Operation *op, mlir::omp::MapInfoOp mapInfoOp, llvm::DenseSet< mlir::TypedValue< mlir::omp::PointerLikeType > > &updateToVars, llvm::DenseSet< mlir::TypedValue< mlir::omp::PointerLikeType > > &updateFromVars)
return success()
static LogicalResult verifyOrderedParent(Operation &op)
static void printOrderClause(OpAsmPrinter &p, Operation *op, ClauseOrderKindAttr order, OrderModifierAttr orderMod)
static ParseResult parseBlockArgClause(OpAsmParser &parser, llvm::SmallVectorImpl< OpAsmParser::Argument > &entryBlockArgs, StringRef keyword, std::optional< MapParseArgs > mapArgs)
static ParseResult parseClauseWithRegionArgs(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &operands, SmallVectorImpl< Type > &types, SmallVectorImpl< OpAsmParser::Argument > &regionPrivateArgs, ArrayAttr *symbols=nullptr, DenseI64ArrayAttr *mapIndices=nullptr, DenseBoolArrayAttr *byref=nullptr, ReductionModifierAttr *modifier=nullptr, UnitAttr *needsBarrier=nullptr)
static LogicalResult verifyPrivateVarsMapping(TargetOp targetOp)
static ParseResult parseScheduleClause(OpAsmParser &parser, ClauseScheduleKindAttr &scheduleAttr, ScheduleModifierAttr &scheduleMod, UnitAttr &scheduleSimd, std::optional< OpAsmParser::UnresolvedOperand > &chunkSize, Type &chunkType)
schedule ::= schedule ( sched-list ) sched-list ::= sched-val | sched-val sched-list | sched-val ,...
static LogicalResult verifyDynGroupprivateClause(Operation *op, AccessGroupModifierAttr accessGroup, FallbackModifierAttr fallback, Value dynGroupprivateSize)
static LogicalResult verifyLinearModifiers(Operation *op, std::optional< ArrayAttr > linearModifiers, OperandRange linearVars, bool isDeclareSimd=false)
OpenMP 5.2, Section 5.4.6: "A linear-modifier may be specified as ref or uval only on a declare simd ...
static void printClauseAttr(OpAsmPrinter &p, Operation *op, ClauseAttr attr)
static ParseResult parseAllocateAndAllocator(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &allocateVars, SmallVectorImpl< Type > &allocateTypes, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &allocatorVars, SmallVectorImpl< Type > &allocatorTypes)
Parse an allocate clause with allocators and a list of operands with types.
static void printMembersIndex(OpAsmPrinter &p, MapInfoOp op, ArrayAttr membersIdx)
static void printCaptureType(OpAsmPrinter &p, Operation *op, VariableCaptureKindAttr mapCaptureType)
static LogicalResult verifyNumTeamsClause(Operation *op, Value numTeamsLower, OperandRange numTeamsUpperVars)
static bool opInGlobalImplicitParallelRegion(Operation *op)
static void printTargetOpRegion(OpAsmPrinter &p, Operation *op, Region &region, ValueRange hasDeviceAddrVars, TypeRange hasDeviceAddrTypes, ValueRange hostEvalVars, TypeRange hostEvalTypes, ValueRange mapVars, TypeRange mapTypes, ValueRange privateVars, TypeRange privateTypes, ArrayAttr privateSyms, UnitAttr privateNeedsBarrier, DenseI64ArrayAttr privateMaps)
static void printUseDeviceAddrUseDevicePtrRegion(OpAsmPrinter &p, Operation *op, Region &region, ValueRange useDeviceAddrVars, TypeRange useDeviceAddrTypes, ValueRange useDevicePtrVars, TypeRange useDevicePtrTypes)
static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars, OperandRange mapIterated)
static LogicalResult verifyPrivateVarList(OpType &op)
static ParseResult parseNumTasksClause(OpAsmParser &parser, ClauseNumTasksTypeAttr &numTasksMod, std::optional< OpAsmParser::UnresolvedOperand > &numTasks, Type &numTasksType)
LogicalResult verifyAlignment(Operation &op, std::optional< uint64_t > alignment)
Verifies align clause in allocate directive.
static ParseResult parseAlignedClause(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &alignedVars, SmallVectorImpl< Type > &alignedTypes, ArrayAttr &alignmentsAttr)
aligned ::= aligned ( aligned-list ) aligned-list := aligned-val | aligned-val aligned-list aligned-v...
static ParseResult parsePrivateReductionRegion(OpAsmParser &parser, Region &region, llvm::SmallVectorImpl< OpAsmParser::UnresolvedOperand > &privateVars, llvm::SmallVectorImpl< Type > &privateTypes, ArrayAttr &privateSyms, UnitAttr &privateNeedsBarrier, ReductionModifierAttr &reductionMod, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &reductionVars, SmallVectorImpl< Type > &reductionTypes, DenseBoolArrayAttr &reductionByref, ArrayAttr &reductionSyms)
static void printLinearClause(OpAsmPrinter &p, Operation *op, ValueRange linearVars, TypeRange linearTypes, ValueRange linearStepVars, TypeRange stepVarTypes, ArrayAttr linearModifiers)
Print Linear Clause.
static ParseResult parseInReductionPrivateReductionRegion(OpAsmParser &parser, Region &region, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &inReductionVars, SmallVectorImpl< Type > &inReductionTypes, DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms, llvm::SmallVectorImpl< OpAsmParser::UnresolvedOperand > &privateVars, llvm::SmallVectorImpl< Type > &privateTypes, ArrayAttr &privateSyms, UnitAttr &privateNeedsBarrier, ReductionModifierAttr &reductionMod, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &reductionVars, SmallVectorImpl< Type > &reductionTypes, DenseBoolArrayAttr &reductionByref, ArrayAttr &reductionSyms)
static LogicalResult checkApplyeesNesting(TileOp op)
Check properties of the loop nest consisting of the transformation's applyees:
static ParseResult parseCaptureType(OpAsmParser &parser, VariableCaptureKindAttr &mapCaptureType)
static ParseResult parseTaskReductionRegion(OpAsmParser &parser, Region &region, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &taskReductionVars, SmallVectorImpl< Type > &taskReductionTypes, DenseBoolArrayAttr &taskReductionByref, ArrayAttr &taskReductionSyms)
static ParseResult parseGrainsizeClause(OpAsmParser &parser, ClauseGrainsizeTypeAttr &grainsizeMod, std::optional< OpAsmParser::UnresolvedOperand > &grainsize, Type &grainsizeType)
static ParseResult parseCopyprivate(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &copyprivateVars, SmallVectorImpl< Type > &copyprivateTypes, ArrayAttr &copyprivateSyms)
copyprivate-entry-list ::= copyprivate-entry | copyprivate-entry-list , copyprivate-entry copyprivate...
static ParseResult parseInReductionClause(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &inReductionVars, SmallVectorImpl< Type > &inReductionTypes, DenseBoolArrayAttr &inReductionByref, ArrayAttr &inReductionSyms)
Parses an in_reduction clause for an operation that does not give its list items entry block argument...
static LogicalResult verifyMapInfoDefinedArgs(Operation *op, StringRef clauseName, OperandRange vars)
static void printGrainsizeClause(OpAsmPrinter &p, Operation *op, ClauseGrainsizeTypeAttr grainsizeMod, Value grainsize, mlir::Type grainsizeType)
static ParseResult verifyScheduleModifiers(OpAsmParser &parser, SmallVectorImpl< SmallString< 12 > > &modifiers)
static bool isUnique(It begin, It end)
Definition ShardOps.cpp:161
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
static SmallVector< Value > getTileSizes(Location loc, x86::amx::TileType tType, RewriterBase &rewriter)
Maps the 2-dim vector shape to the two 16-bit tile sizes.
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual ParseResult parseMinus()=0
Parse a '-' token.
@ Paren
Parens surrounding zero or more operands.
@ None
Zero or more operands with no delimiters.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalEqual()=0
Parse a = token if present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
virtual ParseResult parseLSquare()=0
Parse a [ token.
virtual ParseResult parseRSquare()=0
Parse a ] token.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseOptionalArrow()=0
Parse a '->' token if present.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseOptionalLess()=0
Parse a '<' token if present.
virtual ParseResult parseArrow()=0
Parse a '->' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalLParen()=0
Parse a ( token if present.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
bool empty()
Definition Block.h:172
BlockArgument getArgument(unsigned i)
Definition Block.h:153
unsigned getNumArguments()
Definition Block.h:152
Operation & front()
Definition Block.h:177
SuccessorRange getSuccessors()
Definition Block.h:294
Operation & back()
Definition Block.h:176
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
bool mightHaveTerminator()
Return "true" if this block might have a terminator.
Definition Block.cpp:255
BlockArgListType getArguments()
Definition Block.h:111
iterator end()
Definition Block.h:168
iterator begin()
Definition Block.h:167
IntegerType getI64Type()
Definition Builders.cpp:69
IntegerAttr getI64IntegerAttr(int64_t value)
Definition Builders.cpp:116
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:71
MLIRContext * getContext() const
Definition Builders.h:56
Attr getAttr(Args &&...args)
Get or construct an instance of the attribute Attr with provided arguments.
Definition Builders.h:100
Diagnostic & append(Arg1 &&arg1, Arg2 &&arg2, Args &&...args)
Append arguments to the diagnostic.
Diagnostic & appendOp(Operation &op, const OpPrintingFlags &flags)
Append an operation with the given printing flags.
A class for computing basic dominance information.
Definition Dominance.h:143
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
Definition Dominance.h:161
This class represents a diagnostic that is inflight and set to be reported.
Diagnostic & attachNote(std::optional< Location > noteLoc=std::nullopt)
Attaches a note to this diagnostic.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult parseArgument(Argument &result, bool allowType=false, bool allowAttrs=false)=0
Parse a single argument with the following syntax:
virtual ParseResult parseArgumentList(SmallVectorImpl< Argument > &result, Delimiter delimiter=Delimiter::None, bool allowType=false, bool allowAttrs=false)=0
Parse zero or more arguments with a specified surrounding delimiter.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
virtual void printRegionArgument(BlockArgument arg, ArrayRef< NamedAttribute > argAttrs={}, bool omitType=false)=0
Print a block argument in the usual format of: ssaName : type {attr1=42} loc("here") where location p...
virtual void printOperand(Value value)=0
Print implementations for various things an operation contains.
This class helps build Operations.
Definition Builders.h:209
This class represents an operand of an operation.
Definition Value.h:254
Set of flags used to control the behavior of the various IR print methods (e.g.
This class provides the API for ops that are known to be isolated from above.
This class provides the API for ops that are known to be terminators.
This class indicates that the regions associated with this op don't have terminators.
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
type_range getType() const
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition Operation.h:237
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
Definition Operation.h:711
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:774
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:699
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:702
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
user_range getUsers()
Returns a range of all users.
Definition Operation.h:898
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
BlockArgListType getArguments()
Definition Region.h:94
OpIterator op_begin()
Return iterators that walk the operations nested directly within this region.
Definition Region.h:183
bool isAncestor(Region *other)
Return true if this region is ancestor of the other region.
Definition Region.h:246
iterator_range< OpIterator > getOps()
Definition Region.h:185
bool empty()
Definition Region.h:60
unsigned getNumArguments()
Definition Region.h:136
Location getLoc()
Return a location for this region.
Definition Region.cpp:31
BlockArgument getArgument(unsigned i)
Definition Region.h:137
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:213
BlockListType & getBlocks()
Definition Region.h:45
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents a collection of SymbolTables.
virtual Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
type_range getType() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
Definition Value.h:108
Type getType() const
Return the type of this value.
Definition Value.h:105
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
static WalkResult advance()
Definition WalkResult.h:47
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< bool > content)
bool isReachableFromEntry(Block *a) const
Return true if the specified block is reachable from the entry block of its region.
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
TargetEnterDataOperands TargetEnterExitUpdateDataOperands
omp.target_enter_data, omp.target_exit_data and omp.target_update take the same clauses,...
std::tuple< NewCliOp, OpOperand *, OpOperand * > decodeCli(mlir::Value cli)
Find the omp.new_cli, generator, and consumer of a canonical loop info.
ClauseProcBindKind convertProcBindKind(llvm::omp::ProcBindKind kind)
Convert a proc_bind kind from the LLVM frontend enum to the corresponding OpenMP dialect enum.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
A functor used to set the name of the start of a result group of an operation.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Definition LLVM.h:122
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
bool isPure(Operation *op)
Returns true if the given operation is pure, i.e., is speculatable that does not touch memory.
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
Definition Matchers.h:442
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
Definition Value.h:494
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:139
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
Definition Utils.cpp:1330
detail::DenseArrayAttrImpl< bool > DenseBoolArrayAttr
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
function_ref< void(Block *, StringRef)> OpAsmSetBlockNameFn
A functor used to set the name of blocks in regions directly nested under an operation.
This is the representation of an operand reference.
This class provides APIs and verifiers for ops with regions having a single block.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
T & getOrAddProperties()
Get (or create) the properties of the provided type to be set on the operation on creation.
void addOperands(ValueRange newOperands)
void addAttributes(ArrayRef< NamedAttribute > newAttributes)
Add an array of named attributes.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addTypes(ArrayRef< Type > newTypes)
Region * addRegion()
Create a region that should be attached to the operation.
Extended TargetOperands with kernel_type attribute.
TargetExecModeAttr kernelType
Kernel execution mode for the target region.