MLIR 24.0.0git
OpenMPDialect.cpp
Go to the documentation of this file.
1//===- OpenMPDialect.cpp - MLIR Dialect for OpenMP implementation ---------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the OpenMP dialect and its operations.
10//
11//===----------------------------------------------------------------------===//
12
18#include "mlir/IR/Attributes.h"
21#include "mlir/IR/Matchers.h"
24#include "mlir/IR/SymbolTable.h"
27
28#include "llvm/ADT/ArrayRef.h"
29#include "llvm/ADT/PostOrderIterator.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/STLForwardCompat.h"
32#include "llvm/ADT/SmallString.h"
33#include "llvm/ADT/StringExtras.h"
34#include "llvm/ADT/StringRef.h"
35#include "llvm/ADT/TypeSwitch.h"
36#include "llvm/ADT/bit.h"
37#include "llvm/Support/InterleavedRange.h"
38#include <cstddef>
39#include <iterator>
40#include <optional>
41#include <variant>
42
43#include "mlir/Dialect/OpenMP/OpenMPOpsDialect.cpp.inc"
44#include "mlir/Dialect/OpenMP/OpenMPOpsEnums.cpp.inc"
45#include "mlir/Dialect/OpenMP/OpenMPOpsInterfaces.cpp.inc"
46#include "mlir/Dialect/OpenMP/OpenMPTypeInterfaces.cpp.inc"
47
48using namespace mlir;
49using namespace mlir::omp;
50
53 return attrs.empty() ? nullptr : ArrayAttr::get(context, attrs);
54}
55
58 return boolArray.empty() ? nullptr : DenseBoolArrayAttr::get(ctx, boolArray);
59}
60
63 return intArray.empty() ? nullptr : DenseI64ArrayAttr::get(ctx, intArray);
64}
65
66namespace {
67struct MemRefPointerLikeModel
68 : public PointerLikeType::ExternalModel<MemRefPointerLikeModel,
69 MemRefType> {
70 Type getElementType(Type pointer) const {
71 return llvm::cast<MemRefType>(pointer).getElementType();
72 }
73};
74
75struct LLVMPointerPointerLikeModel
76 : public PointerLikeType::ExternalModel<LLVMPointerPointerLikeModel,
77 LLVM::LLVMPointerType> {
78 Type getElementType(Type pointer) const { return Type(); }
79};
80} // namespace
81
82/// Generate a name of a canonical loop nest of the format
83/// `<prefix>(_r<idx>_s<idx>)*`. Hereby, `_r<idx>` identifies the region
84/// argument index of an operation that has multiple regions, if the operation
85/// has multiple regions.
86/// `_s<idx>` identifies the position of an operation within a region, where
87/// only operations that may potentially contain loops ("container operations"
88/// i.e. have region arguments) are counted. Again, it is omitted if there is
89/// only one such operation in a region. If there are canonical loops nested
90/// inside each other, also may also use the format `_d<num>` where <num> is the
91/// nesting depth of the loop.
92///
93/// The generated name is a best-effort to make canonical loop unique within an
94/// SSA namespace. This also means that regions with IsolatedFromAbove property
95/// do not consider any parents or siblings.
96static std::string generateLoopNestingName(StringRef prefix,
97 CanonicalLoopOp op) {
98 struct Component {
99 /// If true, this component describes a region operand of an operation (the
100 /// operand's owner) If false, this component describes an operation located
101 /// in a parent region
102 bool isRegionArgOfOp;
103 bool skip = false;
104 bool isUnique = false;
105
106 size_t idx;
107 Operation *op;
108 Region *parentRegion;
109 size_t loopDepth;
110
111 Operation *&getOwnerOp() {
112 assert(isRegionArgOfOp && "Must describe a region operand");
113 return op;
114 }
115 size_t &getArgIdx() {
116 assert(isRegionArgOfOp && "Must describe a region operand");
117 return idx;
118 }
119
120 Operation *&getContainerOp() {
121 assert(!isRegionArgOfOp && "Must describe a operation of a region");
122 return op;
123 }
124 size_t &getOpPos() {
125 assert(!isRegionArgOfOp && "Must describe a operation of a region");
126 return idx;
127 }
128 bool isLoopOp() const {
129 assert(!isRegionArgOfOp && "Must describe a operation of a region");
130 return isa<CanonicalLoopOp>(op);
131 }
132 Region *&getParentRegion() {
133 assert(!isRegionArgOfOp && "Must describe a operation of a region");
134 return parentRegion;
135 }
136 size_t &getLoopDepth() {
137 assert(!isRegionArgOfOp && "Must describe a operation of a region");
138 return loopDepth;
139 }
140
141 void skipIf(bool v = true) { skip = skip || v; }
142 };
143
144 // List of ancestors, from inner to outer.
145 // Alternates between
146 // * region argument of an operation
147 // * operation within a region
148 SmallVector<Component> components;
149
150 // Gather a list of parent regions and operations, and the position within
151 // their parent
152 Operation *o = op.getOperation();
153 while (o) {
154 // Operation within a region
155 Region *r = o->getParentRegion();
156 if (!r)
157 break;
158
159 llvm::ReversePostOrderTraversal<Block *> traversal(&r->getBlocks().front());
160 size_t idx = 0;
161 bool found = false;
162 size_t sequentialIdx = -1;
163 bool isOnlyContainerOp = true;
164 for (Block *b : traversal) {
165 for (Operation &op : *b) {
166 if (&op == o && !found) {
167 sequentialIdx = idx;
168 found = true;
169 }
170 if (op.getNumRegions()) {
171 idx += 1;
172 if (idx > 1)
173 isOnlyContainerOp = false;
174 }
175 if (found && !isOnlyContainerOp)
176 break;
177 }
178 }
179
180 Component &containerOpInRegion = components.emplace_back();
181 containerOpInRegion.isRegionArgOfOp = false;
182 containerOpInRegion.isUnique = isOnlyContainerOp;
183 containerOpInRegion.getContainerOp() = o;
184 containerOpInRegion.getOpPos() = sequentialIdx;
185 containerOpInRegion.getParentRegion() = r;
186
187 Operation *parent = r->getParentOp();
188
189 // Region argument of an operation
190 Component &regionArgOfOperation = components.emplace_back();
191 regionArgOfOperation.isRegionArgOfOp = true;
192 regionArgOfOperation.isUnique = true;
193 regionArgOfOperation.getArgIdx() = 0;
194 regionArgOfOperation.getOwnerOp() = parent;
195
196 // The IsolatedFromAbove trait of the parent operation implies that each
197 // individual region argument has its own separate namespace, so no
198 // ambiguity.
199 if (!parent || parent->hasTrait<mlir::OpTrait::IsIsolatedFromAbove>())
200 break;
201
202 // Component only needed if operation has multiple region operands. Region
203 // arguments may be optional, but we currently do not consider this.
204 if (parent->getRegions().size() > 1) {
205 auto getRegionIndex = [](Operation *o, Region *r) {
206 for (auto [idx, region] : llvm::enumerate(o->getRegions())) {
207 if (&region == r)
208 return idx;
209 }
210 llvm_unreachable("Region not child of its parent operation");
211 };
212 regionArgOfOperation.isUnique = false;
213 regionArgOfOperation.getArgIdx() = getRegionIndex(parent, r);
214 }
215
216 // next parent
217 o = parent;
218 }
219
220 // Determine whether a region-argument component is not needed
221 for (Component &c : components)
222 c.skipIf(c.isRegionArgOfOp && c.isUnique);
223
224 // Find runs of nested loops and determine each loop's depth in the loop nest
225 size_t numSurroundingLoops = 0;
226 for (Component &c : llvm::reverse(components)) {
227 if (c.skip)
228 continue;
229
230 // non-skipped multi-argument operands interrupt the loop nest
231 if (c.isRegionArgOfOp) {
232 numSurroundingLoops = 0;
233 continue;
234 }
235
236 // Multiple loops in a region means each of them is the outermost loop of a
237 // new loop nest
238 if (!c.isUnique)
239 numSurroundingLoops = 0;
240
241 c.getLoopDepth() = numSurroundingLoops;
242
243 // Next loop is surrounded by one more loop
244 if (isa<CanonicalLoopOp>(c.getContainerOp()))
245 numSurroundingLoops += 1;
246 }
247
248 // In loop nests, skip all but the innermost loop that contains the depth
249 // number
250 bool isLoopNest = false;
251 for (Component &c : components) {
252 if (c.skip || c.isRegionArgOfOp)
253 continue;
254
255 if (!isLoopNest && c.getLoopDepth() >= 1) {
256 // Innermost loop of a loop nest of at least two loops
257 isLoopNest = true;
258 } else if (isLoopNest) {
259 // Non-innermost loop of a loop nest
260 c.skipIf(c.isUnique);
261
262 // If there is no surrounding loop left, this must have been the outermost
263 // loop; leave loop-nest mode for the next iteration
264 if (c.getLoopDepth() == 0)
265 isLoopNest = false;
266 }
267 }
268
269 // Skip non-loop unambiguous regions (but they should interrupt loop nests, so
270 // we mark them as skipped only after computing loop nests)
271 for (Component &c : components)
272 c.skipIf(!c.isRegionArgOfOp && c.isUnique &&
273 !isa<CanonicalLoopOp>(c.getContainerOp()));
274
275 // Components can be skipped if they are already disambiguated by their parent
276 // (or does not have a parent)
277 bool newRegion = true;
278 for (Component &c : llvm::reverse(components)) {
279 c.skipIf(newRegion && c.isUnique);
280
281 // non-skipped components disambiguate unique children
282 if (!c.skip)
283 newRegion = true;
284
285 // ...except canonical loops that need a suffix for each nest
286 if (!c.isRegionArgOfOp && c.getContainerOp())
287 newRegion = false;
288 }
289
290 // Compile the nesting name string
291 SmallString<64> Name{prefix};
292 llvm::raw_svector_ostream NameOS(Name);
293 for (auto &c : llvm::reverse(components)) {
294 if (c.skip)
295 continue;
296
297 if (c.isRegionArgOfOp)
298 NameOS << "_r" << c.getArgIdx();
299 else if (c.getLoopDepth() >= 1)
300 NameOS << "_d" << c.getLoopDepth();
301 else
302 NameOS << "_s" << c.getOpPos();
303 }
304
305 return NameOS.str().str();
306}
307
308void OpenMPDialect::initialize() {
309 addOperations<
310#define GET_OP_LIST
311#include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
312 >();
313 addAttributes<
314#define GET_ATTRDEF_LIST
315#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"
316 >();
317 addTypes<
318#define GET_TYPEDEF_LIST
319#include "mlir/Dialect/OpenMP/OpenMPOpsTypes.cpp.inc"
320 >();
321
322 declarePromisedInterface<ConvertToLLVMPatternInterface, OpenMPDialect>();
323
324 MemRefType::attachInterface<MemRefPointerLikeModel>(*getContext());
325 LLVM::LLVMPointerType::attachInterface<LLVMPointerPointerLikeModel>(
326 *getContext());
327
328 // Attach default offload module interface to module op to access
329 // offload functionality through
330 mlir::ModuleOp::attachInterface<mlir::omp::OffloadModuleDefaultModel>(
331 *getContext());
332
333 // Attach default declare target interfaces to operations which can be marked
334 // as declare target (Global Operations and Functions/Subroutines in dialects
335 // that Fortran (or other languages that lower to MLIR) translates too
336 mlir::LLVM::GlobalOp::attachInterface<
338 *getContext());
339 mlir::LLVM::LLVMFuncOp::attachInterface<
341 *getContext());
342 mlir::func::FuncOp::attachInterface<
344}
345
346//===----------------------------------------------------------------------===//
347// Dialect operation attribute verification
348//===----------------------------------------------------------------------===//
349
350static LogicalResult verifyDeclareTargetAttr(Operation *op, Attribute attr) {
351 if (!isa<DeclareTargetInterface>(op))
352 return op->emitError() << "omp.declare_target can only be applied to "
353 "DeclareTargetInterface ops";
354
355 auto declareTargetAttr = dyn_cast<DeclareTargetAttr>(attr);
356 if (!declareTargetAttr)
357 return op->emitError()
358 << "omp.declare_target must be an #omp.declaretarget attribute";
359
360 if (isa<mlir::FunctionOpInterface>(op)) {
361 if (declareTargetAttr.getAutomap())
362 return op->emitOpError()
363 << "omp.declare_target 'automap' is not valid on functions";
364
365 // TODO: Disallow the `local` clause (OpenMP 6.0).
366 if (declareTargetAttr.getCaptureClause() ==
367 mlir::omp::DeclareTargetCaptureClause::link)
368 return op->emitOpError()
369 << "omp.declare_target 'link' is not valid on functions";
370 } else {
371 // TODO: Disallow the `indirect` clause (OpenMP 5.1).
372 if (declareTargetAttr.getImplicit())
373 return op->emitOpError()
374 << "omp.declare_target 'implicit' is only valid on functions";
375 }
376 return success();
377}
378
379LogicalResult
380OpenMPDialect::verifyOperationAttribute(Operation *op,
381 NamedAttribute attribute) {
382 if (attribute.getName() == "omp.declare_target")
383 return verifyDeclareTargetAttr(op, attribute.getValue());
384
385 return success();
386}
387
388//===----------------------------------------------------------------------===//
389// Parser and printer for Allocate Clause
390//===----------------------------------------------------------------------===//
391
392/// Parse an allocate clause with allocators and a list of operands with types.
393///
394/// allocate-operand-list :: = allocate-operand |
395/// allocator-operand `,` allocate-operand-list
396/// allocate-operand :: = ssa-id-and-type -> ssa-id-and-type
397/// ssa-id-and-type ::= ssa-id `:` type
398static ParseResult parseAllocateAndAllocator(
399 OpAsmParser &parser,
401 SmallVectorImpl<Type> &allocateTypes,
403 SmallVectorImpl<Type> &allocatorTypes) {
404
405 return parser.parseCommaSeparatedList([&]() {
407 Type type;
408 if (parser.parseOperand(operand) || parser.parseColonType(type))
409 return failure();
410 allocatorVars.push_back(operand);
411 allocatorTypes.push_back(type);
412 if (parser.parseArrow())
413 return failure();
414 if (parser.parseOperand(operand) || parser.parseColonType(type))
415 return failure();
416
417 allocateVars.push_back(operand);
418 allocateTypes.push_back(type);
419 return success();
420 });
421}
422
423/// Print allocate clause
425 OperandRange allocateVars,
426 TypeRange allocateTypes,
427 OperandRange allocatorVars,
428 TypeRange allocatorTypes) {
429 for (unsigned i = 0; i < allocateVars.size(); ++i) {
430 std::string separator = i == allocateVars.size() - 1 ? "" : ", ";
431 p << allocatorVars[i] << " : " << allocatorTypes[i] << " -> ";
432 p << allocateVars[i] << " : " << allocateTypes[i] << separator;
433 }
434}
435
436//===----------------------------------------------------------------------===//
437// Parser and printer for a clause attribute (StringEnumAttr)
438//===----------------------------------------------------------------------===//
439
440template <typename ClauseAttr>
441static ParseResult parseClauseAttr(AsmParser &parser, ClauseAttr &attr) {
442 using ClauseT = decltype(std::declval<ClauseAttr>().getValue());
443 StringRef enumStr;
444 SMLoc loc = parser.getCurrentLocation();
445 if (parser.parseKeyword(&enumStr))
446 return failure();
447 if (std::optional<ClauseT> enumValue = symbolizeEnum<ClauseT>(enumStr)) {
448 attr = ClauseAttr::get(parser.getContext(), *enumValue);
449 return success();
450 }
451 return parser.emitError(loc, "invalid clause value: '") << enumStr << "'";
452}
453
454template <typename ClauseAttr>
455static void printClauseAttr(OpAsmPrinter &p, Operation *op, ClauseAttr attr) {
456 p << stringifyEnum(attr.getValue());
457}
458
459//===----------------------------------------------------------------------===//
460// Parser and printer for Linear Clause
461//===----------------------------------------------------------------------===//
462
463/// linear ::= `linear` `(` linear-list `)`
464/// linear-list := linear-val | linear-val linear-list
465/// linear-val := ssa-id-and-type `=` ssa-id-and-type
466/// | `val` `(` ssa-id-and-type `=` ssa-id-and-type `)`
467/// | `ref` `(` ssa-id-and-type `=` ssa-id-and-type `)`
468/// | `uval` `(` ssa-id-and-type `=` ssa-id-and-type `)`
469static ParseResult parseLinearClause(
470 OpAsmParser &parser,
472 SmallVectorImpl<Type> &linearTypes,
474 SmallVectorImpl<Type> &linearStepTypes, ArrayAttr &linearModifiers) {
475 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 if (mapTypeMod == "target_param")
2344 mapTypeBits |= ClauseMapFlags::target_param;
2345
2346 return success();
2347 };
2348
2349 if (parser.parseCommaSeparatedList(parseTypeAndMod))
2350 return failure();
2351
2352 mapType =
2353 parser.getBuilder().getAttr<mlir::omp::ClauseMapFlagsAttr>(mapTypeBits);
2354
2355 return success();
2356}
2357
2358/// Prints a map_entries map type from its numeric value out into its string
2359/// format.
2360static void printMapClause(OpAsmPrinter &p, Operation *op,
2361 ClauseMapFlagsAttr mapType) {
2363 ClauseMapFlags mapFlags = mapType.getValue();
2364
2365 // handling of always, close, present placed at the beginning of the string
2366 // to aid readability
2367 if (mapTypeToBool(mapFlags, ClauseMapFlags::always))
2368 mapTypeStrs.push_back("always");
2369 if (mapTypeToBool(mapFlags, ClauseMapFlags::implicit))
2370 mapTypeStrs.push_back("implicit");
2371 if (mapTypeToBool(mapFlags, ClauseMapFlags::ompx_hold))
2372 mapTypeStrs.push_back("ompx_hold");
2373 if (mapTypeToBool(mapFlags, ClauseMapFlags::close))
2374 mapTypeStrs.push_back("close");
2375 if (mapTypeToBool(mapFlags, ClauseMapFlags::present))
2376 mapTypeStrs.push_back("present");
2377 if (mapTypeToBool(mapFlags, ClauseMapFlags::target_param))
2378 mapTypeStrs.push_back("target_param");
2379
2380 // special handling of to/from/tofrom/delete and release/alloc, release +
2381 // alloc are the abscense of one of the other flags, whereas tofrom requires
2382 // both the to and from flag to be set.
2383 bool to = mapTypeToBool(mapFlags, ClauseMapFlags::to);
2384 bool from = mapTypeToBool(mapFlags, ClauseMapFlags::from);
2385
2386 if (to && from)
2387 mapTypeStrs.push_back("tofrom");
2388 else if (from)
2389 mapTypeStrs.push_back("from");
2390 else if (to)
2391 mapTypeStrs.push_back("to");
2392
2393 if (mapTypeToBool(mapFlags, ClauseMapFlags::del))
2394 mapTypeStrs.push_back("delete");
2395 if (mapTypeToBool(mapFlags, ClauseMapFlags::return_param))
2396 mapTypeStrs.push_back("return_param");
2397 if (mapTypeToBool(mapFlags, ClauseMapFlags::storage))
2398 mapTypeStrs.push_back("storage");
2399 if (mapTypeToBool(mapFlags, ClauseMapFlags::priv))
2400 mapTypeStrs.push_back("private");
2401 if (mapTypeToBool(mapFlags, ClauseMapFlags::literal))
2402 mapTypeStrs.push_back("literal");
2403 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach))
2404 mapTypeStrs.push_back("attach");
2405 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_always))
2406 mapTypeStrs.push_back("attach_always");
2407 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_never))
2408 mapTypeStrs.push_back("attach_never");
2409 if (mapTypeToBool(mapFlags, ClauseMapFlags::attach_auto))
2410 mapTypeStrs.push_back("attach_auto");
2411 if (mapTypeToBool(mapFlags, ClauseMapFlags::ref_ptr))
2412 mapTypeStrs.push_back("ref_ptr");
2413 if (mapTypeToBool(mapFlags, ClauseMapFlags::ref_ptee))
2414 mapTypeStrs.push_back("ref_ptee");
2415 if (mapTypeToBool(mapFlags, ClauseMapFlags::is_device_ptr))
2416 mapTypeStrs.push_back("is_device_ptr");
2417 if (mapFlags == ClauseMapFlags::none)
2418 mapTypeStrs.push_back("none");
2419
2420 for (unsigned int i = 0; i < mapTypeStrs.size(); ++i) {
2421 p << mapTypeStrs[i];
2422 if (i + 1 < mapTypeStrs.size()) {
2423 p << ", ";
2424 }
2425 }
2426}
2427
2428static ParseResult parseMembersIndex(OpAsmParser &parser,
2429 ArrayAttr &membersIdx) {
2430 SmallVector<Attribute> values, memberIdxs;
2431
2432 auto parseIndices = [&]() -> ParseResult {
2433 int64_t value;
2434 if (parser.parseInteger(value))
2435 return failure();
2436 values.push_back(IntegerAttr::get(parser.getBuilder().getIntegerType(64),
2437 APInt(64, value, /*isSigned=*/false)));
2438 return success();
2439 };
2440
2441 do {
2442 if (failed(parser.parseLSquare()))
2443 return failure();
2444
2445 if (parser.parseCommaSeparatedList(parseIndices))
2446 return failure();
2447
2448 if (failed(parser.parseRSquare()))
2449 return failure();
2450
2451 memberIdxs.push_back(ArrayAttr::get(parser.getContext(), values));
2452 values.clear();
2453 } while (succeeded(parser.parseOptionalComma()));
2454
2455 if (!memberIdxs.empty())
2456 membersIdx = ArrayAttr::get(parser.getContext(), memberIdxs);
2457
2458 return success();
2459}
2460
2461static void printMembersIndex(OpAsmPrinter &p, MapInfoOp op,
2462 ArrayAttr membersIdx) {
2463 if (!membersIdx)
2464 return;
2465
2466 llvm::interleaveComma(membersIdx, p, [&p](Attribute v) {
2467 p << "[";
2468 auto memberIdx = cast<ArrayAttr>(v);
2469 llvm::interleaveComma(memberIdx.getValue(), p, [&p](Attribute v2) {
2470 p << cast<IntegerAttr>(v2).getInt();
2471 });
2472 p << "]";
2473 });
2474}
2475
2477 VariableCaptureKindAttr mapCaptureType) {
2478 std::string typeCapStr;
2479 llvm::raw_string_ostream typeCap(typeCapStr);
2480 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::ByRef)
2481 typeCap << "ByRef";
2482 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::ByCopy)
2483 typeCap << "ByCopy";
2484 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::VLAType)
2485 typeCap << "VLAType";
2486 if (mapCaptureType.getValue() == mlir::omp::VariableCaptureKind::This)
2487 typeCap << "This";
2488 p << typeCapStr;
2489}
2490
2491static ParseResult parseCaptureType(OpAsmParser &parser,
2492 VariableCaptureKindAttr &mapCaptureType) {
2493 StringRef mapCaptureKey;
2494 if (parser.parseKeyword(&mapCaptureKey))
2495 return failure();
2496
2497 if (mapCaptureKey == "This")
2498 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2499 parser.getContext(), mlir::omp::VariableCaptureKind::This);
2500 if (mapCaptureKey == "ByRef")
2501 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2502 parser.getContext(), mlir::omp::VariableCaptureKind::ByRef);
2503 if (mapCaptureKey == "ByCopy")
2504 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2505 parser.getContext(), mlir::omp::VariableCaptureKind::ByCopy);
2506 if (mapCaptureKey == "VLAType")
2507 mapCaptureType = mlir::omp::VariableCaptureKindAttr::get(
2508 parser.getContext(), mlir::omp::VariableCaptureKind::VLAType);
2509
2510 return success();
2511}
2512
2513static LogicalResult verifyMapInfoForMapClause(
2514 Operation *op, mlir::omp::MapInfoOp mapInfoOp,
2517 &updateFromVars) {
2518 mlir::omp::ClauseMapFlags mapTypeBits = mapInfoOp.getMapType();
2519
2520 bool to = mapTypeToBool(mapTypeBits, ClauseMapFlags::to);
2521 bool from = mapTypeToBool(mapTypeBits, ClauseMapFlags::from);
2522 bool del = mapTypeToBool(mapTypeBits, ClauseMapFlags::del);
2523
2524 bool always = mapTypeToBool(mapTypeBits, ClauseMapFlags::always);
2525 bool close = mapTypeToBool(mapTypeBits, ClauseMapFlags::close);
2526 bool implicit = mapTypeToBool(mapTypeBits, ClauseMapFlags::implicit);
2527 bool attach = mapTypeToBool(mapTypeBits, ClauseMapFlags::attach);
2528
2529 if ((isa<TargetDataOp>(op) || isa<TargetOp>(op)) && del)
2530 return emitError(op->getLoc(),
2531 "to, from, tofrom and alloc map types are permitted");
2532
2533 if (isa<TargetEnterDataOp>(op) && (from || del))
2534 return emitError(op->getLoc(), "to and alloc map types are permitted");
2535
2536 if (isa<TargetExitDataOp>(op) && to)
2537 return emitError(op->getLoc(),
2538 "from, release and delete map types are permitted");
2539
2540 if (isa<TargetUpdateOp>(op)) {
2541 if (del) {
2542 return emitError(op->getLoc(),
2543 "at least one of to or from map types must be "
2544 "specified, other map types are not permitted");
2545 }
2546
2547 if (!to && !from && !attach) {
2548 return emitError(op->getLoc(),
2549 "at least one of to or from or attach map types must be "
2550 "specified, other map types are not permitted");
2551 }
2552
2553 auto updateVar = mapInfoOp.getVarPtr();
2554
2555 if ((to && from) || (to && updateFromVars.contains(updateVar)) ||
2556 (from && updateToVars.contains(updateVar))) {
2557 return emitError(
2558 op->getLoc(),
2559 "either to or from map types can be specified, not both");
2560 }
2561
2562 if (always || close || implicit) {
2563 return emitError(
2564 op->getLoc(),
2565 "present, mapper and iterator map type modifiers are permitted");
2566 }
2567
2568 // It's possible we have an attach map, in which case if there is no to
2569 // or from tied to it, we skip insertion.
2570 if (to || from) {
2571 to ? updateToVars.insert(updateVar) : updateFromVars.insert(updateVar);
2572 }
2573 }
2574
2575 if ((mapInfoOp.getVarPtrPtr() && !mapInfoOp.getVarPtrPtrType()) ||
2576 (!mapInfoOp.getVarPtrPtr() && mapInfoOp.getVarPtrPtrType())) {
2577 return emitError(op->getLoc(),
2578 "if varPtrPtr or varPtrPtrType is specified, then both "
2579 "must be present");
2580 }
2581
2582 return success();
2583}
2584
2585static LogicalResult verifyMapClause(Operation *op, OperandRange mapVars,
2586 OperandRange mapIterated) {
2589
2590 for (auto mapOp : mapVars) {
2591 if (!mapOp.getDefiningOp())
2592 return emitError(op->getLoc(), "missing map operation");
2593
2594 if (auto mapInfoOp = mapOp.getDefiningOp<mlir::omp::MapInfoOp>()) {
2595 if (failed(verifyMapInfoForMapClause(op, mapInfoOp, updateToVars,
2596 updateFromVars)))
2597 return failure();
2598 } else if (!isa<DeclareMapperInfoOp>(op)) {
2599 return emitError(op->getLoc(),
2600 "map argument is not a map entry operation");
2601 }
2602 }
2603
2604 // Verify iterated map entries.
2605 for (auto iterVal : mapIterated) {
2606 auto iterOp = iterVal.getDefiningOp<mlir::omp::IteratorOp>();
2607 if (!iterOp)
2608 return op->emitOpError() << "'map_iterated' arguments must be defined by "
2609 "'omp.iterator' ops";
2610
2611 // Check that the iterator body yields a value defined by omp.map.info.
2612 auto yieldOp =
2613 cast<mlir::omp::YieldOp>(iterOp.getRegion().front().getTerminator());
2614 auto yieldedMapInfo =
2615 yieldOp.getResults()[0].getDefiningOp<mlir::omp::MapInfoOp>();
2616 if (!yieldedMapInfo)
2617 return op->emitOpError() << "'map_iterated' iterator body must yield "
2618 "a value defined by 'omp.map.info'";
2619
2620 if (failed(verifyMapInfoForMapClause(op, yieldedMapInfo, updateToVars,
2621 updateFromVars)))
2622 return failure();
2623 }
2624
2625 return success();
2626}
2627
2628template <typename OpType>
2629static LogicalResult verifyPrivateVarList(OpType &op);
2630
2631static LogicalResult verifyPrivateVarsMapping(TargetOp targetOp) {
2632 std::optional<DenseI64ArrayAttr> privateMapIndices =
2633 targetOp.getPrivateMapsAttr();
2634
2635 // None of the private operands are mapped.
2636 if (!privateMapIndices.has_value() || !privateMapIndices.value())
2637 return success();
2638
2639 OperandRange privateVars = targetOp.getPrivateVars();
2640
2641 if (privateMapIndices.value().size() !=
2642 static_cast<int64_t>(privateVars.size()))
2643 return emitError(targetOp.getLoc(), "sizes of `private` operand range and "
2644 "`private_maps` attribute mismatch");
2645
2646 return success();
2647}
2648
2649//===----------------------------------------------------------------------===//
2650// MapInfoOp
2651//===----------------------------------------------------------------------===//
2652
2653static LogicalResult verifyMapInfoDefinedArgs(Operation *op,
2654 StringRef clauseName,
2655 OperandRange vars) {
2656 for (Value var : vars)
2657 if (!llvm::isa_and_present<MapInfoOp>(var.getDefiningOp()))
2658 return op->emitOpError()
2659 << "'" << clauseName
2660 << "' arguments must be defined by 'omp.map.info' ops";
2661 return success();
2662}
2663
2664LogicalResult MapInfoOp::verify() {
2665 if (getMapperId() &&
2667 *this, getMapperIdAttr())) {
2668 return emitError("invalid mapper id");
2669 }
2670
2671 if (failed(verifyMapInfoDefinedArgs(*this, "members", getMembers())))
2672 return failure();
2673
2674 return success();
2675}
2676
2677//===----------------------------------------------------------------------===//
2678// TargetDataOp
2679//===----------------------------------------------------------------------===//
2680
2681void TargetDataOp::build(OpBuilder &builder, OperationState &state,
2682 const TargetDataOperands &clauses) {
2683 TargetDataOp::build(builder, state, clauses.device, clauses.ifExpr,
2684 clauses.mapVars, clauses.mapIterated,
2685 clauses.useDeviceAddrVars, clauses.useDevicePtrVars);
2686}
2687
2688LogicalResult TargetDataOp::verify() {
2689 if (getMapVars().empty() && getMapIterated().empty() &&
2690 getUseDevicePtrVars().empty() && getUseDeviceAddrVars().empty()) {
2691 return ::emitError(this->getLoc(),
2692 "At least one of map, use_device_ptr_vars, or "
2693 "use_device_addr_vars operand must be present");
2694 }
2695
2696 if (failed(verifyMapInfoDefinedArgs(*this, "use_device_ptr",
2697 getUseDevicePtrVars())))
2698 return failure();
2699
2700 if (failed(verifyMapInfoDefinedArgs(*this, "use_device_addr",
2701 getUseDeviceAddrVars())))
2702 return failure();
2703
2704 return verifyMapClause(*this, getMapVars(), getMapIterated());
2705}
2706
2707//===----------------------------------------------------------------------===//
2708// TargetEnterDataOp
2709//===----------------------------------------------------------------------===//
2710
2711void TargetEnterDataOp::build(
2712 OpBuilder &builder, OperationState &state,
2713 const TargetEnterExitUpdateDataOperands &clauses) {
2714 MLIRContext *ctx = builder.getContext();
2715 TargetEnterDataOp::build(
2716 builder, state, makeArrayAttr(ctx, clauses.dependKinds),
2717 clauses.dependVars, makeArrayAttr(ctx, clauses.dependIteratedKinds),
2718 clauses.dependIterated, clauses.device, clauses.ifExpr, clauses.mapVars,
2719 clauses.mapIterated, clauses.nowait);
2720}
2721
2722LogicalResult TargetEnterDataOp::verify() {
2723 LogicalResult verifyDependVars =
2724 verifyDependVarList(*this, getDependKinds(), getDependVars(),
2725 getDependIteratedKinds(), getDependIterated());
2726 return failed(verifyDependVars)
2727 ? verifyDependVars
2728 : verifyMapClause(*this, getMapVars(), getMapIterated());
2729}
2730
2731//===----------------------------------------------------------------------===//
2732// TargetExitDataOp
2733//===----------------------------------------------------------------------===//
2734
2735void TargetExitDataOp::build(OpBuilder &builder, OperationState &state,
2736 const TargetEnterExitUpdateDataOperands &clauses) {
2737 MLIRContext *ctx = builder.getContext();
2738 TargetExitDataOp::build(
2739 builder, state, makeArrayAttr(ctx, clauses.dependKinds),
2740 clauses.dependVars, makeArrayAttr(ctx, clauses.dependIteratedKinds),
2741 clauses.dependIterated, clauses.device, clauses.ifExpr, clauses.mapVars,
2742 clauses.mapIterated, clauses.nowait);
2743}
2744
2745LogicalResult TargetExitDataOp::verify() {
2746 LogicalResult verifyDependVars =
2747 verifyDependVarList(*this, getDependKinds(), getDependVars(),
2748 getDependIteratedKinds(), getDependIterated());
2749 return failed(verifyDependVars)
2750 ? verifyDependVars
2751 : verifyMapClause(*this, getMapVars(), getMapIterated());
2752}
2753
2754//===----------------------------------------------------------------------===//
2755// TargetUpdateOp
2756//===----------------------------------------------------------------------===//
2757
2758void TargetUpdateOp::build(OpBuilder &builder, OperationState &state,
2759 const TargetEnterExitUpdateDataOperands &clauses) {
2760 MLIRContext *ctx = builder.getContext();
2761 TargetUpdateOp::build(builder, state, makeArrayAttr(ctx, clauses.dependKinds),
2762 clauses.dependVars,
2763 makeArrayAttr(ctx, clauses.dependIteratedKinds),
2764 clauses.dependIterated, clauses.device, clauses.ifExpr,
2765 clauses.mapVars, clauses.mapIterated, clauses.nowait);
2766}
2767
2768LogicalResult TargetUpdateOp::verify() {
2769 LogicalResult verifyDependVars =
2770 verifyDependVarList(*this, getDependKinds(), getDependVars(),
2771 getDependIteratedKinds(), getDependIterated());
2772 return failed(verifyDependVars)
2773 ? verifyDependVars
2774 : verifyMapClause(*this, getMapVars(), getMapIterated());
2775}
2776
2777//===----------------------------------------------------------------------===//
2778// TargetOp
2779//===----------------------------------------------------------------------===//
2780
2781void TargetOp::build(OpBuilder &builder, OperationState &state,
2782 const TargetExtOperands &clauses) {
2783 MLIRContext *ctx = builder.getContext();
2784 TargetOp::build(
2785 builder, state, clauses.allocateVars, clauses.allocatorVars,
2786 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
2787 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
2788 makeArrayAttr(ctx, clauses.dependKinds), clauses.dependVars,
2789 makeArrayAttr(ctx, clauses.dependIteratedKinds), clauses.dependIterated,
2790 clauses.device, clauses.dynGroupprivateAccessGroup,
2791 clauses.dynGroupprivateFallback, clauses.dynGroupprivateSize,
2792 clauses.hasDeviceAddrVars, clauses.hostEvalVars, clauses.ifExpr,
2793 clauses.inReductionVars,
2794 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),
2795 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.isDevicePtrVars,
2796 clauses.mapVars, clauses.mapIterated, clauses.nowait, clauses.privateVars,
2797 makeArrayAttr(ctx, clauses.privateSyms), clauses.privateNeedsBarrier,
2798 clauses.threadLimitVars, /*private_maps=*/nullptr, clauses.kernelType);
2799}
2800
2801bool TargetOp::hasHostEvalTripCount() {
2802 TargetExecMode mode = getKernelType();
2803 if (mode == TargetExecMode::spmd || mode == TargetExecMode::spmd_no_loop)
2804 return true;
2805
2806 if (mode == TargetExecMode::bare)
2807 return false;
2808
2809 // If it represents a `target teams distribute` construct, also evaluate the
2810 // `distribute` trip count on the host.
2811 Operation *capturedOp =
2812 cast<ComposableOpInterface>(getOperation()).findCapturedOp();
2813 if (auto loopNestOp = dyn_cast_if_present<LoopNestOp>(capturedOp)) {
2815 loopNestOp.gatherWrappers(loopWrappers);
2816
2817 LoopWrapperInterface *innermostWrapper = loopWrappers.begin();
2818 if (isa<SimdOp>(innermostWrapper))
2819 innermostWrapper = std::next(innermostWrapper);
2820
2821 auto numWrappers = std::distance(innermostWrapper, loopWrappers.end());
2822 if (numWrappers != 1)
2823 return false;
2824
2825 if (!isa<DistributeOp>(innermostWrapper))
2826 return false;
2827
2828 Operation *parentOp = innermostWrapper->getOperation()->getParentOp();
2829 if (isa_and_present<TeamsOp>(parentOp) &&
2830 parentOp->getParentOp() == getOperation())
2831 return true;
2832 }
2833
2834 return false;
2835}
2836
2837/// An `omp.target` `in_reduction` operand is captured by a `map_entries` entry
2838/// when the entry's `MapInfoOp` var_ptr is the same SSA value, or another
2839/// result of the same defining op. At this stage, exact identity can only be
2840/// required for block arguments, which have no defining op. Flang emits
2841/// `hlfir.declare` #0 for the `in_reduction` operand and #1 for the map
2842/// `var_ptr`; these collapse to the same value after lowering, but that cannot
2843/// be enforced here.
2844static bool targetInReductionCapturedBy(Value inReductionVar, Value mapVarPtr) {
2845 if (mapVarPtr == inReductionVar)
2846 return true;
2847 Operation *def = inReductionVar.getDefiningOp();
2848 return def && mapVarPtr.getDefiningOp() == def;
2849}
2850
2851LogicalResult TargetOp::verify() {
2853 getOperation(), getAllocateVars(), getAllocatorVars(),
2854 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
2855 getPrivateVars(), getPrivateSymsAttr())))
2856 return failure();
2857
2858 if (getKernelType() == TargetExecMode::bare && !isCombined())
2859 return emitOpError() << "bare kernel requires 'omp.combined'";
2860
2861 if (failed(verifyDependVarList(*this, getDependKinds(), getDependVars(),
2862 getDependIteratedKinds(),
2863 getDependIterated())))
2864 return failure();
2865
2866 if (failed(verifyMapInfoDefinedArgs(*this, "has_device_addr",
2867 getHasDeviceAddrVars())))
2868 return failure();
2869
2870 if (failed(verifyMapClause(*this, getMapVars(), getMapIterated())))
2871 return failure();
2872
2874 *this, getDynGroupprivateAccessGroupAttr(),
2875 getDynGroupprivateFallbackAttr(), getDynGroupprivateSize())))
2876 return failure();
2877
2878 if (failed(verifyPrivateVarList(*this)))
2879 return failure();
2880
2881 if (failed(verifyReductionVarList(*this, getInReductionSyms(),
2882 getInReductionVars(),
2883 getInReductionByref())))
2884 return failure();
2885
2886 // An `in_reduction` operand on `omp.target` has no dedicated entry block
2887 // argument; inside the region it is accessed through the block argument of a
2888 // matching `map_entries` entry, and the host rewrites that map argument to
2889 // the reduction-private storage. Require every `in_reduction` operand to be
2890 // captured by at least one `map_entries` entry.
2891 for (Value inReductionVar : getInReductionVars()) {
2892 bool captured = false;
2893 for (Value mapVar : getMapVars()) {
2894 auto mapInfo = mapVar.getDefiningOp<MapInfoOp>();
2895 if (targetInReductionCapturedBy(inReductionVar, mapInfo.getVarPtr())) {
2896 captured = true;
2897 break;
2898 }
2899 }
2900 if (!captured)
2901 return emitOpError() << "in_reduction variable must be captured by a "
2902 "matching map_entries entry";
2903 }
2904
2905 return verifyPrivateVarsMapping(*this);
2906}
2907
2908LogicalResult TargetOp::verifyRegions() {
2909 auto teamsOps = getOps<TeamsOp>();
2910 auto numNestedTeams = std::distance(teamsOps.begin(), teamsOps.end());
2911 if (numNestedTeams > 1)
2912 return emitError("target containing multiple 'omp.teams' nested ops");
2913
2914 if (numNestedTeams == 0) {
2915 switch (getKernelType()) {
2916 case TargetExecMode::bare:
2917 return emitOpError()
2918 << "bare kernel must contain a nested 'omp.teams' operation";
2919 case TargetExecMode::spmd_no_loop:
2920 return emitOpError() << "spmd_no_loop kernel must contain a nested "
2921 "'omp.teams' operation";
2922 default:
2923 break;
2924 }
2925 }
2926
2927 Operation *capturedOp =
2928 cast<ComposableOpInterface>(getOperation()).findCapturedOp();
2929 if ((getKernelType() == TargetExecMode::spmd ||
2930 getKernelType() == TargetExecMode::spmd_no_loop) &&
2931 !isa_and_present<LoopNestOp>(capturedOp))
2932 return emitOpError()
2933 << "SPMD kernel must capture an 'omp.loop_nest' operation";
2934
2935 bool isTargetDevice = false;
2936 if (auto offloadMod = (*this)->getParentOfType<OffloadModuleInterface>())
2937 if (offloadMod.getIsTargetDevice())
2938 isTargetDevice = true;
2939
2940 // Check that host_eval values are only used in legal ways.
2941 llvm::ArrayRef<BlockArgument> hostEvalBlockArgs =
2942 cast<BlockArgOpenMPOpInterface>(getOperation()).getHostEvalBlockArgs();
2943
2944 bool hostEvalTripCount = hasHostEvalTripCount();
2945 for (Value hostEvalArg : hostEvalBlockArgs) {
2946 for (Operation *user : hostEvalArg.getUsers()) {
2947 if (auto teamsOp = dyn_cast<TeamsOp>(user)) {
2948 // Check if used in num_teams_lower or any of num_teams_upper_vars
2949 if (hostEvalArg == teamsOp.getNumTeamsLower() ||
2950 llvm::is_contained(teamsOp.getNumTeamsUpperVars(), hostEvalArg) ||
2951 llvm::is_contained(teamsOp.getThreadLimitVars(), hostEvalArg))
2952 continue;
2953
2954 return emitOpError() << "host_eval argument only legal as 'num_teams' "
2955 "and 'thread_limit' in 'omp.teams'";
2956 }
2957 if (auto parallelOp = dyn_cast<ParallelOp>(user)) {
2958 if (llvm::is_contained(parallelOp.getNumThreadsVars(), hostEvalArg))
2959 continue;
2960
2961 return emitOpError()
2962 << "host_eval argument only legal as 'num_threads' in "
2963 "'omp.parallel'";
2964 }
2965 if (auto loopNestOp = dyn_cast<LoopNestOp>(user)) {
2966 if (hostEvalTripCount &&
2967 (llvm::is_contained(loopNestOp.getLoopLowerBounds(), hostEvalArg) ||
2968 llvm::is_contained(loopNestOp.getLoopUpperBounds(), hostEvalArg) ||
2969 llvm::is_contained(loopNestOp.getLoopSteps(), hostEvalArg)))
2970 continue;
2971
2972 return emitOpError() << "host_eval argument only legal as loop bounds "
2973 "and steps in 'omp.loop_nest' when trip count "
2974 "must be evaluated in the host";
2975 }
2976
2977 return emitOpError() << "host_eval argument illegal use in '"
2978 << user->getName() << "' operation";
2979 }
2980 }
2981
2982 if (hostEvalTripCount && !isTargetDevice) {
2983 auto loopOp = cast<LoopNestOp>(capturedOp);
2984 for (auto arg : llvm::concat<Value>(loopOp.getLoopLowerBounds(),
2985 loopOp.getLoopUpperBounds(),
2986 loopOp.getLoopSteps())) {
2987 if (!llvm::is_contained(hostEvalBlockArgs, arg))
2988 return emitOpError() << "nested 'omp.loop_nest' bounds expected to "
2989 "be host-evaluated";
2990 }
2991 }
2992
2993 return success();
2994}
2995
2996//===----------------------------------------------------------------------===//
2997// ParallelOp
2998//===----------------------------------------------------------------------===//
2999
3000void ParallelOp::build(OpBuilder &builder, OperationState &state,
3001 ArrayRef<NamedAttribute> attributes) {
3002 ParallelOp::build(builder, state, /*allocate_vars=*/ValueRange(),
3003 /*allocator_vars=*/ValueRange(),
3004 /*allocate_alignments=*/nullptr,
3005 /*allocate_private_indices=*/nullptr, /*if_expr=*/nullptr,
3006 /*num_threads_vars=*/ValueRange(),
3007 /*private_vars=*/ValueRange(),
3008 /*private_syms=*/nullptr, /*private_needs_barrier=*/nullptr,
3009 /*proc_bind_kind=*/nullptr,
3010 /*reduction_mod =*/nullptr, /*reduction_vars=*/ValueRange(),
3011 /*reduction_byref=*/nullptr, /*reduction_syms=*/nullptr);
3012 state.addAttributes(attributes);
3013}
3014
3015void ParallelOp::build(OpBuilder &builder, OperationState &state,
3016 const ParallelOperands &clauses) {
3017 MLIRContext *ctx = builder.getContext();
3018 ParallelOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
3019 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3020 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3021 clauses.ifExpr, clauses.numThreadsVars, clauses.privateVars,
3022 makeArrayAttr(ctx, clauses.privateSyms),
3023 clauses.privateNeedsBarrier, clauses.procBindKind,
3024 clauses.reductionMod, clauses.reductionVars,
3025 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3026 makeArrayAttr(ctx, clauses.reductionSyms));
3027}
3028
3029template <typename OpType>
3030static LogicalResult verifyPrivateVarList(OpType &op) {
3031 auto privateVars = op.getPrivateVars();
3032 auto privateSyms = op.getPrivateSymsAttr();
3033
3034 if (privateVars.empty() && (privateSyms == nullptr || privateSyms.empty()))
3035 return success();
3036
3037 auto numPrivateVars = privateVars.size();
3038 auto numPrivateSyms = (privateSyms == nullptr) ? 0 : privateSyms.size();
3039
3040 if (numPrivateVars != numPrivateSyms)
3041 return op.emitError() << "inconsistent number of private variables and "
3042 "privatizer op symbols, private vars: "
3043 << numPrivateVars
3044 << " vs. privatizer op symbols: " << numPrivateSyms;
3045
3046 for (auto privateVarInfo : llvm::zip_equal(privateVars, privateSyms)) {
3047 Type varType = std::get<0>(privateVarInfo).getType();
3048 SymbolRefAttr privateSym = cast<SymbolRefAttr>(std::get<1>(privateVarInfo));
3049 PrivateClauseOp privatizerOp =
3051
3052 if (privatizerOp == nullptr)
3053 return op.emitError() << "failed to lookup privatizer op with symbol: '"
3054 << privateSym << "'";
3055
3056 Type privatizerType = privatizerOp.getArgType();
3057
3058 if (privatizerType && (varType != privatizerType))
3059 return op.emitError()
3060 << "type mismatch between a "
3061 << (privatizerOp.getDataSharingType() ==
3062 DataSharingClauseType::Private
3063 ? "private"
3064 : "firstprivate")
3065 << " variable and its privatizer op, var type: " << varType
3066 << " vs. privatizer op type: " << privatizerType;
3067 }
3068
3069 return success();
3070}
3071
3072LogicalResult ParallelOp::verify() {
3073 if (failed(verifyPrivateVarList(*this)))
3074 return failure();
3076 getOperation(), getAllocateVars(), getAllocatorVars(),
3077 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3078 getPrivateVars(), getPrivateSymsAttr(),
3079 /*requirePrivateIndices=*/true)))
3080 return failure();
3081
3082 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3083 getReductionByref());
3084}
3085
3086LogicalResult ParallelOp::verifyRegions() {
3087 auto distChildOps = getOps<DistributeOp>();
3088 int numDistChildOps = std::distance(distChildOps.begin(), distChildOps.end());
3089 if (numDistChildOps > 1)
3090 return emitError()
3091 << "multiple 'omp.distribute' nested inside of 'omp.parallel'";
3092
3093 if (numDistChildOps == 1) {
3094 if (!isComposite())
3095 return emitError()
3096 << "'omp.composite' attribute missing from composite operation";
3097
3098 auto *ompDialect = getContext()->getLoadedDialect<OpenMPDialect>();
3099 Operation &distributeOp = **distChildOps.begin();
3100 for (Operation &childOp : getOps()) {
3101 if (&childOp == &distributeOp || ompDialect != childOp.getDialect())
3102 continue;
3103
3104 if (!childOp.hasTrait<OpTrait::IsTerminator>())
3105 return emitError() << "unexpected OpenMP operation inside of composite "
3106 "'omp.parallel': "
3107 << childOp.getName();
3108 }
3109 } else if (isComposite()) {
3110 return emitError()
3111 << "'omp.composite' attribute present in non-composite operation";
3112 }
3113 return success();
3114}
3115
3116//===----------------------------------------------------------------------===//
3117// TeamsOp
3118//===----------------------------------------------------------------------===//
3119
3121 while ((op = op->getParentOp()))
3122 if (isa<OpenMPDialect>(op->getDialect()))
3123 return false;
3124 return true;
3125}
3126
3127void TeamsOp::build(OpBuilder &builder, OperationState &state,
3128 const TeamsOperands &clauses) {
3129 MLIRContext *ctx = builder.getContext();
3130 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier
3131 TeamsOp::build(
3132 builder, state, clauses.allocateVars, clauses.allocatorVars,
3133 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3134 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3135 clauses.dynGroupprivateAccessGroup, clauses.dynGroupprivateFallback,
3136 clauses.dynGroupprivateSize, clauses.ifExpr, clauses.numTeamsLower,
3137 clauses.numTeamsUpperVars, /*private_vars=*/{}, /*private_syms=*/nullptr,
3138 /*private_needs_barrier=*/nullptr, clauses.reductionMod,
3139 clauses.reductionVars,
3140 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3141 makeArrayAttr(ctx, clauses.reductionSyms), clauses.threadLimitVars);
3142}
3143
3144// Verify num_teams clause
3145static LogicalResult verifyNumTeamsClause(Operation *op, Value numTeamsLower,
3146 OperandRange numTeamsUpperVars) {
3147 // If lower is specified, upper must have exactly one value
3148 if (numTeamsLower) {
3149 if (numTeamsUpperVars.size() != 1)
3150 return op->emitError(
3151 "expected exactly one num_teams upper bound when lower bound is "
3152 "specified");
3153 if (numTeamsLower.getType() != numTeamsUpperVars[0].getType())
3154 return op->emitError(
3155 "expected num_teams upper bound and lower bound to be "
3156 "the same type");
3157 }
3158
3159 return success();
3160}
3161
3162LogicalResult TeamsOp::verify() {
3163 // Check parent region
3164 // TODO If nested inside of a target region, also check that it does not
3165 // contain any statements, declarations or directives other than this
3166 // omp.teams construct. The issue is how to support the initialization of
3167 // this operation's own arguments (allow SSA values across omp.target?).
3168 Operation *op = getOperation();
3169 auto parentTarget = llvm::dyn_cast_if_present<TargetOp>(op->getParentOp());
3170 if (!parentTarget && !opInGlobalImplicitParallelRegion(op))
3171 return emitError("expected to be nested inside of omp.target or not nested "
3172 "in any OpenMP dialect operations");
3173
3174 // Check for num_teams clause restrictions
3175 if (failed(verifyNumTeamsClause(op, this->getNumTeamsLower(),
3176 this->getNumTeamsUpperVars())))
3177 return failure();
3178
3179 if (parentTarget &&
3180 parentTarget.getKernelType() == TargetExecMode::spmd_no_loop &&
3181 (getNumTeamsLower() || !getNumTeamsUpperVars().empty()))
3182 return emitOpError() << "'num_teams' not allowed in SPMD-no-loop kernels";
3183
3185 getOperation(), getAllocateVars(), getAllocatorVars(),
3186 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3187 getPrivateVars(), getPrivateSymsAttr())))
3188 return failure();
3189
3191 op, getDynGroupprivateAccessGroupAttr(),
3192 getDynGroupprivateFallbackAttr(), getDynGroupprivateSize())))
3193 return failure();
3194
3195 if (failed(verifyPrivateVarList(*this)))
3196 return failure();
3197
3198 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3199 getReductionByref());
3200}
3201
3202//===----------------------------------------------------------------------===//
3203// SectionOp
3204//===----------------------------------------------------------------------===//
3205
3206OperandRange SectionOp::getPrivateVars() {
3207 return getParentOp().getPrivateVars();
3208}
3209
3210OperandRange SectionOp::getReductionVars() {
3211 return getParentOp().getReductionVars();
3212}
3213
3214//===----------------------------------------------------------------------===//
3215// SectionsOp
3216//===----------------------------------------------------------------------===//
3217
3218void SectionsOp::build(OpBuilder &builder, OperationState &state,
3219 const SectionsOperands &clauses) {
3220 MLIRContext *ctx = builder.getContext();
3221 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier
3222 SectionsOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
3223 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3224 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3225 clauses.nowait, /*private_vars=*/{},
3226 /*private_syms=*/nullptr, /*private_needs_barrier=*/nullptr,
3227 clauses.reductionMod, clauses.reductionVars,
3228 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3229 makeArrayAttr(ctx, clauses.reductionSyms));
3230}
3231
3232LogicalResult SectionsOp::verify() {
3233 if (isCombined())
3234 return emitOpError() << "cannot be a non-innermost combined construct leaf";
3235
3237 getOperation(), getAllocateVars(), getAllocatorVars(),
3238 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3239 getPrivateVars(), getPrivateSymsAttr())))
3240 return failure();
3241
3242 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3243 getReductionByref());
3244}
3245
3246LogicalResult SectionsOp::verifyRegions() {
3247 for (auto &inst : *getRegion().begin()) {
3248 if (!(isa<SectionOp>(inst) || isa<TerminatorOp>(inst))) {
3249 return emitOpError()
3250 << "expected omp.section op or terminator op inside region";
3251 }
3252 }
3253
3254 return success();
3255}
3256
3257//===----------------------------------------------------------------------===//
3258// ScopeOp
3259//===----------------------------------------------------------------------===//
3260
3261void ScopeOp::build(OpBuilder &builder, OperationState &state,
3262 const ScopeOperands &clauses) {
3263 MLIRContext *ctx = builder.getContext();
3264 ScopeOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
3265 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3266 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3267 clauses.nowait, clauses.privateVars,
3268 makeArrayAttr(ctx, clauses.privateSyms),
3269 clauses.privateNeedsBarrier, clauses.reductionMod,
3270 clauses.reductionVars,
3271 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3272 makeArrayAttr(ctx, clauses.reductionSyms));
3273}
3274
3275LogicalResult ScopeOp::verify() {
3277 getOperation(), getAllocateVars(), getAllocatorVars(),
3278 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3279 getPrivateVars(), getPrivateSymsAttr())))
3280 return failure();
3281
3282 if (failed(verifyPrivateVarList(*this)))
3283 return failure();
3284
3285 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3286 getReductionByref());
3287}
3288
3289//===----------------------------------------------------------------------===//
3290// SingleOp
3291//===----------------------------------------------------------------------===//
3292
3293void SingleOp::build(OpBuilder &builder, OperationState &state,
3294 const SingleOperands &clauses) {
3295 MLIRContext *ctx = builder.getContext();
3296 // TODO Store clauses in op: privateVars, privateSyms, privateNeedsBarrier
3297 SingleOp::build(builder, state, clauses.allocateVars, clauses.allocatorVars,
3298 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3299 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3300 clauses.copyprivateVars,
3301 makeArrayAttr(ctx, clauses.copyprivateSyms), clauses.nowait,
3302 /*private_vars=*/{}, /*private_syms=*/nullptr,
3303 /*private_needs_barrier=*/nullptr);
3304}
3305
3306LogicalResult SingleOp::verify() {
3308 getOperation(), getAllocateVars(), getAllocatorVars(),
3309 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3310 getPrivateVars(), getPrivateSymsAttr())))
3311 return failure();
3312
3313 return verifyCopyprivateVarList(*this, getCopyprivateVars(),
3314 getCopyprivateSyms());
3315}
3316
3317//===----------------------------------------------------------------------===//
3318// WorkshareOp
3319//===----------------------------------------------------------------------===//
3320
3321void WorkshareOp::build(OpBuilder &builder, OperationState &state,
3322 const WorkshareOperands &clauses) {
3323 WorkshareOp::build(builder, state, clauses.nowait);
3324}
3325
3326LogicalResult WorkshareOp::verify() {
3327 if (isCombined())
3328 return emitOpError() << "cannot be a non-innermost combined construct leaf";
3329
3330 return success();
3331}
3332
3333//===----------------------------------------------------------------------===//
3334// WorkshareLoopWrapperOp
3335//===----------------------------------------------------------------------===//
3336
3337LogicalResult WorkshareLoopWrapperOp::verifyRegions() {
3338 if (isa_and_nonnull<LoopWrapperInterface>((*this)->getParentOp()) ||
3339 getNestedWrapper())
3340 return emitOpError() << "expected to be a standalone loop wrapper";
3341
3342 return success();
3343}
3344
3345//===----------------------------------------------------------------------===//
3346// LoopWrapperInterface
3347//===----------------------------------------------------------------------===//
3348
3349LogicalResult LoopWrapperInterface::verifyImpl() {
3350 Operation *op = this->getOperation();
3351 if (!op->hasTrait<OpTrait::NoTerminator>() ||
3353 return emitOpError() << "loop wrapper must also have the `NoTerminator` "
3354 "and `SingleBlock` traits";
3355
3356 if (op->getNumRegions() != 1)
3357 return emitOpError() << "loop wrapper does not contain exactly one region";
3358
3359 Region &region = op->getRegion(0);
3360 if (range_size(region.getOps()) != 1)
3361 return emitOpError()
3362 << "loop wrapper does not contain exactly one nested op";
3363
3364 Operation &firstOp = *region.op_begin();
3365 if (!isa<LoopNestOp, LoopWrapperInterface>(firstOp))
3366 return emitOpError() << "nested in loop wrapper is not another loop "
3367 "wrapper or `omp.loop_nest`";
3368
3369 return success();
3370}
3371
3372//===----------------------------------------------------------------------===//
3373// ComposableOpInterface
3374//===----------------------------------------------------------------------===//
3375
3376Operation *ComposableOpInterface::findCapturedOp() {
3377 Operation *op = this->getOperation();
3378
3379 // Handle the composite case by returning the wrapped omp.loop_nest.
3380 if (auto wrapperOp = dyn_cast<LoopWrapperInterface>(op))
3381 return wrapperOp.getWrappedLoop();
3382
3383 // Do not look further if this op is not combined with any of its children.
3384 // Need to check for composite for the omp.parallel case, which is not a loop
3385 // wrapper itself.
3386 if (!isCombined() && !isComposite())
3387 return op;
3388
3389 Region &region = op->getRegion(0);
3390 for (Operation &nestedOp : region.getOps()) {
3391 if (auto wrapperOp = dyn_cast<LoopWrapperInterface>(&nestedOp))
3392 return wrapperOp.getWrappedLoop();
3393
3394 if (auto composableOp = dyn_cast<ComposableOpInterface>(&nestedOp))
3395 return composableOp.findCapturedOp();
3396 }
3397
3398 // This can only be reached if the op has an omp.combined attribute but the
3399 // corresponding nested composable op has been deleted. In that case, it's
3400 // correct to return this operation.
3401 return op;
3402}
3403
3404LogicalResult ComposableOpInterface::verifyImpl() {
3405 Operation *op = this->getOperation();
3406
3407 if (op->getNumRegions() != 1)
3408 return emitOpError() << "composable ops must have a single region";
3409
3410 if (isComposite() && !isa<LoopWrapperInterface, ParallelOp>(op))
3411 return emitOpError() << "non-loop wrapper cannot be composite";
3412
3413 // If combined, must have exactly one eligible nested op (composable or loop
3414 // wrapper).
3415 if (isCombined()) {
3416 Operation *nestedOp = nullptr;
3417 auto count = llvm::count_if(
3418 op->getRegion(0).getOps(), [&nestedOp](mlir::Operation &op) {
3419 if (isa<ComposableOpInterface, LoopWrapperInterface>(op)) {
3420 nestedOp = &op;
3421 return true;
3422 }
3423 return false;
3424 });
3425
3426 // Make an exception for ops marked as omp.combined with no eligible nested
3427 // ops: this situation should be disallowed, but it can be reached if an
3428 // MLIR optimization pass find that the child operation has no side effects
3429 // (many ComposableOpInterface ops have RecursiveMemoryEffects), so it gets
3430 // deleted without updating the parent's attribute.
3431 //
3432 // Since there's a well defined way of handling that situation (treat it as
3433 // non-combined), we relax the requirement here. Ensuring the parent is
3434 // updated every time a pass that can potentially remove a child composable
3435 // op runs is less preferable as a solution.
3436 if (count == 0)
3437 return success();
3438
3439 if (count > 1)
3440 return emitOpError()
3441 << "multiple eligible child ops found in combined op";
3442
3443 // This operation cannot be combined if its captured nested op can be
3444 // executed more than once (i.e. its block's successors can reach it) or if
3445 // it's not guaranteed to be executed before all exits of the region (i.e.
3446 // it doesn't dominate all blocks with no successors reachable from the
3447 // entry block).
3448 DominanceInfo domInfo;
3449 Block *parentBlock = nestedOp->getBlock();
3450
3451 for (Block *successor : parentBlock->getSuccessors())
3452 if (successor->isReachable(parentBlock))
3453 return emitOpError() << "nested combined child op is part of a loop";
3454
3455 for (Block &block : op->getRegion(0))
3456 if (domInfo.isReachableFromEntry(&block) && block.hasNoSuccessors() &&
3457 !domInfo.dominates(parentBlock, &block))
3458 return emitOpError()
3459 << "nested combined child op doesn't unconditionally execute";
3460 }
3461 return success();
3462}
3463
3464//===----------------------------------------------------------------------===//
3465// LoopOp
3466//===----------------------------------------------------------------------===//
3467
3468void LoopOp::build(OpBuilder &builder, OperationState &state,
3469 const LoopOperands &clauses) {
3470 MLIRContext *ctx = builder.getContext();
3471
3472 LoopOp::build(builder, state, clauses.bindKind, clauses.privateVars,
3473 makeArrayAttr(ctx, clauses.privateSyms),
3474 clauses.privateNeedsBarrier, clauses.order, clauses.orderMod,
3475 clauses.reductionMod, clauses.reductionVars,
3476 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3477 makeArrayAttr(ctx, clauses.reductionSyms));
3478}
3479
3480LogicalResult LoopOp::verify() {
3481 if (failed(verifyPrivateVarList(*this)))
3482 return failure();
3483
3484 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3485 getReductionByref());
3486}
3487
3488LogicalResult LoopOp::verifyRegions() {
3489 if (llvm::isa_and_nonnull<LoopWrapperInterface>((*this)->getParentOp()) ||
3490 getNestedWrapper())
3491 return emitOpError() << "expected to be a standalone loop wrapper";
3492
3493 return success();
3494}
3495
3496//===----------------------------------------------------------------------===//
3497// WsloopOp
3498//===----------------------------------------------------------------------===//
3499
3500void WsloopOp::build(OpBuilder &builder, OperationState &state,
3501 ArrayRef<NamedAttribute> attributes) {
3502 build(builder, state, /*allocate_vars=*/{}, /*allocator_vars=*/{},
3503 /*allocate_alignments=*/nullptr,
3504 /*allocate_private_indices=*/nullptr,
3505 /*linear_vars=*/ValueRange(), /*linear_step_vars=*/ValueRange(),
3506 /*linear_var_types*/ nullptr, /*linear_modifiers=*/nullptr,
3507 /*nowait=*/false, /*order=*/nullptr, /*order_mod=*/nullptr,
3508 /*ordered=*/nullptr, /*private_vars=*/{}, /*private_syms=*/nullptr,
3509 /*private_needs_barrier=*/false,
3510 /*reduction_mod=*/nullptr, /*reduction_vars=*/ValueRange(),
3511 /*reduction_byref=*/nullptr,
3512 /*reduction_syms=*/nullptr, /*schedule_kind=*/nullptr,
3513 /*schedule_chunk=*/nullptr, /*schedule_mod=*/nullptr,
3514 /*schedule_simd=*/false);
3515 state.addAttributes(attributes);
3516}
3517
3518void WsloopOp::build(OpBuilder &builder, OperationState &state,
3519 const WsloopOperands &clauses) {
3520 MLIRContext *ctx = builder.getContext();
3521 WsloopOp::build(
3522 builder, state, clauses.allocateVars, clauses.allocatorVars,
3523 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3524 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3525 clauses.linearVars, clauses.linearStepVars, clauses.linearVarTypes,
3526 clauses.linearModifiers, clauses.nowait, clauses.order, clauses.orderMod,
3527 clauses.ordered, clauses.privateVars,
3528 makeArrayAttr(ctx, clauses.privateSyms), clauses.privateNeedsBarrier,
3529 clauses.reductionMod, clauses.reductionVars,
3530 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3531 makeArrayAttr(ctx, clauses.reductionSyms), clauses.scheduleKind,
3532 clauses.scheduleChunk, clauses.scheduleMod, clauses.scheduleSimd);
3533}
3534
3535LogicalResult WsloopOp::verify() {
3537 getOperation(), getAllocateVars(), getAllocatorVars(),
3538 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3539 getPrivateVars(), getPrivateSymsAttr())))
3540 return failure();
3541
3542 if (failed(
3543 verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars())))
3544 return failure();
3545 if (getLinearVars().size() &&
3546 getLinearVarTypes().value().size() != getLinearVars().size())
3547 return emitError() << "Ill-formed type attributes for linear variables";
3548
3549 if (failed(verifyPrivateVarList(*this)))
3550 return failure();
3551
3552 return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
3553 getReductionByref());
3554}
3555
3556LogicalResult WsloopOp::verifyRegions() {
3557 bool isCompositeChildLeaf =
3558 llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
3559
3560 if (LoopWrapperInterface nested = getNestedWrapper()) {
3561 if (!isComposite())
3562 return emitError()
3563 << "'omp.composite' attribute missing from composite wrapper";
3564
3565 // Check for the allowed leaf constructs that may appear in a composite
3566 // construct directly after DO/FOR.
3567 if (!isa<SimdOp>(nested))
3568 return emitError() << "only supported nested wrapper is 'omp.simd'";
3569
3570 } else if (isComposite() && !isCompositeChildLeaf) {
3571 return emitError()
3572 << "'omp.composite' attribute present in non-composite wrapper";
3573 } else if (!isComposite() && isCompositeChildLeaf) {
3574 return emitError()
3575 << "'omp.composite' attribute missing from composite wrapper";
3576 }
3577
3578 return success();
3579}
3580
3581//===----------------------------------------------------------------------===//
3582// Simd construct [2.9.3.1]
3583//===----------------------------------------------------------------------===//
3584
3585void SimdOp::build(OpBuilder &builder, OperationState &state,
3586 const SimdOperands &clauses) {
3587 MLIRContext *ctx = builder.getContext();
3588 SimdOp::build(builder, state, clauses.alignedVars,
3589 makeArrayAttr(ctx, clauses.alignments), clauses.ifExpr,
3590 clauses.linearVars, clauses.linearStepVars,
3591 clauses.linearVarTypes, clauses.linearModifiers,
3592 clauses.nontemporalVars, clauses.order, clauses.orderMod,
3593 clauses.privateVars, makeArrayAttr(ctx, clauses.privateSyms),
3594 clauses.privateNeedsBarrier, clauses.reductionMod,
3595 clauses.reductionVars,
3596 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3597 makeArrayAttr(ctx, clauses.reductionSyms), clauses.safelen,
3598 clauses.simdlen);
3599}
3600
3601LogicalResult SimdOp::verify() {
3602 if (getSimdlen().has_value() && getSafelen().has_value() &&
3603 getSimdlen().value() > getSafelen().value())
3604 return emitOpError()
3605 << "simdlen clause and safelen clause are both present, but the "
3606 "simdlen value is not less than or equal to safelen value";
3607
3608 if (verifyAlignedClause(*this, getAlignments(), getAlignedVars()).failed())
3609 return failure();
3610
3611 if (verifyNontemporalClause(*this, getNontemporalVars()).failed())
3612 return failure();
3613
3614 if (failed(
3615 verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars())))
3616 return failure();
3617
3618 bool isCompositeChildLeaf =
3619 llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp());
3620
3621 if (!isComposite() && isCompositeChildLeaf)
3622 return emitError()
3623 << "'omp.composite' attribute missing from composite wrapper";
3624
3625 if (isComposite() && !isCompositeChildLeaf)
3626 return emitError()
3627 << "'omp.composite' attribute present in non-composite wrapper";
3628
3629 // Firstprivate is not allowed for SIMD in the standard. Check that none of
3630 // the private decls are for firstprivate.
3631 std::optional<ArrayAttr> privateSyms = getPrivateSyms();
3632 if (privateSyms) {
3633 for (const Attribute &sym : *privateSyms) {
3634 auto symRef = cast<SymbolRefAttr>(sym);
3635 omp::PrivateClauseOp privatizer =
3637 getOperation(), symRef);
3638 if (!privatizer)
3639 return emitError() << "Cannot find privatizer '" << symRef << "'";
3640 if (privatizer.getDataSharingType() ==
3641 DataSharingClauseType::FirstPrivate)
3642 return emitError() << "FIRSTPRIVATE cannot be used with SIMD";
3643 }
3644 }
3645
3646 if (failed(verifyPrivateVarList(*this)))
3647 return failure();
3648
3649 if (getLinearVars().size() &&
3650 getLinearVarTypes().value().size() != getLinearVars().size())
3651 return emitError() << "Ill-formed type attributes for linear variables";
3652
3653 llvm::DenseSet<Value> privateVars(llvm::from_range, getPrivateVars());
3654 llvm::DenseSet<Value> reductionVars(llvm::from_range, getReductionVars());
3655 // TODO Check lastprivate vars when their support is added to SimdOp.
3656 for (Value var : getLinearVars()) {
3657 if (privateVars.contains(var) || reductionVars.contains(var))
3658 return emitOpError()
3659 << "linear variables cannot appear in other data-sharing clauses";
3660 }
3661
3662 return success();
3663}
3664
3665LogicalResult SimdOp::verifyRegions() {
3666 if (getNestedWrapper())
3667 return emitOpError() << "must wrap an 'omp.loop_nest' directly";
3668
3669 return success();
3670}
3671
3672//===----------------------------------------------------------------------===//
3673// Distribute construct [2.9.4.1]
3674//===----------------------------------------------------------------------===//
3675
3676void DistributeOp::build(OpBuilder &builder, OperationState &state,
3677 const DistributeOperands &clauses) {
3678 DistributeOp::build(
3679 builder, state, clauses.allocateVars, clauses.allocatorVars,
3680 makeDenseI64ArrayAttr(builder.getContext(), clauses.allocateAlignments),
3682 clauses.allocatePrivateIndices),
3683 clauses.distScheduleStatic, clauses.distScheduleChunkSize, clauses.order,
3684 clauses.orderMod, clauses.privateVars,
3685 makeArrayAttr(builder.getContext(), clauses.privateSyms),
3686 clauses.privateNeedsBarrier);
3687}
3688
3689LogicalResult DistributeOp::verify() {
3690 if (this->getDistScheduleChunkSize() && !this->getDistScheduleStatic())
3691 return emitOpError() << "chunk size set without "
3692 "dist_schedule_static being present";
3693
3695 getOperation(), getAllocateVars(), getAllocatorVars(),
3696 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3697 getPrivateVars(), getPrivateSymsAttr())))
3698 return failure();
3699
3700 if (failed(verifyPrivateVarList(*this)))
3701 return failure();
3702
3703 return success();
3704}
3705
3706LogicalResult DistributeOp::verifyRegions() {
3707 if (LoopWrapperInterface nested = getNestedWrapper()) {
3708 if (!isComposite())
3709 return emitError()
3710 << "'omp.composite' attribute missing from composite wrapper";
3711 // Check for the allowed leaf constructs that may appear in a composite
3712 // construct directly after DISTRIBUTE.
3713 if (isa<WsloopOp>(nested)) {
3714 Operation *parentOp = (*this)->getParentOp();
3715 if (!llvm::dyn_cast_if_present<ParallelOp>(parentOp) ||
3716 !cast<ComposableOpInterface>(parentOp).isComposite()) {
3717 return emitError() << "an 'omp.wsloop' nested wrapper is only allowed "
3718 "when a composite 'omp.parallel' is the direct "
3719 "parent";
3720 }
3721 } else if (!isa<SimdOp>(nested))
3722 return emitError() << "only supported nested wrappers are 'omp.simd' and "
3723 "'omp.wsloop'";
3724 } else if (isComposite()) {
3725 return emitError()
3726 << "'omp.composite' attribute present in non-composite wrapper";
3727 }
3728
3729 return success();
3730}
3731
3732//===----------------------------------------------------------------------===//
3733// DeclareMapperOp / DeclareMapperInfoOp
3734//===----------------------------------------------------------------------===//
3735
3736void DeclareMapperInfoOp::build(OpBuilder &builder, OperationState &state,
3737 const DeclareMapperInfoOperands &clauses) {
3738 DeclareMapperInfoOp::build(builder, state, clauses.mapVars,
3739 clauses.mapIterated);
3740}
3741
3742LogicalResult DeclareMapperInfoOp::verify() {
3743 return verifyMapClause(*this, getMapVars(), getMapIterated());
3744}
3745
3746LogicalResult DeclareMapperOp::verifyRegions() {
3747 if (!llvm::isa_and_present<DeclareMapperInfoOp>(
3748 getRegion().getBlocks().front().getTerminator()))
3749 return emitOpError() << "expected terminator to be a DeclareMapperInfoOp";
3750
3751 return success();
3752}
3753
3754//===----------------------------------------------------------------------===//
3755// DeclareReductionOp
3756//===----------------------------------------------------------------------===//
3757
3758LogicalResult DeclareReductionOp::verifyRegions() {
3759 if (!getAllocRegion().empty()) {
3760 for (YieldOp yieldOp : getAllocRegion().getOps<YieldOp>()) {
3761 if (yieldOp.getResults().size() != 1 ||
3762 yieldOp.getResults().getTypes()[0] != getType())
3763 return emitOpError() << "expects alloc region to yield a value "
3764 "of the reduction type";
3765 }
3766 }
3767
3768 if (getInitializerRegion().empty())
3769 return emitOpError() << "expects non-empty initializer region";
3770 Block &initializerEntryBlock = getInitializerRegion().front();
3771
3772 if (initializerEntryBlock.getNumArguments() == 1) {
3773 if (!getAllocRegion().empty())
3774 return emitOpError() << "expects two arguments to the initializer region "
3775 "when an allocation region is used";
3776 } else if (initializerEntryBlock.getNumArguments() == 2) {
3777 if (getAllocRegion().empty())
3778 return emitOpError() << "expects one argument to the initializer region "
3779 "when no allocation region is used";
3780 } else {
3781 return emitOpError()
3782 << "expects one or two arguments to the initializer region";
3783 }
3784
3785 for (mlir::Value arg : initializerEntryBlock.getArguments())
3786 if (arg.getType() != getType())
3787 return emitOpError() << "expects initializer region argument to match "
3788 "the reduction type";
3789
3790 for (YieldOp yieldOp : getInitializerRegion().getOps<YieldOp>()) {
3791 if (yieldOp.getResults().size() != 1 ||
3792 yieldOp.getResults().getTypes()[0] != getType())
3793 return emitOpError() << "expects initializer region to yield a value "
3794 "of the reduction type";
3795 }
3796
3797 if (getReductionRegion().empty())
3798 return emitOpError() << "expects non-empty reduction region";
3799 Block &reductionEntryBlock = getReductionRegion().front();
3800 if (reductionEntryBlock.getNumArguments() != 2 ||
3801 reductionEntryBlock.getArgumentTypes()[0] !=
3802 reductionEntryBlock.getArgumentTypes()[1] ||
3803 reductionEntryBlock.getArgumentTypes()[0] != getType())
3804 return emitOpError() << "expects reduction region with two arguments of "
3805 "the reduction type";
3806 for (YieldOp yieldOp : getReductionRegion().getOps<YieldOp>()) {
3807 if (yieldOp.getResults().size() != 1 ||
3808 yieldOp.getResults().getTypes()[0] != getType())
3809 return emitOpError() << "expects reduction region to yield a value "
3810 "of the reduction type";
3811 }
3812
3813 if (!getAtomicReductionRegion().empty()) {
3814 Block &atomicReductionEntryBlock = getAtomicReductionRegion().front();
3815 if (atomicReductionEntryBlock.getNumArguments() != 2 ||
3816 atomicReductionEntryBlock.getArgumentTypes()[0] !=
3817 atomicReductionEntryBlock.getArgumentTypes()[1])
3818 return emitOpError() << "expects atomic reduction region with two "
3819 "arguments of the same type";
3820 auto ptrType = llvm::dyn_cast<PointerLikeType>(
3821 atomicReductionEntryBlock.getArgumentTypes()[0]);
3822 if (!ptrType ||
3823 (ptrType.getElementType() && ptrType.getElementType() != getType()))
3824 return emitOpError() << "expects atomic reduction region arguments to "
3825 "be accumulators containing the reduction type";
3826 }
3827
3828 if (getCleanupRegion().empty())
3829 return success();
3830 Block &cleanupEntryBlock = getCleanupRegion().front();
3831 if (cleanupEntryBlock.getNumArguments() != 1 ||
3832 cleanupEntryBlock.getArgument(0).getType() != getType())
3833 return emitOpError() << "expects cleanup region with one argument "
3834 "of the reduction type";
3835
3836 return success();
3837}
3838
3839//===----------------------------------------------------------------------===//
3840// TaskOp
3841//===----------------------------------------------------------------------===//
3842
3843void TaskOp::build(OpBuilder &builder, OperationState &state,
3844 const TaskOperands &clauses) {
3845 MLIRContext *ctx = builder.getContext();
3846 TaskOp::build(builder, state, clauses.iterated, clauses.affinityVars,
3847 clauses.allocateVars, clauses.allocatorVars,
3848 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3849 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3850 makeArrayAttr(ctx, clauses.dependKinds), clauses.dependVars,
3851 makeArrayAttr(ctx, clauses.dependIteratedKinds),
3852 clauses.dependIterated, clauses.final, clauses.ifExpr,
3853 clauses.inReductionVars,
3854 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),
3855 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.mergeable,
3856 clauses.priority, /*private_vars=*/clauses.privateVars,
3857 /*private_syms=*/makeArrayAttr(ctx, clauses.privateSyms),
3858 clauses.privateNeedsBarrier, clauses.threadset, clauses.untied,
3859 clauses.eventHandle);
3860}
3861
3862LogicalResult TaskOp::verify() {
3864 getOperation(), getAllocateVars(), getAllocatorVars(),
3865 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3866 getPrivateVars(), getPrivateSymsAttr())))
3867 return failure();
3868
3869 LogicalResult verifyDependVars =
3870 verifyDependVarList(*this, getDependKinds(), getDependVars(),
3871 getDependIteratedKinds(), getDependIterated());
3872 if (failed(verifyDependVars))
3873 return verifyDependVars;
3874
3875 if (failed(verifyPrivateVarList(*this)))
3876 return failure();
3877
3878 return verifyReductionVarList(*this, getInReductionSyms(),
3879 getInReductionVars(), getInReductionByref());
3880}
3881
3882//===----------------------------------------------------------------------===//
3883// TaskgroupOp
3884//===----------------------------------------------------------------------===//
3885
3886void TaskgroupOp::build(OpBuilder &builder, OperationState &state,
3887 const TaskgroupOperands &clauses) {
3888 MLIRContext *ctx = builder.getContext();
3889 TaskgroupOp::build(builder, state, clauses.allocateVars,
3890 clauses.allocatorVars,
3891 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3892 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices),
3893 clauses.taskReductionVars,
3894 makeDenseBoolArrayAttr(ctx, clauses.taskReductionByref),
3895 makeArrayAttr(ctx, clauses.taskReductionSyms));
3896}
3897
3898LogicalResult TaskgroupOp::verify() {
3900 getOperation(), getAllocateVars(), getAllocatorVars(),
3901 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr())))
3902 return failure();
3903
3904 return verifyReductionVarList(*this, getTaskReductionSyms(),
3905 getTaskReductionVars(),
3906 getTaskReductionByref());
3907}
3908
3909//===----------------------------------------------------------------------===//
3910// TaskloopContextOp
3911//===----------------------------------------------------------------------===//
3912
3913void TaskloopContextOp::build(OpBuilder &builder, OperationState &state,
3914 const TaskloopContextOperands &clauses) {
3915 MLIRContext *ctx = builder.getContext();
3916 TaskloopContextOp::build(
3917 builder, state, clauses.allocateVars, clauses.allocatorVars,
3918 makeDenseI64ArrayAttr(ctx, clauses.allocateAlignments),
3919 makeDenseI64ArrayAttr(ctx, clauses.allocatePrivateIndices), clauses.final,
3920 clauses.grainsizeMod, clauses.grainsize, clauses.ifExpr,
3921 clauses.inReductionVars,
3922 makeDenseBoolArrayAttr(ctx, clauses.inReductionByref),
3923 makeArrayAttr(ctx, clauses.inReductionSyms), clauses.mergeable,
3924 clauses.nogroup, clauses.numTasksMod, clauses.numTasks, clauses.priority,
3925 /*private_vars=*/clauses.privateVars,
3926 /*private_syms=*/makeArrayAttr(ctx, clauses.privateSyms),
3927 clauses.privateNeedsBarrier, clauses.reductionMod, clauses.reductionVars,
3928 makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
3929 makeArrayAttr(ctx, clauses.reductionSyms), clauses.threadset,
3930 clauses.untied);
3931 state.addAttribute("omp.combined", UnitAttr::get(ctx));
3932}
3933
3934TaskloopWrapperOp TaskloopContextOp::getLoopOp() {
3935 return cast<TaskloopWrapperOp>(
3936 *llvm::find_if(getRegion().front(), [](mlir::Operation &op) {
3937 return isa<TaskloopWrapperOp>(op);
3938 }));
3939}
3940
3941LogicalResult TaskloopContextOp::verify() {
3942 if (failed(verifyPrivateVarList(*this)))
3943 return failure();
3945 getOperation(), getAllocateVars(), getAllocatorVars(),
3946 getAllocateAlignmentsAttr(), getAllocatePrivateIndicesAttr(),
3947 getPrivateVars(), getPrivateSymsAttr())))
3948 return failure();
3949
3950 if (failed(verifyReductionVarList(*this, getReductionSyms(),
3951 getReductionVars(), getReductionByref())) ||
3952 failed(verifyReductionVarList(*this, getInReductionSyms(),
3953 getInReductionVars(),
3954 getInReductionByref())))
3955 return failure();
3956
3957 if (!getReductionVars().empty() && getNogroup())
3958 return emitError("if a reduction clause is present on the taskloop "
3959 "directive, the nogroup clause must not be specified");
3960 for (auto var : getReductionVars()) {
3961 if (llvm::is_contained(getInReductionVars(), var))
3962 return emitError("the same list item cannot appear in both a reduction "
3963 "and an in_reduction clause");
3964 }
3965
3966 if (getGrainsize() && getNumTasks()) {
3967 return emitError(
3968 "the grainsize clause and num_tasks clause are mutually exclusive and "
3969 "may not appear on the same taskloop directive");
3970 }
3971
3972 // Without this restriction, any compound construct including `taskloop` would
3973 // fail to correctly identify the whole chain of operations (see
3974 // ComposableOpInterface::findCapturedOp()), as well as failing to do so even
3975 // for standalone `taskloop` constructs.
3976 if (!isCombined())
3977 return emitOpError("must always contain the 'omp.combined' attribute");
3978
3979 return success();
3980}
3981
3982LogicalResult TaskloopContextOp::verifyRegions() {
3983 Region &region = getRegion();
3984 auto loopWrapperIt = llvm::find_if(region.front(), [](mlir::Operation &op) {
3985 return isa<TaskloopWrapperOp>(op);
3986 });
3987 if (loopWrapperIt == region.front().end())
3988 return emitOpError()
3989 << "expected a TaskloopWrapperOp directly nested in the region";
3990
3991 auto loopWrapperOp = cast<TaskloopWrapperOp>(*loopWrapperIt);
3992 auto loopNestOp = dyn_cast<LoopNestOp>(loopWrapperOp.getWrappedLoop());
3993 // This will fail the verifier for TaskloopWrapperOp and print an error
3994 // message there.
3995 if (!loopNestOp)
3996 return failure();
3997
3998 std::function<bool(Value)> isValidBoundValue = [&](Value value) -> bool {
3999 Region *valueRegion = value.getParentRegion();
4000 // A loop bound value defined outside of the taskloop context region is
4001 // valid. A region is considered an ancestor of itself.
4002 if (!region.isAncestor(valueRegion))
4003 return true;
4004
4005 Operation *defOp = value.getDefiningOp();
4006 if (!defOp || defOp->getNumRegions() != 0 || !isPure(defOp))
4007 return false;
4008
4009 return llvm::all_of(defOp->getOperands(), isValidBoundValue);
4010 };
4011 auto hasUnsupportedTaskloopLocalBound = [&](OperandRange range) -> bool {
4012 return llvm::any_of(range,
4013 [&](Value value) { return !isValidBoundValue(value); });
4014 };
4015
4016 if (hasUnsupportedTaskloopLocalBound(loopNestOp.getLoopLowerBounds()) ||
4017 hasUnsupportedTaskloopLocalBound(loopNestOp.getLoopUpperBounds()) ||
4018 hasUnsupportedTaskloopLocalBound(loopNestOp.getLoopSteps())) {
4019 return emitOpError()
4020 << "expects loop bounds and steps to be defined outside of the "
4021 "taskloop.context region or by pure, regionless operations "
4022 "that do not depend on block arguments";
4023 }
4024
4025 return success();
4026}
4027
4028//===----------------------------------------------------------------------===//
4029// TaskloopWrapperOp
4030//===----------------------------------------------------------------------===//
4031
4032void TaskloopWrapperOp::build(OpBuilder &builder, OperationState &state,
4033 const TaskloopWrapperOperands &clauses) {
4034 TaskloopWrapperOp::build(builder, state);
4035}
4036
4037TaskloopContextOp TaskloopWrapperOp::getTaskloopContext() {
4038 return dyn_cast<TaskloopContextOp>(getOperation()->getParentOp());
4039}
4040
4041LogicalResult TaskloopWrapperOp::verify() {
4042 TaskloopContextOp context = getTaskloopContext();
4043 if (!context)
4044 return emitOpError() << "expected to be nested in a taskloop context op";
4045 return success();
4046}
4047
4048LogicalResult TaskloopWrapperOp::verifyRegions() {
4049 if (LoopWrapperInterface nested = getNestedWrapper()) {
4050 if (!isComposite())
4051 return emitError()
4052 << "'omp.composite' attribute missing from composite wrapper";
4053
4054 // Check for the allowed leaf constructs that may appear in a composite
4055 // construct directly after TASKLOOP.
4056 if (!isa<SimdOp>(nested))
4057 return emitError() << "only supported nested wrapper is 'omp.simd'";
4058 } else if (isComposite()) {
4059 return emitError()
4060 << "'omp.composite' attribute present in non-composite wrapper";
4061 }
4062
4063 return success();
4064}
4065
4066//===----------------------------------------------------------------------===//
4067// LoopNestOp
4068//===----------------------------------------------------------------------===//
4069
4070ParseResult LoopNestOp::parse(OpAsmParser &parser, OperationState &result) {
4071 // Parse an opening `(` followed by induction variables followed by `)`
4074 Type loopVarType;
4076 parser.parseColonType(loopVarType) ||
4077 // Parse loop bounds.
4078 parser.parseEqual() ||
4079 parser.parseOperandList(lbs, ivs.size(), OpAsmParser::Delimiter::Paren) ||
4080 parser.parseKeyword("to") ||
4081 parser.parseOperandList(ubs, ivs.size(), OpAsmParser::Delimiter::Paren))
4082 return failure();
4083
4084 for (auto &iv : ivs)
4085 iv.type = loopVarType;
4086
4087 auto *ctx = parser.getBuilder().getContext();
4088 // Parse "inclusive" flag.
4089 if (succeeded(parser.parseOptionalKeyword("inclusive")))
4090 result.addAttribute("loop_inclusive", UnitAttr::get(ctx));
4091
4092 // Parse step values.
4094 if (parser.parseKeyword("step") ||
4095 parser.parseOperandList(steps, ivs.size(), OpAsmParser::Delimiter::Paren))
4096 return failure();
4097
4098 // Parse collapse
4099 int64_t value = 0;
4100 if (!parser.parseOptionalKeyword("collapse") &&
4101 (parser.parseLParen() || parser.parseInteger(value) ||
4102 parser.parseRParen()))
4103 return failure();
4104 if (value > 1)
4105 result.addAttribute(
4106 "collapse_num_loops",
4107 IntegerAttr::get(parser.getBuilder().getI64Type(), value));
4108
4109 // Parse tiles
4111 auto parseTiles = [&]() -> ParseResult {
4112 int64_t tile;
4113 if (parser.parseInteger(tile))
4114 return failure();
4115 tiles.push_back(tile);
4116 return success();
4117 };
4118
4119 if (!parser.parseOptionalKeyword("tiles") &&
4120 (parser.parseLParen() || parser.parseCommaSeparatedList(parseTiles) ||
4121 parser.parseRParen()))
4122 return failure();
4123
4124 if (tiles.size() > 0)
4125 result.addAttribute("tile_sizes", DenseI64ArrayAttr::get(ctx, tiles));
4126
4127 // Parse the body.
4128 Region *region = result.addRegion();
4129 if (parser.parseRegion(*region, ivs))
4130 return failure();
4131
4132 // Resolve operands.
4133 if (parser.resolveOperands(lbs, loopVarType, result.operands) ||
4134 parser.resolveOperands(ubs, loopVarType, result.operands) ||
4135 parser.resolveOperands(steps, loopVarType, result.operands))
4136 return failure();
4137
4138 // Parse the optional attribute list.
4139 return parser.parseOptionalAttrDict(result.attributes);
4140}
4141
4142void LoopNestOp::print(OpAsmPrinter &p) {
4143 Region &region = getRegion();
4144 auto args = region.getArguments();
4145 p << " (" << args << ") : " << args[0].getType() << " = ("
4146 << getLoopLowerBounds() << ") to (" << getLoopUpperBounds() << ") ";
4147 if (getLoopInclusive())
4148 p << "inclusive ";
4149 p << "step (" << getLoopSteps() << ") ";
4150 if (int64_t numCollapse = getCollapseNumLoops())
4151 if (numCollapse > 1)
4152 p << "collapse(" << numCollapse << ") ";
4153
4154 if (const auto tiles = getTileSizes())
4155 p << "tiles(" << tiles.value() << ") ";
4156
4157 p.printRegion(region, /*printEntryBlockArgs=*/false);
4158}
4159
4160void LoopNestOp::build(OpBuilder &builder, OperationState &state,
4161 const LoopNestOperands &clauses) {
4162 MLIRContext *ctx = builder.getContext();
4163 LoopNestOp::build(builder, state, clauses.collapseNumLoops,
4164 clauses.loopLowerBounds, clauses.loopUpperBounds,
4165 clauses.loopSteps, clauses.loopInclusive,
4166 makeDenseI64ArrayAttr(ctx, clauses.tileSizes));
4167}
4168
4169LogicalResult LoopNestOp::verify() {
4170 if (getLoopLowerBounds().empty())
4171 return emitOpError() << "must represent at least one loop";
4172
4173 if (getLoopLowerBounds().size() != getIVs().size())
4174 return emitOpError() << "number of range arguments and IVs do not match";
4175
4176 for (auto [lb, iv] : llvm::zip_equal(getLoopLowerBounds(), getIVs())) {
4177 if (lb.getType() != iv.getType())
4178 return emitOpError()
4179 << "range argument type does not match corresponding IV type";
4180 }
4181
4182 uint64_t numIVs = getIVs().size();
4183
4184 if (const auto &numCollapse = getCollapseNumLoops())
4185 if (numCollapse > numIVs)
4186 return emitOpError()
4187 << "collapse value is larger than the number of loops";
4188
4189 if (const auto &tiles = getTileSizes())
4190 if (tiles.value().size() > numIVs)
4191 return emitOpError() << "too few canonical loops for tile dimensions";
4192
4193 if (!llvm::dyn_cast_if_present<LoopWrapperInterface>((*this)->getParentOp()))
4194 return emitOpError() << "expects parent op to be a loop wrapper";
4195
4196 return success();
4197}
4198
4199void LoopNestOp::gatherWrappers(
4201 Operation *parent = (*this)->getParentOp();
4202 while (auto wrapper =
4203 llvm::dyn_cast_if_present<LoopWrapperInterface>(parent)) {
4204 wrappers.push_back(wrapper);
4205 parent = parent->getParentOp();
4206 }
4207}
4208
4209//===----------------------------------------------------------------------===//
4210// OpenMP canonical loop handling
4211//===----------------------------------------------------------------------===//
4212
4213std::tuple<NewCliOp, OpOperand *, OpOperand *>
4214mlir::omp ::decodeCli(Value cli) {
4215
4216 // Defining a CLI for a generated loop is optional; if there is none then
4217 // there is no followup-tranformation
4218 if (!cli)
4219 return {{}, nullptr, nullptr};
4220
4221 assert(cli.getType() == CanonicalLoopInfoType::get(cli.getContext()) &&
4222 "Unexpected type of cli");
4223
4224 NewCliOp create = cast<NewCliOp>(cli.getDefiningOp());
4225 OpOperand *gen = nullptr;
4226 OpOperand *cons = nullptr;
4227 for (OpOperand &use : cli.getUses()) {
4228 auto op = cast<LoopTransformationInterface>(use.getOwner());
4229
4230 unsigned opnum = use.getOperandNumber();
4231 if (op.isGeneratee(opnum)) {
4232 assert(!gen && "Each CLI may have at most one def");
4233 gen = &use;
4234 } else if (op.isApplyee(opnum)) {
4235 assert(!cons && "Each CLI may have at most one consumer");
4236 cons = &use;
4237 } else {
4238 llvm_unreachable("Unexpected operand for a CLI");
4239 }
4240 }
4241
4242 return {create, gen, cons};
4243}
4244
4245ClauseProcBindKind
4246mlir::omp::convertProcBindKind(llvm::omp::ProcBindKind kind) {
4247 switch (kind) {
4248 case llvm::omp::ProcBindKind::OMP_PROC_BIND_close:
4249 return ClauseProcBindKind::Close;
4250 case llvm::omp::ProcBindKind::OMP_PROC_BIND_master:
4251 return ClauseProcBindKind::Master;
4252 case llvm::omp::ProcBindKind::OMP_PROC_BIND_primary:
4253 return ClauseProcBindKind::Primary;
4254 case llvm::omp::ProcBindKind::OMP_PROC_BIND_spread:
4255 return ClauseProcBindKind::Spread;
4256 case llvm::omp::ProcBindKind::OMP_PROC_BIND_default:
4257 case llvm::omp::ProcBindKind::OMP_PROC_BIND_unknown:
4258 break;
4259 }
4260 llvm_unreachable("unexpected proc-bind kind");
4261}
4262
4263void NewCliOp::build(::mlir::OpBuilder &odsBuilder,
4264 ::mlir::OperationState &odsState) {
4265 odsState.addTypes(CanonicalLoopInfoType::get(odsBuilder.getContext()));
4266}
4267
4268void NewCliOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
4269 Value result = getResult();
4270 auto [newCli, gen, cons] = decodeCli(result);
4271
4272 // Structured binding `gen` cannot be captured in lambdas before C++20
4273 OpOperand *generator = gen;
4274
4275 // Derive the CLI variable name from its generator:
4276 // * "canonloop" for omp.canonical_loop
4277 // * custom name for loop transformation generatees
4278 // * "cli" as fallback if no generator
4279 // * "_r<idx>" suffix for nested loops, where <idx> is the sequential order
4280 // at that level
4281 // * "_s<idx>" suffix for operations with multiple regions, where <idx> is
4282 // the index of that region
4283 std::string cliName{"cli"};
4284 if (gen) {
4285 cliName =
4287 .Case([&](CanonicalLoopOp op) {
4288 return generateLoopNestingName("canonloop", op);
4289 })
4290 .Case([&](UnrollHeuristicOp op) -> std::string {
4291 llvm_unreachable("heuristic unrolling does not generate a loop");
4292 })
4293 .Case([&](FuseOp op) -> std::string {
4294 unsigned opnum = generator->getOperandNumber();
4295 // The position of the first loop to be fused is the same position
4296 // as the resulting fused loop
4297 if (op.getFirst().has_value() && opnum != op.getFirst().value())
4298 return "canonloop_fuse";
4299 else
4300 return "fused";
4301 })
4302 .Case([&](TileOp op) -> std::string {
4303 auto [generateesFirst, generateesCount] =
4304 op.getGenerateesODSOperandIndexAndLength();
4305 unsigned firstGrid = generateesFirst;
4306 unsigned firstIntratile = generateesFirst + generateesCount / 2;
4307 unsigned end = generateesFirst + generateesCount;
4308 unsigned opnum = generator->getOperandNumber();
4309 // In the OpenMP apply and looprange clauses, indices are 1-based
4310 if (firstGrid <= opnum && opnum < firstIntratile) {
4311 unsigned gridnum = opnum - firstGrid + 1;
4312 return ("grid" + Twine(gridnum)).str();
4313 }
4314 if (firstIntratile <= opnum && opnum < end) {
4315 unsigned intratilenum = opnum - firstIntratile + 1;
4316 return ("intratile" + Twine(intratilenum)).str();
4317 }
4318 llvm_unreachable("Unexpected generatee argument");
4319 })
4320 .DefaultUnreachable("TODO: Custom name for this operation");
4321 }
4322
4323 setNameFn(result, cliName);
4324}
4325
4326LogicalResult NewCliOp::verify() {
4327 Value cli = getResult();
4328
4329 assert(cli.getType() == CanonicalLoopInfoType::get(cli.getContext()) &&
4330 "Unexpected type of cli");
4331
4332 // Check that the CLI is used in at most generator and one consumer
4333 OpOperand *gen = nullptr;
4334 OpOperand *cons = nullptr;
4335 for (mlir::OpOperand &use : cli.getUses()) {
4336 auto op = cast<mlir::omp::LoopTransformationInterface>(use.getOwner());
4337
4338 unsigned opnum = use.getOperandNumber();
4339 if (op.isGeneratee(opnum)) {
4340 if (gen) {
4341 InFlightDiagnostic error =
4342 emitOpError("CLI must have at most one generator");
4343 error.attachNote(gen->getOwner()->getLoc())
4344 .append("first generator here:");
4345 error.attachNote(use.getOwner()->getLoc())
4346 .append("second generator here:");
4347 return error;
4348 }
4349
4350 gen = &use;
4351 } else if (op.isApplyee(opnum)) {
4352 if (cons) {
4353 InFlightDiagnostic error =
4354 emitOpError("CLI must have at most one consumer");
4355 error.attachNote(cons->getOwner()->getLoc())
4356 .append("first consumer here:")
4357 .appendOp(*cons->getOwner(),
4358 OpPrintingFlags().printGenericOpForm());
4359 error.attachNote(use.getOwner()->getLoc())
4360 .append("second consumer here:")
4361 .appendOp(*use.getOwner(), OpPrintingFlags().printGenericOpForm());
4362 return error;
4363 }
4364
4365 cons = &use;
4366 } else {
4367 llvm_unreachable("Unexpected operand for a CLI");
4368 }
4369 }
4370
4371 // If the CLI is source of a transformation, it must have a generator
4372 if (cons && !gen) {
4373 InFlightDiagnostic error = emitOpError("CLI has no generator");
4374 error.attachNote(cons->getOwner()->getLoc())
4375 .append("see consumer here: ")
4376 .appendOp(*cons->getOwner(), OpPrintingFlags().printGenericOpForm());
4377 return error;
4378 }
4379
4380 return success();
4381}
4382
4383void CanonicalLoopOp::build(OpBuilder &odsBuilder, OperationState &odsState,
4384 Value tripCount) {
4385 odsState.addOperands(tripCount);
4386 odsState.addOperands(Value());
4387 (void)odsState.addRegion();
4388}
4389
4390void CanonicalLoopOp::build(OpBuilder &odsBuilder, OperationState &odsState,
4391 Value tripCount, ::mlir::Value cli) {
4392 odsState.addOperands(tripCount);
4393 odsState.addOperands(cli);
4394 (void)odsState.addRegion();
4395}
4396
4397void CanonicalLoopOp::getAsmBlockNames(OpAsmSetBlockNameFn setNameFn) {
4398 setNameFn(&getRegion().front(), "body_entry");
4399}
4400
4401void CanonicalLoopOp::getAsmBlockArgumentNames(Region &region,
4402 OpAsmSetValueNameFn setNameFn) {
4403 std::string ivName = generateLoopNestingName("iv", *this);
4404 setNameFn(region.getArgument(0), ivName);
4405}
4406
4407void CanonicalLoopOp::print(OpAsmPrinter &p) {
4408 if (getCli())
4409 p << '(' << getCli() << ')';
4410 p << ' ' << getInductionVar() << " : " << getInductionVar().getType()
4411 << " in range(" << getTripCount() << ") ";
4412
4413 p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,
4414 /*printBlockTerminators=*/true);
4415
4416 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
4417}
4418
4419mlir::ParseResult CanonicalLoopOp::parse(::mlir::OpAsmParser &parser,
4421 CanonicalLoopInfoType cliType =
4422 CanonicalLoopInfoType::get(parser.getContext());
4423
4424 // Parse (optional) omp.cli identifier
4426 SmallVector<mlir::Value, 1> cliOperand;
4427 if (!parser.parseOptionalLParen()) {
4428 if (parser.parseOperand(cli) ||
4429 parser.resolveOperand(cli, cliType, cliOperand) || parser.parseRParen())
4430 return failure();
4431 }
4432
4433 // We derive the type of tripCount from inductionVariable. MLIR requires the
4434 // type of tripCount to be known when calling resolveOperand so we have parse
4435 // the type before processing the inductionVariable.
4436 OpAsmParser::Argument inductionVariable;
4438 if (parser.parseArgument(inductionVariable, /*allowType*/ true) ||
4439 parser.parseKeyword("in") || parser.parseKeyword("range") ||
4440 parser.parseLParen() || parser.parseOperand(tripcount) ||
4441 parser.parseRParen() ||
4442 parser.resolveOperand(tripcount, inductionVariable.type, result.operands))
4443 return failure();
4444
4445 // Parse the loop body.
4446 Region *region = result.addRegion();
4447 if (parser.parseRegion(*region, {inductionVariable}))
4448 return failure();
4449
4450 // We parsed the cli operand forst, but because it is optional, it must be
4451 // last in the operand list.
4452 result.operands.append(cliOperand);
4453
4454 // Parse the optional attribute list.
4455 if (parser.parseOptionalAttrDict(result.attributes))
4456 return failure();
4457
4458 return mlir::success();
4459}
4460
4461LogicalResult CanonicalLoopOp::verify() {
4462 // The region's entry must accept the induction variable
4463 // It can also be empty if just created
4464 if (!getRegion().empty()) {
4465 Region &region = getRegion();
4466 if (region.getNumArguments() != 1)
4467 return emitOpError(
4468 "Canonical loop region must have exactly one argument");
4469
4470 if (getInductionVar().getType() != getTripCount().getType())
4471 return emitOpError(
4472 "Region argument must be the same type as the trip count");
4473 }
4474
4475 return success();
4476}
4477
4478Value CanonicalLoopOp::getInductionVar() { return getRegion().getArgument(0); }
4479
4480std::pair<unsigned, unsigned>
4481CanonicalLoopOp::getApplyeesODSOperandIndexAndLength() {
4482 // No applyees
4483 return {0, 0};
4484}
4485
4486std::pair<unsigned, unsigned>
4487CanonicalLoopOp::getGenerateesODSOperandIndexAndLength() {
4488 return getODSOperandIndexAndLength(odsIndex_cli);
4489}
4490
4491//===----------------------------------------------------------------------===//
4492// UnrollHeuristicOp
4493//===----------------------------------------------------------------------===//
4494
4495void UnrollHeuristicOp::build(::mlir::OpBuilder &odsBuilder,
4496 ::mlir::OperationState &odsState,
4497 ::mlir::Value cli) {
4498 odsState.addOperands(cli);
4499}
4500
4501void UnrollHeuristicOp::print(OpAsmPrinter &p) {
4502 p << '(' << getApplyee() << ')';
4503
4504 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
4505}
4506
4507mlir::ParseResult UnrollHeuristicOp::parse(::mlir::OpAsmParser &parser,
4509 auto cliType = CanonicalLoopInfoType::get(parser.getContext());
4510
4511 if (parser.parseLParen())
4512 return failure();
4513
4515 if (parser.parseOperand(applyee) ||
4516 parser.resolveOperand(applyee, cliType, result.operands))
4517 return failure();
4518
4519 if (parser.parseRParen())
4520 return failure();
4521
4522 // Optional output loop (full unrolling has none)
4523 if (!parser.parseOptionalArrow()) {
4524 if (parser.parseLParen() || parser.parseRParen())
4525 return failure();
4526 }
4527
4528 // Parse the optional attribute list.
4529 if (parser.parseOptionalAttrDict(result.attributes))
4530 return failure();
4531
4532 return mlir::success();
4533}
4534
4535std::pair<unsigned, unsigned>
4536UnrollHeuristicOp ::getApplyeesODSOperandIndexAndLength() {
4537 return getODSOperandIndexAndLength(odsIndex_applyee);
4538}
4539
4540std::pair<unsigned, unsigned>
4541UnrollHeuristicOp::getGenerateesODSOperandIndexAndLength() {
4542 return {0, 0};
4543}
4544
4545//===----------------------------------------------------------------------===//
4546// UnrollFullOp
4547//===----------------------------------------------------------------------===//
4548
4549void UnrollFullOp::build(::mlir::OpBuilder &odsBuilder,
4550 ::mlir::OperationState &odsState, ::mlir::Value cli) {
4551 odsState.addOperands(cli);
4552}
4553
4554void UnrollFullOp::print(OpAsmPrinter &p) {
4555 p << '(' << getApplyee() << ')';
4556
4557 p.printOptionalAttrDict((*this)->getDiscardableAttrDictionary().getValue());
4558}
4559
4560mlir::ParseResult UnrollFullOp::parse(::mlir::OpAsmParser &parser,
4562 auto cliType = CanonicalLoopInfoType::get(parser.getContext());
4563
4564 if (parser.parseLParen())
4565 return failure();
4566
4568 if (parser.parseOperand(applyee) ||
4569 parser.resolveOperand(applyee, cliType, result.operands))
4570 return failure();
4571
4572 if (parser.parseRParen())
4573 return failure();
4574
4575 // Optional output loop; full unrolling has none.
4576 if (!parser.parseOptionalArrow()) {
4577 if (parser.parseLParen() || parser.parseRParen())
4578 return failure();
4579 }
4580
4581 // Parse the optional attribute list.
4582 if (parser.parseOptionalAttrDict(result.attributes))
4583 return failure();
4584
4585 return mlir::success();
4586}
4587
4588std::pair<unsigned, unsigned>
4589UnrollFullOp::getApplyeesODSOperandIndexAndLength() {
4590 return getODSOperandIndexAndLength(odsIndex_applyee);
4591}
4592
4593std::pair<unsigned, unsigned>
4594UnrollFullOp::getGenerateesODSOperandIndexAndLength() {
4595 return {0, 0};
4596}
4597
4598LogicalResult UnrollFullOp::verify() {
4599 auto [create, gen, cons] = decodeCli(getApplyee());
4600 if (!gen)
4601 return emitOpError() << "applyee CLI has no generator";
4602
4603 // Full unrolling leaves no loop, so the trip count must be constant. Only
4604 // omp.canonical_loop states one.
4605 if (auto loop = dyn_cast<CanonicalLoopOp>(gen->getOwner())) {
4606 if (!matchPattern(loop.getTripCount(), m_Constant()))
4607 return emitOpError() << "applyee loop must have a constant trip count";
4608 }
4609
4610 return success();
4611}
4612
4613//===----------------------------------------------------------------------===//
4614// UnrollPartialOp
4615//===----------------------------------------------------------------------===//
4616
4617void UnrollPartialOp::build(::mlir::OpBuilder &odsBuilder,
4619 uint64_t unrollFactor) {
4620 odsState.addOperands(cli);
4621 Properties &props = odsState.getOrAddProperties<Properties>();
4622 props.unroll_factor = odsBuilder.getI64IntegerAttr(unrollFactor);
4623}
4624
4625void UnrollPartialOp::print(OpAsmPrinter &p) {
4626 p << '(' << getApplyee() << ')';
4627
4628 SmallVector<NamedAttribute> attrs((*this)->getDiscardableAttrs());
4629 attrs.emplace_back(getUnrollFactorAttrName(), getUnrollFactorAttr());
4630 llvm::sort(attrs);
4631 p.printOptionalAttrDict(attrs);
4632}
4633
4634mlir::ParseResult UnrollPartialOp::parse(::mlir::OpAsmParser &parser,
4636 auto cliType = CanonicalLoopInfoType::get(parser.getContext());
4637
4638 if (parser.parseLParen())
4639 return failure();
4640
4642 if (parser.parseOperand(applyee) ||
4643 parser.resolveOperand(applyee, cliType, result.operands))
4644 return failure();
4645
4646 if (parser.parseRParen())
4647 return failure();
4648
4649 // The unroll factor is carried by the `unroll_factor` attribute.
4650 if (parser.parseOptionalAttrDict(result.attributes))
4651 return failure();
4652
4653 return mlir::success();
4654}
4655
4656std::pair<unsigned, unsigned>
4657UnrollPartialOp::getApplyeesODSOperandIndexAndLength() {
4658 return getODSOperandIndexAndLength(odsIndex_applyee);
4659}
4660
4661std::pair<unsigned, unsigned>
4662UnrollPartialOp::getGenerateesODSOperandIndexAndLength() {
4663 return {0, 0};
4664}
4665
4666//===----------------------------------------------------------------------===//
4667// TileOp
4668//===----------------------------------------------------------------------===//
4669
4670static void printLoopTransformClis(OpAsmPrinter &p, TileOp op,
4671 OperandRange generatees,
4672 OperandRange applyees) {
4673 if (!generatees.empty())
4674 p << '(' << llvm::interleaved(generatees) << ')';
4675
4676 if (!applyees.empty())
4677 p << " <- (" << llvm::interleaved(applyees) << ')';
4678}
4679
4680static ParseResult parseLoopTransformClis(
4681 OpAsmParser &parser,
4684 if (parser.parseOptionalLess()) {
4685 // Syntax 1: generatees present
4686
4687 if (parser.parseOperandList(generateesOperands,
4689 return failure();
4690
4691 if (parser.parseLess())
4692 return failure();
4693 } else {
4694 // Syntax 2: generatees omitted
4695 }
4696
4697 // Parse `<-` (`<` has already been parsed)
4698 if (parser.parseMinus())
4699 return failure();
4700
4701 if (parser.parseOperandList(applyeesOperands,
4703 return failure();
4704
4705 return success();
4706}
4707
4708/// Check properties of the loop nest consisting of the transformation's
4709/// applyees:
4710/// 1. They are nested inside each other
4711/// 2. They are perfectly nested
4712/// (no code with side-effects in-between the loops)
4713/// 3. They are rectangular
4714/// (loop bounds are invariant in respect to the outer loops)
4715///
4716/// TODO: Generalize for LoopTransformationInterface.
4717static LogicalResult checkApplyeesNesting(TileOp op) {
4718 // Collect the loops from the nest
4719 bool isOnlyCanonLoops = true;
4721 for (Value applyee : op.getApplyees()) {
4722 auto [create, gen, cons] = decodeCli(applyee);
4723
4724 if (!gen)
4725 return op.emitOpError() << "applyee CLI has no generator";
4726
4727 auto loop = dyn_cast_or_null<CanonicalLoopOp>(gen->getOwner());
4728 canonLoops.push_back(loop);
4729 if (!loop)
4730 isOnlyCanonLoops = false;
4731 }
4732
4733 // FIXME: We currently can only verify non-rectangularity and perfect nest of
4734 // omp.canonical_loop.
4735 if (!isOnlyCanonLoops)
4736 return success();
4737
4738 DenseSet<Value> parentIVs;
4739 for (auto i : llvm::seq<int>(1, canonLoops.size())) {
4740 auto parentLoop = canonLoops[i - 1];
4741 auto loop = canonLoops[i];
4742
4743 if (parentLoop.getOperation() != loop.getOperation()->getParentOp())
4744 return op.emitOpError()
4745 << "tiled loop nest must be nested within each other";
4746
4747 parentIVs.insert(parentLoop.getInductionVar());
4748
4749 // Canonical loop must be perfectly nested, i.e. the body of the parent must
4750 // only contain the omp.canonical_loop of the nested loops, and
4751 // omp.terminator
4752 bool isPerfectlyNested = [&]() {
4753 auto &parentBody = parentLoop.getRegion();
4754 if (!parentBody.hasOneBlock())
4755 return false;
4756 auto &parentBlock = parentBody.getBlocks().front();
4757
4758 auto nestedLoopIt = parentBlock.begin();
4759 if (nestedLoopIt == parentBlock.end() ||
4760 (&*nestedLoopIt != loop.getOperation()))
4761 return false;
4762
4763 auto termIt = std::next(nestedLoopIt);
4764 if (termIt == parentBlock.end() || !isa<TerminatorOp>(termIt))
4765 return false;
4766
4767 if (std::next(termIt) != parentBlock.end())
4768 return false;
4769
4770 return true;
4771 }();
4772 if (!isPerfectlyNested)
4773 return op.emitOpError() << "tiled loop nest must be perfectly nested";
4774
4775 if (parentIVs.contains(loop.getTripCount()))
4776 return op.emitOpError() << "tiled loop nest must be rectangular";
4777 }
4778
4779 // TODO: The tile sizes must be computed before the loop, but checking this
4780 // requires dominance analysis. For instance:
4781 //
4782 // %canonloop = omp.new_cli
4783 // omp.canonical_loop(%canonloop) %iv : i32 in range(%tc) {
4784 // // write to %x
4785 // omp.terminator
4786 // }
4787 // %ts = llvm.load %x
4788 // omp.tile <- (%canonloop) sizes(%ts : i32)
4789
4790 return success();
4791}
4792
4793LogicalResult TileOp::verify() {
4794 if (getApplyees().empty())
4795 return emitOpError() << "must apply to at least one loop";
4796
4797 if (getSizes().size() != getApplyees().size())
4798 return emitOpError() << "there must be one tile size for each applyee";
4799
4800 if (!getGeneratees().empty() &&
4801 2 * getSizes().size() != getGeneratees().size())
4802 return emitOpError()
4803 << "expecting two times the number of generatees than applyees";
4804
4805 return checkApplyeesNesting(*this);
4806}
4807
4808std::pair<unsigned, unsigned> TileOp ::getApplyeesODSOperandIndexAndLength() {
4809 return getODSOperandIndexAndLength(odsIndex_applyees);
4810}
4811
4812std::pair<unsigned, unsigned> TileOp::getGenerateesODSOperandIndexAndLength() {
4813 return getODSOperandIndexAndLength(odsIndex_generatees);
4814}
4815
4816//===----------------------------------------------------------------------===//
4817// FuseOp
4818//===----------------------------------------------------------------------===//
4819
4820static void printLoopTransformClis(OpAsmPrinter &p, FuseOp op,
4821 OperandRange generatees,
4822 OperandRange applyees) {
4823 if (!generatees.empty())
4824 p << '(' << llvm::interleaved(generatees) << ')';
4825
4826 if (!applyees.empty())
4827 p << " <- (" << llvm::interleaved(applyees) << ')';
4828}
4829
4830LogicalResult FuseOp::verify() {
4831 if (getApplyees().size() < 2)
4832 return emitOpError() << "must apply to at least two loops";
4833
4834 if (getFirst().has_value() && getCount().has_value()) {
4835 int64_t first = getFirst().value();
4836 int64_t count = getCount().value();
4837 if ((unsigned)(first + count - 1) > getApplyees().size())
4838 return emitOpError() << "the numbers of applyees must be at least first "
4839 "minus one plus count attributes";
4840 if (!getGeneratees().empty() &&
4841 getGeneratees().size() != getApplyees().size() + 1 - count)
4842 return emitOpError() << "the number of generatees must be the number of "
4843 "aplyees plus one minus count";
4844
4845 } else {
4846 if (!getGeneratees().empty() && getGeneratees().size() != 1)
4847 return emitOpError()
4848 << "in a complete fuse the number of generatees must be exactly 1";
4849 }
4850 for (auto &&applyee : getApplyees()) {
4851 auto [create, gen, cons] = decodeCli(applyee);
4852
4853 if (!gen)
4854 return emitOpError() << "applyee CLI has no generator";
4855 auto loop = dyn_cast_or_null<CanonicalLoopOp>(gen->getOwner());
4856 if (!loop)
4857 return emitOpError()
4858 << "currently only supports omp.canonical_loop as applyee";
4859 }
4860 return success();
4861}
4862std::pair<unsigned, unsigned> FuseOp::getApplyeesODSOperandIndexAndLength() {
4863 return getODSOperandIndexAndLength(odsIndex_applyees);
4864}
4865
4866std::pair<unsigned, unsigned> FuseOp::getGenerateesODSOperandIndexAndLength() {
4867 return getODSOperandIndexAndLength(odsIndex_generatees);
4868}
4869
4870//===----------------------------------------------------------------------===//
4871// Critical construct (2.17.1)
4872//===----------------------------------------------------------------------===//
4873
4874void CriticalDeclareOp::build(OpBuilder &builder, OperationState &state,
4875 const CriticalDeclareOperands &clauses) {
4876 CriticalDeclareOp::build(builder, state, clauses.symName,
4877 clauses.symVisibility, clauses.hint);
4878}
4879
4880LogicalResult CriticalDeclareOp::verify() {
4881 return verifySynchronizationHint(*this, getHint());
4882}
4883
4884LogicalResult CriticalOp::verify() {
4885 SymbolRefAttr currentName = getNameAttr();
4886
4887 CriticalOp parentCritical = (*this)->getParentOfType<CriticalOp>();
4888
4889 while (parentCritical) {
4890 SymbolRefAttr parentName = parentCritical.getNameAttr();
4891
4892 if (currentName == parentName) {
4893 if (currentName) {
4894 return emitOpError() << "cannot be nested inside another omp.critical "
4895 "region with the same name ("
4896 << currentName << ")";
4897 } else {
4898 return emitOpError() << "cannot be nested inside another unnamed "
4899 "omp.critical region";
4900 }
4901 }
4902
4903 parentCritical = parentCritical->getParentOfType<CriticalOp>();
4904 }
4905
4906 return success();
4907}
4908
4909LogicalResult CriticalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
4910 if (getNameAttr()) {
4911 SymbolRefAttr symbolRef = getNameAttr();
4912 auto decl = symbolTable.lookupNearestSymbolFrom<CriticalDeclareOp>(
4913 *this, symbolRef);
4914 if (!decl) {
4915 return emitOpError() << "expected symbol reference " << symbolRef
4916 << " to point to a critical declaration";
4917 }
4918 }
4919
4920 return success();
4921}
4922
4923//===----------------------------------------------------------------------===//
4924// Spec 5.1: Error directive (2.5.4)
4925//===----------------------------------------------------------------------===//
4926
4927LogicalResult ErrorOp::verify() {
4928 if (getMessage() && getMessageExpr())
4929 return emitOpError() << "the message must be provided either as a constant "
4930 "`message` attribute or as a `message_expr` "
4931 "operand, but not both";
4932 return success();
4933}
4934
4935//===----------------------------------------------------------------------===//
4936// Ordered construct
4937//===----------------------------------------------------------------------===//
4938
4939static LogicalResult verifyOrderedParent(Operation &op) {
4940 bool hasRegion = op.getNumRegions() > 0;
4941 auto loopOp = op.getParentOfType<LoopNestOp>();
4942 if (!loopOp) {
4943 if (hasRegion)
4944 return success();
4945
4946 // TODO: Consider if this needs to be the case only for the standalone
4947 // variant of the ordered construct.
4948 return op.emitOpError() << "must be nested inside of a loop";
4949 }
4950
4951 Operation *wrapper = loopOp->getParentOp();
4952 if (auto wsloopOp = dyn_cast<WsloopOp>(wrapper)) {
4953 IntegerAttr orderedAttr = wsloopOp.getOrderedAttr();
4954 if (!orderedAttr)
4955 return op.emitOpError() << "the enclosing worksharing-loop region must "
4956 "have an ordered clause";
4957
4958 if (hasRegion && orderedAttr.getInt() != 0)
4959 return op.emitOpError() << "the enclosing loop's ordered clause must not "
4960 "have a parameter present";
4961
4962 if (!hasRegion && orderedAttr.getInt() == 0)
4963 return op.emitOpError() << "the enclosing loop's ordered clause must "
4964 "have a parameter present";
4965 } else if (!isa<SimdOp>(wrapper)) {
4966 return op.emitOpError() << "must be nested inside of a worksharing, simd "
4967 "or worksharing simd loop";
4968 }
4969 return success();
4970}
4971
4972void OrderedOp::build(OpBuilder &builder, OperationState &state,
4973 const OrderedOperands &clauses) {
4974 OrderedOp::build(builder, state, clauses.doacrossDependType,
4975 clauses.doacrossNumLoops, clauses.doacrossDependVars);
4976}
4977
4978LogicalResult OrderedOp::verify() {
4979 if (failed(verifyOrderedParent(**this)))
4980 return failure();
4981
4982 auto wrapper = (*this)->getParentOfType<WsloopOp>();
4983 if (!wrapper || *wrapper.getOrdered() != *getDoacrossNumLoops())
4984 return emitOpError() << "number of variables in depend clause does not "
4985 << "match number of iteration variables in the "
4986 << "doacross loop";
4987
4988 return success();
4989}
4990
4991void OrderedRegionOp::build(OpBuilder &builder, OperationState &state,
4992 const OrderedRegionOperands &clauses) {
4993 OrderedRegionOp::build(builder, state, clauses.parLevelSimd);
4994}
4995
4996LogicalResult OrderedRegionOp::verify() { return verifyOrderedParent(**this); }
4997
4998//===----------------------------------------------------------------------===//
4999// TaskwaitOp
5000//===----------------------------------------------------------------------===//
5001
5002void TaskwaitOp::build(OpBuilder &builder, OperationState &state,
5003 const TaskwaitOperands &clauses) {
5004 // TODO Store clauses in op: depend_iterated_kinds, depend_iterated, nowait.
5005 MLIRContext *ctx = builder.getContext();
5006 TaskwaitOp::build(
5007 builder, state,
5008 /*depend_kinds=*/makeArrayAttr(ctx, clauses.dependKinds),
5009 /*depend_vars=*/clauses.dependVars,
5010 /*depend_iterated_kinds=*/makeArrayAttr(ctx, clauses.dependIteratedKinds),
5011 /*depend_iterated=*/ValueRange(clauses.dependIterated),
5012 /*nowait=*/nullptr);
5013}
5014
5015//===----------------------------------------------------------------------===//
5016// Verifier for AtomicReadOp
5017//===----------------------------------------------------------------------===//
5018
5019LogicalResult AtomicReadOp::verify() {
5020 if (verifyCommon().failed())
5021 return mlir::failure();
5022
5023 int64_t version = 50;
5024 if (auto moduleOp = getOperation()->getParentOfType<ModuleOp>())
5025 if (Attribute verAttr = moduleOp->getDiscardableAttr("omp.version"))
5026 version = llvm::cast<VersionAttr>(verAttr).getVersion();
5027
5028 if (auto mo = getMemoryOrder()) {
5029 if (*mo == ClauseMemoryOrderKind::Release) {
5030 return emitError("memory-order must not be release for atomic reads");
5031 }
5032 if (*mo == ClauseMemoryOrderKind::Acq_rel) {
5033 // acq_rel is prohibited on read only in OpenMP 5.0; allowed in 5.1+.
5034 if (version < 51)
5035 return emitError("memory-order must not be acq_rel for atomic reads");
5036 }
5037 }
5038 return verifySynchronizationHint(*this, getHint());
5039}
5040
5041//===----------------------------------------------------------------------===//
5042// Verifier for AtomicWriteOp
5043//===----------------------------------------------------------------------===//
5044
5045LogicalResult AtomicWriteOp::verify() {
5046 if (verifyCommon().failed())
5047 return mlir::failure();
5048
5049 int64_t version = 50;
5050 if (auto moduleOp = getOperation()->getParentOfType<ModuleOp>())
5051 if (Attribute verAttr = moduleOp->getDiscardableAttr("omp.version"))
5052 version = llvm::cast<VersionAttr>(verAttr).getVersion();
5053
5054 if (auto mo = getMemoryOrder()) {
5055 if (*mo == ClauseMemoryOrderKind::Acquire) {
5056 return emitError("memory-order must not be acquire for atomic writes");
5057 }
5058 if (*mo == ClauseMemoryOrderKind::Acq_rel) {
5059 // acq_rel is prohibited on write only in OpenMP 5.0; allowed in 5.1+.
5060 if (version < 51)
5061 return emitError("memory-order must not be acq_rel for atomic writes");
5062 }
5063 }
5064 return verifySynchronizationHint(*this, getHint());
5065}
5066
5067//===----------------------------------------------------------------------===//
5068// Verifier for AtomicUpdateOp
5069//===----------------------------------------------------------------------===//
5070
5071LogicalResult AtomicUpdateOp::canonicalize(AtomicUpdateOp op,
5072 PatternRewriter &rewriter) {
5073 if (op.isNoOp()) {
5074 rewriter.eraseOp(op);
5075 return success();
5076 }
5077 if (Value writeVal = op.getWriteOpVal()) {
5078 rewriter.replaceOpWithNewOp<AtomicWriteOp>(
5079 op, op.getX(), writeVal, op.getHintAttr(), op.getMemoryOrderAttr());
5080 return success();
5081 }
5082 return failure();
5083}
5084
5085LogicalResult AtomicUpdateOp::verify() {
5086 if (verifyCommon().failed())
5087 return mlir::failure();
5088
5089 int64_t version = 50;
5090 if (auto moduleOp = getOperation()->getParentOfType<ModuleOp>())
5091 if (Attribute verAttr = moduleOp->getDiscardableAttr("omp.version"))
5092 version = llvm::cast<VersionAttr>(verAttr).getVersion();
5093
5094 if (auto mo = getMemoryOrder()) {
5095 if (*mo == ClauseMemoryOrderKind::Acq_rel ||
5096 *mo == ClauseMemoryOrderKind::Acquire) {
5097 // This restriction applies only to OpenMP 5.0; removed in 5.1.
5098 if (version < 51)
5099 return emitError(
5100 "memory-order must not be acq_rel or acquire for atomic updates");
5101 }
5102 }
5103
5104 return verifySynchronizationHint(*this, getHint());
5105}
5106
5107LogicalResult AtomicUpdateOp::verifyRegions() { return verifyRegionsCommon(); }
5108
5109//===----------------------------------------------------------------------===//
5110// Verifier for AtomicCaptureOp
5111//===----------------------------------------------------------------------===//
5112
5113AtomicReadOp AtomicCaptureOp::getAtomicReadOp() {
5114 if (auto op = dyn_cast<AtomicReadOp>(getFirstOp()))
5115 return op;
5116 return dyn_cast<AtomicReadOp>(getSecondOp());
5117}
5118
5119AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() {
5120 if (auto op = dyn_cast<AtomicWriteOp>(getFirstOp()))
5121 return op;
5122 return dyn_cast<AtomicWriteOp>(getSecondOp());
5123}
5124
5125AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {
5126 if (auto op = dyn_cast<AtomicUpdateOp>(getFirstOp()))
5127 return op;
5128 return dyn_cast<AtomicUpdateOp>(getSecondOp());
5129}
5130
5131AtomicCompareOp AtomicCaptureOp::getAtomicCompareOp() {
5132 if (auto op = dyn_cast<AtomicCompareOp>(getFirstOp()))
5133 return op;
5134 return dyn_cast<AtomicCompareOp>(getSecondOp());
5135}
5136
5137LogicalResult AtomicCaptureOp::verify() {
5138 return verifySynchronizationHint(*this, getHint());
5139}
5140
5141LogicalResult AtomicCaptureOp::verifyRegions() {
5142 if (verifyRegionsCommon().failed())
5143 return mlir::failure();
5144
5145 if (getFirstOp()->getInherentAttr("hint").value_or(Attribute{}) ||
5146 getSecondOp()->getInherentAttr("hint").value_or(Attribute{}))
5147 return emitOpError(
5148 "operations inside capture region must not have hint clause");
5149
5150 if (getFirstOp()->getInherentAttr("memory_order").value_or(Attribute{}) ||
5151 getSecondOp()->getInherentAttr("memory_order").value_or(Attribute{}))
5152 return emitOpError(
5153 "operations inside capture region must not have memory_order clause");
5154 return success();
5155}
5156
5157//===----------------------------------------------------------------------===//
5158// AtomicCompareOp
5159//===----------------------------------------------------------------------===//
5160
5161LogicalResult AtomicCompareOp::verify() {
5162 if (verifyCommon().failed())
5163 return mlir::failure();
5164 // OpenMP 5.2 [15.8.3]: the fail clause argument must be one of seq_cst,
5165 // acquire or relaxed ('release' and 'acq_rel' are not valid failure
5166 // orderings and map to invalid cmpxchg failure orderings).
5167 if (auto failOrder = getFailMemoryOrder()) {
5168 if (*failOrder != ClauseMemoryOrderKind::Seq_cst &&
5169 *failOrder != ClauseMemoryOrderKind::Acquire &&
5170 *failOrder != ClauseMemoryOrderKind::Relaxed)
5171 return emitOpError(
5172 "fail_memory_order must be 'seq_cst', 'acquire' or 'relaxed'");
5173 }
5174 return verifySynchronizationHint(*this, getHint());
5175}
5176
5177LogicalResult AtomicCompareOp::verifyRegions() {
5178 if (verifyRegionsCommon().failed())
5179 return mlir::failure();
5180
5181 if (verifyOperator().failed())
5182 return mlir::failure();
5183
5184 Block &block = getRegion().front();
5185
5186 Operation *terminator = block.getTerminator();
5187 if (!terminator || !isa<YieldOp>(terminator))
5188 return emitOpError("region must be terminated with omp.yield");
5189
5190 return success();
5191}
5192
5193//===----------------------------------------------------------------------===//
5194// CancelOp
5195//===----------------------------------------------------------------------===//
5196
5197void CancelOp::build(OpBuilder &builder, OperationState &state,
5198 const CancelOperands &clauses) {
5199 CancelOp::build(builder, state, clauses.cancelDirective, clauses.ifExpr);
5200}
5201
5203 Operation *parent = thisOp->getParentOp();
5204 while (parent) {
5205 if (parent->getDialect() == thisOp->getDialect())
5206 return parent;
5207 parent = parent->getParentOp();
5208 }
5209 return nullptr;
5210}
5211
5212LogicalResult CancelOp::verify() {
5213 ClauseCancellationConstructType cct = getCancelDirective();
5214 // The next OpenMP operation in the chain of parents
5215 Operation *structuralParent = getParentInSameDialect((*this).getOperation());
5216 if (!structuralParent)
5217 return emitOpError() << "Orphaned cancel construct";
5218
5219 if ((cct == ClauseCancellationConstructType::Parallel) &&
5220 !mlir::isa<ParallelOp>(structuralParent)) {
5221 return emitOpError() << "cancel parallel must appear "
5222 << "inside a parallel region";
5223 }
5224 if (cct == ClauseCancellationConstructType::Loop) {
5225 // structural parent will be omp.loop_nest, directly nested inside
5226 // omp.wsloop
5227 auto wsloopOp = mlir::dyn_cast<WsloopOp>(structuralParent->getParentOp());
5228
5229 if (!wsloopOp) {
5230 return emitOpError()
5231 << "cancel loop must appear inside a worksharing-loop region";
5232 }
5233 if (wsloopOp.getNowaitAttr()) {
5234 return emitError() << "A worksharing construct that is canceled "
5235 << "must not have a nowait clause";
5236 }
5237 if (wsloopOp.getOrderedAttr()) {
5238 return emitError() << "A worksharing construct that is canceled "
5239 << "must not have an ordered clause";
5240 }
5241
5242 } else if (cct == ClauseCancellationConstructType::Sections) {
5243 // structural parent will be an omp.section, directly nested inside
5244 // omp.sections
5245 auto sectionsOp =
5246 mlir::dyn_cast<SectionsOp>(structuralParent->getParentOp());
5247 if (!sectionsOp) {
5248 return emitOpError() << "cancel sections must appear "
5249 << "inside a sections region";
5250 }
5251 if (sectionsOp.getNowait()) {
5252 return emitError() << "A sections construct that is canceled "
5253 << "must not have a nowait clause";
5254 }
5255 }
5256 if ((cct == ClauseCancellationConstructType::Taskgroup) &&
5257 (!mlir::isa<omp::TaskOp>(structuralParent) &&
5258 !mlir::isa<omp::TaskloopWrapperOp>(structuralParent->getParentOp()))) {
5259 return emitOpError() << "cancel taskgroup must appear "
5260 << "inside a task region";
5261 }
5262 return success();
5263}
5264
5265//===----------------------------------------------------------------------===//
5266// CancellationPointOp
5267//===----------------------------------------------------------------------===//
5268
5269void CancellationPointOp::build(OpBuilder &builder, OperationState &state,
5270 const CancellationPointOperands &clauses) {
5271 CancellationPointOp::build(builder, state, clauses.cancelDirective);
5272}
5273
5274LogicalResult CancellationPointOp::verify() {
5275 ClauseCancellationConstructType cct = getCancelDirective();
5276 // The next OpenMP operation in the chain of parents
5277 Operation *structuralParent = getParentInSameDialect((*this).getOperation());
5278 if (!structuralParent)
5279 return emitOpError() << "Orphaned cancellation point";
5280
5281 if ((cct == ClauseCancellationConstructType::Parallel) &&
5282 !mlir::isa<ParallelOp>(structuralParent)) {
5283 return emitOpError() << "cancellation point parallel must appear "
5284 << "inside a parallel region";
5285 }
5286 // Strucutal parent here will be an omp.loop_nest. Get the parent of that to
5287 // find the wsloop
5288 if ((cct == ClauseCancellationConstructType::Loop) &&
5289 !mlir::isa<WsloopOp>(structuralParent->getParentOp())) {
5290 return emitOpError() << "cancellation point loop must appear "
5291 << "inside a worksharing-loop region";
5292 }
5293 if ((cct == ClauseCancellationConstructType::Sections) &&
5294 !mlir::isa<omp::SectionOp>(structuralParent)) {
5295 return emitOpError() << "cancellation point sections must appear "
5296 << "inside a sections region";
5297 }
5298 if ((cct == ClauseCancellationConstructType::Taskgroup) &&
5299 (!mlir::isa<omp::TaskOp>(structuralParent) &&
5300 !mlir::isa<omp::TaskloopWrapperOp>(structuralParent->getParentOp()))) {
5301 return emitOpError() << "cancellation point taskgroup must appear "
5302 << "inside a task region";
5303 }
5304 return success();
5305}
5306
5307//===----------------------------------------------------------------------===//
5308// MapBoundsOp
5309//===----------------------------------------------------------------------===//
5310
5311LogicalResult MapBoundsOp::verify() {
5312 auto extent = getExtent();
5313 auto upperbound = getUpperBound();
5314 if (!extent && !upperbound)
5315 return emitError("expected extent or upperbound.");
5316 return success();
5317}
5318
5319void PrivateClauseOp::build(OpBuilder &odsBuilder, OperationState &odsState,
5320 TypeRange /*result_types*/, StringAttr symName,
5321 TypeAttr type) {
5322 PrivateClauseOp::build(
5323 odsBuilder, odsState, symName, /*sym_visibility=*/nullptr, type,
5324 DataSharingClauseTypeAttr::get(odsBuilder.getContext(),
5325 DataSharingClauseType::Private));
5326}
5327
5328LogicalResult PrivateClauseOp::verifyRegions() {
5329 Type argType = getArgType();
5330 auto verifyTerminator = [&](Operation *terminator,
5331 bool yieldsValue) -> LogicalResult {
5332 if (!terminator->getBlock()->getSuccessors().empty())
5333 return success();
5334
5335 if (!llvm::isa<YieldOp>(terminator))
5336 return mlir::emitError(terminator->getLoc())
5337 << "expected exit block terminator to be an `omp.yield` op.";
5338
5339 YieldOp yieldOp = llvm::cast<YieldOp>(terminator);
5340 TypeRange yieldedTypes = yieldOp.getResults().getTypes();
5341
5342 if (!yieldsValue) {
5343 if (yieldedTypes.empty())
5344 return success();
5345
5346 return mlir::emitError(terminator->getLoc())
5347 << "Did not expect any values to be yielded.";
5348 }
5349
5350 if (yieldedTypes.size() == 1 && yieldedTypes.front() == argType)
5351 return success();
5352
5353 auto error = mlir::emitError(yieldOp.getLoc())
5354 << "Invalid yielded value. Expected type: " << argType
5355 << ", got: ";
5356
5357 if (yieldedTypes.empty())
5358 error << "None";
5359 else
5360 error << yieldedTypes;
5361
5362 return error;
5363 };
5364
5365 auto verifyRegion = [&](Region &region, unsigned expectedNumArgs,
5366 StringRef regionName,
5367 bool yieldsValue) -> LogicalResult {
5368 assert(!region.empty());
5369
5370 if (region.getNumArguments() != expectedNumArgs)
5371 return mlir::emitError(region.getLoc())
5372 << "`" << regionName << "`: " << "expected " << expectedNumArgs
5373 << " region arguments, got: " << region.getNumArguments();
5374
5375 for (Block &block : region) {
5376 // MLIR will verify the absence of the terminator for us.
5377 if (!block.mightHaveTerminator())
5378 continue;
5379
5380 if (failed(verifyTerminator(block.getTerminator(), yieldsValue)))
5381 return failure();
5382 }
5383
5384 return success();
5385 };
5386
5387 // Ensure all of the region arguments have the same type
5388 for (Region *region : getRegions())
5389 for (Type ty : region->getArgumentTypes())
5390 if (ty != argType)
5391 return emitError() << "Region argument type mismatch: got " << ty
5392 << " expected " << argType << ".";
5393
5394 mlir::Region &initRegion = getInitRegion();
5395 if (!initRegion.empty() &&
5396 failed(verifyRegion(getInitRegion(), /*expectedNumArgs=*/2, "init",
5397 /*yieldsValue=*/true)))
5398 return failure();
5399
5400 DataSharingClauseType dsType = getDataSharingType();
5401
5402 if (dsType == DataSharingClauseType::Private && !getCopyRegion().empty())
5403 return emitError("`private` clauses do not require a `copy` region.");
5404
5405 if (dsType == DataSharingClauseType::FirstPrivate && getCopyRegion().empty())
5406 return emitError(
5407 "`firstprivate` clauses require at least a `copy` region.");
5408
5409 if (dsType == DataSharingClauseType::FirstPrivate &&
5410 failed(verifyRegion(getCopyRegion(), /*expectedNumArgs=*/2, "copy",
5411 /*yieldsValue=*/true)))
5412 return failure();
5413
5414 if (!getDeallocRegion().empty() &&
5415 failed(verifyRegion(getDeallocRegion(), /*expectedNumArgs=*/1, "dealloc",
5416 /*yieldsValue=*/false)))
5417 return failure();
5418
5419 return success();
5420}
5421
5422//===----------------------------------------------------------------------===//
5423// Spec 5.2: Masked construct (10.5)
5424//===----------------------------------------------------------------------===//
5425
5426void MaskedOp::build(OpBuilder &builder, OperationState &state,
5427 const MaskedOperands &clauses) {
5428 MaskedOp::build(builder, state, clauses.filteredThreadId);
5429}
5430
5431//===----------------------------------------------------------------------===//
5432// Spec 5.2: Scan construct (5.6)
5433//===----------------------------------------------------------------------===//
5434
5435void ScanOp::build(OpBuilder &builder, OperationState &state,
5436 const ScanOperands &clauses) {
5437 ScanOp::build(builder, state, clauses.inclusiveVars, clauses.exclusiveVars);
5438}
5439
5440LogicalResult ScanOp::verify() {
5441 if (hasExclusiveVars() == hasInclusiveVars())
5442 return emitError(
5443 "Exactly one of EXCLUSIVE or INCLUSIVE clause is expected");
5444 if (WsloopOp parentWsLoopOp = (*this)->getParentOfType<WsloopOp>()) {
5445 if (parentWsLoopOp.getReductionModAttr() &&
5446 parentWsLoopOp.getReductionModAttr().getValue() ==
5447 ReductionModifier::inscan)
5448 return success();
5449 }
5450 if (SimdOp parentSimdOp = (*this)->getParentOfType<SimdOp>()) {
5451 if (parentSimdOp.getReductionModAttr() &&
5452 parentSimdOp.getReductionModAttr().getValue() ==
5453 ReductionModifier::inscan)
5454 return success();
5455 }
5456 return emitError("SCAN directive needs to be enclosed within a parent "
5457 "worksharing loop construct or SIMD construct with INSCAN "
5458 "reduction modifier");
5459}
5460
5461/// Verifies align clause in allocate directive
5462LogicalResult verifyAlignment(Operation &op,
5463 std::optional<uint64_t> alignment) {
5464 if (alignment.has_value()) {
5465 if ((alignment.value() != 0) && !llvm::has_single_bit(alignment.value()))
5466 return op.emitError()
5467 << "ALIGN value : " << alignment.value() << " must be power of 2";
5468 }
5469 return success();
5470}
5471
5472LogicalResult AllocateDirOp::verify() {
5473 return verifyAlignment(*getOperation(), getAlign());
5474}
5475
5476//===----------------------------------------------------------------------===//
5477// AllocSharedMemOp
5478//===----------------------------------------------------------------------===//
5479
5480LogicalResult AllocSharedMemOp::verify() {
5481 return verifyAlignment(*getOperation(), getMemAlignment());
5482}
5483
5484//===----------------------------------------------------------------------===//
5485// FreeSharedMemOp
5486//===----------------------------------------------------------------------===//
5487
5488LogicalResult FreeSharedMemOp::verify() {
5489 return verifyAlignment(*getOperation(), getMemAlignment());
5490}
5491
5492//===----------------------------------------------------------------------===//
5493// WorkdistributeOp
5494//===----------------------------------------------------------------------===//
5495
5496LogicalResult WorkdistributeOp::verify() {
5497 if (isCombined())
5498 return emitOpError() << "cannot be a non-innermost combined construct leaf";
5499
5500 // Check that region exists and is not empty
5501 Region &region = getRegion();
5502 if (region.empty())
5503 return emitOpError("region cannot be empty");
5504 // Verify single entry point.
5505 Block &entryBlock = region.front();
5506 if (entryBlock.empty())
5507 return emitOpError("region must contain a structured block");
5508 // Verify single exit point.
5509 bool hasTerminator = false;
5510 for (Block &block : region) {
5511 if (isa<TerminatorOp>(block.back())) {
5512 if (hasTerminator) {
5513 return emitOpError("region must have exactly one terminator");
5514 }
5515 hasTerminator = true;
5516 }
5517 }
5518 if (!hasTerminator) {
5519 return emitOpError("region must be terminated with omp.terminator");
5520 }
5521 auto walkResult = region.walk([&](Operation *op) -> WalkResult {
5522 // No implicit barrier at end
5523 if (isa<BarrierOp>(op)) {
5524 return emitOpError(
5525 "explicit barriers are not allowed in workdistribute region");
5526 }
5527 // Check for invalid nested constructs
5528 if (isa<ParallelOp>(op)) {
5529 return emitOpError(
5530 "nested parallel constructs not allowed in workdistribute");
5531 }
5532 if (isa<TeamsOp>(op)) {
5533 return emitOpError(
5534 "nested teams constructs not allowed in workdistribute");
5535 }
5536 return WalkResult::advance();
5537 });
5538 if (walkResult.wasInterrupted())
5539 return failure();
5540
5541 Operation *parentOp = (*this)->getParentOp();
5542 if (!llvm::dyn_cast<TeamsOp>(parentOp))
5543 return emitOpError("workdistribute must be nested under teams");
5544 return success();
5545}
5546
5547//===----------------------------------------------------------------------===//
5548// Declare simd [7.7]
5549//===----------------------------------------------------------------------===//
5550
5551LogicalResult DeclareSimdOp::verify() {
5552 // Must be nested inside a function-like op
5553 auto func =
5554 dyn_cast_if_present<mlir::FunctionOpInterface>((*this)->getParentOp());
5555 if (!func)
5556 return emitOpError() << "must be nested inside a function";
5557
5558 if (getInbranch() && getNotinbranch())
5559 return emitOpError("cannot have both 'inbranch' and 'notinbranch'");
5560
5561 if (failed(verifyLinearModifiers(*this, getLinearModifiers(), getLinearVars(),
5562 /*isDeclareSimd=*/true)))
5563 return failure();
5564
5565 return verifyAlignedClause(*this, getAlignments(), getAlignedVars());
5566}
5567
5568void DeclareSimdOp::build(OpBuilder &odsBuilder, OperationState &odsState,
5569 const DeclareSimdOperands &clauses) {
5570 MLIRContext *ctx = odsBuilder.getContext();
5571 DeclareSimdOp::build(odsBuilder, odsState, clauses.alignedVars,
5572 makeArrayAttr(ctx, clauses.alignments), clauses.inbranch,
5573 clauses.linearVars, clauses.linearStepVars,
5574 clauses.linearVarTypes, clauses.linearModifiers,
5575 clauses.notinbranch, clauses.simdlen,
5576 clauses.uniformVars);
5577}
5578
5579//===----------------------------------------------------------------------===//
5580// Parser and printer for Uniform Clause
5581//===----------------------------------------------------------------------===//
5582
5583/// uniform ::= `uniform` `(` uniform-list `)`
5584/// uniform-list := uniform-val (`,` uniform-val)*
5585/// uniform-val := ssa-id `:` type
5586static ParseResult
5589 SmallVectorImpl<Type> &uniformTypes) {
5590 return parser.parseCommaSeparatedList([&]() -> mlir::ParseResult {
5591 if (parser.parseOperand(uniformVars.emplace_back()) ||
5592 parser.parseColonType(uniformTypes.emplace_back()))
5593 return mlir::failure();
5594 return mlir::success();
5595 });
5596}
5597
5598/// Print Uniform Clauses
5600 ValueRange uniformVars, TypeRange uniformTypes) {
5601 for (unsigned i = 0; i < uniformVars.size(); ++i) {
5602 if (i != 0)
5603 p << ", ";
5604 p << uniformVars[i] << " : " << uniformTypes[i];
5605 }
5606}
5607
5608//===----------------------------------------------------------------------===//
5609// Parser and printer for Affinity Clause
5610//===----------------------------------------------------------------------===//
5611
5612static ParseResult parseAffinityClause(
5613 OpAsmParser &parser,
5616 SmallVectorImpl<Type> &iteratedTypes,
5617 SmallVectorImpl<Type> &affinityVarTypes) {
5618 if (failed(parseSplitIteratedList(
5619 parser, iterated, iteratedTypes, affinityVars, affinityVarTypes,
5620 /*parsePrefix=*/[&]() -> ParseResult { return success(); })))
5621 return failure();
5622 return success();
5623}
5624
5626 ValueRange iterated, ValueRange affinityVars,
5627 TypeRange iteratedTypes,
5628 TypeRange affinityVarTypes) {
5629 auto nop = [&](Value, Type) {};
5630 printSplitIteratedList(p, iterated, iteratedTypes, affinityVars,
5631 affinityVarTypes,
5632 /*plain prefix*/ nop,
5633 /*iterated prefix*/ nop);
5634}
5635
5636//===----------------------------------------------------------------------===//
5637// Parser, printer, and verifier for Iterator modifier
5638//===----------------------------------------------------------------------===//
5639
5640static ParseResult
5645 SmallVectorImpl<Type> &lbTypes,
5646 SmallVectorImpl<Type> &ubTypes,
5647 SmallVectorImpl<Type> &stepTypes) {
5648
5649 llvm::SMLoc ivLoc = parser.getCurrentLocation();
5651
5652 // Parse induction variables: %i : i32, %j : i32
5653 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
5654 OpAsmParser::Argument &arg = ivArgs.emplace_back();
5655 if (parser.parseArgument(arg))
5656 return failure();
5657
5658 // Optional type, default to Index if not provided
5659 if (succeeded(parser.parseOptionalColon())) {
5660 if (parser.parseType(arg.type))
5661 return failure();
5662 } else {
5663 arg.type = parser.getBuilder().getIndexType();
5664 }
5665 return success();
5666 }))
5667 return failure();
5668
5669 // ) = (
5670 if (parser.parseRParen() || parser.parseEqual() || parser.parseLParen())
5671 return failure();
5672
5673 // Parse Ranges: (%lb to %ub step %st, ...)
5674 if (parser.parseCommaSeparatedList([&]() -> ParseResult {
5675 OpAsmParser::UnresolvedOperand lb, ub, st;
5676 if (parser.parseOperand(lb) || parser.parseKeyword("to") ||
5677 parser.parseOperand(ub) || parser.parseKeyword("step") ||
5678 parser.parseOperand(st))
5679 return failure();
5680
5681 lbs.push_back(lb);
5682 ubs.push_back(ub);
5683 steps.push_back(st);
5684 return success();
5685 }))
5686 return failure();
5687
5688 if (parser.parseRParen())
5689 return failure();
5690
5691 if (ivArgs.size() != lbs.size())
5692 return parser.emitError(ivLoc)
5693 << "mismatch: " << ivArgs.size() << " variables but " << lbs.size()
5694 << " ranges";
5695
5696 for (auto &arg : ivArgs) {
5697 lbTypes.push_back(arg.type);
5698 ubTypes.push_back(arg.type);
5699 stepTypes.push_back(arg.type);
5700 }
5701
5702 return parser.parseRegion(region, ivArgs);
5703}
5704
5706 ValueRange lbs, ValueRange ubs,
5708 TypeRange) {
5709 Block &entry = region.front();
5710
5711 for (unsigned i = 0, e = entry.getNumArguments(); i < e; ++i) {
5712 if (i != 0)
5713 p << ", ";
5714 p.printRegionArgument(entry.getArgument(i));
5715 }
5716 p << ") = (";
5717
5718 // (%lb0 to %ub0 step %step0, %lb1 to %ub1 step %step1, ...)
5719 for (unsigned i = 0, e = lbs.size(); i < e; ++i) {
5720 if (i)
5721 p << ", ";
5722 p << lbs[i] << " to " << ubs[i] << " step " << steps[i];
5723 }
5724 p << ") ";
5725
5726 p.printRegion(region, /*printEntryBlockArgs=*/false,
5727 /*printBlockTerminators=*/true);
5728}
5729
5730LogicalResult IteratorOp::verify() {
5731 auto iteratedTy = llvm::dyn_cast<omp::IteratedType>(getIterated().getType());
5732 if (!iteratedTy)
5733 return emitOpError() << "result must be omp.iterated<entry_ty>";
5734
5735 for (auto [lb, ub, step] : llvm::zip_equal(
5736 getLoopLowerBounds(), getLoopUpperBounds(), getLoopSteps())) {
5737 if (matchPattern(step, m_Zero()))
5738 return emitOpError() << "loop step must not be zero";
5739
5740 IntegerAttr lbAttr;
5741 IntegerAttr ubAttr;
5742 IntegerAttr stepAttr;
5743 if (!matchPattern(lb, m_Constant(&lbAttr)) ||
5744 !matchPattern(ub, m_Constant(&ubAttr)) ||
5745 !matchPattern(step, m_Constant(&stepAttr)))
5746 continue;
5747
5748 const APInt &lbVal = lbAttr.getValue();
5749 const APInt &ubVal = ubAttr.getValue();
5750 const APInt &stepVal = stepAttr.getValue();
5751 if (stepVal.isStrictlyPositive() && lbVal.sgt(ubVal))
5752 return emitOpError() << "positive loop step requires lower bound to be "
5753 "less than or equal to upper bound";
5754 if (stepVal.isNegative() && lbVal.slt(ubVal))
5755 return emitOpError() << "negative loop step requires lower bound to be "
5756 "greater than or equal to upper bound";
5757 }
5758
5759 Block &b = getRegion().front();
5760 auto yield = llvm::dyn_cast<omp::YieldOp>(b.getTerminator());
5761
5762 if (!yield)
5763 return emitOpError() << "region must be terminated by omp.yield";
5764
5765 if (yield.getNumOperands() != 1)
5766 return emitOpError()
5767 << "omp.yield in omp.iterator region must yield exactly one value";
5768
5769 mlir::Type yieldedTy = yield.getOperand(0).getType();
5770 mlir::Type elemTy = iteratedTy.getElementType();
5771
5772 if (yieldedTy != elemTy)
5773 return emitOpError() << "omp.iterated element type (" << elemTy
5774 << ") does not match omp.yield operand type ("
5775 << yieldedTy << ")";
5776
5777 return success();
5778}
5779
5780//===----------------------------------------------------------------------===//
5781// GroupprivateOp
5782//===----------------------------------------------------------------------===//
5783
5784LogicalResult
5785GroupprivateOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
5786 auto *symbol = symbolTable.lookupNearestSymbolFrom(*this, getSymNameAttr());
5787 if (!symbol)
5788 return emitOpError() << "expected symbol reference '" << getSymName()
5789 << "' to point to a global variable";
5790
5791 if (isa<FunctionOpInterface>(symbol))
5792 return emitOpError() << "expected symbol reference '" << getSymName()
5793 << "' to point to a global variable, not a function";
5794
5795 return success();
5796}
5797
5798#define GET_ATTRDEF_CLASSES
5799#include "mlir/Dialect/OpenMP/OpenMPOpsAttributes.cpp.inc"
5800
5801#define GET_OP_CLASSES
5802#include "mlir/Dialect/OpenMP/OpenMPOps.cpp.inc"
5803
5804#define GET_TYPEDEF_CLASSES
5805#include "mlir/Dialect/OpenMP/OpenMPOpsTypes.cpp.inc"
return success()
static std::optional< int64_t > getUpperBound(Value iv)
Gets the constant upper bound on an affine.for iv.
static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region, const Twine &name)
Definition EmitC.cpp:1523
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
ArrayAttr()
b getContext())
static const mlir::GenInfo * generator
static 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 Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
Definition SPIRVOps.cpp:229
static bool isUnique(It begin, It end)
Definition ShardOps.cpp:161
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
static SmallVector< Value > getTileSizes(Location loc, x86::amx::TileType tType, RewriterBase &rewriter)
Maps the 2-dim vector shape to the two 16-bit tile sizes.
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual ParseResult parseMinus()=0
Parse a '-' token.
@ Paren
Parens surrounding zero or more operands.
@ None
Zero or more operands with no delimiters.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalEqual()=0
Parse a = token if present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
virtual ParseResult parseLSquare()=0
Parse a [ token.
virtual ParseResult parseRSquare()=0
Parse a ] token.
ParseResult parseInteger(IntT &result)
Parse an integer value from the stream.
virtual ParseResult parseOptionalArrow()=0
Parse a '->' token if present.
virtual ParseResult parseLess()=0
Parse a '<' token.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseOptionalLess()=0
Parse a '<' token if present.
virtual ParseResult parseArrow()=0
Parse a '->' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalLParen()=0
Parse a ( token if present.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
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:738
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:801
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:726
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:729
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
user_range getUsers()
Returns a range of all users.
Definition Operation.h:925
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
BlockArgListType getArguments()
Definition Region.h:94
OpIterator op_begin()
Return iterators that walk the operations nested directly within this region.
Definition Region.h: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:732
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:310
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:1380
detail::DenseArrayAttrImpl< bool > DenseBoolArrayAttr
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
function_ref< void(Block *, StringRef)> OpAsmSetBlockNameFn
A functor used to set the name of blocks in regions directly nested under an operation.
This is the representation of an operand reference.
This class provides APIs and verifiers for ops with regions having a single block.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
T & getOrAddProperties()
Get (or create) the properties of the provided type to be set on the operation on creation.
void addOperands(ValueRange newOperands)
void addAttributes(ArrayRef< NamedAttribute > newAttributes)
Add an array of named attributes.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addTypes(ArrayRef< Type > newTypes)
Region * addRegion()
Create a region that should be attached to the operation.
Extended TargetOperands with kernel_type attribute.
TargetExecModeAttr kernelType
Kernel execution mode for the target region.