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