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