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