MLIR 24.0.0git
OpenACC.cpp
Go to the documentation of this file.
1//===- OpenACC.cpp - OpenACC MLIR Operations ------------------------------===//
2//
3// Part of the MLIR 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
12#include "mlir/IR/Builders.h"
14#include "mlir/IR/BuiltinOps.h"
16#include "mlir/IR/IRMapping.h"
17#include "mlir/IR/Matchers.h"
19#include "mlir/IR/SymbolTable.h"
20#include "mlir/Support/LLVM.h"
22#include "llvm/ADT/SmallSet.h"
23#include "llvm/ADT/TypeSwitch.h"
24#include "llvm/Support/LogicalResult.h"
25#include <variant>
26
27using namespace mlir;
28using namespace acc;
29
30#include "mlir/Dialect/OpenACC/OpenACCOpsDialect.cpp.inc"
31#include "mlir/Dialect/OpenACC/OpenACCOpsEnums.cpp.inc"
32#include "mlir/Dialect/OpenACC/OpenACCOpsInterfaces.cpp.inc"
33#include "mlir/Dialect/OpenACC/OpenACCTypeInterfaces.cpp.inc"
34#include "mlir/Dialect/OpenACCMPCommon/Interfaces/OpenACCMPOpsInterfaces.cpp.inc"
35
36namespace {
37struct PrivateTypePointerLikeModel
38 : public PointerLikeType::ExternalModel<PrivateTypePointerLikeModel,
39 PrivateType> {
40 Type getElementType(Type type) const {
41 return cast<PrivateType>(type).getBaseTy();
42 }
43
44 Value genCast(Type, OpBuilder &builder, Location loc, Value value,
45 Type resultType) const {
46 if (value.getType() == resultType)
47 return value;
48 if (!isa<PointerLikeType>(resultType))
49 return {};
50 return UnwrapPrivateOp::create(builder, loc, resultType, value).getResult();
51 }
52
53 MemRefType getAsMemRefType(Type type, ModuleOp module) const {
54 Type baseTy = cast<PrivateType>(type).getBaseTy();
55 if (auto memrefTy = dyn_cast<MemRefType>(baseTy))
56 return memrefTy;
57 if (auto ptrLikeTy = dyn_cast<PointerLikeType>(baseTy))
58 return ptrLikeTy.getAsMemRefType(module);
59 return {};
60 }
61};
62
63/// Helper function for any of the times we need to modify an ArrayAttr based on
64/// a device type list. Returns a new ArrayAttr with all of the
65/// existingDeviceTypes, plus the effective new ones(or an added none if hte new
66/// list is empty).
67mlir::ArrayAttr addDeviceTypeAffectedOperandHelper(
68 MLIRContext *context, mlir::ArrayAttr existingDeviceTypes,
69 llvm::ArrayRef<acc::DeviceType> newDeviceTypes) {
71 if (existingDeviceTypes)
72 llvm::copy(existingDeviceTypes, std::back_inserter(deviceTypes));
73
74 if (newDeviceTypes.empty())
75 deviceTypes.push_back(
76 acc::DeviceTypeAttr::get(context, acc::DeviceType::None));
77
78 for (DeviceType dt : newDeviceTypes)
79 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, dt));
80
81 return mlir::ArrayAttr::get(context, deviceTypes);
82}
83
84/// Helper function for any of the times we need to add operands that are
85/// affected by a device type list. Returns a new ArrayAttr with all of the
86/// existingDeviceTypes, plus the effective new ones (or an added none, if the
87/// new list is empty). Additionally, adds the arguments to the argCollection
88/// the correct number of times. This will also update a 'segments' array, even
89/// if it won't be used.
90mlir::ArrayAttr addDeviceTypeAffectedOperandHelper(
91 MLIRContext *context, mlir::ArrayAttr existingDeviceTypes,
92 llvm::ArrayRef<acc::DeviceType> newDeviceTypes, mlir::ValueRange arguments,
93 mlir::MutableOperandRange argCollection,
96 if (existingDeviceTypes)
97 llvm::copy(existingDeviceTypes, std::back_inserter(deviceTypes));
98
99 if (newDeviceTypes.empty()) {
100 argCollection.append(arguments);
101 segments.push_back(arguments.size());
102 deviceTypes.push_back(
103 acc::DeviceTypeAttr::get(context, acc::DeviceType::None));
104 }
105
106 for (DeviceType dt : newDeviceTypes) {
107 argCollection.append(arguments);
108 segments.push_back(arguments.size());
109 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, dt));
110 }
111
112 return mlir::ArrayAttr::get(context, deviceTypes);
113}
114
115/// Overload for when the 'segments' aren't needed.
116mlir::ArrayAttr addDeviceTypeAffectedOperandHelper(
117 MLIRContext *context, mlir::ArrayAttr existingDeviceTypes,
118 llvm::ArrayRef<acc::DeviceType> newDeviceTypes, mlir::ValueRange arguments,
119 mlir::MutableOperandRange argCollection) {
121 return addDeviceTypeAffectedOperandHelper(context, existingDeviceTypes,
122 newDeviceTypes, arguments,
123 argCollection, segments);
124}
125} // namespace
126
127//===----------------------------------------------------------------------===//
128// OpenACC operations
129//===----------------------------------------------------------------------===//
130
131void OpenACCDialect::initialize() {
132 addOperations<
133#define GET_OP_LIST
134#include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc"
135 >();
136 addAttributes<
137#define GET_ATTRDEF_LIST
138#include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"
139 >();
140 addTypes<
141#define GET_TYPEDEF_LIST
142#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"
143 >();
144
145 PrivateType::attachInterface<PrivateTypePointerLikeModel>(*getContext());
146}
147
148//===----------------------------------------------------------------------===//
149// RegionBranchOpInterface for acc.kernels / acc.parallel / acc.serial /
150// acc.kernel_environment / acc.data / acc.host_data / acc.loop
151//===----------------------------------------------------------------------===//
152
153/// Generic helper for single-region OpenACC ops that execute their body once
154/// and then continue after the operation with their results (if any).
155static void
157 RegionBranchPoint point,
159 if (point.isParent()) {
160 regions.push_back(RegionSuccessor(&region));
161 return;
162 }
163
164 regions.push_back(RegionSuccessor(op));
165}
166
168 RegionSuccessor successor) {
169 return successor.isOperation() ? ValueRange(op->getResults()) : ValueRange();
170}
171
172void KernelsOp::getSuccessorRegions(RegionBranchPoint point,
174 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
175 regions);
176}
177
178ValueRange KernelsOp::getSuccessorInputs(RegionSuccessor successor) {
179 return getSingleRegionSuccessorInputs(getOperation(), successor);
180}
181
182void ParallelOp::getSuccessorRegions(
184 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
185 regions);
186}
187
188ValueRange ParallelOp::getSuccessorInputs(RegionSuccessor successor) {
189 return getSingleRegionSuccessorInputs(getOperation(), successor);
190}
191
192void SerialOp::getSuccessorRegions(RegionBranchPoint point,
194 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
195 regions);
196}
197
198ValueRange SerialOp::getSuccessorInputs(RegionSuccessor successor) {
199 return getSingleRegionSuccessorInputs(getOperation(), successor);
200}
201
202void DataOp::getSuccessorRegions(RegionBranchPoint point,
204 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
205 regions);
206}
207
208ValueRange DataOp::getSuccessorInputs(RegionSuccessor successor) {
209 return getSingleRegionSuccessorInputs(getOperation(), successor);
210}
211
212void HostDataOp::getSuccessorRegions(
214 getSingleRegionOpSuccessorRegions(getOperation(), getRegion(), point,
215 regions);
216}
217
218ValueRange HostDataOp::getSuccessorInputs(RegionSuccessor successor) {
219 return getSingleRegionSuccessorInputs(getOperation(), successor);
220}
221
222/// Whether the body of a structured `acc.loop` is proven to run. This decides
223/// which edges out of the parent are feasible; the edges out of the region are
224/// unaffected.
225enum class BodyExecution {
226 /// The body runs at least once, so the parent cannot bypass the region.
228 /// The body never runs, so the parent cannot enter the region.
230 /// Neither could be proven, so the parent may do either.
232};
233
234/// Prove whether the body of `loopOp` runs. A counted dimension whose entry
235/// test already fails at its lower bound runs exactly zero times, so a single
236/// comparison decides both `Always` and `Never`. Bounds that are not constant
237/// prove nothing.
238static BodyExecution getBodyExecution(LoopOp loopOp) {
239 // A container-like loop describes its iteration space inside the region.
240 if (loopOp.isContainerLike())
242
243 // The verifier guarantees one lower bound, upper bound and step per
244 // dimension.
245 ValueRange lbs = loopOp.getLowerbound();
246 ValueRange ubs = loopOp.getUpperbound();
247 ValueRange steps = loopOp.getStep();
248
250 for (unsigned i = 0, e = lbs.size(); i < e; ++i) {
251 std::optional<int64_t> lb = getConstantIntValue(lbs[i]);
252 std::optional<int64_t> ub = getConstantIntValue(ubs[i]);
253 std::optional<int64_t> step = getConstantIntValue(steps[i]);
254 // An unknown bound cannot be tested, and a zero step either spins forever
255 // or never starts. Neither proves `Always`, but a later dimension may
256 // still prove the nest empty: `(0 to %n)` collapsed with `(0 to 0)`.
257 if (!lb || !ub || !step || *step == 0) {
259 continue;
260 }
261
262 // The entry test at the lower bound. A descending dimension compares
263 // against its bound the other way round. The attribute is absent when
264 // every dimension is exclusive as in `scf.for`, and the verifier otherwise
265 // guarantees one entry per dimension.
266 std::optional<ArrayRef<bool>> inclusiveUbs =
267 loopOp.getInclusiveUpperbound();
268 bool inclusiveUb = inclusiveUbs && (*inclusiveUbs)[i];
269 assert(*step != 0 && "zero step should have been filtered out");
270 bool runsOnce = *step > 0 ? (inclusiveUb ? *lb <= *ub : *lb < *ub)
271 : (inclusiveUb ? *lb >= *ub : *lb > *ub);
272 // The dimensions are iterated as a nest, so one empty dimension empties
273 // the whole nest whatever the others do, while the body runs only if every
274 // dimension runs.
275 if (!runsOnce)
277 }
278
279 // No dimension was empty, so the body runs unless some dimension was
280 // unknown.
281 return result;
282}
283
284void LoopOp::getSuccessorRegions(RegionBranchPoint point,
286 // Unstructured loops: the body may contain arbitrary CFG and early exits.
287 // At the RegionBranch level, only model entry into the body and exit to the
288 // parent; any backedges are represented inside the region CFG.
289 if (getUnstructured()) {
290 if (point.isParent()) {
291 regions.push_back(RegionSuccessor(&getRegion()));
292 return;
293 }
294 regions.push_back(RegionSuccessor(getOperation()));
295 return;
296 }
297
298 // Structured loops: model a loop-shaped region graph similar to scf.for,
299 // minus the entry edge the loop is proven not to take.
300 if (point.isParent()) {
301 switch (getBodyExecution(*this)) {
303 regions.push_back(RegionSuccessor(&getRegion()));
304 return;
306 regions.push_back(RegionSuccessor(getOperation()));
307 return;
309 break;
310 }
311 }
312
313 regions.push_back(RegionSuccessor(&getRegion()));
314 regions.push_back(RegionSuccessor(getOperation()));
315}
316
317ValueRange LoopOp::getSuccessorInputs(RegionSuccessor successor) {
318 return getSingleRegionSuccessorInputs(getOperation(), successor);
319}
320
321//===----------------------------------------------------------------------===//
322// RegionBranchTerminatorOpInterface
323//===----------------------------------------------------------------------===//
324
326TerminatorOp::getMutableSuccessorOperands(RegionSuccessor /*point*/) {
327 // `acc.terminator` does not forward operands.
328 return MutableOperandRange(getOperation(), /*start=*/0, /*length=*/0);
329}
330
331//===----------------------------------------------------------------------===//
332// device_type support helpers
333//===----------------------------------------------------------------------===//
334
335static bool hasDeviceTypeValues(std::optional<mlir::ArrayAttr> arrayAttr) {
336 return arrayAttr && *arrayAttr && arrayAttr->size() > 0;
337}
338
339static bool hasDeviceType(std::optional<mlir::ArrayAttr> arrayAttr,
340 mlir::acc::DeviceType deviceType) {
341 if (!hasDeviceTypeValues(arrayAttr))
342 return false;
343
344 for (auto attr : *arrayAttr) {
345 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
346 if (deviceTypeAttr.getValue() == deviceType)
347 return true;
348 }
349
350 return false;
351}
352
354 std::optional<mlir::ArrayAttr> deviceTypes) {
355 if (!hasDeviceTypeValues(deviceTypes))
356 return;
357
358 p << "[";
359 llvm::interleaveComma(*deviceTypes, p,
360 [&](mlir::Attribute attr) { p << attr; });
361 p << "]";
362}
363
364static std::optional<unsigned> findSegment(ArrayAttr segments,
365 mlir::acc::DeviceType deviceType) {
366 unsigned segmentIdx = 0;
367 for (auto attr : segments) {
368 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
369 if (deviceTypeAttr.getValue() == deviceType)
370 return std::make_optional(segmentIdx);
371 ++segmentIdx;
372 }
373 return std::nullopt;
374}
375
377getValuesFromSegments(std::optional<mlir::ArrayAttr> arrayAttr,
379 std::optional<llvm::ArrayRef<int32_t>> segments,
380 mlir::acc::DeviceType deviceType) {
381 if (!arrayAttr)
382 return range.take_front(0);
383 if (auto pos = findSegment(*arrayAttr, deviceType)) {
384 int32_t nbOperandsBefore = 0;
385 for (unsigned i = 0; i < *pos; ++i)
386 nbOperandsBefore += (*segments)[i];
387 return range.drop_front(nbOperandsBefore).take_front((*segments)[*pos]);
388 }
389 return range.take_front(0);
390}
391
392static mlir::Value
393getWaitDevnumValue(std::optional<mlir::ArrayAttr> deviceTypeAttr,
395 std::optional<llvm::ArrayRef<int32_t>> segments,
396 std::optional<mlir::ArrayAttr> hasWaitDevnum,
397 mlir::acc::DeviceType deviceType) {
398 if (!hasDeviceTypeValues(deviceTypeAttr))
399 return {};
400 if (auto pos = findSegment(*deviceTypeAttr, deviceType)) {
402 auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>((*hasWaitDevnum)[*pos]);
403 if (boolAttr && boolAttr.getValue())
404 return getValuesFromSegments(deviceTypeAttr, operands, segments,
405 deviceType)
406 .front();
407 }
408 }
409 return {};
410}
411
413getWaitValuesWithoutDevnum(std::optional<mlir::ArrayAttr> deviceTypeAttr,
415 std::optional<llvm::ArrayRef<int32_t>> segments,
416 std::optional<mlir::ArrayAttr> hasWaitDevnum,
417 mlir::acc::DeviceType deviceType) {
418 auto range =
419 getValuesFromSegments(deviceTypeAttr, operands, segments, deviceType);
420 if (range.empty())
421 return range;
422 if (auto pos = findSegment(*deviceTypeAttr, deviceType)) {
424 auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>((*hasWaitDevnum)[*pos]);
425 if (boolAttr.getValue())
426 return range.drop_front(1); // first value is devnum
427 }
428 }
429 return range;
430}
431
432template <typename Op>
433static LogicalResult checkWaitAndAsyncConflict(Op op) {
434 for (uint32_t dtypeInt = 0; dtypeInt != acc::getMaxEnumValForDeviceType();
435 ++dtypeInt) {
436 auto dtype = static_cast<acc::DeviceType>(dtypeInt);
437
438 // The asyncOnly attribute represent the async clause without value.
439 // Therefore the attribute and operand cannot appear at the same time.
440 if (hasDeviceType(op.getAsyncOperandsDeviceType(), dtype) &&
441 op.hasAsyncOnly(dtype))
442 return op.emitError(
443 "asyncOnly attribute cannot appear with asyncOperand");
444
445 // The wait attribute represent the wait clause without values. Therefore
446 // the attribute and operands cannot appear at the same time.
447 if (hasDeviceType(op.getWaitOperandsDeviceType(), dtype) &&
448 op.hasWaitOnly(dtype))
449 return op.emitError("wait attribute cannot appear with waitOperands");
450 }
451 return success();
452}
453
454template <typename Op>
455static LogicalResult checkVarAndVarType(Op op) {
456 if (!op.getVar())
457 return op.emitError("must have var operand");
458
459 // A variable must have a type that is either pointer-like or mappable.
460 if (!mlir::isa<mlir::acc::PointerLikeType>(op.getVar().getType()) &&
461 !mlir::isa<mlir::acc::MappableType>(op.getVar().getType()))
462 return op.emitError("var must be mappable or pointer-like");
463
464 // When it is a pointer-like type, the varType must capture the target type.
465 if (mlir::isa<mlir::acc::PointerLikeType>(op.getVar().getType()) &&
466 op.getVarType() == op.getVar().getType())
467 return op.emitError("varType must capture the element type of var");
468
469 return success();
470}
471
472template <typename Op>
473static LogicalResult checkVarAndAccVar(Op op) {
474 if (op.getVar().getType() != op.getAccVar().getType())
475 return op.emitError("input and output types must match");
476
477 return success();
478}
479
480template <typename Op>
481static LogicalResult checkNoModifier(Op op) {
482 if (op.getModifiers() != acc::DataClauseModifier::none)
483 return op.emitError("no data clause modifiers are allowed");
484 return success();
485}
486
487template <typename Op>
488static LogicalResult
489checkValidModifier(Op op, acc::DataClauseModifier validModifiers) {
490 if (acc::bitEnumContainsAny(op.getModifiers(), ~validModifiers))
491 return op.emitError(
492 "invalid data clause modifiers: " +
493 acc::stringifyDataClauseModifier(op.getModifiers() & ~validModifiers));
494
495 return success();
496}
497
498template <typename OpT, typename RecipeOpT>
499static LogicalResult checkRecipe(OpT op, llvm::StringRef operandName) {
500 // Mappable types do not need a recipe because it is possible to generate one
501 // from its API. Reject reductions though because no API is available for them
502 // at this time.
503 if (mlir::acc::isMappableType(op.getVar().getType()) &&
504 !std::is_same_v<OpT, acc::ReductionOp>)
505 return success();
506
507 mlir::SymbolRefAttr operandRecipe = op.getRecipeAttr();
508 if (!operandRecipe)
509 return op->emitOpError() << "recipe expected for " << operandName;
510
511 auto decl =
513 if (!decl)
514 return op->emitOpError()
515 << "expected symbol reference " << operandRecipe << " to point to a "
516 << operandName << " declaration";
517 return success();
518}
519
520static ParseResult parseVar(mlir::OpAsmParser &parser,
522 // Either `var` or `varPtr` keyword is required.
523 if (failed(parser.parseOptionalKeyword("varPtr"))) {
524 if (failed(parser.parseKeyword("var")))
525 return failure();
526 }
527 if (failed(parser.parseLParen()))
528 return failure();
529 if (failed(parser.parseOperand(var)))
530 return failure();
531
532 return success();
533}
534
536 mlir::Value var) {
537 if (mlir::isa<mlir::acc::PointerLikeType>(var.getType()))
538 p << "varPtr(";
539 else
540 p << "var(";
541 p.printOperand(var);
542}
543
544static ParseResult parseAccVar(mlir::OpAsmParser &parser,
546 mlir::Type &accVarType) {
547 // Either `accVar` or `accPtr` keyword is required.
548 if (failed(parser.parseOptionalKeyword("accPtr"))) {
549 if (failed(parser.parseKeyword("accVar")))
550 return failure();
551 }
552 if (failed(parser.parseLParen()))
553 return failure();
554 if (failed(parser.parseOperand(var)))
555 return failure();
556 if (failed(parser.parseColon()))
557 return failure();
558 if (failed(parser.parseType(accVarType)))
559 return failure();
560 if (failed(parser.parseRParen()))
561 return failure();
562
563 return success();
564}
565
567 mlir::Value accVar, mlir::Type accVarType) {
568 if (mlir::isa<mlir::acc::PointerLikeType>(accVar.getType()))
569 p << "accPtr(";
570 else
571 p << "accVar(";
572 p.printOperand(accVar);
573 p << " : ";
574 p.printType(accVarType);
575 p << ")";
576}
577
578static ParseResult parseVarPtrType(mlir::OpAsmParser &parser,
579 mlir::Type &varPtrType,
580 mlir::TypeAttr &varTypeAttr) {
581 if (failed(parser.parseType(varPtrType)))
582 return failure();
583 if (failed(parser.parseRParen()))
584 return failure();
585
586 if (succeeded(parser.parseOptionalKeyword("varType"))) {
587 if (failed(parser.parseLParen()))
588 return failure();
589 mlir::Type varType;
590 if (failed(parser.parseType(varType)))
591 return failure();
592 varTypeAttr = mlir::TypeAttr::get(varType);
593 if (failed(parser.parseRParen()))
594 return failure();
595 } else {
596 // Set `varType` from the element type of the type of `varPtr`.
597 if (auto ptrTy = dyn_cast<acc::PointerLikeType>(varPtrType)) {
598 Type elementType = ptrTy.getElementType();
599 // Opaque pointers (e.g. !llvm.ptr) have no element type; fall back to
600 // using varPtrType itself so that the attribute is always valid.
601 varTypeAttr = mlir::TypeAttr::get(elementType ? elementType : varPtrType);
602 } else {
603 varTypeAttr = mlir::TypeAttr::get(varPtrType);
604 }
605 }
606
607 return success();
608}
609
611 mlir::Type varPtrType, mlir::TypeAttr varTypeAttr) {
612 p.printType(varPtrType);
613 p << ")";
614
615 // Print the `varType` only if it differs from the element type of
616 // `varPtr`'s type.
617 mlir::Type varType = varTypeAttr.getValue();
618 mlir::Type typeToCheckAgainst =
619 mlir::isa<mlir::acc::PointerLikeType>(varPtrType)
620 ? mlir::cast<mlir::acc::PointerLikeType>(varPtrType).getElementType()
621 : varPtrType;
622 // Opaque pointers (e.g. !llvm.ptr) have no element type; use varPtrType as
623 // the baseline so that the inferred varType is not redundantly printed.
624 if (!typeToCheckAgainst)
625 typeToCheckAgainst = varPtrType;
626 if (typeToCheckAgainst != varType) {
627 p << " varType(";
628 p.printType(varType);
629 p << ")";
630 }
631}
632
633// A location cannot be parsed through the generic attribute directive: the
634// generated parser needs a concrete attribute class, while any of the location
635// attributes may appear here.
636static ParseResult parseSourceLocation(mlir::OpAsmParser &parser,
637 mlir::LocationAttr &locAttr) {
638 llvm::SMLoc attrLoc = parser.getCurrentLocation();
639 mlir::Attribute attr;
640 if (failed(parser.parseAttribute(attr)))
641 return failure();
642 locAttr = mlir::dyn_cast<mlir::LocationAttr>(attr);
643 if (!locAttr)
644 return parser.emitError(attrLoc, "expected location attribute");
645 return success();
646}
647
649 mlir::LocationAttr locAttr) {
650 p.printAttribute(locAttr);
651}
652
653static ParseResult parseRecipeSym(mlir::OpAsmParser &parser,
654 mlir::SymbolRefAttr &recipeAttr) {
655 if (failed(parser.parseAttribute(recipeAttr)))
656 return failure();
657 return success();
658}
659
661 mlir::SymbolRefAttr recipeAttr) {
662 p << recipeAttr;
663}
664
667 return parser.parseAttribute(attr);
668}
669
672 p << attr;
673}
674
675static ParseResult parseArrayAttr(mlir::OpAsmParser &parser,
676 mlir::ArrayAttr &attr) {
677 return parser.parseAttribute(attr);
678}
679
681 mlir::ArrayAttr attr) {
682 p << attr;
683}
684
685//===----------------------------------------------------------------------===//
686// DataBoundsOp
687//===----------------------------------------------------------------------===//
688LogicalResult acc::DataBoundsOp::verify() {
689 auto extent = getExtent();
690 auto upperbound = getUpperbound();
691 if (!extent && !upperbound)
692 return emitError("expected extent or upperbound.");
693 return success();
694}
695
696//===----------------------------------------------------------------------===//
697// PrivateOp
698//===----------------------------------------------------------------------===//
699LogicalResult acc::PrivateOp::verify() {
700 if (getDataClause() != acc::DataClause::acc_private)
701 return emitError(
702 "data clause associated with private operation must match its intent");
703 if (failed(checkVarAndVarType(*this)))
704 return failure();
705 if (failed(checkNoModifier(*this)))
706 return failure();
707 if (failed(
709 return failure();
710 return success();
711}
712
713//===----------------------------------------------------------------------===//
714// FirstprivateOp
715//===----------------------------------------------------------------------===//
716LogicalResult acc::FirstprivateOp::verify() {
717 if (getDataClause() != acc::DataClause::acc_firstprivate)
718 return emitError("data clause associated with firstprivate operation must "
719 "match its intent");
720 if (failed(checkVarAndVarType(*this)))
721 return failure();
722 if (failed(checkNoModifier(*this)))
723 return failure();
725 *this, "firstprivate")))
726 return failure();
727 return success();
728}
729
730//===----------------------------------------------------------------------===//
731// ReductionOp
732//===----------------------------------------------------------------------===//
733LogicalResult acc::ReductionOp::verify() {
734 if (getDataClause() != acc::DataClause::acc_reduction)
735 return emitError("data clause associated with reduction operation must "
736 "match its intent");
737 if (failed(checkVarAndVarType(*this)))
738 return failure();
739 if (failed(checkNoModifier(*this)))
740 return failure();
742 *this, "reduction")))
743 return failure();
744 return success();
745}
746
747//===----------------------------------------------------------------------===//
748// DevicePtrOp
749//===----------------------------------------------------------------------===//
750LogicalResult acc::DevicePtrOp::verify() {
751 if (getDataClause() != acc::DataClause::acc_deviceptr)
752 return emitError("data clause associated with deviceptr operation must "
753 "match its intent");
754 if (failed(checkVarAndVarType(*this)))
755 return failure();
756 if (failed(checkVarAndAccVar(*this)))
757 return failure();
758 if (failed(checkNoModifier(*this)))
759 return failure();
760 return success();
761}
762
763//===----------------------------------------------------------------------===//
764// PresentOp
765//===----------------------------------------------------------------------===//
766LogicalResult acc::PresentOp::verify() {
767 if (getDataClause() != acc::DataClause::acc_present)
768 return emitError(
769 "data clause associated with present operation must match its intent");
770 if (failed(checkVarAndVarType(*this)))
771 return failure();
772 if (failed(checkVarAndAccVar(*this)))
773 return failure();
774 if (failed(checkNoModifier(*this)))
775 return failure();
776 return success();
777}
778
779//===----------------------------------------------------------------------===//
780// CopyinOp
781//===----------------------------------------------------------------------===//
782LogicalResult acc::CopyinOp::verify() {
783 // Test for all clauses this operation can be decomposed from:
784 if (!getImplicit() && getDataClause() != acc::DataClause::acc_copyin &&
785 getDataClause() != acc::DataClause::acc_copyin_readonly &&
786 getDataClause() != acc::DataClause::acc_copy &&
787 getDataClause() != acc::DataClause::acc_reduction)
788 return emitError(
789 "data clause associated with copyin operation must match its intent"
790 " or specify original clause this operation was decomposed from");
791 if (failed(checkVarAndVarType(*this)))
792 return failure();
793 if (failed(checkVarAndAccVar(*this)))
794 return failure();
795 if (failed(checkValidModifier(*this, acc::DataClauseModifier::readonly |
796 acc::DataClauseModifier::always |
797 acc::DataClauseModifier::capture)))
798 return failure();
799 return success();
800}
801
802bool acc::CopyinOp::isCopyinReadonly() {
803 return getDataClause() == acc::DataClause::acc_copyin_readonly ||
804 acc::bitEnumContainsAny(getModifiers(),
805 acc::DataClauseModifier::readonly);
806}
807
808//===----------------------------------------------------------------------===//
809// CreateOp
810//===----------------------------------------------------------------------===//
811LogicalResult acc::CreateOp::verify() {
812 // Test for all clauses this operation can be decomposed from:
813 if (getDataClause() != acc::DataClause::acc_create &&
814 getDataClause() != acc::DataClause::acc_create_zero &&
815 getDataClause() != acc::DataClause::acc_copyout &&
816 getDataClause() != acc::DataClause::acc_copyout_zero)
817 return emitError(
818 "data clause associated with create operation must match its intent"
819 " or specify original clause this operation was decomposed from");
820 if (failed(checkVarAndVarType(*this)))
821 return failure();
822 if (failed(checkVarAndAccVar(*this)))
823 return failure();
824 // this op is the entry part of copyout, so it also needs to allow all
825 // modifiers allowed on copyout.
826 if (failed(checkValidModifier(*this, acc::DataClauseModifier::zero |
827 acc::DataClauseModifier::always |
828 acc::DataClauseModifier::capture)))
829 return failure();
830 return success();
831}
832
833bool acc::CreateOp::isCreateZero() {
834 // The zero modifier is encoded in the data clause.
835 return getDataClause() == acc::DataClause::acc_create_zero ||
836 getDataClause() == acc::DataClause::acc_copyout_zero ||
837 acc::bitEnumContainsAny(getModifiers(), acc::DataClauseModifier::zero);
838}
839
840//===----------------------------------------------------------------------===//
841// NoCreateOp
842//===----------------------------------------------------------------------===//
843LogicalResult acc::NoCreateOp::verify() {
844 if (getDataClause() != acc::DataClause::acc_no_create)
845 return emitError("data clause associated with no_create operation must "
846 "match its intent");
847 if (failed(checkVarAndVarType(*this)))
848 return failure();
849 if (failed(checkVarAndAccVar(*this)))
850 return failure();
851 if (failed(checkNoModifier(*this)))
852 return failure();
853 return success();
854}
855
856//===----------------------------------------------------------------------===//
857// AttachOp
858//===----------------------------------------------------------------------===//
859LogicalResult acc::AttachOp::verify() {
860 if (getDataClause() != acc::DataClause::acc_attach)
861 return emitError(
862 "data clause associated with attach operation must match its intent");
863 if (failed(checkVarAndVarType(*this)))
864 return failure();
865 if (failed(checkVarAndAccVar(*this)))
866 return failure();
867 if (failed(checkNoModifier(*this)))
868 return failure();
869 return success();
870}
871
872//===----------------------------------------------------------------------===//
873// DeclareDeviceResidentOp
874//===----------------------------------------------------------------------===//
875
876LogicalResult acc::DeclareDeviceResidentOp::verify() {
877 if (getDataClause() != acc::DataClause::acc_declare_device_resident)
878 return emitError("data clause associated with device_resident operation "
879 "must match its intent");
880 if (failed(checkVarAndVarType(*this)))
881 return failure();
882 if (failed(checkVarAndAccVar(*this)))
883 return failure();
884 if (failed(checkNoModifier(*this)))
885 return failure();
886 return success();
887}
888
889//===----------------------------------------------------------------------===//
890// DeclareLinkOp
891//===----------------------------------------------------------------------===//
892
893LogicalResult acc::DeclareLinkOp::verify() {
894 if (getDataClause() != acc::DataClause::acc_declare_link)
895 return emitError(
896 "data clause associated with link operation must match its intent");
897 if (failed(checkVarAndVarType(*this)))
898 return failure();
899 if (failed(checkVarAndAccVar(*this)))
900 return failure();
901 if (failed(checkNoModifier(*this)))
902 return failure();
903 return success();
904}
905
906//===----------------------------------------------------------------------===//
907// CopyoutOp
908//===----------------------------------------------------------------------===//
909LogicalResult acc::CopyoutOp::verify() {
910 // Test for all clauses this operation can be decomposed from:
911 if (getDataClause() != acc::DataClause::acc_copyout &&
912 getDataClause() != acc::DataClause::acc_copyout_zero &&
913 getDataClause() != acc::DataClause::acc_copy &&
914 getDataClause() != acc::DataClause::acc_reduction)
915 return emitError(
916 "data clause associated with copyout operation must match its intent"
917 " or specify original clause this operation was decomposed from");
918 if (!getVar() || !getAccVar())
919 return emitError("must have both host and device pointers");
920 if (failed(checkVarAndVarType(*this)))
921 return failure();
922 if (failed(checkVarAndAccVar(*this)))
923 return failure();
924 if (failed(checkValidModifier(*this, acc::DataClauseModifier::zero |
925 acc::DataClauseModifier::always |
926 acc::DataClauseModifier::capture)))
927 return failure();
928 return success();
929}
930
931bool acc::CopyoutOp::isCopyoutZero() {
932 return getDataClause() == acc::DataClause::acc_copyout_zero ||
933 acc::bitEnumContainsAny(getModifiers(), acc::DataClauseModifier::zero);
934}
935
936//===----------------------------------------------------------------------===//
937// DeleteOp
938//===----------------------------------------------------------------------===//
939LogicalResult acc::DeleteOp::verify() {
940 // Test for all clauses this operation can be decomposed from:
941 if (getDataClause() != acc::DataClause::acc_delete &&
942 getDataClause() != acc::DataClause::acc_create &&
943 getDataClause() != acc::DataClause::acc_create_zero &&
944 getDataClause() != acc::DataClause::acc_copyin &&
945 getDataClause() != acc::DataClause::acc_copyin_readonly &&
946 getDataClause() != acc::DataClause::acc_present &&
947 getDataClause() != acc::DataClause::acc_no_create &&
948 getDataClause() != acc::DataClause::acc_declare_device_resident &&
949 getDataClause() != acc::DataClause::acc_declare_link)
950 return emitError(
951 "data clause associated with delete operation must match its intent"
952 " or specify original clause this operation was decomposed from");
953 if (!getAccVar())
954 return emitError("must have device pointer");
955 // This op is the exit part of copyin and create - thus allow all modifiers
956 // allowed on either case.
957 if (failed(checkValidModifier(*this, acc::DataClauseModifier::zero |
958 acc::DataClauseModifier::readonly |
959 acc::DataClauseModifier::always |
960 acc::DataClauseModifier::capture)))
961 return failure();
962 return success();
963}
964
965//===----------------------------------------------------------------------===//
966// DetachOp
967//===----------------------------------------------------------------------===//
968LogicalResult acc::DetachOp::verify() {
969 // Test for all clauses this operation can be decomposed from:
970 if (getDataClause() != acc::DataClause::acc_detach &&
971 getDataClause() != acc::DataClause::acc_attach)
972 return emitError(
973 "data clause associated with detach operation must match its intent"
974 " or specify original clause this operation was decomposed from");
975 if (!getAccVar())
976 return emitError("must have device pointer");
977 if (failed(checkNoModifier(*this)))
978 return failure();
979 return success();
980}
981
982//===----------------------------------------------------------------------===//
983// HostOp
984//===----------------------------------------------------------------------===//
985LogicalResult acc::UpdateHostOp::verify() {
986 // Test for all clauses this operation can be decomposed from:
987 if (getDataClause() != acc::DataClause::acc_update_host &&
988 getDataClause() != acc::DataClause::acc_update_self)
989 return emitError(
990 "data clause associated with host operation must match its intent"
991 " or specify original clause this operation was decomposed from");
992 if (!getVar() || !getAccVar())
993 return emitError("must have both host and device pointers");
994 if (failed(checkVarAndVarType(*this)))
995 return failure();
996 if (failed(checkVarAndAccVar(*this)))
997 return failure();
998 if (failed(checkNoModifier(*this)))
999 return failure();
1000 return success();
1001}
1002
1003//===----------------------------------------------------------------------===//
1004// DeviceOp
1005//===----------------------------------------------------------------------===//
1006LogicalResult acc::UpdateDeviceOp::verify() {
1007 // Test for all clauses this operation can be decomposed from:
1008 if (getDataClause() != acc::DataClause::acc_update_device)
1009 return emitError(
1010 "data clause associated with device operation must match its intent"
1011 " or specify original clause this operation was decomposed from");
1012 if (failed(checkVarAndVarType(*this)))
1013 return failure();
1014 if (failed(checkVarAndAccVar(*this)))
1015 return failure();
1016 if (failed(checkNoModifier(*this)))
1017 return failure();
1018 return success();
1019}
1020
1021//===----------------------------------------------------------------------===//
1022// UseDeviceOp
1023//===----------------------------------------------------------------------===//
1024LogicalResult acc::UseDeviceOp::verify() {
1025 // Test for all clauses this operation can be decomposed from:
1026 if (getDataClause() != acc::DataClause::acc_use_device)
1027 return emitError(
1028 "data clause associated with use_device operation must match its intent"
1029 " or specify original clause this operation was decomposed from");
1030 if (failed(checkVarAndVarType(*this)))
1031 return failure();
1032 if (failed(checkVarAndAccVar(*this)))
1033 return failure();
1034 if (failed(checkNoModifier(*this)))
1035 return failure();
1036 return success();
1037}
1038
1039//===----------------------------------------------------------------------===//
1040// CacheOp
1041//===----------------------------------------------------------------------===//
1042LogicalResult acc::CacheOp::verify() {
1043 // Test for all clauses this operation can be decomposed from:
1044 if (getDataClause() != acc::DataClause::acc_cache &&
1045 getDataClause() != acc::DataClause::acc_cache_readonly)
1046 return emitError(
1047 "data clause associated with cache operation must match its intent"
1048 " or specify original clause this operation was decomposed from");
1049 if (failed(checkVarAndVarType(*this)))
1050 return failure();
1051 if (failed(checkVarAndAccVar(*this)))
1052 return failure();
1053 if (failed(checkValidModifier(*this, acc::DataClauseModifier::readonly)))
1054 return failure();
1055 return success();
1056}
1057
1058bool acc::CacheOp::isCacheReadonly() {
1059 return getDataClause() == acc::DataClause::acc_cache_readonly ||
1060 acc::bitEnumContainsAny(getModifiers(),
1061 acc::DataClauseModifier::readonly);
1062}
1063
1064//===----------------------------------------------------------------------===//
1065// Data entry/exit operations - getEffects implementations
1066//===----------------------------------------------------------------------===//
1067
1068// This function returns true iff the given operation is enclosed
1069// in any ACC_COMPUTE_CONSTRUCT_OPS operation.
1070// It is quite alike acc::getEnclosingComputeOp() utility,
1071// but we cannot use it here.
1075
1076/// Helper to add an effect on an operand, referenced by its mutable range.
1077template <typename EffectTy>
1080 &effects,
1081 MutableOperandRange operand) {
1082 for (unsigned i = 0, e = operand.size(); i < e; ++i)
1083 effects.emplace_back(EffectTy::get(), &operand[i]);
1084}
1085
1086/// Helper to add an effect on a result value.
1087template <typename EffectTy>
1090 &effects,
1091 Value result) {
1092 effects.emplace_back(EffectTy::get(), mlir::cast<mlir::OpResult>(result));
1093}
1094
1095// PrivateOp: accVar result write.
1096void acc::PrivateOp::getEffects(
1098 &effects) {
1099 // If acc.private is enclosed into a compute operation,
1100 // then it denotes the device side privatization, hence
1101 // it does not access the CurrentDeviceIdResource.
1102 if (!isEnclosedIntoComputeOp(getOperation()))
1103 effects.emplace_back(MemoryEffects::Read::get(),
1105 // TODO: should this be MemoryEffects::Allocate?
1107}
1108
1109// FirstprivateOp: var read, accVar result write.
1110void acc::FirstprivateOp::getEffects(
1112 &effects) {
1113 // If acc.firstprivate is enclosed into a compute operation,
1114 // then it denotes the device side privatization, hence
1115 // it does not access the CurrentDeviceIdResource.
1116 if (!isEnclosedIntoComputeOp(getOperation()))
1117 effects.emplace_back(MemoryEffects::Read::get(),
1119 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
1121}
1122
1123// ReductionOp: var read, accVar result write.
1124void acc::ReductionOp::getEffects(
1126 &effects) {
1127 // If acc.reduction is enclosed into a compute operation,
1128 // then it denotes the device side reduction, hence
1129 // it does not access the CurrentDeviceIdResource.
1130 if (!isEnclosedIntoComputeOp(getOperation()))
1131 effects.emplace_back(MemoryEffects::Read::get(),
1133 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
1135}
1136
1137// DevicePtrOp: RuntimeCounters read.
1138void acc::DevicePtrOp::getEffects(
1140 &effects) {
1141 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1142 effects.emplace_back(MemoryEffects::Read::get(),
1144}
1145
1146// PresentOp: RuntimeCounters read+write.
1147void acc::PresentOp::getEffects(
1149 &effects) {
1150 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1151 effects.emplace_back(MemoryEffects::Write::get(),
1153 effects.emplace_back(MemoryEffects::Read::get(),
1155}
1156
1157// CopyinOp: RuntimeCounters read+write, var read, accVar result write.
1158void acc::CopyinOp::getEffects(
1160 &effects) {
1161 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1162 effects.emplace_back(MemoryEffects::Write::get(),
1164 effects.emplace_back(MemoryEffects::Read::get(),
1166 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
1168}
1169
1170// CreateOp: RuntimeCounters read+write, accVar result write.
1171void acc::CreateOp::getEffects(
1173 &effects) {
1174 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1175 effects.emplace_back(MemoryEffects::Write::get(),
1177 effects.emplace_back(MemoryEffects::Read::get(),
1179 // TODO: should this be MemoryEffects::Allocate?
1181}
1182
1183// NoCreateOp: RuntimeCounters read+write.
1184void acc::NoCreateOp::getEffects(
1186 &effects) {
1187 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1188 effects.emplace_back(MemoryEffects::Write::get(),
1190 effects.emplace_back(MemoryEffects::Read::get(),
1192}
1193
1194// AttachOp: RuntimeCounters read+write, var read.
1195void acc::AttachOp::getEffects(
1197 &effects) {
1198 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1199 effects.emplace_back(MemoryEffects::Write::get(),
1201 effects.emplace_back(MemoryEffects::Read::get(),
1203 // TODO: should we also add MemoryEffects::Write?
1204 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
1205}
1206
1207// GetDevicePtrOp: RuntimeCounters read.
1208void acc::GetDevicePtrOp::getEffects(
1210 &effects) {
1211 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1212 effects.emplace_back(MemoryEffects::Read::get(),
1214}
1215
1216// UpdateDeviceOp: var read, accVar result write.
1217void acc::UpdateDeviceOp::getEffects(
1219 &effects) {
1220 effects.emplace_back(MemoryEffects::Read::get(),
1222 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
1224}
1225
1226// UseDeviceOp: RuntimeCounters read.
1227void acc::UseDeviceOp::getEffects(
1229 &effects) {
1230 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1231 effects.emplace_back(MemoryEffects::Read::get(),
1233}
1234
1235// DeclareDeviceResidentOp: RuntimeCounters write, var read.
1236void acc::DeclareDeviceResidentOp::getEffects(
1238 &effects) {
1239 effects.emplace_back(MemoryEffects::Write::get(),
1241 effects.emplace_back(MemoryEffects::Read::get(),
1243 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
1244}
1245
1246// DeclareLinkOp: RuntimeCounters write, var read.
1247void acc::DeclareLinkOp::getEffects(
1249 &effects) {
1250 effects.emplace_back(MemoryEffects::Write::get(),
1252 effects.emplace_back(MemoryEffects::Read::get(),
1254 addOperandEffect<MemoryEffects::Read>(effects, getVarMutable());
1255}
1256
1257// CacheOp: NoMemoryEffect
1258void acc::CacheOp::getEffects(
1260 &effects) {}
1261
1262// CopyoutOp: RuntimeCounters read+write, accVar read, var write.
1263void acc::CopyoutOp::getEffects(
1265 &effects) {
1266 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1267 effects.emplace_back(MemoryEffects::Write::get(),
1269 effects.emplace_back(MemoryEffects::Read::get(),
1271 addOperandEffect<MemoryEffects::Read>(effects, getAccVarMutable());
1272 addOperandEffect<MemoryEffects::Write>(effects, getVarMutable());
1273}
1274
1275// DeleteOp: RuntimeCounters read+write, accVar read.
1276void acc::DeleteOp::getEffects(
1278 &effects) {
1279 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1280 effects.emplace_back(MemoryEffects::Write::get(),
1282 effects.emplace_back(MemoryEffects::Read::get(),
1284 addOperandEffect<MemoryEffects::Read>(effects, getAccVarMutable());
1285}
1286
1287// DetachOp: RuntimeCounters read+write, accVar read.
1288void acc::DetachOp::getEffects(
1290 &effects) {
1291 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1292 effects.emplace_back(MemoryEffects::Write::get(),
1294 effects.emplace_back(MemoryEffects::Read::get(),
1296 addOperandEffect<MemoryEffects::Read>(effects, getAccVarMutable());
1297}
1298
1299// UpdateHostOp: RuntimeCounters read+write, accVar read, var write.
1300void acc::UpdateHostOp::getEffects(
1302 &effects) {
1303 effects.emplace_back(MemoryEffects::Read::get(), acc::RuntimeCounters::get());
1304 effects.emplace_back(MemoryEffects::Write::get(),
1306 effects.emplace_back(MemoryEffects::Read::get(),
1308 addOperandEffect<MemoryEffects::Read>(effects, getAccVarMutable());
1309 addOperandEffect<MemoryEffects::Write>(effects, getVarMutable());
1310}
1311
1312namespace {
1313/// Pattern to remove operation without region that have constant false `ifCond`
1314/// and remove the condition from the operation if the `ifCond` is a true
1315/// constant.
1316template <typename OpTy>
1317struct RemoveConstantIfCondition : public OpRewritePattern<OpTy> {
1318 using OpRewritePattern<OpTy>::OpRewritePattern;
1319
1320 LogicalResult matchAndRewrite(OpTy op,
1321 PatternRewriter &rewriter) const override {
1322 // Early return if there is no condition.
1323 Value ifCond = op.getIfCond();
1324 if (!ifCond)
1325 return failure();
1326
1327 IntegerAttr constAttr;
1328 if (!matchPattern(ifCond, m_Constant(&constAttr)))
1329 return failure();
1330 if (constAttr.getInt())
1331 rewriter.modifyOpInPlace(op, [&]() { op.getIfCondMutable().erase(0); });
1332 else
1333 rewriter.eraseOp(op);
1334
1335 return success();
1336 }
1337};
1338
1339/// Replaces the given op with the contents of the given single-block region,
1340/// using the operands of the block terminator to replace operation results.
1341static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op,
1342 Region &region, ValueRange blockArgs = {}) {
1343 assert(region.hasOneBlock() && "expected single-block region");
1344 Block *block = &region.front();
1345 Operation *terminator = block->getTerminator();
1346 ValueRange results = terminator->getOperands();
1347 rewriter.inlineBlockBefore(block, op, blockArgs);
1348 rewriter.replaceOp(op, results);
1349 rewriter.eraseOp(terminator);
1350}
1351
1352/// Pattern to remove operation with region that have constant false `ifCond`
1353/// and remove the condition from the operation if the `ifCond` is constant
1354/// true.
1355template <typename OpTy>
1356struct RemoveConstantIfConditionWithRegion : public OpRewritePattern<OpTy> {
1357 using OpRewritePattern<OpTy>::OpRewritePattern;
1358
1359 LogicalResult matchAndRewrite(OpTy op,
1360 PatternRewriter &rewriter) const override {
1361 // Early return if there is no condition.
1362 Value ifCond = op.getIfCond();
1363 if (!ifCond)
1364 return failure();
1365
1366 IntegerAttr constAttr;
1367 if (!matchPattern(ifCond, m_Constant(&constAttr)))
1368 return failure();
1369 if (constAttr.getInt()) {
1370 rewriter.modifyOpInPlace(op, [&]() { op.getIfCondMutable().erase(0); });
1371 return success();
1372 }
1373
1374 // With a constant false condition the construct has no effect, so it is
1375 // replaced by its body. An empty region has no body, so the op is simply
1376 // erased.
1377 Region &region = op.getRegion();
1378 if (region.empty()) {
1379 if (op->getNumResults() != 0)
1380 return rewriter.notifyMatchFailure(op, "empty region with results");
1381 rewriter.eraseOp(op);
1382 return success();
1383 }
1384 if (!region.hasOneBlock())
1385 return rewriter.notifyMatchFailure(op, "expected single-block region");
1386
1387 replaceOpWithRegion(rewriter, op, region);
1388 return success();
1389 }
1390};
1391
1392//===----------------------------------------------------------------------===//
1393// Recipe Region Helpers
1394//===----------------------------------------------------------------------===//
1395
1396/// Create and populate an init region for privatization recipes.
1397/// Returns success if the region is populated, failure otherwise.
1398/// Sets needsFree to indicate if the allocated memory requires deallocation.
1399/// The `hostVar` is the original host variable used to derive
1400/// language-specific metadata via `genPrivateVariableInfo`.
1401/// The `varInfo` output parameter is set to the variable info produced.
1402static LogicalResult createInitRegion(OpBuilder &builder, Location loc,
1403 Region &initRegion, Value hostVar,
1404 StringRef varName, ValueRange bounds,
1405 bool &needsFree,
1406 acc::VariableInfoAttr &varInfo,
1407 SmallVectorImpl<Value> &destroyValues) {
1408 Type varType = hostVar.getType();
1409
1410 // Create init block with arguments: original value + bounds
1411 SmallVector<Type> argTypes{varType};
1412 SmallVector<Location> argLocs{loc};
1413 for (Value bound : bounds) {
1414 argTypes.push_back(bound.getType());
1415 argLocs.push_back(loc);
1416 }
1417
1418 Block *initBlock = builder.createBlock(&initRegion);
1419 initBlock->addArguments(argTypes, argLocs);
1420 builder.setInsertionPointToStart(initBlock);
1421
1422 Value privatizedValue;
1423
1424 // Get the block argument that represents the original variable
1425 Value blockArgVar = initBlock->getArgument(0);
1426
1427 // Generate init region body based on variable type
1428 if (isa<MappableType>(varType)) {
1429 auto mappableTy = cast<MappableType>(varType);
1430 auto typedVar = cast<TypedValue<MappableType>>(blockArgVar);
1431 auto typedHostVar = cast<TypedValue<MappableType>>(hostVar);
1432 varInfo = mappableTy.genPrivateVariableInfo(typedHostVar);
1433 privatizedValue =
1434 mappableTy.generatePrivateInit(builder, loc, typedVar, varName, bounds,
1435 {}, varInfo, needsFree, destroyValues);
1436 if (!privatizedValue)
1437 return failure();
1438 } else {
1439 assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType");
1440 auto pointerLikeTy = cast<PointerLikeType>(varType);
1441 // Use PointerLikeType's allocation API with the block argument
1442 privatizedValue = pointerLikeTy.genAllocate(builder, loc, varName, varType,
1443 blockArgVar, needsFree);
1444 if (!privatizedValue)
1445 return failure();
1446 }
1447
1448 // Add yield operation to init block
1449 SmallVector<Value> initResults{privatizedValue};
1450 initResults.append(destroyValues);
1451 acc::YieldOp::create(builder, loc, initResults);
1452
1453 return success();
1454}
1455
1456/// Create and populate a copy region for firstprivate recipes.
1457/// Returns success if the region is populated, failure otherwise.
1458/// `varInfo` must be the attribute produced by `createInitRegion` for
1459/// `MappableType` (it is unused for `PointerLikeType` copy paths).
1460static LogicalResult createCopyRegion(OpBuilder &builder, Location loc,
1461 Region &copyRegion, Type varType,
1462 ValueRange bounds,
1463 acc::VariableInfoAttr varInfo) {
1464 // Create copy block with arguments: original value + privatized value +
1465 // bounds
1466 SmallVector<Type> copyArgTypes{varType, varType};
1467 SmallVector<Location> copyArgLocs{loc, loc};
1468 for (Value bound : bounds) {
1469 copyArgTypes.push_back(bound.getType());
1470 copyArgLocs.push_back(loc);
1471 }
1472
1473 Block *copyBlock = builder.createBlock(&copyRegion);
1474 copyBlock->addArguments(copyArgTypes, copyArgLocs);
1475 builder.setInsertionPointToStart(copyBlock);
1476
1477 Value originalArg = copyBlock->getArgument(0);
1478 Value privatizedArg = copyBlock->getArgument(1);
1479
1480 if (isa<MappableType>(varType)) {
1481 auto mappableTy = cast<MappableType>(varType);
1482 // generateCopy(src, dest): copy from original (arg0) into privatized
1483 // (arg1).
1484 if (!mappableTy.generateCopy(
1485 builder, loc, cast<TypedValue<MappableType>>(originalArg),
1486 cast<TypedValue<MappableType>>(privatizedArg), bounds, varInfo))
1487 return failure();
1488 } else {
1489 assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType");
1490 auto pointerLikeTy = cast<PointerLikeType>(varType);
1491 if (!pointerLikeTy.genCopy(
1492 builder, loc, cast<TypedValue<PointerLikeType>>(privatizedArg),
1493 cast<TypedValue<PointerLikeType>>(originalArg), varType))
1494 return failure();
1495 }
1496
1497 // Add terminator to copy block
1498 acc::TerminatorOp::create(builder, loc);
1499
1500 return success();
1501}
1502
1503/// Create and populate a destroy region for privatization recipes.
1504/// Returns success if the region is populated, failure otherwise.
1505/// The `varInfo` carries language-specific metadata produced by
1506/// `createInitRegion`.
1507static LogicalResult
1508createDestroyRegion(OpBuilder &builder, Location loc, Region &destroyRegion,
1509 Type varType, Value allocRes, ValueRange destroyValues,
1510 ValueRange bounds, acc::VariableInfoAttr varInfo) {
1511 // Create destroy block with arguments: original value + privatized value +
1512 // values preserved for destruction + bounds.
1513 SmallVector<Type> destroyArgTypes{varType, varType};
1514 SmallVector<Location> destroyArgLocs{loc, loc};
1515 for (Value destroyValue : destroyValues) {
1516 destroyArgTypes.push_back(destroyValue.getType());
1517 destroyArgLocs.push_back(loc);
1518 }
1519 for (Value bound : bounds) {
1520 destroyArgTypes.push_back(bound.getType());
1521 destroyArgLocs.push_back(loc);
1522 }
1523
1524 Block *destroyBlock = builder.createBlock(&destroyRegion);
1525 destroyBlock->addArguments(destroyArgTypes, destroyArgLocs);
1526 builder.setInsertionPointToStart(destroyBlock);
1527
1528 auto varToFree =
1529 cast<TypedValue<PointerLikeType>>(destroyBlock->getArgument(1));
1530 if (isa<MappableType>(varType)) {
1531 auto mappableTy = cast<MappableType>(varType);
1532 ValueRange destroyArgs =
1533 destroyBlock->getArguments().slice(2, destroyValues.size());
1534 ValueRange destroyBounds =
1535 destroyBlock->getArguments().drop_front(2 + destroyValues.size());
1536 if (!mappableTy.generatePrivateDestroy(builder, loc, varToFree, destroyArgs,
1537 destroyBounds, varInfo))
1538 return failure();
1539 } else {
1540 assert(isa<PointerLikeType>(varType) && "Expected PointerLikeType");
1541 auto pointerLikeTy = cast<PointerLikeType>(varType);
1542 if (!pointerLikeTy.genFree(builder, loc, varToFree, allocRes, varType))
1543 return failure();
1544 }
1545
1546 acc::TerminatorOp::create(builder, loc);
1547 return success();
1548}
1549} // namespace
1550
1551//===----------------------------------------------------------------------===//
1552// PrivateRecipeOp
1553//===----------------------------------------------------------------------===//
1554
1556 Operation *op, Region &region, StringRef regionType, StringRef regionName,
1557 Type type, bool verifyYield, bool optional = false) {
1558 if (optional && region.empty())
1559 return success();
1560
1561 if (region.empty())
1562 return op->emitOpError() << "expects non-empty " << regionName << " region";
1563 Block &firstBlock = region.front();
1564 if (firstBlock.getNumArguments() < 1 ||
1565 firstBlock.getArgument(0).getType() != type)
1566 return op->emitOpError() << "expects " << regionName
1567 << " region first "
1568 "argument of the "
1569 << regionType << " type";
1570
1571 if (verifyYield) {
1572 for (YieldOp yieldOp : region.getOps<acc::YieldOp>()) {
1573 if (yieldOp.getOperands().size() != 1 ||
1574 yieldOp.getOperands().getTypes()[0] != type)
1575 return op->emitOpError() << "expects " << regionName
1576 << " region to "
1577 "yield a value of the "
1578 << regionType << " type";
1579 }
1580 }
1581 return success();
1582}
1583
1584LogicalResult acc::PrivateRecipeOp::verifyRegions() {
1585 if (failed(verifyInitLikeSingleArgRegion(*this, getInitRegion(),
1586 "privatization", "init", getType(),
1587 /*verifyYield=*/false)))
1588 return failure();
1590 *this, getDestroyRegion(), "privatization", "destroy", getType(),
1591 /*verifyYield=*/false, /*optional=*/true)))
1592 return failure();
1593 return success();
1594}
1595
1596std::optional<PrivateRecipeOp>
1597PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
1598 StringRef recipeName, Value hostVar,
1599 StringRef varName, ValueRange bounds) {
1600 Type varType = hostVar.getType();
1601
1602 // First, validate that we can handle this variable type
1603 bool isMappable = isa<MappableType>(varType);
1604 bool isPointerLike = isa<PointerLikeType>(varType);
1605
1606 // Unsupported type
1607 if (!isMappable && !isPointerLike)
1608 return std::nullopt;
1609
1610 OpBuilder::InsertionGuard guard(builder);
1611
1612 // Create the recipe operation first so regions have proper parent context
1613 auto recipe = PrivateRecipeOp::create(builder, loc, recipeName,
1614 /*sym_visibility=*/nullptr, varType);
1615
1616 // Populate the init region
1617 bool needsFree = false;
1618 acc::VariableInfoAttr varInfo;
1619 SmallVector<Value> destroyValues;
1620 if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), hostVar,
1621 varName, bounds, needsFree, varInfo,
1622 destroyValues))) {
1623 recipe.erase();
1624 return std::nullopt;
1625 }
1626
1627 // Only create destroy region if the allocation needs deallocation
1628 if (needsFree) {
1629 // Extract the allocated value from the init block's yield operation
1630 auto yieldOp =
1631 cast<acc::YieldOp>(recipe.getInitRegion().front().getTerminator());
1632 Value allocRes = yieldOp.getOperand(0);
1633
1634 if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(),
1635 varType, allocRes, destroyValues, bounds,
1636 varInfo))) {
1637 recipe.erase();
1638 return std::nullopt;
1639 }
1640 }
1641
1642 return recipe;
1643}
1644
1645std::optional<PrivateRecipeOp>
1646PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
1647 StringRef recipeName,
1648 FirstprivateRecipeOp firstprivRecipe) {
1649 // Create the private.recipe op with the same type as the firstprivate.recipe.
1650 OpBuilder::InsertionGuard guard(builder);
1651 auto varType = firstprivRecipe.getType();
1652 auto recipe = PrivateRecipeOp::create(builder, loc, recipeName,
1653 /*sym_visibility=*/nullptr, varType);
1654
1655 // Clone the init region
1656 IRMapping mapping;
1657 firstprivRecipe.getInitRegion().cloneInto(&recipe.getInitRegion(), mapping);
1658
1659 // Clone destroy region if the firstprivate.recipe has one.
1660 if (!firstprivRecipe.getDestroyRegion().empty()) {
1661 IRMapping mapping;
1662 firstprivRecipe.getDestroyRegion().cloneInto(&recipe.getDestroyRegion(),
1663 mapping);
1664 }
1665 return recipe;
1666}
1667
1668//===----------------------------------------------------------------------===//
1669// FirstprivateRecipeOp
1670//===----------------------------------------------------------------------===//
1671
1672LogicalResult acc::FirstprivateRecipeOp::verifyRegions() {
1673 if (failed(verifyInitLikeSingleArgRegion(*this, getInitRegion(),
1674 "privatization", "init", getType(),
1675 /*verifyYield=*/false)))
1676 return failure();
1677
1678 if (getCopyRegion().empty())
1679 return emitOpError() << "expects non-empty copy region";
1680
1681 Block &firstBlock = getCopyRegion().front();
1682 if (firstBlock.getNumArguments() < 2 ||
1683 firstBlock.getArgument(0).getType() != getType())
1684 return emitOpError() << "expects copy region with two arguments of the "
1685 "privatization type";
1686
1687 if (getDestroyRegion().empty())
1688 return success();
1689
1690 if (failed(verifyInitLikeSingleArgRegion(*this, getDestroyRegion(),
1691 "privatization", "destroy",
1692 getType(), /*verifyYield=*/false)))
1693 return failure();
1694
1695 return success();
1696}
1697
1698std::optional<FirstprivateRecipeOp>
1699FirstprivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
1700 StringRef recipeName, Value hostVar,
1701 StringRef varName, ValueRange bounds) {
1702 Type varType = hostVar.getType();
1703
1704 // First, validate that we can handle this variable type
1705 bool isMappable = isa<MappableType>(varType);
1706 bool isPointerLike = isa<PointerLikeType>(varType);
1707
1708 // Unsupported type
1709 if (!isMappable && !isPointerLike)
1710 return std::nullopt;
1711
1712 OpBuilder::InsertionGuard guard(builder);
1713
1714 // Create the recipe operation first so regions have proper parent context
1715 auto recipe = FirstprivateRecipeOp::create(
1716 builder, loc, recipeName, /*sym_visibility=*/nullptr, varType);
1717
1718 // Populate the init region
1719 bool needsFree = false;
1720 // Filled by createInitRegion for mappable variables (genPrivateVariableInfo);
1721 // then passed through to copy/destroy so generateCopy /
1722 // generatePrivateDestroy receive the same metadata as generatePrivateInit.
1723 acc::VariableInfoAttr varInfo;
1724 SmallVector<Value> destroyValues;
1725 if (failed(createInitRegion(builder, loc, recipe.getInitRegion(), hostVar,
1726 varName, bounds, needsFree, varInfo,
1727 destroyValues))) {
1728 recipe.erase();
1729 return std::nullopt;
1730 }
1731
1732 // Populate the copy region (uses varInfo for MappableType::generateCopy).
1733 if (failed(createCopyRegion(builder, loc, recipe.getCopyRegion(), varType,
1734 bounds, varInfo))) {
1735 recipe.erase();
1736 return std::nullopt;
1737 }
1738
1739 // Only create destroy region if the allocation needs deallocation
1740 if (needsFree) {
1741 // Extract the allocated value from the init block's yield operation
1742 auto yieldOp =
1743 cast<acc::YieldOp>(recipe.getInitRegion().front().getTerminator());
1744 Value allocRes = yieldOp.getOperand(0);
1745
1746 if (failed(createDestroyRegion(builder, loc, recipe.getDestroyRegion(),
1747 varType, allocRes, destroyValues, bounds,
1748 varInfo))) {
1749 recipe.erase();
1750 return std::nullopt;
1751 }
1752 }
1753
1754 return recipe;
1755}
1756
1757//===----------------------------------------------------------------------===//
1758// ReductionRecipeOp
1759//===----------------------------------------------------------------------===//
1760
1761LogicalResult acc::ReductionRecipeOp::verifyRegions() {
1762 if (failed(verifyInitLikeSingleArgRegion(*this, getInitRegion(), "reduction",
1763 "init", getType(),
1764 /*verifyYield=*/false)))
1765 return failure();
1766
1767 if (getCombinerRegion().empty())
1768 return emitOpError() << "expects non-empty combiner region";
1769
1770 Block &reductionBlock = getCombinerRegion().front();
1771 if (reductionBlock.getNumArguments() < 2 ||
1772 reductionBlock.getArgument(0).getType() != getType() ||
1773 reductionBlock.getArgument(1).getType() != getType())
1774 return emitOpError() << "expects combiner region with the first two "
1775 << "arguments of the reduction type";
1776
1777 for (YieldOp yieldOp : getCombinerRegion().getOps<YieldOp>()) {
1778 if (yieldOp.getOperands().size() != 1 ||
1779 yieldOp.getOperands().getTypes()[0] != getType())
1780 return emitOpError() << "expects combiner region to yield a value "
1781 "of the reduction type";
1782 }
1783
1784 return success();
1785}
1786
1787//===----------------------------------------------------------------------===//
1788// ParallelOp
1789//===----------------------------------------------------------------------===//
1790
1791/// Check dataOperands for acc.parallel, acc.serial and acc.kernels.
1792template <typename Op>
1793static LogicalResult checkDataOperands(Op op,
1794 const mlir::ValueRange &operands) {
1795 for (mlir::Value operand : operands)
1796 if (!mlir::isa<acc::AttachOp, acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,
1797 acc::DeleteOp, acc::DetachOp, acc::DevicePtrOp,
1798 acc::GetDevicePtrOp, acc::NoCreateOp, acc::PresentOp,
1799 acc::MapInfoOp>(operand.getDefiningOp()))
1800 return op.emitError(
1801 "expect data entry/exit operation or acc.getdeviceptr "
1802 "as defining op");
1803 return success();
1804}
1805
1806template <typename OpT, typename RecipeOpT>
1807static LogicalResult checkPrivateOperands(mlir::Operation *accConstructOp,
1808 const mlir::ValueRange &operands,
1809 llvm::StringRef operandName) {
1811 for (mlir::Value operand : operands) {
1812 if (!mlir::isa<OpT>(operand.getDefiningOp()))
1813 return accConstructOp->emitOpError()
1814 << "expected " << operandName << " as defining op";
1815 if (!set.insert(operand).second)
1816 return accConstructOp->emitOpError()
1817 << operandName << " operand appears more than once";
1818 }
1819 return success();
1820}
1821
1822unsigned ParallelOp::getNumDataOperands() {
1823 return getReductionOperands().size() + getPrivateOperands().size() +
1824 getFirstprivateOperands().size() + getDataClauseOperands().size();
1825}
1826
1827Value ParallelOp::getDataOperand(unsigned i) {
1828 unsigned numOptional = getAsyncOperands().size();
1829 numOptional += getNumGangs().size();
1830 numOptional += getNumWorkers().size();
1831 numOptional += getVectorLength().size();
1832 numOptional += getIfCond() ? 1 : 0;
1833 numOptional += getSelfCond() ? 1 : 0;
1834 return getOperand(getWaitOperands().size() + numOptional + i);
1835}
1836
1837template <typename Op>
1838static LogicalResult verifyDeviceTypeCountMatch(Op op, OperandRange operands,
1839 ArrayAttr deviceTypes,
1840 llvm::StringRef keyword) {
1841 if (!operands.empty() &&
1842 (!deviceTypes || deviceTypes.getValue().size() != operands.size()))
1843 return op.emitOpError() << keyword << " operands count must match "
1844 << keyword << " device_type count";
1845 return success();
1846}
1847
1848template <typename Op>
1850 Op op, OperandRange operands, DenseI32ArrayAttr segments,
1851 ArrayAttr deviceTypes, llvm::StringRef keyword, int32_t maxInSegment = 0) {
1852 std::size_t numOperandsInSegments = 0;
1853 std::size_t nbOfSegments = 0;
1854
1855 if (segments) {
1856 for (auto segCount : segments.asArrayRef()) {
1857 if (maxInSegment != 0 && segCount > maxInSegment)
1858 return op.emitOpError() << keyword << " expects a maximum of "
1859 << maxInSegment << " values per segment";
1860 numOperandsInSegments += segCount;
1861 ++nbOfSegments;
1862 }
1863 }
1864
1865 if ((numOperandsInSegments != operands.size()) ||
1866 (!deviceTypes && !operands.empty()))
1867 return op.emitOpError()
1868 << keyword << " operand count does not match count in segments";
1869 if (deviceTypes && deviceTypes.getValue().size() != nbOfSegments)
1870 return op.emitOpError()
1871 << keyword << " segment count does not match device_type count";
1872 return success();
1873}
1874
1875LogicalResult acc::ParallelOp::verify() {
1876 if (failed(checkPrivateOperands<mlir::acc::PrivateOp,
1877 mlir::acc::PrivateRecipeOp>(
1878 *this, getPrivateOperands(), "private")))
1879 return failure();
1880 if (failed(checkPrivateOperands<mlir::acc::FirstprivateOp,
1881 mlir::acc::FirstprivateRecipeOp>(
1882 *this, getFirstprivateOperands(), "firstprivate")))
1883 return failure();
1884 if (failed(checkPrivateOperands<mlir::acc::ReductionOp,
1885 mlir::acc::ReductionRecipeOp>(
1886 *this, getReductionOperands(), "reduction")))
1887 return failure();
1888
1890 *this, getNumGangs(), getNumGangsSegmentsAttr(),
1891 getNumGangsDeviceTypeAttr(), "num_gangs", 3)))
1892 return failure();
1893
1895 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
1896 getWaitOperandsDeviceTypeAttr(), "wait")))
1897 return failure();
1898
1899 if (failed(verifyDeviceTypeCountMatch(*this, getNumWorkers(),
1900 getNumWorkersDeviceTypeAttr(),
1901 "num_workers")))
1902 return failure();
1903
1904 if (failed(verifyDeviceTypeCountMatch(*this, getVectorLength(),
1905 getVectorLengthDeviceTypeAttr(),
1906 "vector_length")))
1907 return failure();
1908
1910 getAsyncOperandsDeviceTypeAttr(),
1911 "async")))
1912 return failure();
1913
1915 return failure();
1916
1917 return checkDataOperands<acc::ParallelOp>(*this, getDataClauseOperands());
1918}
1919
1920static mlir::Value
1921getValueInDeviceTypeSegment(std::optional<mlir::ArrayAttr> arrayAttr,
1923 mlir::acc::DeviceType deviceType) {
1924 if (!arrayAttr)
1925 return {};
1926 if (auto pos = findSegment(*arrayAttr, deviceType))
1927 return range[*pos];
1928 return {};
1929}
1930
1931bool acc::ParallelOp::hasAsyncOnly() {
1932 return hasAsyncOnly(mlir::acc::DeviceType::None);
1933}
1934
1935bool acc::ParallelOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
1936 return hasDeviceType(getAsyncOnly(), deviceType);
1937}
1938
1939mlir::Value acc::ParallelOp::getAsyncValue() {
1940 return getAsyncValue(mlir::acc::DeviceType::None);
1941}
1942
1943mlir::Value acc::ParallelOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
1945 getAsyncOperands(), deviceType);
1946}
1947
1948mlir::Value acc::ParallelOp::getNumWorkersValue() {
1949 return getNumWorkersValue(mlir::acc::DeviceType::None);
1950}
1951
1953acc::ParallelOp::getNumWorkersValue(mlir::acc::DeviceType deviceType) {
1954 return getValueInDeviceTypeSegment(getNumWorkersDeviceType(), getNumWorkers(),
1955 deviceType);
1956}
1957
1958mlir::Value acc::ParallelOp::getVectorLengthValue() {
1959 return getVectorLengthValue(mlir::acc::DeviceType::None);
1960}
1961
1963acc::ParallelOp::getVectorLengthValue(mlir::acc::DeviceType deviceType) {
1964 return getValueInDeviceTypeSegment(getVectorLengthDeviceType(),
1965 getVectorLength(), deviceType);
1966}
1967
1968mlir::Operation::operand_range ParallelOp::getNumGangsValues() {
1969 return getNumGangsValues(mlir::acc::DeviceType::None);
1970}
1971
1973ParallelOp::getNumGangsValues(mlir::acc::DeviceType deviceType) {
1974 return getValuesFromSegments(getNumGangsDeviceType(), getNumGangs(),
1975 getNumGangsSegments(), deviceType);
1976}
1977
1979 std::optional<mlir::ArrayAttr> numGangsDeviceType,
1981 std::optional<llvm::ArrayRef<int32_t>> numGangsSegments,
1982 std::optional<mlir::ArrayAttr> numWorkersDeviceType,
1984 std::optional<mlir::ArrayAttr> vectorLengthDeviceType,
1985 mlir::Operation::operand_range vectorLength,
1986 mlir::acc::DeviceType deviceType) {
1987 return !getValuesFromSegments(numGangsDeviceType, numGangs, numGangsSegments,
1988 deviceType)
1989 .empty() ||
1990 getValueInDeviceTypeSegment(numWorkersDeviceType, numWorkers,
1991 deviceType) ||
1992 getValueInDeviceTypeSegment(vectorLengthDeviceType, vectorLength,
1993 deviceType);
1994}
1995
1996bool acc::ParallelOp::hasAnyGangWorkerVector(mlir::acc::DeviceType deviceType) {
1998 getNumGangsDeviceType(), getNumGangs(), getNumGangsSegments(),
1999 getNumWorkersDeviceType(), getNumWorkers(), getVectorLengthDeviceType(),
2000 getVectorLength(), deviceType);
2001}
2002
2003bool acc::ParallelOp::isEffectivelySerial() {
2004 return isGangWorkerVectorAllOne(*this);
2005}
2006
2007bool acc::ParallelOp::hasWaitOnly() {
2008 return hasWaitOnly(mlir::acc::DeviceType::None);
2009}
2010
2011bool acc::ParallelOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
2012 return hasDeviceType(getWaitOnly(), deviceType);
2013}
2014
2015mlir::Operation::operand_range ParallelOp::getWaitValues() {
2016 return getWaitValues(mlir::acc::DeviceType::None);
2017}
2018
2020ParallelOp::getWaitValues(mlir::acc::DeviceType deviceType) {
2022 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
2023 getHasWaitDevnum(), deviceType);
2024}
2025
2026mlir::Value ParallelOp::getWaitDevnum() {
2027 return getWaitDevnum(mlir::acc::DeviceType::None);
2028}
2029
2030mlir::Value ParallelOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
2031 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),
2032 getWaitOperandsSegments(), getHasWaitDevnum(),
2033 deviceType);
2034}
2035
2036void ParallelOp::build(mlir::OpBuilder &odsBuilder,
2037 mlir::OperationState &odsState,
2038 mlir::ValueRange numGangs, mlir::ValueRange numWorkers,
2039 mlir::ValueRange vectorLength,
2040 mlir::ValueRange asyncOperands,
2041 mlir::ValueRange waitOperands, mlir::Value ifCond,
2042 mlir::Value selfCond, mlir::ValueRange reductionOperands,
2043 mlir::ValueRange gangPrivateOperands,
2044 mlir::ValueRange gangFirstPrivateOperands,
2045 mlir::ValueRange dataClauseOperands) {
2046 ParallelOp::build(
2047 odsBuilder, odsState, asyncOperands, /*asyncOperandsDeviceType=*/nullptr,
2048 /*asyncOnly=*/nullptr, waitOperands, /*waitOperandsSegments=*/nullptr,
2049 /*waitOperandsDeviceType=*/nullptr, /*hasWaitDevnum=*/nullptr,
2050 /*waitOnly=*/nullptr, numGangs, /*numGangsSegments=*/nullptr,
2051 /*numGangsDeviceType=*/nullptr, numWorkers,
2052 /*numWorkersDeviceType=*/nullptr, vectorLength,
2053 /*vectorLengthDeviceType=*/nullptr, ifCond, selfCond,
2054 /*selfAttr=*/false, reductionOperands, gangPrivateOperands,
2055 gangFirstPrivateOperands, dataClauseOperands,
2056 /*defaultAttr=*/nullptr, /*combined=*/false);
2057}
2058
2059void acc::ParallelOp::addNumWorkersOperand(
2060 MLIRContext *context, mlir::Value newValue,
2061 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2062 setNumWorkersDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2063 context, getNumWorkersDeviceTypeAttr(), effectiveDeviceTypes, newValue,
2064 getNumWorkersMutable()));
2065}
2066void acc::ParallelOp::addVectorLengthOperand(
2067 MLIRContext *context, mlir::Value newValue,
2068 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2069 setVectorLengthDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2070 context, getVectorLengthDeviceTypeAttr(), effectiveDeviceTypes, newValue,
2071 getVectorLengthMutable()));
2072}
2073
2074void acc::ParallelOp::addAsyncOnly(
2075 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2076 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(
2077 context, getAsyncOnlyAttr(), effectiveDeviceTypes));
2078}
2079
2080void acc::ParallelOp::addAsyncOperand(
2081 MLIRContext *context, mlir::Value newValue,
2082 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2083 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2084 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,
2085 getAsyncOperandsMutable()));
2086}
2087
2088void acc::ParallelOp::addNumGangsOperands(
2089 MLIRContext *context, mlir::ValueRange newValues,
2090 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2092 if (getNumGangsSegments())
2093 llvm::copy(*getNumGangsSegments(), std::back_inserter(segments));
2094
2095 setNumGangsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2096 context, getNumGangsDeviceTypeAttr(), effectiveDeviceTypes, newValues,
2097 getNumGangsMutable(), segments));
2098
2099 setNumGangsSegments(segments);
2100}
2101void acc::ParallelOp::addWaitOnly(
2102 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2103 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),
2104 effectiveDeviceTypes));
2105}
2106void acc::ParallelOp::addWaitOperands(
2107 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,
2108 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2109
2111 if (getWaitOperandsSegments())
2112 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));
2113
2114 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2115 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,
2116 getWaitOperandsMutable(), segments));
2117 setWaitOperandsSegments(segments);
2118
2120 if (getHasWaitDevnumAttr())
2121 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));
2122 hasDevnums.insert(
2123 hasDevnums.end(),
2124 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),
2125 mlir::BoolAttr::get(context, hasDevnum));
2126 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));
2127}
2128
2129void acc::ParallelOp::addPrivatization(MLIRContext *context,
2130 mlir::acc::PrivateOp op,
2131 mlir::acc::PrivateRecipeOp recipe) {
2132 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2133 getPrivateOperandsMutable().append(op.getResult());
2134}
2135
2136void acc::ParallelOp::addFirstPrivatization(
2137 MLIRContext *context, mlir::acc::FirstprivateOp op,
2138 mlir::acc::FirstprivateRecipeOp recipe) {
2139 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2140 getFirstprivateOperandsMutable().append(op.getResult());
2141}
2142
2143void acc::ParallelOp::addReduction(MLIRContext *context,
2144 mlir::acc::ReductionOp op,
2145 mlir::acc::ReductionRecipeOp recipe) {
2146 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2147 getReductionOperandsMutable().append(op.getResult());
2148}
2149
2150static ParseResult parseNumGangs(
2151 mlir::OpAsmParser &parser,
2153 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,
2154 mlir::DenseI32ArrayAttr &segments) {
2157
2158 do {
2159 if (failed(parser.parseLBrace()))
2160 return failure();
2161
2162 int32_t crtOperandsSize = operands.size();
2163 if (failed(parser.parseCommaSeparatedList(
2165 if (parser.parseOperand(operands.emplace_back()) ||
2166 parser.parseColonType(types.emplace_back()))
2167 return failure();
2168 return success();
2169 })))
2170 return failure();
2171 seg.push_back(operands.size() - crtOperandsSize);
2172
2173 if (failed(parser.parseRBrace()))
2174 return failure();
2175
2176 if (succeeded(parser.parseOptionalLSquare())) {
2177 if (parser.parseAttribute(attributes.emplace_back()) ||
2178 parser.parseRSquare())
2179 return failure();
2180 } else {
2181 attributes.push_back(mlir::acc::DeviceTypeAttr::get(
2182 parser.getContext(), mlir::acc::DeviceType::None));
2183 }
2184 } while (succeeded(parser.parseOptionalComma()));
2185
2186 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),
2187 attributes.end());
2188 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);
2189 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);
2190
2191 return success();
2192}
2193
2195 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
2196 if (deviceTypeAttr.getValue() != mlir::acc::DeviceType::None)
2197 p << " [" << attr << "]";
2198}
2199
2201 mlir::OperandRange operands, mlir::TypeRange types,
2202 std::optional<mlir::ArrayAttr> deviceTypes,
2203 std::optional<mlir::DenseI32ArrayAttr> segments) {
2204 unsigned opIdx = 0;
2205 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {
2206 p << "{";
2207 llvm::interleaveComma(
2208 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {
2209 p << operands[opIdx] << " : " << operands[opIdx].getType();
2210 ++opIdx;
2211 });
2212 p << "}";
2213 printSingleDeviceType(p, it.value());
2214 });
2215}
2216
2218 mlir::OpAsmParser &parser,
2220 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,
2221 mlir::DenseI32ArrayAttr &segments) {
2224
2225 do {
2226 if (failed(parser.parseLBrace()))
2227 return failure();
2228
2229 int32_t crtOperandsSize = operands.size();
2230
2231 if (failed(parser.parseCommaSeparatedList(
2233 if (parser.parseOperand(operands.emplace_back()) ||
2234 parser.parseColonType(types.emplace_back()))
2235 return failure();
2236 return success();
2237 })))
2238 return failure();
2239
2240 seg.push_back(operands.size() - crtOperandsSize);
2241
2242 if (failed(parser.parseRBrace()))
2243 return failure();
2244
2245 if (succeeded(parser.parseOptionalLSquare())) {
2246 if (parser.parseAttribute(attributes.emplace_back()) ||
2247 parser.parseRSquare())
2248 return failure();
2249 } else {
2250 attributes.push_back(mlir::acc::DeviceTypeAttr::get(
2251 parser.getContext(), mlir::acc::DeviceType::None));
2252 }
2253 } while (succeeded(parser.parseOptionalComma()));
2254
2255 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),
2256 attributes.end());
2257 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);
2258 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);
2259
2260 return success();
2261}
2262
2265 mlir::TypeRange types, std::optional<mlir::ArrayAttr> deviceTypes,
2266 std::optional<mlir::DenseI32ArrayAttr> segments) {
2267 unsigned opIdx = 0;
2268 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {
2269 p << "{";
2270 llvm::interleaveComma(
2271 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {
2272 p << operands[opIdx] << " : " << operands[opIdx].getType();
2273 ++opIdx;
2274 });
2275 p << "}";
2276 printSingleDeviceType(p, it.value());
2277 });
2278}
2279
2280static ParseResult parseWaitClause(
2281 mlir::OpAsmParser &parser,
2283 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,
2284 mlir::DenseI32ArrayAttr &segments, mlir::ArrayAttr &hasDevNum,
2285 mlir::ArrayAttr &keywordOnly) {
2286 llvm::SmallVector<mlir::Attribute> deviceTypeAttrs, keywordAttrs, devnum;
2288
2289 bool needCommaBeforeOperands = false;
2290
2291 // Keyword only
2292 if (failed(parser.parseOptionalLParen())) {
2293 keywordAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
2294 parser.getContext(), mlir::acc::DeviceType::None));
2295 keywordOnly = ArrayAttr::get(parser.getContext(), keywordAttrs);
2296 return success();
2297 }
2298
2299 // Parse keyword only attributes
2300 if (succeeded(parser.parseOptionalLSquare())) {
2301 if (failed(parser.parseCommaSeparatedList([&]() {
2302 if (parser.parseAttribute(keywordAttrs.emplace_back()))
2303 return failure();
2304 return success();
2305 })))
2306 return failure();
2307 if (parser.parseRSquare())
2308 return failure();
2309 needCommaBeforeOperands = true;
2310 }
2311
2312 if (needCommaBeforeOperands && failed(parser.parseComma()))
2313 return failure();
2314
2315 do {
2316 if (failed(parser.parseLBrace()))
2317 return failure();
2318
2319 int32_t crtOperandsSize = operands.size();
2320
2321 if (succeeded(parser.parseOptionalKeyword("devnum"))) {
2322 if (failed(parser.parseColon()))
2323 return failure();
2324 devnum.push_back(BoolAttr::get(parser.getContext(), true));
2325 } else {
2326 devnum.push_back(BoolAttr::get(parser.getContext(), false));
2327 }
2328
2329 if (failed(parser.parseCommaSeparatedList(
2331 if (parser.parseOperand(operands.emplace_back()) ||
2332 parser.parseColonType(types.emplace_back()))
2333 return failure();
2334 return success();
2335 })))
2336 return failure();
2337
2338 seg.push_back(operands.size() - crtOperandsSize);
2339
2340 if (failed(parser.parseRBrace()))
2341 return failure();
2342
2343 if (succeeded(parser.parseOptionalLSquare())) {
2344 if (parser.parseAttribute(deviceTypeAttrs.emplace_back()) ||
2345 parser.parseRSquare())
2346 return failure();
2347 } else {
2348 deviceTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
2349 parser.getContext(), mlir::acc::DeviceType::None));
2350 }
2351 } while (succeeded(parser.parseOptionalComma()));
2352
2353 if (failed(parser.parseRParen()))
2354 return failure();
2355
2356 deviceTypes = ArrayAttr::get(parser.getContext(), deviceTypeAttrs);
2357 keywordOnly = ArrayAttr::get(parser.getContext(), keywordAttrs);
2358 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);
2359 hasDevNum = ArrayAttr::get(parser.getContext(), devnum);
2360
2361 return success();
2362}
2363
2364static bool hasOnlyDeviceTypeNone(std::optional<mlir::ArrayAttr> attrs) {
2365 if (!hasDeviceTypeValues(attrs))
2366 return false;
2367 if (attrs->size() != 1)
2368 return false;
2369 if (auto deviceTypeAttr =
2370 mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*attrs)[0]))
2371 return deviceTypeAttr.getValue() == mlir::acc::DeviceType::None;
2372 return false;
2373}
2374
2376 mlir::OperandRange operands, mlir::TypeRange types,
2377 std::optional<mlir::ArrayAttr> deviceTypes,
2378 std::optional<mlir::DenseI32ArrayAttr> segments,
2379 std::optional<mlir::ArrayAttr> hasDevNum,
2380 std::optional<mlir::ArrayAttr> keywordOnly) {
2381
2382 if (operands.begin() == operands.end() && hasOnlyDeviceTypeNone(keywordOnly))
2383 return;
2384
2385 p << "(";
2386
2387 printDeviceTypes(p, keywordOnly);
2388 if (hasDeviceTypeValues(keywordOnly) && hasDeviceTypeValues(deviceTypes))
2389 p << ", ";
2390
2391 if (hasDeviceTypeValues(deviceTypes)) {
2392 unsigned opIdx = 0;
2393 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {
2394 p << "{";
2395 auto boolAttr = mlir::dyn_cast<mlir::BoolAttr>((*hasDevNum)[it.index()]);
2396 if (boolAttr && boolAttr.getValue())
2397 p << "devnum: ";
2398 llvm::interleaveComma(
2399 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {
2400 p << operands[opIdx] << " : " << operands[opIdx].getType();
2401 ++opIdx;
2402 });
2403 p << "}";
2404 printSingleDeviceType(p, it.value());
2405 });
2406 }
2407
2408 p << ")";
2409}
2410
2411static ParseResult parseDeviceTypeOperands(
2412 mlir::OpAsmParser &parser,
2414 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes) {
2416 if (failed(parser.parseCommaSeparatedList([&]() {
2417 if (parser.parseOperand(operands.emplace_back()) ||
2418 parser.parseColonType(types.emplace_back()))
2419 return failure();
2420 if (succeeded(parser.parseOptionalLSquare())) {
2421 if (parser.parseAttribute(attributes.emplace_back()) ||
2422 parser.parseRSquare())
2423 return failure();
2424 } else {
2425 attributes.push_back(mlir::acc::DeviceTypeAttr::get(
2426 parser.getContext(), mlir::acc::DeviceType::None));
2427 }
2428 return success();
2429 })))
2430 return failure();
2431 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),
2432 attributes.end());
2433 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);
2434 return success();
2435}
2436
2437static void
2439 mlir::OperandRange operands, mlir::TypeRange types,
2440 std::optional<mlir::ArrayAttr> deviceTypes) {
2441 if (!hasDeviceTypeValues(deviceTypes))
2442 return;
2443 llvm::interleaveComma(llvm::zip(*deviceTypes, operands), p, [&](auto it) {
2444 p << std::get<1>(it) << " : " << std::get<1>(it).getType();
2445 printSingleDeviceType(p, std::get<0>(it));
2446 });
2447}
2448
2450 mlir::OpAsmParser &parser,
2452 llvm::SmallVectorImpl<Type> &types, mlir::ArrayAttr &deviceTypes,
2453 mlir::ArrayAttr &keywordOnlyDeviceType) {
2454
2455 llvm::SmallVector<mlir::Attribute> keywordOnlyDeviceTypeAttributes;
2456 bool needCommaBeforeOperands = false;
2457
2458 if (failed(parser.parseOptionalLParen())) {
2459 // Keyword only
2460 keywordOnlyDeviceTypeAttributes.push_back(mlir::acc::DeviceTypeAttr::get(
2461 parser.getContext(), mlir::acc::DeviceType::None));
2462 keywordOnlyDeviceType =
2463 ArrayAttr::get(parser.getContext(), keywordOnlyDeviceTypeAttributes);
2464 return success();
2465 }
2466
2467 // Parse keyword only attributes
2468 if (succeeded(parser.parseOptionalLSquare())) {
2469 // Parse keyword only attributes
2470 if (failed(parser.parseCommaSeparatedList([&]() {
2471 if (parser.parseAttribute(
2472 keywordOnlyDeviceTypeAttributes.emplace_back()))
2473 return failure();
2474 return success();
2475 })))
2476 return failure();
2477 if (parser.parseRSquare())
2478 return failure();
2479 keywordOnlyDeviceType =
2480 ArrayAttr::get(parser.getContext(), keywordOnlyDeviceTypeAttributes);
2481 needCommaBeforeOperands = true;
2482 }
2483
2484 if (needCommaBeforeOperands) {
2485 if (succeeded(parser.parseOptionalRParen()))
2486 return success();
2487 if (failed(parser.parseComma()))
2488 return failure();
2489 }
2490
2492 if (failed(parser.parseCommaSeparatedList([&]() {
2493 if (parser.parseOperand(operands.emplace_back()) ||
2494 parser.parseColonType(types.emplace_back()))
2495 return failure();
2496 if (succeeded(parser.parseOptionalLSquare())) {
2497 if (parser.parseAttribute(attributes.emplace_back()) ||
2498 parser.parseRSquare())
2499 return failure();
2500 } else {
2501 attributes.push_back(mlir::acc::DeviceTypeAttr::get(
2502 parser.getContext(), mlir::acc::DeviceType::None));
2503 }
2504 return success();
2505 })))
2506 return failure();
2507
2508 if (failed(parser.parseRParen()))
2509 return failure();
2510
2511 llvm::SmallVector<mlir::Attribute> arrayAttr(attributes.begin(),
2512 attributes.end());
2513 deviceTypes = ArrayAttr::get(parser.getContext(), arrayAttr);
2514 return success();
2515}
2516
2519 mlir::TypeRange types, std::optional<mlir::ArrayAttr> deviceTypes,
2520 std::optional<mlir::ArrayAttr> keywordOnlyDeviceTypes) {
2521
2522 if (operands.begin() == operands.end() &&
2523 hasOnlyDeviceTypeNone(keywordOnlyDeviceTypes)) {
2524 return;
2525 }
2526
2527 p << "(";
2528 printDeviceTypes(p, keywordOnlyDeviceTypes);
2529 if (hasDeviceTypeValues(keywordOnlyDeviceTypes) &&
2530 hasDeviceTypeValues(deviceTypes))
2531 p << ", ";
2532 printDeviceTypeOperands(p, op, operands, types, deviceTypes);
2533 p << ")";
2534}
2535
2537 mlir::OpAsmParser &parser,
2538 std::optional<OpAsmParser::UnresolvedOperand> &operand,
2539 mlir::Type &operandType, mlir::UnitAttr &attr) {
2540 // Keyword only
2541 if (failed(parser.parseOptionalLParen())) {
2542 attr = mlir::UnitAttr::get(parser.getContext());
2543 return success();
2544 }
2545
2547 if (failed(parser.parseOperand(op)))
2548 return failure();
2549 operand = op;
2550 if (failed(parser.parseColon()))
2551 return failure();
2552 if (failed(parser.parseType(operandType)))
2553 return failure();
2554 if (failed(parser.parseRParen()))
2555 return failure();
2556
2557 return success();
2558}
2559
2561 mlir::Operation *op,
2562 std::optional<mlir::Value> operand,
2563 mlir::Type operandType,
2564 mlir::UnitAttr attr) {
2565 if (attr)
2566 return;
2567
2568 p << "(";
2569 p.printOperand(*operand);
2570 p << " : ";
2571 p.printType(operandType);
2572 p << ")";
2573}
2574
2576 mlir::OpAsmParser &parser,
2578 llvm::SmallVectorImpl<Type> &types, mlir::UnitAttr &attr) {
2579 // Keyword only
2580 if (failed(parser.parseOptionalLParen())) {
2581 attr = mlir::UnitAttr::get(parser.getContext());
2582 return success();
2583 }
2584
2585 if (failed(parser.parseCommaSeparatedList([&]() {
2586 if (parser.parseOperand(operands.emplace_back()))
2587 return failure();
2588 return success();
2589 })))
2590 return failure();
2591 if (failed(parser.parseColon()))
2592 return failure();
2593 if (failed(parser.parseCommaSeparatedList([&]() {
2594 if (parser.parseType(types.emplace_back()))
2595 return failure();
2596 return success();
2597 })))
2598 return failure();
2599 if (failed(parser.parseRParen()))
2600 return failure();
2601
2602 return success();
2603}
2604
2606 mlir::Operation *op,
2607 mlir::OperandRange operands,
2608 mlir::TypeRange types,
2609 mlir::UnitAttr attr) {
2610 if (attr)
2611 return;
2612
2613 p << "(";
2614 llvm::interleaveComma(operands, p, [&](auto it) { p << it; });
2615 p << " : ";
2616 llvm::interleaveComma(types, p, [&](auto it) { p << it; });
2617 p << ")";
2618}
2619
2620static ParseResult
2622 mlir::acc::CombinedConstructsTypeAttr &attr) {
2623 if (succeeded(parser.parseOptionalKeyword("kernels"))) {
2624 attr = mlir::acc::CombinedConstructsTypeAttr::get(
2625 parser.getContext(), mlir::acc::CombinedConstructsType::KernelsLoop);
2626 } else if (succeeded(parser.parseOptionalKeyword("parallel"))) {
2627 attr = mlir::acc::CombinedConstructsTypeAttr::get(
2628 parser.getContext(), mlir::acc::CombinedConstructsType::ParallelLoop);
2629 } else if (succeeded(parser.parseOptionalKeyword("serial"))) {
2630 attr = mlir::acc::CombinedConstructsTypeAttr::get(
2631 parser.getContext(), mlir::acc::CombinedConstructsType::SerialLoop);
2632 } else {
2633 parser.emitError(parser.getCurrentLocation(),
2634 "expected compute construct name");
2635 return failure();
2636 }
2637 return success();
2638}
2639
2640static void
2642 mlir::acc::CombinedConstructsTypeAttr attr) {
2643 if (attr) {
2644 switch (attr.getValue()) {
2645 case mlir::acc::CombinedConstructsType::KernelsLoop:
2646 p << "kernels";
2647 break;
2648 case mlir::acc::CombinedConstructsType::ParallelLoop:
2649 p << "parallel";
2650 break;
2651 case mlir::acc::CombinedConstructsType::SerialLoop:
2652 p << "serial";
2653 break;
2654 };
2655 }
2656}
2657
2658//===----------------------------------------------------------------------===//
2659// SerialOp
2660//===----------------------------------------------------------------------===//
2661
2662unsigned SerialOp::getNumDataOperands() {
2663 return getReductionOperands().size() + getPrivateOperands().size() +
2664 getFirstprivateOperands().size() + getDataClauseOperands().size();
2665}
2666
2667Value SerialOp::getDataOperand(unsigned i) {
2668 unsigned numOptional = getAsyncOperands().size();
2669 numOptional += getIfCond() ? 1 : 0;
2670 numOptional += getSelfCond() ? 1 : 0;
2671 return getOperand(getWaitOperands().size() + numOptional + i);
2672}
2673
2674bool acc::SerialOp::hasAsyncOnly() {
2675 return hasAsyncOnly(mlir::acc::DeviceType::None);
2676}
2677
2678bool acc::SerialOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
2679 return hasDeviceType(getAsyncOnly(), deviceType);
2680}
2681
2682mlir::Value acc::SerialOp::getAsyncValue() {
2683 return getAsyncValue(mlir::acc::DeviceType::None);
2684}
2685
2686mlir::Value acc::SerialOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
2688 getAsyncOperands(), deviceType);
2689}
2690
2691bool acc::SerialOp::hasWaitOnly() {
2692 return hasWaitOnly(mlir::acc::DeviceType::None);
2693}
2694
2695bool acc::SerialOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
2696 return hasDeviceType(getWaitOnly(), deviceType);
2697}
2698
2699mlir::Operation::operand_range SerialOp::getWaitValues() {
2700 return getWaitValues(mlir::acc::DeviceType::None);
2701}
2702
2704SerialOp::getWaitValues(mlir::acc::DeviceType deviceType) {
2706 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
2707 getHasWaitDevnum(), deviceType);
2708}
2709
2710mlir::Value SerialOp::getWaitDevnum() {
2711 return getWaitDevnum(mlir::acc::DeviceType::None);
2712}
2713
2714mlir::Value SerialOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
2715 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),
2716 getWaitOperandsSegments(), getHasWaitDevnum(),
2717 deviceType);
2718}
2719
2720LogicalResult acc::SerialOp::verify() {
2721 if (failed(checkPrivateOperands<mlir::acc::PrivateOp,
2722 mlir::acc::PrivateRecipeOp>(
2723 *this, getPrivateOperands(), "private")))
2724 return failure();
2725 if (failed(checkPrivateOperands<mlir::acc::FirstprivateOp,
2726 mlir::acc::FirstprivateRecipeOp>(
2727 *this, getFirstprivateOperands(), "firstprivate")))
2728 return failure();
2729 if (failed(checkPrivateOperands<mlir::acc::ReductionOp,
2730 mlir::acc::ReductionRecipeOp>(
2731 *this, getReductionOperands(), "reduction")))
2732 return failure();
2733
2735 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
2736 getWaitOperandsDeviceTypeAttr(), "wait")))
2737 return failure();
2738
2740 getAsyncOperandsDeviceTypeAttr(),
2741 "async")))
2742 return failure();
2743
2745 return failure();
2746
2747 return checkDataOperands<acc::SerialOp>(*this, getDataClauseOperands());
2748}
2749
2750void acc::SerialOp::addAsyncOnly(
2751 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2752 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(
2753 context, getAsyncOnlyAttr(), effectiveDeviceTypes));
2754}
2755
2756void acc::SerialOp::addAsyncOperand(
2757 MLIRContext *context, mlir::Value newValue,
2758 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2759 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2760 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,
2761 getAsyncOperandsMutable()));
2762}
2763
2764void acc::SerialOp::addWaitOnly(
2765 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2766 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),
2767 effectiveDeviceTypes));
2768}
2769void acc::SerialOp::addWaitOperands(
2770 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,
2771 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2772
2774 if (getWaitOperandsSegments())
2775 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));
2776
2777 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2778 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,
2779 getWaitOperandsMutable(), segments));
2780 setWaitOperandsSegments(segments);
2781
2783 if (getHasWaitDevnumAttr())
2784 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));
2785 hasDevnums.insert(
2786 hasDevnums.end(),
2787 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),
2788 mlir::BoolAttr::get(context, hasDevnum));
2789 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));
2790}
2791
2792void acc::SerialOp::addPrivatization(MLIRContext *context,
2793 mlir::acc::PrivateOp op,
2794 mlir::acc::PrivateRecipeOp recipe) {
2795 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2796 getPrivateOperandsMutable().append(op.getResult());
2797}
2798
2799void acc::SerialOp::addFirstPrivatization(
2800 MLIRContext *context, mlir::acc::FirstprivateOp op,
2801 mlir::acc::FirstprivateRecipeOp recipe) {
2802 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2803 getFirstprivateOperandsMutable().append(op.getResult());
2804}
2805
2806void acc::SerialOp::addReduction(MLIRContext *context,
2807 mlir::acc::ReductionOp op,
2808 mlir::acc::ReductionRecipeOp recipe) {
2809 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2810 getReductionOperandsMutable().append(op.getResult());
2811}
2812
2813//===----------------------------------------------------------------------===//
2814// KernelsOp
2815//===----------------------------------------------------------------------===//
2816
2817unsigned KernelsOp::getNumDataOperands() {
2818 return getDataClauseOperands().size();
2819}
2820
2821Value KernelsOp::getDataOperand(unsigned i) {
2822 unsigned numOptional = getAsyncOperands().size();
2823 numOptional += getWaitOperands().size();
2824 numOptional += getNumGangs().size();
2825 numOptional += getNumWorkers().size();
2826 numOptional += getVectorLength().size();
2827 numOptional += getIfCond() ? 1 : 0;
2828 numOptional += getSelfCond() ? 1 : 0;
2829 return getOperand(numOptional + i);
2830}
2831
2832bool acc::KernelsOp::hasAsyncOnly() {
2833 return hasAsyncOnly(mlir::acc::DeviceType::None);
2834}
2835
2836bool acc::KernelsOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
2837 return hasDeviceType(getAsyncOnly(), deviceType);
2838}
2839
2840mlir::Value acc::KernelsOp::getAsyncValue() {
2841 return getAsyncValue(mlir::acc::DeviceType::None);
2842}
2843
2844mlir::Value acc::KernelsOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
2846 getAsyncOperands(), deviceType);
2847}
2848
2849mlir::Value acc::KernelsOp::getNumWorkersValue() {
2850 return getNumWorkersValue(mlir::acc::DeviceType::None);
2851}
2852
2854acc::KernelsOp::getNumWorkersValue(mlir::acc::DeviceType deviceType) {
2855 return getValueInDeviceTypeSegment(getNumWorkersDeviceType(), getNumWorkers(),
2856 deviceType);
2857}
2858
2859mlir::Value acc::KernelsOp::getVectorLengthValue() {
2860 return getVectorLengthValue(mlir::acc::DeviceType::None);
2861}
2862
2864acc::KernelsOp::getVectorLengthValue(mlir::acc::DeviceType deviceType) {
2865 return getValueInDeviceTypeSegment(getVectorLengthDeviceType(),
2866 getVectorLength(), deviceType);
2867}
2868
2869mlir::Operation::operand_range KernelsOp::getNumGangsValues() {
2870 return getNumGangsValues(mlir::acc::DeviceType::None);
2871}
2872
2874KernelsOp::getNumGangsValues(mlir::acc::DeviceType deviceType) {
2875 return getValuesFromSegments(getNumGangsDeviceType(), getNumGangs(),
2876 getNumGangsSegments(), deviceType);
2877}
2878
2879bool acc::KernelsOp::hasAnyGangWorkerVector(mlir::acc::DeviceType deviceType) {
2881 getNumGangsDeviceType(), getNumGangs(), getNumGangsSegments(),
2882 getNumWorkersDeviceType(), getNumWorkers(), getVectorLengthDeviceType(),
2883 getVectorLength(), deviceType);
2884}
2885
2886bool acc::KernelsOp::isEffectivelySerial() {
2887 return isGangWorkerVectorAllOne(*this);
2888}
2889
2890bool acc::KernelsOp::hasWaitOnly() {
2891 return hasWaitOnly(mlir::acc::DeviceType::None);
2892}
2893
2894bool acc::KernelsOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
2895 return hasDeviceType(getWaitOnly(), deviceType);
2896}
2897
2898mlir::Operation::operand_range KernelsOp::getWaitValues() {
2899 return getWaitValues(mlir::acc::DeviceType::None);
2900}
2901
2903KernelsOp::getWaitValues(mlir::acc::DeviceType deviceType) {
2905 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
2906 getHasWaitDevnum(), deviceType);
2907}
2908
2909mlir::Value KernelsOp::getWaitDevnum() {
2910 return getWaitDevnum(mlir::acc::DeviceType::None);
2911}
2912
2913mlir::Value KernelsOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
2914 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),
2915 getWaitOperandsSegments(), getHasWaitDevnum(),
2916 deviceType);
2917}
2918
2919LogicalResult acc::KernelsOp::verify() {
2921 *this, getNumGangs(), getNumGangsSegmentsAttr(),
2922 getNumGangsDeviceTypeAttr(), "num_gangs", 3)))
2923 return failure();
2924
2926 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
2927 getWaitOperandsDeviceTypeAttr(), "wait")))
2928 return failure();
2929
2930 if (failed(verifyDeviceTypeCountMatch(*this, getNumWorkers(),
2931 getNumWorkersDeviceTypeAttr(),
2932 "num_workers")))
2933 return failure();
2934
2935 if (failed(verifyDeviceTypeCountMatch(*this, getVectorLength(),
2936 getVectorLengthDeviceTypeAttr(),
2937 "vector_length")))
2938 return failure();
2939
2941 getAsyncOperandsDeviceTypeAttr(),
2942 "async")))
2943 return failure();
2944
2946 return failure();
2947
2948 return checkDataOperands<acc::KernelsOp>(*this, getDataClauseOperands());
2949}
2950
2951void acc::KernelsOp::addPrivatization(MLIRContext *context,
2952 mlir::acc::PrivateOp op,
2953 mlir::acc::PrivateRecipeOp recipe) {
2954 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2955 getPrivateOperandsMutable().append(op.getResult());
2956}
2957
2958void acc::KernelsOp::addFirstPrivatization(
2959 MLIRContext *context, mlir::acc::FirstprivateOp op,
2960 mlir::acc::FirstprivateRecipeOp recipe) {
2961 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2962 getFirstprivateOperandsMutable().append(op.getResult());
2963}
2964
2965void acc::KernelsOp::addReduction(MLIRContext *context,
2966 mlir::acc::ReductionOp op,
2967 mlir::acc::ReductionRecipeOp recipe) {
2968 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
2969 getReductionOperandsMutable().append(op.getResult());
2970}
2971
2972void acc::KernelsOp::addNumWorkersOperand(
2973 MLIRContext *context, mlir::Value newValue,
2974 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2975 setNumWorkersDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2976 context, getNumWorkersDeviceTypeAttr(), effectiveDeviceTypes, newValue,
2977 getNumWorkersMutable()));
2978}
2979
2980void acc::KernelsOp::addVectorLengthOperand(
2981 MLIRContext *context, mlir::Value newValue,
2982 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2983 setVectorLengthDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2984 context, getVectorLengthDeviceTypeAttr(), effectiveDeviceTypes, newValue,
2985 getVectorLengthMutable()));
2986}
2987void acc::KernelsOp::addAsyncOnly(
2988 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2989 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(
2990 context, getAsyncOnlyAttr(), effectiveDeviceTypes));
2991}
2992
2993void acc::KernelsOp::addAsyncOperand(
2994 MLIRContext *context, mlir::Value newValue,
2995 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
2996 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
2997 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,
2998 getAsyncOperandsMutable()));
2999}
3000
3001void acc::KernelsOp::addNumGangsOperands(
3002 MLIRContext *context, mlir::ValueRange newValues,
3003 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3005 if (getNumGangsSegmentsAttr())
3006 llvm::copy(*getNumGangsSegments(), std::back_inserter(segments));
3007
3008 setNumGangsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
3009 context, getNumGangsDeviceTypeAttr(), effectiveDeviceTypes, newValues,
3010 getNumGangsMutable(), segments));
3011
3012 setNumGangsSegments(segments);
3013}
3014
3015void acc::KernelsOp::addWaitOnly(
3016 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3017 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),
3018 effectiveDeviceTypes));
3019}
3020void acc::KernelsOp::addWaitOperands(
3021 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,
3022 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3023
3025 if (getWaitOperandsSegments())
3026 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));
3027
3028 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
3029 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,
3030 getWaitOperandsMutable(), segments));
3031 setWaitOperandsSegments(segments);
3032
3034 if (getHasWaitDevnumAttr())
3035 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));
3036 hasDevnums.insert(
3037 hasDevnums.end(),
3038 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),
3039 mlir::BoolAttr::get(context, hasDevnum));
3040 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));
3041}
3042
3043//===----------------------------------------------------------------------===//
3044// HostDataOp
3045//===----------------------------------------------------------------------===//
3046
3047LogicalResult acc::HostDataOp::verify() {
3048 if (getDataClauseOperands().empty())
3049 return emitError("at least one operand must appear on the host_data "
3050 "operation");
3051
3053 for (mlir::Value operand : getDataClauseOperands()) {
3054 auto useDeviceOp =
3055 mlir::dyn_cast_if_present<acc::UseDeviceOp>(operand.getDefiningOp());
3056 if (!useDeviceOp)
3057 return emitError("expect data entry operation as defining op");
3058
3059 // Check for duplicate use_device clauses
3060 if (!seenVars.insert(useDeviceOp.getVar()).second)
3061 return emitError("duplicate use_device variable");
3062 }
3063 return success();
3064}
3065
3066void acc::HostDataOp::getCanonicalizationPatterns(RewritePatternSet &results,
3067 MLIRContext *context) {
3068 results.add<RemoveConstantIfConditionWithRegion<HostDataOp>>(context);
3069}
3070
3071//===----------------------------------------------------------------------===//
3072// LoopOp
3073//===----------------------------------------------------------------------===//
3074
3075static ParseResult parseGangValue(
3076 OpAsmParser &parser, llvm::StringRef keyword,
3079 llvm::SmallVector<GangArgTypeAttr> &attributes, GangArgTypeAttr gangArgType,
3080 bool &needCommaBetweenValues, bool &newValue) {
3081 if (succeeded(parser.parseOptionalKeyword(keyword))) {
3082 if (parser.parseEqual())
3083 return failure();
3084 if (parser.parseOperand(operands.emplace_back()) ||
3085 parser.parseColonType(types.emplace_back()))
3086 return failure();
3087 attributes.push_back(gangArgType);
3088 needCommaBetweenValues = true;
3089 newValue = true;
3090 }
3091 return success();
3092}
3093
3094static ParseResult parseGangClause(
3095 OpAsmParser &parser,
3097 llvm::SmallVectorImpl<Type> &gangOperandsType, mlir::ArrayAttr &gangArgType,
3098 mlir::ArrayAttr &deviceType, mlir::DenseI32ArrayAttr &segments,
3099 mlir::ArrayAttr &gangOnlyDeviceType) {
3100 llvm::SmallVector<GangArgTypeAttr> gangArgTypeAttributes;
3101 llvm::SmallVector<mlir::Attribute> deviceTypeAttributes;
3102 llvm::SmallVector<mlir::Attribute> gangOnlyDeviceTypeAttributes;
3104 bool needCommaBetweenValues = false;
3105 bool needCommaBeforeOperands = false;
3106
3107 if (failed(parser.parseOptionalLParen())) {
3108 // Gang only keyword
3109 gangOnlyDeviceTypeAttributes.push_back(mlir::acc::DeviceTypeAttr::get(
3110 parser.getContext(), mlir::acc::DeviceType::None));
3111 gangOnlyDeviceType =
3112 ArrayAttr::get(parser.getContext(), gangOnlyDeviceTypeAttributes);
3113 return success();
3114 }
3115
3116 // Parse gang only attributes
3117 if (succeeded(parser.parseOptionalLSquare())) {
3118 // Parse gang only attributes
3119 if (failed(parser.parseCommaSeparatedList([&]() {
3120 if (parser.parseAttribute(
3121 gangOnlyDeviceTypeAttributes.emplace_back()))
3122 return failure();
3123 return success();
3124 })))
3125 return failure();
3126 if (parser.parseRSquare())
3127 return failure();
3128 needCommaBeforeOperands = true;
3129 }
3130
3131 auto argNum = mlir::acc::GangArgTypeAttr::get(parser.getContext(),
3132 mlir::acc::GangArgType::Num);
3133 auto argDim = mlir::acc::GangArgTypeAttr::get(parser.getContext(),
3134 mlir::acc::GangArgType::Dim);
3135 auto argStatic = mlir::acc::GangArgTypeAttr::get(
3136 parser.getContext(), mlir::acc::GangArgType::Static);
3137
3138 do {
3139 if (needCommaBeforeOperands) {
3140 needCommaBeforeOperands = false;
3141 continue;
3142 }
3143
3144 if (failed(parser.parseLBrace()))
3145 return failure();
3146
3147 int32_t crtOperandsSize = gangOperands.size();
3148 while (true) {
3149 bool newValue = false;
3150 bool needValue = false;
3151 if (needCommaBetweenValues) {
3152 if (succeeded(parser.parseOptionalComma()))
3153 needValue = true; // expect a new value after comma.
3154 else
3155 break;
3156 }
3157
3158 if (failed(parseGangValue(parser, LoopOp::getGangNumKeyword(),
3159 gangOperands, gangOperandsType,
3160 gangArgTypeAttributes, argNum,
3161 needCommaBetweenValues, newValue)))
3162 return failure();
3163 if (failed(parseGangValue(parser, LoopOp::getGangDimKeyword(),
3164 gangOperands, gangOperandsType,
3165 gangArgTypeAttributes, argDim,
3166 needCommaBetweenValues, newValue)))
3167 return failure();
3168 if (failed(parseGangValue(parser, LoopOp::getGangStaticKeyword(),
3169 gangOperands, gangOperandsType,
3170 gangArgTypeAttributes, argStatic,
3171 needCommaBetweenValues, newValue)))
3172 return failure();
3173
3174 if (!newValue && needValue) {
3175 parser.emitError(parser.getCurrentLocation(),
3176 "new value expected after comma");
3177 return failure();
3178 }
3179
3180 if (!newValue)
3181 break;
3182 }
3183
3184 if (gangOperands.empty())
3185 return parser.emitError(
3186 parser.getCurrentLocation(),
3187 "expect at least one of num, dim or static values");
3188
3189 if (failed(parser.parseRBrace()))
3190 return failure();
3191
3192 if (succeeded(parser.parseOptionalLSquare())) {
3193 if (parser.parseAttribute(deviceTypeAttributes.emplace_back()) ||
3194 parser.parseRSquare())
3195 return failure();
3196 } else {
3197 deviceTypeAttributes.push_back(mlir::acc::DeviceTypeAttr::get(
3198 parser.getContext(), mlir::acc::DeviceType::None));
3199 }
3200
3201 seg.push_back(gangOperands.size() - crtOperandsSize);
3202
3203 } while (succeeded(parser.parseOptionalComma()));
3204
3205 if (failed(parser.parseRParen()))
3206 return failure();
3207
3208 llvm::SmallVector<mlir::Attribute> arrayAttr(gangArgTypeAttributes.begin(),
3209 gangArgTypeAttributes.end());
3210 gangArgType = ArrayAttr::get(parser.getContext(), arrayAttr);
3211 deviceType = ArrayAttr::get(parser.getContext(), deviceTypeAttributes);
3212
3214 gangOnlyDeviceTypeAttributes.begin(), gangOnlyDeviceTypeAttributes.end());
3215 gangOnlyDeviceType = ArrayAttr::get(parser.getContext(), gangOnlyAttr);
3216
3217 segments = DenseI32ArrayAttr::get(parser.getContext(), seg);
3218 return success();
3219}
3220
3222 mlir::OperandRange operands, mlir::TypeRange types,
3223 std::optional<mlir::ArrayAttr> gangArgTypes,
3224 std::optional<mlir::ArrayAttr> deviceTypes,
3225 std::optional<mlir::DenseI32ArrayAttr> segments,
3226 std::optional<mlir::ArrayAttr> gangOnlyDeviceTypes) {
3227
3228 if (operands.begin() == operands.end() &&
3229 hasOnlyDeviceTypeNone(gangOnlyDeviceTypes)) {
3230 return;
3231 }
3232
3233 p << "(";
3234
3235 printDeviceTypes(p, gangOnlyDeviceTypes);
3236
3237 if (hasDeviceTypeValues(gangOnlyDeviceTypes) &&
3238 hasDeviceTypeValues(deviceTypes))
3239 p << ", ";
3240
3241 if (hasDeviceTypeValues(deviceTypes)) {
3242 unsigned opIdx = 0;
3243 llvm::interleaveComma(llvm::enumerate(*deviceTypes), p, [&](auto it) {
3244 p << "{";
3245 llvm::interleaveComma(
3246 llvm::seq<int32_t>(0, (*segments)[it.index()]), p, [&](auto it) {
3247 auto gangArgTypeAttr = mlir::dyn_cast<mlir::acc::GangArgTypeAttr>(
3248 (*gangArgTypes)[opIdx]);
3249 if (gangArgTypeAttr.getValue() == mlir::acc::GangArgType::Num)
3250 p << LoopOp::getGangNumKeyword();
3251 else if (gangArgTypeAttr.getValue() == mlir::acc::GangArgType::Dim)
3252 p << LoopOp::getGangDimKeyword();
3253 else if (gangArgTypeAttr.getValue() ==
3254 mlir::acc::GangArgType::Static)
3255 p << LoopOp::getGangStaticKeyword();
3256 p << "=" << operands[opIdx] << " : " << operands[opIdx].getType();
3257 ++opIdx;
3258 });
3259 p << "}";
3260 printSingleDeviceType(p, it.value());
3261 });
3262 }
3263 p << ")";
3264}
3265
3267 std::optional<mlir::ArrayAttr> segments,
3268 llvm::SmallSet<mlir::acc::DeviceType, 3> &deviceTypes) {
3269 if (!segments)
3270 return false;
3271 for (auto attr : *segments) {
3272 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
3273 if (!deviceTypes.insert(deviceTypeAttr.getValue()).second)
3274 return true;
3275 }
3276 return false;
3277}
3278
3279/// Check for duplicates in the DeviceType array attribute.
3280/// Returns std::nullopt if no duplicates, or the duplicate DeviceType if found.
3281static std::optional<mlir::acc::DeviceType>
3282checkDeviceTypes(mlir::ArrayAttr deviceTypes) {
3283 llvm::SmallSet<mlir::acc::DeviceType, 3> crtDeviceTypes;
3284 if (!deviceTypes)
3285 return std::nullopt;
3286 for (auto attr : deviceTypes) {
3287 auto deviceTypeAttr =
3288 mlir::dyn_cast_or_null<mlir::acc::DeviceTypeAttr>(attr);
3289 if (!deviceTypeAttr)
3290 return mlir::acc::DeviceType::None;
3291 if (!crtDeviceTypes.insert(deviceTypeAttr.getValue()).second)
3292 return deviceTypeAttr.getValue();
3293 }
3294 return std::nullopt;
3295}
3296
3297LogicalResult acc::LoopOp::verify() {
3298 if (getUpperbound().size() != getStep().size())
3299 return emitError() << "number of upperbounds expected to be the same as "
3300 "number of steps";
3301
3302 if (getUpperbound().size() != getLowerbound().size())
3303 return emitError() << "number of upperbounds expected to be the same as "
3304 "number of lowerbounds";
3305
3306 if (!getUpperbound().empty() && getInclusiveUpperbound() &&
3307 (getUpperbound().size() != getInclusiveUpperbound()->size()))
3308 return emitError() << "inclusiveUpperbound size is expected to be the same"
3309 << " as upperbound size";
3310
3311 // Check collapse
3312 if (getCollapseAttr() && !getCollapseDeviceTypeAttr())
3313 return emitOpError() << "collapse device_type attr must be define when"
3314 << " collapse attr is present";
3315
3316 if (getCollapseAttr() && getCollapseDeviceTypeAttr() &&
3317 getCollapseAttr().getValue().size() !=
3318 getCollapseDeviceTypeAttr().getValue().size())
3319 return emitOpError() << "collapse attribute count must match collapse"
3320 << " device_type count";
3321 if (auto duplicateDeviceType = checkDeviceTypes(getCollapseDeviceTypeAttr()))
3322 return emitOpError() << "duplicate device_type `"
3323 << acc::stringifyDeviceType(*duplicateDeviceType)
3324 << "` found in collapseDeviceType attribute";
3325
3326 // Check gang
3327 if (!getGangOperands().empty()) {
3328 if (!getGangOperandsArgType())
3329 return emitOpError() << "gangOperandsArgType attribute must be defined"
3330 << " when gang operands are present";
3331
3332 if (getGangOperands().size() !=
3333 getGangOperandsArgTypeAttr().getValue().size())
3334 return emitOpError() << "gangOperandsArgType attribute count must match"
3335 << " gangOperands count";
3336 }
3337 if (getGangAttr()) {
3338 if (auto duplicateDeviceType = checkDeviceTypes(getGangAttr()))
3339 return emitOpError() << "duplicate device_type `"
3340 << acc::stringifyDeviceType(*duplicateDeviceType)
3341 << "` found in gang attribute";
3342 }
3343
3345 *this, getGangOperands(), getGangOperandsSegmentsAttr(),
3346 getGangOperandsDeviceTypeAttr(), "gang")))
3347 return failure();
3348
3349 // Check worker
3350 if (auto duplicateDeviceType = checkDeviceTypes(getWorkerAttr()))
3351 return emitOpError() << "duplicate device_type `"
3352 << acc::stringifyDeviceType(*duplicateDeviceType)
3353 << "` found in worker attribute";
3354 if (auto duplicateDeviceType =
3355 checkDeviceTypes(getWorkerNumOperandsDeviceTypeAttr()))
3356 return emitOpError() << "duplicate device_type `"
3357 << acc::stringifyDeviceType(*duplicateDeviceType)
3358 << "` found in workerNumOperandsDeviceType attribute";
3359 if (failed(verifyDeviceTypeCountMatch(*this, getWorkerNumOperands(),
3360 getWorkerNumOperandsDeviceTypeAttr(),
3361 "worker")))
3362 return failure();
3363
3364 // Check vector
3365 if (auto duplicateDeviceType = checkDeviceTypes(getVectorAttr()))
3366 return emitOpError() << "duplicate device_type `"
3367 << acc::stringifyDeviceType(*duplicateDeviceType)
3368 << "` found in vector attribute";
3369 if (auto duplicateDeviceType =
3370 checkDeviceTypes(getVectorOperandsDeviceTypeAttr()))
3371 return emitOpError() << "duplicate device_type `"
3372 << acc::stringifyDeviceType(*duplicateDeviceType)
3373 << "` found in vectorOperandsDeviceType attribute";
3374 if (failed(verifyDeviceTypeCountMatch(*this, getVectorOperands(),
3375 getVectorOperandsDeviceTypeAttr(),
3376 "vector")))
3377 return failure();
3378
3380 *this, getTileOperands(), getTileOperandsSegmentsAttr(),
3381 getTileOperandsDeviceTypeAttr(), "tile")))
3382 return failure();
3383
3384 // auto, independent and seq attribute are mutually exclusive.
3385 llvm::SmallSet<mlir::acc::DeviceType, 3> deviceTypes;
3386 if (hasDuplicateDeviceTypes(getAuto_(), deviceTypes) ||
3387 hasDuplicateDeviceTypes(getIndependent(), deviceTypes) ||
3388 hasDuplicateDeviceTypes(getSeq(), deviceTypes)) {
3389 return emitError() << "only one of auto, independent, seq can be present "
3390 "at the same time";
3391 }
3392
3393 // Check that at least one of auto, independent, or seq is present
3394 // for the device-independent default clauses.
3395 auto hasDeviceNone = [](mlir::acc::DeviceTypeAttr attr) -> bool {
3396 return attr.getValue() == mlir::acc::DeviceType::None;
3397 };
3398 bool hasDefaultSeq =
3399 getSeqAttr()
3400 ? llvm::any_of(getSeqAttr().getAsRange<mlir::acc::DeviceTypeAttr>(),
3401 hasDeviceNone)
3402 : false;
3403 bool hasDefaultIndependent =
3404 getIndependentAttr()
3405 ? llvm::any_of(
3406 getIndependentAttr().getAsRange<mlir::acc::DeviceTypeAttr>(),
3407 hasDeviceNone)
3408 : false;
3409 bool hasDefaultAuto =
3410 getAuto_Attr()
3411 ? llvm::any_of(getAuto_Attr().getAsRange<mlir::acc::DeviceTypeAttr>(),
3412 hasDeviceNone)
3413 : false;
3414 if (!hasDefaultSeq && !hasDefaultIndependent && !hasDefaultAuto) {
3415 return emitError()
3416 << "at least one of auto, independent, seq must be present";
3417 }
3418
3419 // Gang, worker and vector are incompatible with seq.
3420 if (getSeqAttr()) {
3421 for (auto attr : getSeqAttr()) {
3422 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
3423 if (hasVector(deviceTypeAttr.getValue()) ||
3424 getVectorValue(deviceTypeAttr.getValue()) ||
3425 hasWorker(deviceTypeAttr.getValue()) ||
3426 getWorkerValue(deviceTypeAttr.getValue()) ||
3427 hasGang(deviceTypeAttr.getValue()) ||
3428 getGangValue(mlir::acc::GangArgType::Num,
3429 deviceTypeAttr.getValue()) ||
3430 getGangValue(mlir::acc::GangArgType::Dim,
3431 deviceTypeAttr.getValue()) ||
3432 getGangValue(mlir::acc::GangArgType::Static,
3433 deviceTypeAttr.getValue()))
3434 return emitError() << "gang, worker or vector cannot appear with seq";
3435 }
3436 }
3437
3438 if (failed(checkPrivateOperands<mlir::acc::PrivateOp,
3439 mlir::acc::PrivateRecipeOp>(
3440 *this, getPrivateOperands(), "private")))
3441 return failure();
3442
3443 if (failed(checkPrivateOperands<mlir::acc::FirstprivateOp,
3444 mlir::acc::FirstprivateRecipeOp>(
3445 *this, getFirstprivateOperands(), "firstprivate")))
3446 return failure();
3447
3448 if (failed(checkPrivateOperands<mlir::acc::ReductionOp,
3449 mlir::acc::ReductionRecipeOp>(
3450 *this, getReductionOperands(), "reduction")))
3451 return failure();
3452
3453 if (getCombined().has_value() &&
3454 (getCombined().value() != acc::CombinedConstructsType::ParallelLoop &&
3455 getCombined().value() != acc::CombinedConstructsType::KernelsLoop &&
3456 getCombined().value() != acc::CombinedConstructsType::SerialLoop)) {
3457 return emitError("unexpected combined constructs attribute");
3458 }
3459
3460 // Check non-empty body().
3461 if (getRegion().empty())
3462 return emitError("expected non-empty body.");
3463
3464 if (getUnstructured()) {
3465 if (!isContainerLike())
3466 return emitError(
3467 "unstructured acc.loop must not have induction variables");
3468 } else if (isContainerLike()) {
3469 // When it is container-like - it is expected to hold a loop-like operation.
3470 // Obtain the maximum collapse count - we use this to check that there
3471 // are enough loops contained.
3472 uint64_t collapseCount = getCollapseValue().value_or(1);
3473 if (getCollapseAttr()) {
3474 for (auto collapseEntry : getCollapseAttr()) {
3475 auto intAttr = mlir::dyn_cast<IntegerAttr>(collapseEntry);
3476 if (intAttr.getValue().getZExtValue() > collapseCount)
3477 collapseCount = intAttr.getValue().getZExtValue();
3478 }
3479 }
3480
3481 // We want to check that we find enough loop-like operations inside.
3482 // PreOrder walk allows us to walk in a breadth-first manner at each nesting
3483 // level.
3484 mlir::Operation *expectedParent = this->getOperation();
3485 bool foundSibling = false;
3486 getRegion().walk<WalkOrder::PreOrder>([&](mlir::Operation *op) {
3487 if (mlir::isa<mlir::LoopLikeOpInterface>(op)) {
3488 // This effectively checks that we are not looking at a sibling loop.
3489 if (op->getParentOfType<mlir::LoopLikeOpInterface>() !=
3490 expectedParent) {
3491 foundSibling = true;
3493 }
3494
3495 collapseCount--;
3496 expectedParent = op;
3497 }
3498 // We found enough contained loops.
3499 if (collapseCount == 0)
3502 });
3503
3504 if (foundSibling)
3505 return emitError("found sibling loops inside container-like acc.loop");
3506 if (collapseCount != 0)
3507 return emitError("failed to find enough loop-like operations inside "
3508 "container-like acc.loop");
3509 }
3510
3511 return success();
3512}
3513
3514unsigned LoopOp::getNumDataOperands() {
3515 return getReductionOperands().size() + getPrivateOperands().size() +
3516 getFirstprivateOperands().size();
3517}
3518
3519Value LoopOp::getDataOperand(unsigned i) {
3520 unsigned numOptional =
3521 getLowerbound().size() + getUpperbound().size() + getStep().size();
3522 numOptional += getGangOperands().size();
3523 numOptional += getVectorOperands().size();
3524 numOptional += getWorkerNumOperands().size();
3525 numOptional += getTileOperands().size();
3526 numOptional += getCacheOperands().size();
3527 return getOperand(numOptional + i);
3528}
3529
3530bool LoopOp::hasAuto() { return hasAuto(mlir::acc::DeviceType::None); }
3531
3532bool LoopOp::hasAuto(mlir::acc::DeviceType deviceType) {
3533 return hasDeviceType(getAuto_(), deviceType);
3534}
3535
3536bool LoopOp::hasIndependent() {
3537 return hasIndependent(mlir::acc::DeviceType::None);
3538}
3539
3540bool LoopOp::hasIndependent(mlir::acc::DeviceType deviceType) {
3541 return hasDeviceType(getIndependent(), deviceType);
3542}
3543
3544bool LoopOp::hasSeq() { return hasSeq(mlir::acc::DeviceType::None); }
3545
3546bool LoopOp::hasSeq(mlir::acc::DeviceType deviceType) {
3547 return hasDeviceType(getSeq(), deviceType);
3548}
3549
3550mlir::Value LoopOp::getVectorValue() {
3551 return getVectorValue(mlir::acc::DeviceType::None);
3552}
3553
3554mlir::Value LoopOp::getVectorValue(mlir::acc::DeviceType deviceType) {
3555 return getValueInDeviceTypeSegment(getVectorOperandsDeviceType(),
3556 getVectorOperands(), deviceType);
3557}
3558
3559bool LoopOp::hasVector() { return hasVector(mlir::acc::DeviceType::None); }
3560
3561bool LoopOp::hasVector(mlir::acc::DeviceType deviceType) {
3562 return hasDeviceType(getVector(), deviceType);
3563}
3564
3565mlir::Value LoopOp::getWorkerValue() {
3566 return getWorkerValue(mlir::acc::DeviceType::None);
3567}
3568
3569mlir::Value LoopOp::getWorkerValue(mlir::acc::DeviceType deviceType) {
3570 return getValueInDeviceTypeSegment(getWorkerNumOperandsDeviceType(),
3571 getWorkerNumOperands(), deviceType);
3572}
3573
3574bool LoopOp::hasWorker() { return hasWorker(mlir::acc::DeviceType::None); }
3575
3576bool LoopOp::hasWorker(mlir::acc::DeviceType deviceType) {
3577 return hasDeviceType(getWorker(), deviceType);
3578}
3579
3580mlir::Operation::operand_range LoopOp::getTileValues() {
3581 return getTileValues(mlir::acc::DeviceType::None);
3582}
3583
3585LoopOp::getTileValues(mlir::acc::DeviceType deviceType) {
3586 return getValuesFromSegments(getTileOperandsDeviceType(), getTileOperands(),
3587 getTileOperandsSegments(), deviceType);
3588}
3589
3590std::optional<int64_t> LoopOp::getCollapseValue() {
3591 return getCollapseValue(mlir::acc::DeviceType::None);
3592}
3593
3594std::optional<int64_t>
3595LoopOp::getCollapseValue(mlir::acc::DeviceType deviceType) {
3596 if (!getCollapseAttr())
3597 return std::nullopt;
3598 if (auto pos = findSegment(getCollapseDeviceTypeAttr(), deviceType)) {
3599 auto intAttr =
3600 mlir::dyn_cast<IntegerAttr>(getCollapseAttr().getValue()[*pos]);
3601 return intAttr.getValue().getZExtValue();
3602 }
3603 return std::nullopt;
3604}
3605
3606mlir::Value LoopOp::getGangValue(mlir::acc::GangArgType gangArgType) {
3607 return getGangValue(gangArgType, mlir::acc::DeviceType::None);
3608}
3609
3610mlir::Value LoopOp::getGangValue(mlir::acc::GangArgType gangArgType,
3611 mlir::acc::DeviceType deviceType) {
3612 if (getGangOperands().empty())
3613 return {};
3614 if (auto pos = findSegment(*getGangOperandsDeviceType(), deviceType)) {
3615 int32_t nbOperandsBefore = 0;
3616 for (unsigned i = 0; i < *pos; ++i)
3617 nbOperandsBefore += (*getGangOperandsSegments())[i];
3619 getGangOperands()
3620 .drop_front(nbOperandsBefore)
3621 .take_front((*getGangOperandsSegments())[*pos]);
3622
3623 int32_t argTypeIdx = nbOperandsBefore;
3624 for (auto value : values) {
3625 auto gangArgTypeAttr = mlir::dyn_cast<mlir::acc::GangArgTypeAttr>(
3626 (*getGangOperandsArgType())[argTypeIdx]);
3627 if (gangArgTypeAttr.getValue() == gangArgType)
3628 return value;
3629 ++argTypeIdx;
3630 }
3631 }
3632 return {};
3633}
3634
3635bool LoopOp::hasGang() { return hasGang(mlir::acc::DeviceType::None); }
3636
3637bool LoopOp::hasGang(mlir::acc::DeviceType deviceType) {
3638 return hasDeviceType(getGang(), deviceType);
3639}
3640
3641llvm::SmallVector<mlir::Region *> acc::LoopOp::getLoopRegions() {
3642 return {&getRegion()};
3643}
3644
3645/// loop-control ::= `control` `(` ssa-id-and-type-list `)` `=`
3646/// `(` ssa-id-and-type-list `)` `to` `(` ssa-id-and-type-list `)` `step`
3647/// `(` ssa-id-and-type-list `)`
3648/// region
3649ParseResult
3652 SmallVectorImpl<Type> &lowerboundType,
3654 SmallVectorImpl<Type> &upperboundType,
3656 SmallVectorImpl<Type> &stepType) {
3657
3659 if (succeeded(
3660 parser.parseOptionalKeyword(acc::LoopOp::getControlKeyword()))) {
3661 if (parser.parseLParen() ||
3662 parser.parseArgumentList(inductionVars, OpAsmParser::Delimiter::None,
3663 /*allowType=*/true) ||
3664 parser.parseRParen() || parser.parseEqual() || parser.parseLParen() ||
3665 parser.parseOperandList(lowerbound, inductionVars.size(),
3667 parser.parseColonTypeList(lowerboundType) || parser.parseRParen() ||
3668 parser.parseKeyword("to") || parser.parseLParen() ||
3669 parser.parseOperandList(upperbound, inductionVars.size(),
3671 parser.parseColonTypeList(upperboundType) || parser.parseRParen() ||
3672 parser.parseKeyword("step") || parser.parseLParen() ||
3673 parser.parseOperandList(step, inductionVars.size(),
3675 parser.parseColonTypeList(stepType) || parser.parseRParen())
3676 return failure();
3677 }
3678 return parser.parseRegion(region, inductionVars);
3679}
3680
3682 ValueRange lowerbound, TypeRange lowerboundType,
3683 ValueRange upperbound, TypeRange upperboundType,
3684 ValueRange steps, TypeRange stepType) {
3685 ValueRange regionArgs = region.front().getArguments();
3686 if (!regionArgs.empty()) {
3687 p << acc::LoopOp::getControlKeyword() << "(";
3688 llvm::interleaveComma(regionArgs, p,
3689 [&p](Value v) { p << v << " : " << v.getType(); });
3690 p << ") = (" << lowerbound << " : " << lowerboundType << ") to ("
3691 << upperbound << " : " << upperboundType << ") " << " step (" << steps
3692 << " : " << stepType << ") ";
3693 }
3694 p.printRegion(region, /*printEntryBlockArgs=*/false);
3695}
3696
3697void acc::LoopOp::addSeq(MLIRContext *context,
3698 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3699 setSeqAttr(addDeviceTypeAffectedOperandHelper(context, getSeqAttr(),
3700 effectiveDeviceTypes));
3701}
3702
3703void acc::LoopOp::addIndependent(
3704 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3705 setIndependentAttr(addDeviceTypeAffectedOperandHelper(
3706 context, getIndependentAttr(), effectiveDeviceTypes));
3707}
3708
3709void acc::LoopOp::addAuto(MLIRContext *context,
3710 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3711 setAuto_Attr(addDeviceTypeAffectedOperandHelper(context, getAuto_Attr(),
3712 effectiveDeviceTypes));
3713}
3714
3715void acc::LoopOp::setCollapseForDeviceTypes(
3716 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes,
3717 llvm::APInt value) {
3720
3721 assert((getCollapseAttr() == nullptr) ==
3722 (getCollapseDeviceTypeAttr() == nullptr));
3723 assert(value.getBitWidth() == 64);
3724
3725 if (getCollapseAttr()) {
3726 for (const auto &existing :
3727 llvm::zip_equal(getCollapseAttr(), getCollapseDeviceTypeAttr())) {
3728 newValues.push_back(std::get<0>(existing));
3729 newDeviceTypes.push_back(std::get<1>(existing));
3730 }
3731 }
3732
3733 if (effectiveDeviceTypes.empty()) {
3734 // If the effective device-types list is empty, this is before there are any
3735 // being applied by device_type, so this should be added as a 'none'.
3736 newValues.push_back(
3737 mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), value));
3738 newDeviceTypes.push_back(
3739 acc::DeviceTypeAttr::get(context, DeviceType::None));
3740 } else {
3741 for (DeviceType dt : effectiveDeviceTypes) {
3742 newValues.push_back(
3743 mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), value));
3744 newDeviceTypes.push_back(acc::DeviceTypeAttr::get(context, dt));
3745 }
3746 }
3747
3748 setCollapseAttr(ArrayAttr::get(context, newValues));
3749 setCollapseDeviceTypeAttr(ArrayAttr::get(context, newDeviceTypes));
3750}
3751
3752void acc::LoopOp::setTileForDeviceTypes(
3753 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes,
3754 ValueRange values) {
3756 if (getTileOperandsSegments())
3757 llvm::copy(*getTileOperandsSegments(), std::back_inserter(segments));
3758
3759 setTileOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
3760 context, getTileOperandsDeviceTypeAttr(), effectiveDeviceTypes, values,
3761 getTileOperandsMutable(), segments));
3762
3763 setTileOperandsSegments(segments);
3764}
3765
3766void acc::LoopOp::addVectorOperand(
3767 MLIRContext *context, mlir::Value newValue,
3768 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3769 setVectorOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
3770 context, getVectorOperandsDeviceTypeAttr(), effectiveDeviceTypes,
3771 newValue, getVectorOperandsMutable()));
3772}
3773
3774void acc::LoopOp::addEmptyVector(
3775 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3776 setVectorAttr(addDeviceTypeAffectedOperandHelper(context, getVectorAttr(),
3777 effectiveDeviceTypes));
3778}
3779
3780void acc::LoopOp::addWorkerNumOperand(
3781 MLIRContext *context, mlir::Value newValue,
3782 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3783 setWorkerNumOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
3784 context, getWorkerNumOperandsDeviceTypeAttr(), effectiveDeviceTypes,
3785 newValue, getWorkerNumOperandsMutable()));
3786}
3787
3788void acc::LoopOp::addEmptyWorker(
3789 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3790 setWorkerAttr(addDeviceTypeAffectedOperandHelper(context, getWorkerAttr(),
3791 effectiveDeviceTypes));
3792}
3793
3794void acc::LoopOp::addEmptyGang(
3795 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3796 setGangAttr(addDeviceTypeAffectedOperandHelper(context, getGangAttr(),
3797 effectiveDeviceTypes));
3798}
3799
3800bool acc::LoopOp::hasParallelismFlag(DeviceType dt) {
3801 auto hasDevice = [=](DeviceTypeAttr attr) -> bool {
3802 return attr.getValue() == dt;
3803 };
3804 auto testFromArr = [=](ArrayAttr arr) -> bool {
3805 return llvm::any_of(arr.getAsRange<DeviceTypeAttr>(), hasDevice);
3806 };
3807
3808 if (ArrayAttr arr = getSeqAttr(); arr && testFromArr(arr))
3809 return true;
3810 if (ArrayAttr arr = getIndependentAttr(); arr && testFromArr(arr))
3811 return true;
3812 if (ArrayAttr arr = getAuto_Attr(); arr && testFromArr(arr))
3813 return true;
3814
3815 return false;
3816}
3817
3818bool acc::LoopOp::hasDefaultGangWorkerVector() {
3819 return hasAnyGangWorkerVector(DeviceType::None);
3820}
3821
3822bool acc::LoopOp::hasAnyGangWorkerVector(DeviceType deviceType) {
3823 return hasVector(deviceType) || getVectorValue(deviceType) ||
3824 hasWorker(deviceType) || getWorkerValue(deviceType) ||
3825 hasGang(deviceType) || getGangValue(GangArgType::Num, deviceType) ||
3826 getGangValue(GangArgType::Dim, deviceType) ||
3827 getGangValue(GangArgType::Static, deviceType);
3828}
3829
3830acc::LoopParMode
3831acc::LoopOp::getDefaultOrDeviceTypeParallelism(DeviceType deviceType) {
3832 if (hasSeq(deviceType))
3833 return LoopParMode::loop_seq;
3834 if (hasAuto(deviceType))
3835 return LoopParMode::loop_auto;
3836 if (hasIndependent(deviceType))
3837 return LoopParMode::loop_independent;
3838 if (hasSeq())
3839 return LoopParMode::loop_seq;
3840 if (hasAuto())
3841 return LoopParMode::loop_auto;
3842 assert(hasIndependent() &&
3843 "loop must have default auto, seq, or independent");
3844 return LoopParMode::loop_independent;
3845}
3846
3847void acc::LoopOp::addGangOperands(
3848 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes,
3851 if (std::optional<ArrayRef<int32_t>> existingSegments =
3852 getGangOperandsSegments())
3853 llvm::copy(*existingSegments, std::back_inserter(segments));
3854
3855 unsigned beforeCount = segments.size();
3856
3857 setGangOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
3858 context, getGangOperandsDeviceTypeAttr(), effectiveDeviceTypes, values,
3859 getGangOperandsMutable(), segments));
3860
3861 setGangOperandsSegments(segments);
3862
3863 // This is a bit of extra work to make sure we update the 'types' correctly by
3864 // adding to the types collection the correct number of times. We could
3865 // potentially add something similar to the
3866 // addDeviceTypeAffectedOperandHelper, but it seems that would be pretty
3867 // excessive for a one-off case.
3868 unsigned numAdded = segments.size() - beforeCount;
3869
3870 if (numAdded > 0) {
3872 if (getGangOperandsArgTypeAttr())
3873 llvm::copy(getGangOperandsArgTypeAttr(), std::back_inserter(gangTypes));
3874
3875 for (auto i : llvm::index_range(0u, numAdded)) {
3876 llvm::transform(argTypes, std::back_inserter(gangTypes),
3877 [=](mlir::acc::GangArgType gangTy) {
3878 return mlir::acc::GangArgTypeAttr::get(context, gangTy);
3879 });
3880 (void)i;
3881 }
3882
3883 setGangOperandsArgTypeAttr(mlir::ArrayAttr::get(context, gangTypes));
3884 }
3885}
3886
3887void acc::LoopOp::addPrivatization(MLIRContext *context,
3888 mlir::acc::PrivateOp op,
3889 mlir::acc::PrivateRecipeOp recipe) {
3890 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
3891 getPrivateOperandsMutable().append(op.getResult());
3892}
3893
3894void acc::LoopOp::addFirstPrivatization(
3895 MLIRContext *context, mlir::acc::FirstprivateOp op,
3896 mlir::acc::FirstprivateRecipeOp recipe) {
3897 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
3898 getFirstprivateOperandsMutable().append(op.getResult());
3899}
3900
3901void acc::LoopOp::addReduction(MLIRContext *context, mlir::acc::ReductionOp op,
3902 mlir::acc::ReductionRecipeOp recipe) {
3903 op.setRecipeAttr(mlir::SymbolRefAttr::get(context, recipe.getSymName()));
3904 getReductionOperandsMutable().append(op.getResult());
3905}
3906
3907//===----------------------------------------------------------------------===//
3908// DataOp
3909//===----------------------------------------------------------------------===//
3910
3911LogicalResult acc::DataOp::verify() {
3912 // 2.6.5. Data Construct restriction
3913 // At least one copy, copyin, copyout, create, no_create, present, deviceptr,
3914 // attach, or default clause must appear on a data construct.
3915 if (getOperands().empty() && !getDefaultAttr())
3916 return emitError("at least one operand or the default attribute "
3917 "must appear on the data operation");
3918
3919 for (mlir::Value operand : getDataClauseOperands())
3920 if (isa<BlockArgument>(operand) ||
3921 !mlir::isa<acc::AttachOp, acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,
3922 acc::DeleteOp, acc::DetachOp, acc::DevicePtrOp,
3923 acc::GetDevicePtrOp, acc::NoCreateOp, acc::PresentOp,
3924 acc::MapInfoOp>(operand.getDefiningOp()))
3925 return emitError("expect data entry/exit operation or acc.getdeviceptr "
3926 "as defining op");
3927
3929 return failure();
3930
3931 return success();
3932}
3933
3934unsigned DataOp::getNumDataOperands() { return getDataClauseOperands().size(); }
3935
3936Value DataOp::getDataOperand(unsigned i) {
3937 unsigned numOptional = getIfCond() ? 1 : 0;
3938 numOptional += getAsyncOperands().size() ? 1 : 0;
3939 numOptional += getWaitOperands().size();
3940 return getOperand(numOptional + i);
3941}
3942
3943bool acc::DataOp::hasAsyncOnly() {
3944 return hasAsyncOnly(mlir::acc::DeviceType::None);
3945}
3946
3947bool acc::DataOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
3948 return hasDeviceType(getAsyncOnly(), deviceType);
3949}
3950
3951mlir::Value DataOp::getAsyncValue() {
3952 return getAsyncValue(mlir::acc::DeviceType::None);
3953}
3954
3955mlir::Value DataOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
3957 getAsyncOperands(), deviceType);
3958}
3959
3960bool DataOp::hasWaitOnly() { return hasWaitOnly(mlir::acc::DeviceType::None); }
3961
3962bool DataOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
3963 return hasDeviceType(getWaitOnly(), deviceType);
3964}
3965
3966mlir::Operation::operand_range DataOp::getWaitValues() {
3967 return getWaitValues(mlir::acc::DeviceType::None);
3968}
3969
3971DataOp::getWaitValues(mlir::acc::DeviceType deviceType) {
3973 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
3974 getHasWaitDevnum(), deviceType);
3975}
3976
3977mlir::Value DataOp::getWaitDevnum() {
3978 return getWaitDevnum(mlir::acc::DeviceType::None);
3979}
3980
3981mlir::Value DataOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
3982 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),
3983 getWaitOperandsSegments(), getHasWaitDevnum(),
3984 deviceType);
3985}
3986
3987void acc::DataOp::addAsyncOnly(
3988 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3989 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(
3990 context, getAsyncOnlyAttr(), effectiveDeviceTypes));
3991}
3992
3993void acc::DataOp::addAsyncOperand(
3994 MLIRContext *context, mlir::Value newValue,
3995 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
3996 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
3997 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,
3998 getAsyncOperandsMutable()));
3999}
4000
4001void acc::DataOp::addWaitOnly(MLIRContext *context,
4002 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4003 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),
4004 effectiveDeviceTypes));
4005}
4006
4007void acc::DataOp::addWaitOperands(
4008 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,
4009 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4010
4012 if (getWaitOperandsSegments())
4013 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));
4014
4015 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
4016 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,
4017 getWaitOperandsMutable(), segments));
4018 setWaitOperandsSegments(segments);
4019
4021 if (getHasWaitDevnumAttr())
4022 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));
4023 hasDevnums.insert(
4024 hasDevnums.end(),
4025 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),
4026 mlir::BoolAttr::get(context, hasDevnum));
4027 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));
4028}
4029
4030//===----------------------------------------------------------------------===//
4031// ExitDataOp
4032//===----------------------------------------------------------------------===//
4033
4034LogicalResult acc::ExitDataOp::verify() {
4035 // 2.6.6. Data Exit Directive restriction
4036 // At least one copyout, delete, or detach clause must appear on an exit data
4037 // directive.
4038 if (getDataClauseOperands().empty())
4039 return emitError("at least one operand must be present in dataOperands on "
4040 "the exit data operation");
4041
4042 // The async attribute represent the async clause without value. Therefore the
4043 // attribute and operand cannot appear at the same time.
4044 if (getAsyncOperand() && getAsync())
4045 return emitError("async attribute cannot appear with asyncOperand");
4046
4047 // The wait attribute represent the wait clause without values. Therefore the
4048 // attribute and operands cannot appear at the same time.
4049 if (!getWaitOperands().empty() && getWait())
4050 return emitError("wait attribute cannot appear with waitOperands");
4051
4052 if (getWaitDevnum() && getWaitOperands().empty())
4053 return emitError("wait_devnum cannot appear without waitOperands");
4054
4055 return success();
4056}
4057
4058unsigned ExitDataOp::getNumDataOperands() {
4059 return getDataClauseOperands().size();
4060}
4061
4062Value ExitDataOp::getDataOperand(unsigned i) {
4063 unsigned numOptional = getIfCond() ? 1 : 0;
4064 numOptional += getAsyncOperand() ? 1 : 0;
4065 numOptional += getWaitDevnum() ? 1 : 0;
4066 return getOperand(getWaitOperands().size() + numOptional + i);
4067}
4068
4069void ExitDataOp::getCanonicalizationPatterns(RewritePatternSet &results,
4070 MLIRContext *context) {
4071 results.add<RemoveConstantIfCondition<ExitDataOp>>(context);
4072}
4073
4074void ExitDataOp::addAsyncOnly(MLIRContext *context,
4075 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4076 assert(effectiveDeviceTypes.empty());
4077 assert(!getAsyncAttr());
4078 assert(!getAsyncOperand());
4079
4080 setAsyncAttr(mlir::UnitAttr::get(context));
4081}
4082
4083void ExitDataOp::addAsyncOperand(
4084 MLIRContext *context, mlir::Value newValue,
4085 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4086 assert(effectiveDeviceTypes.empty());
4087 assert(!getAsyncAttr());
4088 assert(!getAsyncOperand());
4089
4090 getAsyncOperandMutable().append(newValue);
4091}
4092
4093void ExitDataOp::addWaitOnly(MLIRContext *context,
4094 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4095 assert(effectiveDeviceTypes.empty());
4096
4097 if (getWaitAttr())
4098 return;
4099
4100 setWaitAttr(mlir::UnitAttr::get(context));
4101
4102 getWaitDevnumMutable().clear();
4103 getWaitOperandsMutable().clear();
4104}
4105
4106void ExitDataOp::addWaitOperands(
4107 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,
4108 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4109 assert(effectiveDeviceTypes.empty());
4110
4111 if (getWaitAttr())
4112 return;
4113
4114 // FIXME: At one point we need to figure out how to support multiple devnums
4115 // here. For now, assert. Eventually we probably want to make dev-num and
4116 // operands work in 'lock-step', so that getWaitDevnum().size() ==
4117 // getWaitOperandsMutable().size().
4118 assert(!getWaitDevnum() && "Merging devnum not yet implemented");
4119
4120 // if hasDevnum, the first value is the devnum. The 'rest' go into the
4121 // operands list.
4122 if (hasDevnum) {
4123 getWaitDevnumMutable().append(newValues.front());
4124 newValues = newValues.drop_front();
4125 }
4126
4127 getWaitOperandsMutable().append(newValues);
4128}
4129
4130//===----------------------------------------------------------------------===//
4131// EnterDataOp
4132//===----------------------------------------------------------------------===//
4133
4134LogicalResult acc::EnterDataOp::verify() {
4135 // 2.6.6. Data Enter Directive restriction
4136 // At least one copyin, create, or attach clause must appear on an enter data
4137 // directive.
4138 if (getDataClauseOperands().empty())
4139 return emitError("at least one operand must be present in dataOperands on "
4140 "the enter data operation");
4141
4142 // The async attribute represent the async clause without value. Therefore the
4143 // attribute and operand cannot appear at the same time.
4144 if (getAsyncOperand() && getAsync())
4145 return emitError("async attribute cannot appear with asyncOperand");
4146
4147 // The wait attribute represent the wait clause without values. Therefore the
4148 // attribute and operands cannot appear at the same time.
4149 if (!getWaitOperands().empty() && getWait())
4150 return emitError("wait attribute cannot appear with waitOperands");
4151
4152 if (getWaitDevnum() && getWaitOperands().empty())
4153 return emitError("wait_devnum cannot appear without waitOperands");
4154
4155 for (mlir::Value operand : getDataClauseOperands())
4156 if (!mlir::isa<acc::AttachOp, acc::CreateOp, acc::CopyinOp, acc::MapInfoOp>(
4157 operand.getDefiningOp()))
4158 return emitError("expect data entry operation as defining op");
4159
4160 return success();
4161}
4162
4163unsigned EnterDataOp::getNumDataOperands() {
4164 return getDataClauseOperands().size();
4165}
4166
4167Value EnterDataOp::getDataOperand(unsigned i) {
4168 unsigned numOptional = getIfCond() ? 1 : 0;
4169 numOptional += getAsyncOperand() ? 1 : 0;
4170 numOptional += getWaitDevnum() ? 1 : 0;
4171 return getOperand(getWaitOperands().size() + numOptional + i);
4172}
4173
4174void EnterDataOp::getCanonicalizationPatterns(RewritePatternSet &results,
4175 MLIRContext *context) {
4176 results.add<RemoveConstantIfCondition<EnterDataOp>>(context);
4177}
4178
4179void EnterDataOp::addAsyncOnly(
4180 MLIRContext *context, llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4181 assert(effectiveDeviceTypes.empty());
4182 assert(!getAsyncAttr());
4183 assert(!getAsyncOperand());
4184
4185 setAsyncAttr(mlir::UnitAttr::get(context));
4186}
4187
4188void EnterDataOp::addAsyncOperand(
4189 MLIRContext *context, mlir::Value newValue,
4190 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4191 assert(effectiveDeviceTypes.empty());
4192 assert(!getAsyncAttr());
4193 assert(!getAsyncOperand());
4194
4195 getAsyncOperandMutable().append(newValue);
4196}
4197
4198void EnterDataOp::addWaitOnly(MLIRContext *context,
4199 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4200 assert(effectiveDeviceTypes.empty());
4201
4202 if (getWaitAttr())
4203 return;
4204
4205 setWaitAttr(mlir::UnitAttr::get(context));
4206
4207 getWaitDevnumMutable().clear();
4208 getWaitOperandsMutable().clear();
4209}
4210
4211void EnterDataOp::addWaitOperands(
4212 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,
4213 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4214 assert(effectiveDeviceTypes.empty());
4215
4216 if (getWaitAttr())
4217 return;
4218
4219 // FIXME: At one point we need to figure out how to support multiple devnums
4220 // here. For now, assert. Eventually we probably want to make dev-num and
4221 // operands work in 'lock-step', so that getWaitDevnum().size() ==
4222 // getWaitOperandsMutable().size().
4223 assert(!getWaitDevnum() && "Merging devnum not yet implemented");
4224
4225 // if hasDevnum, the first value is the devnum. The 'rest' go into the
4226 // operands list.
4227 if (hasDevnum) {
4228 getWaitDevnumMutable().append(newValues.front());
4229 newValues = newValues.drop_front();
4230 }
4231
4232 getWaitOperandsMutable().append(newValues);
4233}
4234
4235//===----------------------------------------------------------------------===//
4236// AtomicReadOp
4237//===----------------------------------------------------------------------===//
4238
4239LogicalResult AtomicReadOp::verify() { return verifyCommon(); }
4240
4241//===----------------------------------------------------------------------===//
4242// AtomicWriteOp
4243//===----------------------------------------------------------------------===//
4244
4245LogicalResult AtomicWriteOp::verify() { return verifyCommon(); }
4246
4247//===----------------------------------------------------------------------===//
4248// AtomicUpdateOp
4249//===----------------------------------------------------------------------===//
4250
4251LogicalResult AtomicUpdateOp::canonicalize(AtomicUpdateOp op,
4252 PatternRewriter &rewriter) {
4253 if (op.isNoOp()) {
4254 rewriter.eraseOp(op);
4255 return success();
4256 }
4257
4258 if (Value writeVal = op.getWriteOpVal()) {
4259 rewriter.replaceOpWithNewOp<AtomicWriteOp>(op, op.getX(), writeVal,
4260 op.getIfCond());
4261 return success();
4262 }
4263
4264 return failure();
4265}
4266
4267LogicalResult AtomicUpdateOp::verify() { return verifyCommon(); }
4268
4269LogicalResult AtomicUpdateOp::verifyRegions() { return verifyRegionsCommon(); }
4270
4271//===----------------------------------------------------------------------===//
4272// AtomicCaptureOp
4273//===----------------------------------------------------------------------===//
4274
4275AtomicReadOp AtomicCaptureOp::getAtomicReadOp() {
4276 if (auto op = dyn_cast<AtomicReadOp>(getFirstOp()))
4277 return op;
4278 return dyn_cast<AtomicReadOp>(getSecondOp());
4279}
4280
4281AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() {
4282 if (auto op = dyn_cast<AtomicWriteOp>(getFirstOp()))
4283 return op;
4284 return dyn_cast<AtomicWriteOp>(getSecondOp());
4285}
4286
4287AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {
4288 if (auto op = dyn_cast<AtomicUpdateOp>(getFirstOp()))
4289 return op;
4290 return dyn_cast<AtomicUpdateOp>(getSecondOp());
4291}
4292
4293LogicalResult AtomicCaptureOp::verifyRegions() { return verifyRegionsCommon(); }
4294
4295//===----------------------------------------------------------------------===//
4296// DeclareEnterOp
4297//===----------------------------------------------------------------------===//
4298
4299template <typename Op>
4300static LogicalResult
4302 bool requireAtLeastOneOperand = true) {
4303 if (operands.empty() && requireAtLeastOneOperand)
4304 return emitError(
4305 op->getLoc(),
4306 "at least one operand must appear on the declare operation");
4307
4308 for (mlir::Value operand : operands) {
4309 if (isa<BlockArgument>(operand) ||
4310 !mlir::isa<acc::CopyinOp, acc::CopyoutOp, acc::CreateOp,
4311 acc::DevicePtrOp, acc::GetDevicePtrOp, acc::PresentOp,
4312 acc::DeclareDeviceResidentOp, acc::DeclareLinkOp,
4313 acc::MapInfoOp>(operand.getDefiningOp()))
4314 return op.emitError(
4315 "expect valid declare data entry operation or acc.getdeviceptr "
4316 "as defining op");
4317
4318 mlir::Value var{getVar(operand.getDefiningOp())};
4319 assert(var && "declare operands can only be data entry operations which "
4320 "must have var");
4321 (void)var;
4322 // acc.map_info encodes the clause effects in mapFlags instead.
4323 if (!mlir::isa<acc::MapInfoOp>(operand.getDefiningOp())) {
4324 std::optional<mlir::acc::DataClause> dataClauseOptional{
4325 getDataClause(operand.getDefiningOp())};
4326 assert(dataClauseOptional.has_value() &&
4327 "declare operands can only be data entry operations which must "
4328 "have dataClause");
4329 (void)dataClauseOptional;
4330 }
4331 }
4332
4333 return success();
4334}
4335
4336LogicalResult acc::DeclareEnterOp::verify() {
4337 return checkDeclareOperands(*this, this->getDataClauseOperands());
4338}
4339
4340//===----------------------------------------------------------------------===//
4341// DeclareExitOp
4342//===----------------------------------------------------------------------===//
4343
4344LogicalResult acc::DeclareExitOp::verify() {
4345 if (getToken())
4346 return checkDeclareOperands(*this, this->getDataClauseOperands(),
4347 /*requireAtLeastOneOperand=*/false);
4348 return checkDeclareOperands(*this, this->getDataClauseOperands());
4349}
4350
4351//===----------------------------------------------------------------------===//
4352// DeclareOp
4353//===----------------------------------------------------------------------===//
4354
4355LogicalResult acc::DeclareOp::verify() {
4356 return checkDeclareOperands(*this, this->getDataClauseOperands());
4357}
4358
4359//===----------------------------------------------------------------------===//
4360// RoutineOp
4361//===----------------------------------------------------------------------===//
4362
4363static unsigned getParallelismForDeviceType(acc::RoutineOp op,
4364 acc::DeviceType dtype) {
4365 unsigned parallelism = 0;
4366 parallelism += (op.hasGang(dtype) || op.getGangDimValue(dtype)) ? 1 : 0;
4367 parallelism += op.hasWorker(dtype) ? 1 : 0;
4368 parallelism += op.hasVector(dtype) ? 1 : 0;
4369 parallelism += op.hasSeq(dtype) ? 1 : 0;
4370 return parallelism;
4371}
4372
4373LogicalResult acc::RoutineOp::verify() {
4374 unsigned baseParallelism =
4375 getParallelismForDeviceType(*this, acc::DeviceType::None);
4376
4377 if (baseParallelism > 1)
4378 return emitError() << "only one of `gang`, `worker`, `vector`, `seq` can "
4379 "be present at the same time";
4380
4381 for (uint32_t dtypeInt = 0; dtypeInt != acc::getMaxEnumValForDeviceType();
4382 ++dtypeInt) {
4383 auto dtype = static_cast<acc::DeviceType>(dtypeInt);
4384 if (dtype == acc::DeviceType::None)
4385 continue;
4386 unsigned parallelism = getParallelismForDeviceType(*this, dtype);
4387
4388 if (parallelism > 1 || (baseParallelism == 1 && parallelism == 1))
4389 return emitError() << "only one of `gang`, `worker`, `vector`, `seq` can "
4390 "be present at the same time for device_type `"
4391 << acc::stringifyDeviceType(dtype) << "`";
4392 }
4393
4394 return success();
4395}
4396
4397static ParseResult parseBindName(OpAsmParser &parser,
4398 mlir::ArrayAttr &bindIdName,
4399 mlir::ArrayAttr &bindStrName,
4400 mlir::ArrayAttr &deviceIdTypes,
4401 mlir::ArrayAttr &deviceStrTypes) {
4402 llvm::SmallVector<mlir::Attribute> bindIdNameAttrs;
4403 llvm::SmallVector<mlir::Attribute> bindStrNameAttrs;
4404 llvm::SmallVector<mlir::Attribute> deviceIdTypeAttrs;
4405 llvm::SmallVector<mlir::Attribute> deviceStrTypeAttrs;
4406
4407 if (failed(parser.parseCommaSeparatedList([&]() {
4408 llvm::SMLoc attrLoc = parser.getCurrentLocation();
4409 mlir::Attribute newAttr;
4410 bool isSymbolRefAttr;
4411 if (parser.parseAttribute(newAttr))
4412 return failure();
4413 if (auto symbolRefAttr = dyn_cast<mlir::SymbolRefAttr>(newAttr)) {
4414 bindIdNameAttrs.push_back(symbolRefAttr);
4415 isSymbolRefAttr = true;
4416 } else if (auto stringAttr = dyn_cast<mlir::StringAttr>(newAttr)) {
4417 bindStrNameAttrs.push_back(stringAttr);
4418 isSymbolRefAttr = false;
4419 } else {
4420 parser.emitError(attrLoc,
4421 "expected symbol reference or string attribute");
4422 return failure();
4423 }
4424 if (failed(parser.parseOptionalLSquare())) {
4425 if (isSymbolRefAttr) {
4426 deviceIdTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
4427 parser.getContext(), mlir::acc::DeviceType::None));
4428 } else {
4429 deviceStrTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
4430 parser.getContext(), mlir::acc::DeviceType::None));
4431 }
4432 } else {
4433 if (isSymbolRefAttr) {
4434 if (parser.parseAttribute(deviceIdTypeAttrs.emplace_back()) ||
4435 parser.parseRSquare())
4436 return failure();
4437 } else {
4438 if (parser.parseAttribute(deviceStrTypeAttrs.emplace_back()) ||
4439 parser.parseRSquare())
4440 return failure();
4441 }
4442 }
4443 return success();
4444 })))
4445 return failure();
4446
4447 bindIdName = ArrayAttr::get(parser.getContext(), bindIdNameAttrs);
4448 bindStrName = ArrayAttr::get(parser.getContext(), bindStrNameAttrs);
4449 deviceIdTypes = ArrayAttr::get(parser.getContext(), deviceIdTypeAttrs);
4450 deviceStrTypes = ArrayAttr::get(parser.getContext(), deviceStrTypeAttrs);
4451
4452 return success();
4453}
4454
4456 std::optional<mlir::ArrayAttr> bindIdName,
4457 std::optional<mlir::ArrayAttr> bindStrName,
4458 std::optional<mlir::ArrayAttr> deviceIdTypes,
4459 std::optional<mlir::ArrayAttr> deviceStrTypes) {
4460 // Create combined vectors for all bind names and device types
4463
4464 // Append bindIdName and deviceIdTypes
4465 if (hasDeviceTypeValues(deviceIdTypes)) {
4466 allBindNames.append(bindIdName->begin(), bindIdName->end());
4467 allDeviceTypes.append(deviceIdTypes->begin(), deviceIdTypes->end());
4468 }
4469
4470 // Append bindStrName and deviceStrTypes
4471 if (hasDeviceTypeValues(deviceStrTypes)) {
4472 allBindNames.append(bindStrName->begin(), bindStrName->end());
4473 allDeviceTypes.append(deviceStrTypes->begin(), deviceStrTypes->end());
4474 }
4475
4476 // Print the combined sequence
4477 if (!allBindNames.empty())
4478 llvm::interleaveComma(llvm::zip(allBindNames, allDeviceTypes), p,
4479 [&](const auto &pair) {
4480 p << std::get<0>(pair);
4481 printSingleDeviceType(p, std::get<1>(pair));
4482 });
4483}
4484
4485static ParseResult parseRoutineGangClause(OpAsmParser &parser,
4486 mlir::ArrayAttr &gang,
4487 mlir::ArrayAttr &gangDim,
4488 mlir::ArrayAttr &gangDimDeviceTypes) {
4489
4490 llvm::SmallVector<mlir::Attribute> gangAttrs, gangDimAttrs,
4491 gangDimDeviceTypeAttrs;
4492 bool needCommaBeforeOperands = false;
4493
4494 // Gang keyword only
4495 if (failed(parser.parseOptionalLParen())) {
4496 gangAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
4497 parser.getContext(), mlir::acc::DeviceType::None));
4498 gang = ArrayAttr::get(parser.getContext(), gangAttrs);
4499 return success();
4500 }
4501
4502 // Parse keyword only attributes
4503 if (succeeded(parser.parseOptionalLSquare())) {
4504 if (failed(parser.parseCommaSeparatedList([&]() {
4505 if (parser.parseAttribute(gangAttrs.emplace_back()))
4506 return failure();
4507 return success();
4508 })))
4509 return failure();
4510 if (parser.parseRSquare())
4511 return failure();
4512 needCommaBeforeOperands = true;
4513 }
4514
4515 if (needCommaBeforeOperands && failed(parser.parseComma()))
4516 return failure();
4517
4518 if (failed(parser.parseCommaSeparatedList([&]() {
4519 if (parser.parseKeyword(acc::RoutineOp::getGangDimKeyword()) ||
4520 parser.parseColon() ||
4521 parser.parseAttribute(gangDimAttrs.emplace_back()))
4522 return failure();
4523 if (succeeded(parser.parseOptionalLSquare())) {
4524 if (parser.parseAttribute(gangDimDeviceTypeAttrs.emplace_back()) ||
4525 parser.parseRSquare())
4526 return failure();
4527 } else {
4528 gangDimDeviceTypeAttrs.push_back(mlir::acc::DeviceTypeAttr::get(
4529 parser.getContext(), mlir::acc::DeviceType::None));
4530 }
4531 return success();
4532 })))
4533 return failure();
4534
4535 if (failed(parser.parseRParen()))
4536 return failure();
4537
4538 gang = ArrayAttr::get(parser.getContext(), gangAttrs);
4539 gangDim = ArrayAttr::get(parser.getContext(), gangDimAttrs);
4540 gangDimDeviceTypes =
4541 ArrayAttr::get(parser.getContext(), gangDimDeviceTypeAttrs);
4542
4543 return success();
4544}
4545
4547 std::optional<mlir::ArrayAttr> gang,
4548 std::optional<mlir::ArrayAttr> gangDim,
4549 std::optional<mlir::ArrayAttr> gangDimDeviceTypes) {
4550
4551 if (!hasDeviceTypeValues(gangDimDeviceTypes) && hasDeviceTypeValues(gang) &&
4552 gang->size() == 1) {
4553 auto deviceTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*gang)[0]);
4554 if (deviceTypeAttr.getValue() == mlir::acc::DeviceType::None)
4555 return;
4556 }
4557
4558 p << "(";
4559
4560 printDeviceTypes(p, gang);
4561
4562 if (hasDeviceTypeValues(gang) && hasDeviceTypeValues(gangDimDeviceTypes))
4563 p << ", ";
4564
4565 if (hasDeviceTypeValues(gangDimDeviceTypes))
4566 llvm::interleaveComma(llvm::zip(*gangDim, *gangDimDeviceTypes), p,
4567 [&](const auto &pair) {
4568 p << acc::RoutineOp::getGangDimKeyword() << ": ";
4569 p << std::get<0>(pair);
4570 printSingleDeviceType(p, std::get<1>(pair));
4571 });
4572
4573 p << ")";
4574}
4575
4576static ParseResult parseDeviceTypeArrayAttr(OpAsmParser &parser,
4577 mlir::ArrayAttr &deviceTypes) {
4579 // Keyword only
4580 if (failed(parser.parseOptionalLParen())) {
4581 attributes.push_back(mlir::acc::DeviceTypeAttr::get(
4582 parser.getContext(), mlir::acc::DeviceType::None));
4583 deviceTypes = ArrayAttr::get(parser.getContext(), attributes);
4584 return success();
4585 }
4586
4587 // Parse device type attributes
4588 if (succeeded(parser.parseOptionalLSquare())) {
4589 if (failed(parser.parseCommaSeparatedList([&]() {
4590 if (parser.parseAttribute(attributes.emplace_back()))
4591 return failure();
4592 return success();
4593 })))
4594 return failure();
4595 if (parser.parseRSquare() || parser.parseRParen())
4596 return failure();
4597 }
4598 deviceTypes = ArrayAttr::get(parser.getContext(), attributes);
4599 return success();
4600}
4601
4602static void
4604 std::optional<mlir::ArrayAttr> deviceTypes) {
4605
4606 if (hasDeviceTypeValues(deviceTypes) && deviceTypes->size() == 1) {
4607 auto deviceTypeAttr =
4608 mlir::dyn_cast<mlir::acc::DeviceTypeAttr>((*deviceTypes)[0]);
4609 if (deviceTypeAttr.getValue() == mlir::acc::DeviceType::None)
4610 return;
4611 }
4612
4613 if (!hasDeviceTypeValues(deviceTypes))
4614 return;
4615
4616 p << "([";
4617 llvm::interleaveComma(*deviceTypes, p, [&](mlir::Attribute attr) {
4618 auto dTypeAttr = mlir::dyn_cast<mlir::acc::DeviceTypeAttr>(attr);
4619 p << dTypeAttr;
4620 });
4621 p << "])";
4622}
4623
4624bool RoutineOp::hasWorker() { return hasWorker(mlir::acc::DeviceType::None); }
4625
4626bool RoutineOp::hasWorker(mlir::acc::DeviceType deviceType) {
4627 return hasDeviceType(getWorker(), deviceType);
4628}
4629
4630bool RoutineOp::hasVector() { return hasVector(mlir::acc::DeviceType::None); }
4631
4632bool RoutineOp::hasVector(mlir::acc::DeviceType deviceType) {
4633 return hasDeviceType(getVector(), deviceType);
4634}
4635
4636bool RoutineOp::hasSeq() { return hasSeq(mlir::acc::DeviceType::None); }
4637
4638bool RoutineOp::hasSeq(mlir::acc::DeviceType deviceType) {
4639 return hasDeviceType(getSeq(), deviceType);
4640}
4641
4642std::optional<std::variant<mlir::SymbolRefAttr, mlir::StringAttr>>
4643RoutineOp::getBindNameValue() {
4644 return getBindNameValue(mlir::acc::DeviceType::None);
4645}
4646
4647std::optional<std::variant<mlir::SymbolRefAttr, mlir::StringAttr>>
4648RoutineOp::getBindNameValue(mlir::acc::DeviceType deviceType) {
4649 if (hasDeviceTypeValues(getBindIdNameDeviceType())) {
4650 if (auto pos = findSegment(*getBindIdNameDeviceType(), deviceType)) {
4651 auto attr = (*getBindIdName())[*pos];
4652 auto symbolRefAttr = dyn_cast<mlir::SymbolRefAttr>(attr);
4653 assert(symbolRefAttr && "expected SymbolRef");
4654 return symbolRefAttr;
4655 }
4656 }
4657
4658 if (hasDeviceTypeValues(getBindStrNameDeviceType())) {
4659 if (auto pos = findSegment(*getBindStrNameDeviceType(), deviceType)) {
4660 auto attr = (*getBindStrName())[*pos];
4661 auto stringAttr = dyn_cast<mlir::StringAttr>(attr);
4662 assert(stringAttr && "expected String");
4663 return stringAttr;
4664 }
4665 }
4666
4667 return std::nullopt;
4668}
4669
4670std::optional<std::variant<mlir::SymbolRefAttr, mlir::StringAttr>>
4671RoutineOp::getEffectiveBindName(mlir::acc::DeviceType targetDeviceType) {
4672 assert(targetDeviceType != DeviceType::Star &&
4673 "expected a valid target device type");
4674
4675 if (!getBindIdName() && !getBindStrName())
4676 return std::nullopt;
4677
4678 // None queries the default bind.
4679 if (targetDeviceType == DeviceType::None)
4680 return getBindNameValue();
4681
4682 // Prefer an exact device-specific bind.
4683 if (auto bindName = getBindNameValue(targetDeviceType))
4684 return bindName;
4685
4686 // The device_type(*) clause applies only if no other device_type clause names
4687 // the target device.
4688 bool hasNamedDeviceType = hasGang(targetDeviceType) ||
4689 getGangDimValue(targetDeviceType).has_value() ||
4690 hasWorker(targetDeviceType) ||
4691 hasVector(targetDeviceType) ||
4692 hasSeq(targetDeviceType);
4693 if (!hasNamedDeviceType) {
4694 if (auto bindName = getBindNameValue(DeviceType::Star))
4695 return bindName;
4696 }
4697
4698 return getBindNameValue();
4699}
4700
4701bool RoutineOp::hasGang() { return hasGang(mlir::acc::DeviceType::None); }
4702
4703bool RoutineOp::hasGang(mlir::acc::DeviceType deviceType) {
4704 return hasDeviceType(getGang(), deviceType);
4705}
4706
4707std::optional<int64_t> RoutineOp::getGangDimValue() {
4708 return getGangDimValue(mlir::acc::DeviceType::None);
4709}
4710
4711std::optional<int64_t>
4712RoutineOp::getGangDimValue(mlir::acc::DeviceType deviceType) {
4713 if (!hasDeviceTypeValues(getGangDimDeviceType()))
4714 return std::nullopt;
4715 if (auto pos = findSegment(*getGangDimDeviceType(), deviceType)) {
4716 auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>((*getGangDim())[*pos]);
4717 return intAttr.getInt();
4718 }
4719 return std::nullopt;
4720}
4721
4722void RoutineOp::addSeq(MLIRContext *context,
4723 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4724 setSeqAttr(addDeviceTypeAffectedOperandHelper(context, getSeqAttr(),
4725 effectiveDeviceTypes));
4726}
4727
4728void RoutineOp::addVector(MLIRContext *context,
4729 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4730 setVectorAttr(addDeviceTypeAffectedOperandHelper(context, getVectorAttr(),
4731 effectiveDeviceTypes));
4732}
4733
4734void RoutineOp::addWorker(MLIRContext *context,
4735 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4736 setWorkerAttr(addDeviceTypeAffectedOperandHelper(context, getWorkerAttr(),
4737 effectiveDeviceTypes));
4738}
4739
4740void RoutineOp::addGang(MLIRContext *context,
4741 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4742 setGangAttr(addDeviceTypeAffectedOperandHelper(context, getGangAttr(),
4743 effectiveDeviceTypes));
4744}
4745
4746void RoutineOp::addGang(MLIRContext *context,
4747 llvm::ArrayRef<DeviceType> effectiveDeviceTypes,
4748 uint64_t val) {
4751
4752 if (getGangDimAttr())
4753 llvm::copy(getGangDimAttr(), std::back_inserter(dimValues));
4754 if (getGangDimDeviceTypeAttr())
4755 llvm::copy(getGangDimDeviceTypeAttr(), std::back_inserter(deviceTypes));
4756
4757 assert(dimValues.size() == deviceTypes.size());
4758
4759 if (effectiveDeviceTypes.empty()) {
4760 dimValues.push_back(
4761 mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), val));
4762 deviceTypes.push_back(
4763 acc::DeviceTypeAttr::get(context, acc::DeviceType::None));
4764 } else {
4765 for (DeviceType dt : effectiveDeviceTypes) {
4766 dimValues.push_back(
4767 mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), val));
4768 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, dt));
4769 }
4770 }
4771 assert(dimValues.size() == deviceTypes.size());
4772
4773 setGangDimAttr(mlir::ArrayAttr::get(context, dimValues));
4774 setGangDimDeviceTypeAttr(mlir::ArrayAttr::get(context, deviceTypes));
4775}
4776
4777void RoutineOp::addBindStrName(MLIRContext *context,
4778 llvm::ArrayRef<DeviceType> effectiveDeviceTypes,
4779 mlir::StringAttr val) {
4780 unsigned before = getBindStrNameDeviceTypeAttr()
4781 ? getBindStrNameDeviceTypeAttr().size()
4782 : 0;
4783
4784 setBindStrNameDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
4785 context, getBindStrNameDeviceTypeAttr(), effectiveDeviceTypes));
4786 unsigned after = getBindStrNameDeviceTypeAttr().size();
4787
4789 if (getBindStrNameAttr())
4790 llvm::copy(getBindStrNameAttr(), std::back_inserter(vals));
4791 for (unsigned i = 0; i < after - before; ++i)
4792 vals.push_back(val);
4793
4794 setBindStrNameAttr(mlir::ArrayAttr::get(context, vals));
4795}
4796
4797void RoutineOp::addBindIDName(MLIRContext *context,
4798 llvm::ArrayRef<DeviceType> effectiveDeviceTypes,
4799 mlir::SymbolRefAttr val) {
4800 unsigned before =
4801 getBindIdNameDeviceTypeAttr() ? getBindIdNameDeviceTypeAttr().size() : 0;
4802
4803 setBindIdNameDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
4804 context, getBindIdNameDeviceTypeAttr(), effectiveDeviceTypes));
4805 unsigned after = getBindIdNameDeviceTypeAttr().size();
4806
4808 if (getBindIdNameAttr())
4809 llvm::copy(getBindIdNameAttr(), std::back_inserter(vals));
4810 for (unsigned i = 0; i < after - before; ++i)
4811 vals.push_back(val);
4812
4813 setBindIdNameAttr(mlir::ArrayAttr::get(context, vals));
4814}
4815
4816//===----------------------------------------------------------------------===//
4817// InitOp
4818//===----------------------------------------------------------------------===//
4819
4820LogicalResult acc::InitOp::verify() {
4821 if (getOperation()->getParentOfType<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>())
4822 return emitOpError("cannot be nested in a compute operation");
4823 return success();
4824}
4825
4826void acc::InitOp::addDeviceType(MLIRContext *context,
4827 mlir::acc::DeviceType deviceType) {
4829 if (getDeviceTypesAttr())
4830 llvm::copy(getDeviceTypesAttr(), std::back_inserter(deviceTypes));
4831
4832 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, deviceType));
4833 setDeviceTypesAttr(mlir::ArrayAttr::get(context, deviceTypes));
4834}
4835
4836//===----------------------------------------------------------------------===//
4837// ShutdownOp
4838//===----------------------------------------------------------------------===//
4839
4840LogicalResult acc::ShutdownOp::verify() {
4841 if (getOperation()->getParentOfType<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>())
4842 return emitOpError("cannot be nested in a compute operation");
4843 return success();
4844}
4845
4846void acc::ShutdownOp::addDeviceType(MLIRContext *context,
4847 mlir::acc::DeviceType deviceType) {
4849 if (getDeviceTypesAttr())
4850 llvm::copy(getDeviceTypesAttr(), std::back_inserter(deviceTypes));
4851
4852 deviceTypes.push_back(acc::DeviceTypeAttr::get(context, deviceType));
4853 setDeviceTypesAttr(mlir::ArrayAttr::get(context, deviceTypes));
4854}
4855
4856//===----------------------------------------------------------------------===//
4857// SetOp
4858//===----------------------------------------------------------------------===//
4859
4860LogicalResult acc::SetOp::verify() {
4861 if (getOperation()->getParentOfType<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>())
4862 return emitOpError("cannot be nested in a compute operation");
4863 if (!getDeviceTypeAttr() && !getDefaultAsync() && !getDeviceNum())
4864 return emitOpError("at least one default_async, device_num, or device_type "
4865 "operand must appear");
4866 return success();
4867}
4868
4869//===----------------------------------------------------------------------===//
4870// UpdateOp
4871//===----------------------------------------------------------------------===//
4872
4873LogicalResult acc::UpdateOp::verify() {
4874 // At least one of host or device should have a value.
4875 if (getDataClauseOperands().empty())
4876 return emitError("at least one value must be present in dataOperands");
4877
4879 getAsyncOperandsDeviceTypeAttr(),
4880 "async")))
4881 return failure();
4882
4884 *this, getWaitOperands(), getWaitOperandsSegmentsAttr(),
4885 getWaitOperandsDeviceTypeAttr(), "wait")))
4886 return failure();
4887
4889 return failure();
4890
4891 for (mlir::Value operand : getDataClauseOperands())
4892 if (!mlir::isa<acc::UpdateDeviceOp, acc::UpdateHostOp, acc::GetDevicePtrOp,
4893 acc::MapInfoOp>(operand.getDefiningOp()))
4894 return emitError("expect data entry/exit operation or acc.getdeviceptr "
4895 "as defining op");
4896
4897 return success();
4898}
4899
4900unsigned UpdateOp::getNumDataOperands() {
4901 return getDataClauseOperands().size();
4902}
4903
4904Value UpdateOp::getDataOperand(unsigned i) {
4905 unsigned numOptional = getAsyncOperands().size();
4906 numOptional += getIfCond() ? 1 : 0;
4907 return getOperand(getWaitOperands().size() + numOptional + i);
4908}
4909
4910void UpdateOp::getCanonicalizationPatterns(RewritePatternSet &results,
4911 MLIRContext *context) {
4912 results.add<RemoveConstantIfCondition<UpdateOp>>(context);
4913}
4914
4915bool UpdateOp::hasAsyncOnly() {
4916 return hasAsyncOnly(mlir::acc::DeviceType::None);
4917}
4918
4919bool UpdateOp::hasAsyncOnly(mlir::acc::DeviceType deviceType) {
4920 return hasDeviceType(getAsyncOnly(), deviceType);
4921}
4922
4923mlir::Value UpdateOp::getAsyncValue() {
4924 return getAsyncValue(mlir::acc::DeviceType::None);
4925}
4926
4927mlir::Value UpdateOp::getAsyncValue(mlir::acc::DeviceType deviceType) {
4929 return {};
4930
4931 if (auto pos = findSegment(*getAsyncOperandsDeviceType(), deviceType))
4932 return getAsyncOperands()[*pos];
4933
4934 return {};
4935}
4936
4937bool UpdateOp::hasWaitOnly() {
4938 return hasWaitOnly(mlir::acc::DeviceType::None);
4939}
4940
4941bool UpdateOp::hasWaitOnly(mlir::acc::DeviceType deviceType) {
4942 return hasDeviceType(getWaitOnly(), deviceType);
4943}
4944
4945mlir::Operation::operand_range UpdateOp::getWaitValues() {
4946 return getWaitValues(mlir::acc::DeviceType::None);
4947}
4948
4950UpdateOp::getWaitValues(mlir::acc::DeviceType deviceType) {
4952 getWaitOperandsDeviceType(), getWaitOperands(), getWaitOperandsSegments(),
4953 getHasWaitDevnum(), deviceType);
4954}
4955
4956mlir::Value UpdateOp::getWaitDevnum() {
4957 return getWaitDevnum(mlir::acc::DeviceType::None);
4958}
4959
4960mlir::Value UpdateOp::getWaitDevnum(mlir::acc::DeviceType deviceType) {
4961 return getWaitDevnumValue(getWaitOperandsDeviceType(), getWaitOperands(),
4962 getWaitOperandsSegments(), getHasWaitDevnum(),
4963 deviceType);
4964}
4965
4966void UpdateOp::addAsyncOnly(MLIRContext *context,
4967 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4968 setAsyncOnlyAttr(addDeviceTypeAffectedOperandHelper(
4969 context, getAsyncOnlyAttr(), effectiveDeviceTypes));
4970}
4971
4972void UpdateOp::addAsyncOperand(
4973 MLIRContext *context, mlir::Value newValue,
4974 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4975 setAsyncOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
4976 context, getAsyncOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValue,
4977 getAsyncOperandsMutable()));
4978}
4979
4980void UpdateOp::addWaitOnly(MLIRContext *context,
4981 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4982 setWaitOnlyAttr(addDeviceTypeAffectedOperandHelper(context, getWaitOnlyAttr(),
4983 effectiveDeviceTypes));
4984}
4985
4986void UpdateOp::addWaitOperands(
4987 MLIRContext *context, bool hasDevnum, mlir::ValueRange newValues,
4988 llvm::ArrayRef<DeviceType> effectiveDeviceTypes) {
4989
4991 if (getWaitOperandsSegments())
4992 llvm::copy(*getWaitOperandsSegments(), std::back_inserter(segments));
4993
4994 setWaitOperandsDeviceTypeAttr(addDeviceTypeAffectedOperandHelper(
4995 context, getWaitOperandsDeviceTypeAttr(), effectiveDeviceTypes, newValues,
4996 getWaitOperandsMutable(), segments));
4997 setWaitOperandsSegments(segments);
4998
5000 if (getHasWaitDevnumAttr())
5001 llvm::copy(getHasWaitDevnumAttr(), std::back_inserter(hasDevnums));
5002 hasDevnums.insert(
5003 hasDevnums.end(),
5004 std::max(effectiveDeviceTypes.size(), static_cast<size_t>(1)),
5005 mlir::BoolAttr::get(context, hasDevnum));
5006 setHasWaitDevnumAttr(mlir::ArrayAttr::get(context, hasDevnums));
5007}
5008
5009//===----------------------------------------------------------------------===//
5010// WaitOp
5011//===----------------------------------------------------------------------===//
5012
5013LogicalResult acc::WaitOp::verify() {
5014 // The async attribute represent the async clause without value. Therefore the
5015 // attribute and operand cannot appear at the same time.
5016 if (getAsyncOperand() && getAsync())
5017 return emitError("async attribute cannot appear with asyncOperand");
5018
5019 if (getWaitDevnum() && getWaitOperands().empty())
5020 return emitError("wait_devnum cannot appear without waitOperands");
5021
5022 return success();
5023}
5024
5025#define GET_OP_CLASSES
5026#include "mlir/Dialect/OpenACC/OpenACCOps.cpp.inc"
5027
5028#define GET_ATTRDEF_CLASSES
5029#include "mlir/Dialect/OpenACC/OpenACCOpsAttributes.cpp.inc"
5030
5031#define GET_TYPEDEF_CLASSES
5032#include "mlir/Dialect/OpenACC/OpenACCOpsTypes.cpp.inc"
5033
5034//===----------------------------------------------------------------------===//
5035// acc dialect utilities
5036//===----------------------------------------------------------------------===//
5037
5040 auto varPtr{llvm::TypeSwitch<mlir::Operation *,
5042 accDataClauseOp)
5043 .Case<ACC_DATA_ENTRY_OPS, mlir::acc::MapInfoOp>(
5044 [&](auto entry) { return entry.getVarPtr(); })
5045 .Case<mlir::acc::CopyoutOp, mlir::acc::UpdateHostOp>(
5046 [&](auto exit) { return exit.getVarPtr(); })
5047 .Default([&](mlir::Operation *) {
5049 })};
5050 return varPtr;
5051}
5052
5054 auto varPtr{llvm::TypeSwitch<mlir::Operation *, mlir::Value>(accDataClauseOp)
5055 .Case<ACC_DATA_ENTRY_OPS, mlir::acc::MapInfoOp>(
5056 [&](auto entry) { return entry.getVar(); })
5057 .Default([&](mlir::Operation *) { return mlir::Value(); })};
5058 return varPtr;
5059}
5060
5062 auto varType{llvm::TypeSwitch<mlir::Operation *, mlir::Type>(accDataClauseOp)
5063 .Case<ACC_DATA_ENTRY_OPS, mlir::acc::MapInfoOp>(
5064 [&](auto entry) { return entry.getVarType(); })
5065 .Case<mlir::acc::CopyoutOp, mlir::acc::UpdateHostOp>(
5066 [&](auto exit) { return exit.getVarType(); })
5067 .Default([&](mlir::Operation *) { return mlir::Type(); })};
5068 return varType;
5069}
5070
5073 auto accPtr{
5076 accDataClauseOp)
5077 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS, mlir::acc::MapInfoOp>(
5078 [&](auto dataClause) { return dataClause.getAccPtr(); })
5079 .Default([&](mlir::Operation *) {
5081 })};
5082 return accPtr;
5083}
5084
5086 auto accPtr{
5088 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS, mlir::acc::MapInfoOp>(
5089 [&](auto dataClause) { return dataClause.getAccVar(); })
5090 .Default([&](mlir::Operation *) { return mlir::Value(); })};
5091 return accPtr;
5092}
5093
5095 auto varPtrPtr{
5097 .Case<ACC_DATA_ENTRY_OPS, mlir::acc::MapInfoOp>(
5098 [&](auto dataClause) { return dataClause.getVarPtrPtr(); })
5099 .Default([&](mlir::Operation *) { return mlir::Value(); })};
5100 return varPtrPtr;
5101}
5102
5107 accDataClauseOp)
5108 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS, mlir::acc::MapInfoOp>(
5109 [&](auto dataClause) {
5111 dataClause.getBounds().begin(),
5112 dataClause.getBounds().end());
5113 })
5114 .Default([&](mlir::Operation *) {
5116 })};
5117 return bounds;
5118}
5119
5123 accDataClauseOp)
5124 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto dataClause) {
5126 dataClause.getAsyncOperands().begin(),
5127 dataClause.getAsyncOperands().end());
5128 })
5129 .Default([&](mlir::Operation *) {
5131 });
5132}
5133
5134mlir::ArrayAttr
5137 .Case<ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto dataClause) {
5138 return dataClause.getAsyncOperandsDeviceTypeAttr();
5139 })
5140 .Default([&](mlir::Operation *) { return mlir::ArrayAttr{}; });
5141}
5142
5143mlir::ArrayAttr mlir::acc::getAsyncOnly(mlir::Operation *accDataClauseOp) {
5146 [&](auto dataClause) { return dataClause.getAsyncOnlyAttr(); })
5147 .Default([&](mlir::Operation *) { return mlir::ArrayAttr{}; });
5148}
5149
5151acc::getDeviceTypesByPrecedence(DeviceType deviceType) {
5152 SmallVector<DeviceType, 3> deviceTypes;
5153 if (deviceType != DeviceType::None && deviceType != DeviceType::Star)
5154 deviceTypes.push_back(deviceType);
5155 deviceTypes.push_back(DeviceType::Star);
5156 deviceTypes.push_back(DeviceType::None);
5157 return deviceTypes;
5158}
5159
5160std::optional<llvm::StringRef> mlir::acc::getVarName(mlir::Operation *accOp) {
5161 auto name{
5163 .Case<ACC_DATA_ENTRY_OPS, mlir::acc::MapInfoOp>(
5164 [&](auto entry) { return entry.getName(); })
5165 .Default([&](mlir::Operation *) -> std::optional<llvm::StringRef> {
5166 return {};
5167 })};
5168 return name;
5169}
5170
5171std::optional<mlir::acc::DataClause>
5173 auto dataClause{
5175 accDataEntryOp)
5176 .Case<ACC_DATA_ENTRY_OPS>(
5177 [&](auto entry) { return entry.getDataClause(); })
5178 .Default([&](mlir::Operation *) { return std::nullopt; })};
5179 return dataClause;
5180}
5181
5183 return mlir::isa_and_nonnull<ACC_DATA_ENTRY_OPS, mlir::acc::MapInfoOp>(op);
5184}
5185
5187 return llvm::TypeSwitch<mlir::Operation *, bool>(accDataEntryOp)
5188 .Case<ACC_DATA_ENTRY_OPS>([&](auto entry) { return entry.getImplicit(); })
5189 .Case<mlir::acc::MapInfoOp>([&](auto mapInfo) {
5190 return bitEnumContainsAny(mapInfo.getMapFlags(),
5191 mlir::acc::MapFlags::implicit);
5192 })
5193 .Default([&](mlir::Operation *) { return false; });
5194}
5195
5197 return llvm::TypeSwitch<mlir::Operation *, bool>(accDataClauseOp)
5198 .Case<ACC_DATA_CLAUSE_OPS>(
5199 [&](auto dataClause) { return dataClause.getSynthetic(); })
5200 .Default([&](mlir::Operation *) { return false; });
5201}
5202
5204 auto dataOperands{
5207 mlir::acc::KernelEnvironmentOp>(
5208 [&](auto entry) { return entry.getDataClauseOperands(); })
5209 .Default([&](mlir::Operation *) { return mlir::ValueRange(); })};
5210 return dataOperands;
5211}
5212
5215 auto dataOperands{
5218 mlir::acc::KernelEnvironmentOp>(
5219 [&](auto entry) { return entry.getDataClauseOperandsMutable(); })
5220 .Default([&](mlir::Operation *) { return nullptr; })};
5221 return dataOperands;
5222}
5223
5224mlir::SymbolRefAttr mlir::acc::getRecipe(mlir::Operation *accOp) {
5225 auto recipe{
5227 .Case<ACC_DATA_ENTRY_OPS>(
5228 [&](auto entry) { return entry.getRecipeAttr(); })
5229 .Default([&](mlir::Operation *) { return mlir::SymbolRefAttr{}; })};
5230 return recipe;
5231}
return success()
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
static void printSourceLocation(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::LocationAttr locAttr)
Definition OpenACC.cpp:648
void printRoutineGangClause(OpAsmPrinter &p, Operation *op, std::optional< mlir::ArrayAttr > gang, std::optional< mlir::ArrayAttr > gangDim, std::optional< mlir::ArrayAttr > gangDimDeviceTypes)
Definition OpenACC.cpp:4546
bool hasDuplicateDeviceTypes(std::optional< mlir::ArrayAttr > segments, llvm::SmallSet< mlir::acc::DeviceType, 3 > &deviceTypes)
Definition OpenACC.cpp:3266
static LogicalResult verifyDeviceTypeCountMatch(Op op, OperandRange operands, ArrayAttr deviceTypes, llvm::StringRef keyword)
Definition OpenACC.cpp:1838
static ParseResult parseArrayAttr(mlir::OpAsmParser &parser, mlir::ArrayAttr &attr)
Definition OpenACC.cpp:675
static ParseResult parseBindName(OpAsmParser &parser, mlir::ArrayAttr &bindIdName, mlir::ArrayAttr &bindStrName, mlir::ArrayAttr &deviceIdTypes, mlir::ArrayAttr &deviceStrTypes)
Definition OpenACC.cpp:4397
static void printRecipeSym(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::SymbolRefAttr recipeAttr)
Definition OpenACC.cpp:660
static mlir::Operation::operand_range getWaitValuesWithoutDevnum(std::optional< mlir::ArrayAttr > deviceTypeAttr, mlir::Operation::operand_range operands, std::optional< llvm::ArrayRef< int32_t > > segments, std::optional< mlir::ArrayAttr > hasWaitDevnum, mlir::acc::DeviceType deviceType)
Definition OpenACC.cpp:413
static void printArrayAttr(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::ArrayAttr attr)
Definition OpenACC.cpp:680
static bool hasOnlyDeviceTypeNone(std::optional< mlir::ArrayAttr > attrs)
Definition OpenACC.cpp:2364
static ParseResult parseRecipeSym(mlir::OpAsmParser &parser, mlir::SymbolRefAttr &recipeAttr)
Definition OpenACC.cpp:653
static void printAccVar(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::Value accVar, mlir::Type accVarType)
Definition OpenACC.cpp:566
static mlir::Value getWaitDevnumValue(std::optional< mlir::ArrayAttr > deviceTypeAttr, mlir::Operation::operand_range operands, std::optional< llvm::ArrayRef< int32_t > > segments, std::optional< mlir::ArrayAttr > hasWaitDevnum, mlir::acc::DeviceType deviceType)
Definition OpenACC.cpp:393
static bool hasAnyGangWorkerVectorForDeviceType(std::optional< mlir::ArrayAttr > numGangsDeviceType, mlir::Operation::operand_range numGangs, std::optional< llvm::ArrayRef< int32_t > > numGangsSegments, std::optional< mlir::ArrayAttr > numWorkersDeviceType, mlir::Operation::operand_range numWorkers, std::optional< mlir::ArrayAttr > vectorLengthDeviceType, mlir::Operation::operand_range vectorLength, mlir::acc::DeviceType deviceType)
Definition OpenACC.cpp:1978
static void printVar(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::Value var)
Definition OpenACC.cpp:535
static void printWaitClause(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments, std::optional< mlir::ArrayAttr > hasDevNum, std::optional< mlir::ArrayAttr > keywordOnly)
Definition OpenACC.cpp:2375
static ParseResult parseWaitClause(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::DenseI32ArrayAttr &segments, mlir::ArrayAttr &hasDevNum, mlir::ArrayAttr &keywordOnly)
Definition OpenACC.cpp:2280
static BodyExecution getBodyExecution(LoopOp loopOp)
Prove whether the body of loopOp runs.
Definition OpenACC.cpp:238
static bool hasDeviceTypeValues(std::optional< mlir::ArrayAttr > arrayAttr)
Definition OpenACC.cpp:335
static void printDeviceTypeArrayAttr(mlir::OpAsmPrinter &p, mlir::Operation *op, std::optional< mlir::ArrayAttr > deviceTypes)
Definition OpenACC.cpp:4603
static ParseResult parseGangValue(OpAsmParser &parser, llvm::StringRef keyword, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, llvm::SmallVector< GangArgTypeAttr > &attributes, GangArgTypeAttr gangArgType, bool &needCommaBetweenValues, bool &newValue)
Definition OpenACC.cpp:3075
static ParseResult parseCombinedConstructsLoop(mlir::OpAsmParser &parser, mlir::acc::CombinedConstructsTypeAttr &attr)
Definition OpenACC.cpp:2621
static std::optional< mlir::acc::DeviceType > checkDeviceTypes(mlir::ArrayAttr deviceTypes)
Check for duplicates in the DeviceType array attribute.
Definition OpenACC.cpp:3282
static LogicalResult checkDeclareOperands(Op &op, const mlir::ValueRange &operands, bool requireAtLeastOneOperand=true)
Definition OpenACC.cpp:4301
static LogicalResult checkVarAndAccVar(Op op)
Definition OpenACC.cpp:473
static ParseResult parseOperandsWithKeywordOnly(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::UnitAttr &attr)
Definition OpenACC.cpp:2575
static void printDeviceTypes(mlir::OpAsmPrinter &p, std::optional< mlir::ArrayAttr > deviceTypes)
Definition OpenACC.cpp:353
static LogicalResult checkVarAndVarType(Op op)
Definition OpenACC.cpp:455
static LogicalResult checkValidModifier(Op op, acc::DataClauseModifier validModifiers)
Definition OpenACC.cpp:489
static void addOperandEffect(SmallVectorImpl< SideEffects::EffectInstance< MemoryEffects::Effect > > &effects, MutableOperandRange operand)
Helper to add an effect on an operand, referenced by its mutable range.
Definition OpenACC.cpp:1078
ParseResult parseLoopControl(OpAsmParser &parser, Region &region, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &lowerbound, SmallVectorImpl< Type > &lowerboundType, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &upperbound, SmallVectorImpl< Type > &upperboundType, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &step, SmallVectorImpl< Type > &stepType)
loop-control ::= control ( ssa-id-and-type-list ) = ( ssa-id-and-type-list ) to ( ssa-id-and-type-lis...
Definition OpenACC.cpp:3650
static LogicalResult checkDataOperands(Op op, const mlir::ValueRange &operands)
Check dataOperands for acc.parallel, acc.serial and acc.kernels.
Definition OpenACC.cpp:1793
static ParseResult parseDeviceTypeOperands(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes)
Definition OpenACC.cpp:2411
static mlir::Value getValueInDeviceTypeSegment(std::optional< mlir::ArrayAttr > arrayAttr, mlir::Operation::operand_range range, mlir::acc::DeviceType deviceType)
Definition OpenACC.cpp:1921
static void addResultEffect(SmallVectorImpl< SideEffects::EffectInstance< MemoryEffects::Effect > > &effects, Value result)
Helper to add an effect on a result value.
Definition OpenACC.cpp:1088
static LogicalResult checkNoModifier(Op op)
Definition OpenACC.cpp:481
static ParseResult parseAccVar(mlir::OpAsmParser &parser, OpAsmParser::UnresolvedOperand &var, mlir::Type &accVarType)
Definition OpenACC.cpp:544
static std::optional< unsigned > findSegment(ArrayAttr segments, mlir::acc::DeviceType deviceType)
Definition OpenACC.cpp:364
static ParseResult parseDenseBoolArrayAttr(mlir::OpAsmParser &parser, mlir::DenseBoolArrayAttr &attr)
Definition OpenACC.cpp:665
static mlir::Operation::operand_range getValuesFromSegments(std::optional< mlir::ArrayAttr > arrayAttr, mlir::Operation::operand_range range, std::optional< llvm::ArrayRef< int32_t > > segments, mlir::acc::DeviceType deviceType)
Definition OpenACC.cpp:377
static ParseResult parseNumGangs(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::DenseI32ArrayAttr &segments)
Definition OpenACC.cpp:2150
static void getSingleRegionOpSuccessorRegions(Operation *op, Region &region, RegionBranchPoint point, SmallVectorImpl< RegionSuccessor > &regions)
Generic helper for single-region OpenACC ops that execute their body once and then continue after the...
Definition OpenACC.cpp:156
static ParseResult parseVar(mlir::OpAsmParser &parser, OpAsmParser::UnresolvedOperand &var)
Definition OpenACC.cpp:520
void printLoopControl(OpAsmPrinter &p, Operation *op, Region &region, ValueRange lowerbound, TypeRange lowerboundType, ValueRange upperbound, TypeRange upperboundType, ValueRange steps, TypeRange stepType)
Definition OpenACC.cpp:3681
static ValueRange getSingleRegionSuccessorInputs(Operation *op, RegionSuccessor successor)
Definition OpenACC.cpp:167
static void printDenseBoolArrayAttr(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::DenseBoolArrayAttr attr)
Definition OpenACC.cpp:670
static ParseResult parseDeviceTypeArrayAttr(OpAsmParser &parser, mlir::ArrayAttr &deviceTypes)
Definition OpenACC.cpp:4576
static ParseResult parseRoutineGangClause(OpAsmParser &parser, mlir::ArrayAttr &gang, mlir::ArrayAttr &gangDim, mlir::ArrayAttr &gangDimDeviceTypes)
Definition OpenACC.cpp:4485
static void printDeviceTypeOperandsWithSegment(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments)
Definition OpenACC.cpp:2263
static void printDeviceTypeOperands(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes)
Definition OpenACC.cpp:2438
static void printOperandWithKeywordOnly(mlir::OpAsmPrinter &p, mlir::Operation *op, std::optional< mlir::Value > operand, mlir::Type operandType, mlir::UnitAttr attr)
Definition OpenACC.cpp:2560
static ParseResult parseSourceLocation(mlir::OpAsmParser &parser, mlir::LocationAttr &locAttr)
Definition OpenACC.cpp:636
static ParseResult parseDeviceTypeOperandsWithSegment(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::DenseI32ArrayAttr &segments)
Definition OpenACC.cpp:2217
static bool isEnclosedIntoComputeOp(mlir::Operation *op)
Definition OpenACC.cpp:1072
static ParseResult parseOperandWithKeywordOnly(mlir::OpAsmParser &parser, std::optional< OpAsmParser::UnresolvedOperand > &operand, mlir::Type &operandType, mlir::UnitAttr &attr)
Definition OpenACC.cpp:2536
static void printVarPtrType(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::Type varPtrType, mlir::TypeAttr varTypeAttr)
Definition OpenACC.cpp:610
static ParseResult parseGangClause(OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &gangOperands, llvm::SmallVectorImpl< Type > &gangOperandsType, mlir::ArrayAttr &gangArgType, mlir::ArrayAttr &deviceType, mlir::DenseI32ArrayAttr &segments, mlir::ArrayAttr &gangOnlyDeviceType)
Definition OpenACC.cpp:3094
static LogicalResult verifyInitLikeSingleArgRegion(Operation *op, Region &region, StringRef regionType, StringRef regionName, Type type, bool verifyYield, bool optional=false)
Definition OpenACC.cpp:1555
static void printOperandsWithKeywordOnly(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, mlir::UnitAttr attr)
Definition OpenACC.cpp:2605
static void printSingleDeviceType(mlir::OpAsmPrinter &p, mlir::Attribute attr)
Definition OpenACC.cpp:2194
static LogicalResult checkRecipe(OpT op, llvm::StringRef operandName)
Definition OpenACC.cpp:499
static LogicalResult checkPrivateOperands(mlir::Operation *accConstructOp, const mlir::ValueRange &operands, llvm::StringRef operandName)
Definition OpenACC.cpp:1807
static void printDeviceTypeOperandsWithKeywordOnly(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::ArrayAttr > keywordOnlyDeviceTypes)
Definition OpenACC.cpp:2517
static bool hasDeviceType(std::optional< mlir::ArrayAttr > arrayAttr, mlir::acc::DeviceType deviceType)
Definition OpenACC.cpp:339
void printGangClause(OpAsmPrinter &p, Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > gangArgTypes, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments, std::optional< mlir::ArrayAttr > gangOnlyDeviceTypes)
Definition OpenACC.cpp:3221
static ParseResult parseDeviceTypeOperandsWithKeywordOnly(mlir::OpAsmParser &parser, llvm::SmallVectorImpl< mlir::OpAsmParser::UnresolvedOperand > &operands, llvm::SmallVectorImpl< Type > &types, mlir::ArrayAttr &deviceTypes, mlir::ArrayAttr &keywordOnlyDeviceType)
Definition OpenACC.cpp:2449
static ParseResult parseVarPtrType(mlir::OpAsmParser &parser, mlir::Type &varPtrType, mlir::TypeAttr &varTypeAttr)
Definition OpenACC.cpp:578
static LogicalResult checkWaitAndAsyncConflict(Op op)
Definition OpenACC.cpp:433
static LogicalResult verifyDeviceTypeAndSegmentCountMatch(Op op, OperandRange operands, DenseI32ArrayAttr segments, ArrayAttr deviceTypes, llvm::StringRef keyword, int32_t maxInSegment=0)
Definition OpenACC.cpp:1849
static unsigned getParallelismForDeviceType(acc::RoutineOp op, acc::DeviceType dtype)
Definition OpenACC.cpp:4363
static void printNumGangs(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::OperandRange operands, mlir::TypeRange types, std::optional< mlir::ArrayAttr > deviceTypes, std::optional< mlir::DenseI32ArrayAttr > segments)
Definition OpenACC.cpp:2200
BodyExecution
Whether the body of a structured acc.loop is proven to run.
Definition OpenACC.cpp:225
@ Always
The body runs at least once, so the parent cannot bypass the region.
Definition OpenACC.cpp:227
@ Never
The body never runs, so the parent cannot enter the region.
Definition OpenACC.cpp:229
@ Maybe
Neither could be proven, so the parent may do either.
Definition OpenACC.cpp:231
static void printCombinedConstructsLoop(mlir::OpAsmPrinter &p, mlir::Operation *op, mlir::acc::CombinedConstructsTypeAttr attr)
Definition OpenACC.cpp:2641
static void printBindName(mlir::OpAsmPrinter &p, mlir::Operation *op, std::optional< mlir::ArrayAttr > bindIdName, std::optional< mlir::ArrayAttr > bindStrName, std::optional< mlir::ArrayAttr > deviceIdTypes, std::optional< mlir::ArrayAttr > deviceStrTypes)
Definition OpenACC.cpp:4455
static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp)
ArrayAttr()
b getContext())
false
Parses a map_entries map type from a string format back into its numeric value.
static void replaceOpWithRegion(RewriterBase &rewriter, Operation *op, Region &region)
Replaces the given op with the contents of the given single-block region, using the operands of the b...
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
virtual ParseResult parseLBrace()=0
Parse a { token.
@ 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 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 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 parseRSquare()=0
Parse a ] token.
virtual ParseResult parseRBrace()=0
Parse a } token.
virtual ParseResult parseOptionalRParen()=0
Parse a ) token if present.
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 ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalLParen()=0
Parse a ( token if present.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseOptionalLSquare()=0
Parse a [ token if present.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual void printType(Type type)
virtual void printAttribute(Attribute attr)
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:34
BlockArgument getArgument(unsigned i)
Definition Block.h:154
unsigned getNumArguments()
Definition Block.h:153
iterator_range< args_iterator > addArguments(TypeRange types, ArrayRef< Location > locs)
Add one argument to the argument list for each type specified in the list.
Definition Block.cpp:165
Operation & front()
Definition Block.h:178
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:112
static BoolAttr get(MLIRContext *context, bool value)
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
Location objects represent source locations information in MLIR.
Definition Location.h:32
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:71
This class provides a mutable adaptor for a range of operands.
Definition ValueRange.h:119
unsigned size() const
Returns the current size of the range.
Definition ValueRange.h:157
void append(ValueRange values)
Append the given values to the range.
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 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 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 printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
virtual void printOperand(Value value)=0
Print implementations for various things an operation contains.
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
Location getLoc()
The source location the operation was defined or derived from.
This provides public APIs that all operations should have.
This class implements the operand iterators for the Operation class.
Definition ValueRange.h:44
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OperandRange operand_range
Definition Operation.h:398
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:405
result_range getResults()
Definition Operation.h:442
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 represents a point being branched from in the methods of the RegionBranchOpInterface.
bool isParent() const
Returns true if branching from the parent op.
This class represents a successor of a region.
bool isOperation() const
Return true if the successor is an operation.
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
iterator_range< OpIterator > getOps()
Definition Region.h:180
bool empty()
Definition Region.h:60
bool hasOneBlock()
Return true if this region has exactly one block.
Definition Region.h:68
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues={})
Inline the operations of block 'source' into block 'dest' before the given position.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
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 specific instance of an effect.
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:392
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
Base attribute class for language-specific variable information carried through the OpenACC type inte...
static DenseArrayAttrImpl get(MLIRContext *context, ArrayRef< int32_t > content)
#define ACC_COMPUTE_CONSTRUCT_OPS
Definition OpenACC.h:58
#define ACC_COMPUTE_AND_DATA_CONSTRUCT_OPS
Definition OpenACC.h:69
#define ACC_DATA_CLAUSE_OPS
Definition OpenACC.h:57
#define ACC_DATA_ENTRY_OPS
Definition OpenACC.h:44
#define ACC_DATA_EXIT_OPS
Definition OpenACC.h:54
SmallVector< DeviceType, 3 > getDeviceTypesByPrecedence(DeviceType deviceType)
The clauses of a construct can be given once per device type.
Definition OpenACC.cpp:5151
bool getSyntheticFlag(mlir::Operation *accDataClauseOp)
Used to find out whether the implementation created the data operation for its own bookkeeping,...
Definition OpenACC.cpp:5196
mlir::Value getAccVar(mlir::Operation *accDataClauseOp)
Used to obtain the accVar from a data clause operation.
Definition OpenACC.cpp:5085
mlir::Value getVar(mlir::Operation *accDataClauseOp)
Used to obtain the var from a data clause operation.
Definition OpenACC.cpp:5053
mlir::TypedValue< mlir::acc::PointerLikeType > getAccPtr(mlir::Operation *accDataClauseOp)
Used to obtain the accVar from a data clause operation if it implements PointerLikeType.
Definition OpenACC.cpp:5072
std::optional< mlir::acc::DataClause > getDataClause(mlir::Operation *accDataEntryOp)
Used to obtain the dataClause from a data entry operation.
Definition OpenACC.cpp:5172
mlir::MutableOperandRange getMutableDataOperands(mlir::Operation *accOp)
Used to get a mutable range iterating over the data operands.
Definition OpenACC.cpp:5214
mlir::SmallVector< mlir::Value > getBounds(mlir::Operation *accDataClauseOp)
Used to obtain bounds from an acc data clause operation.
Definition OpenACC.cpp:5104
bool isDataEntryOp(mlir::Operation *op)
Used to find out whether op is a data entry operation or an acc.map_info.
Definition OpenACC.cpp:5182
std::optional< ClauseDefaultValue > getDefaultAttr(mlir::Operation *op)
Looks for an OpenACC default attribute on the current operation op or in a parent operation which enc...
bool hasWaitDevnum(OpTy op, DeviceType deviceType)
Returns whether the wait clause op gives for deviceType carries a devnum modifier,...
mlir::ValueRange getDataOperands(mlir::Operation *accOp)
Used to get an immutable range iterating over the data operands.
Definition OpenACC.cpp:5203
std::optional< llvm::StringRef > getVarName(mlir::Operation *accOp)
Used to obtain the name from an acc operation.
Definition OpenACC.cpp:5160
bool isGangWorkerVectorAllOne(ComputeOpT op)
Definition OpenACC.h:269
bool getImplicitFlag(mlir::Operation *accDataEntryOp)
Used to find out whether data operation is implicit.
Definition OpenACC.cpp:5186
mlir::SymbolRefAttr getRecipe(mlir::Operation *accOp)
Used to get the recipe attribute from a data clause operation.
Definition OpenACC.cpp:5224
mlir::SmallVector< mlir::Value > getAsyncOperands(mlir::Operation *accDataClauseOp)
Used to obtain async operands from an acc data clause operation.
Definition OpenACC.cpp:5121
bool isMappableType(mlir::Type type)
Used to check whether the provided type implements the MappableType interface.
Definition OpenACC.h:184
mlir::Value getVarPtrPtr(mlir::Operation *accDataClauseOp)
Used to obtain the varPtrPtr from a data clause operation.
Definition OpenACC.cpp:5094
mlir::ArrayAttr getAsyncOnly(mlir::Operation *accDataClauseOp)
Returns an array of acc:DeviceTypeAttr attributes attached to an acc data clause operation,...
Definition OpenACC.cpp:5143
mlir::Type getVarType(mlir::Operation *accDataClauseOp)
Used to obtains the varType from a data clause operation which records the type of variable.
Definition OpenACC.cpp:5061
mlir::TypedValue< mlir::acc::PointerLikeType > getVarPtr(mlir::Operation *accDataClauseOp)
Used to obtain the var from a data clause operation if it implements PointerLikeType.
Definition OpenACC.cpp:5039
mlir::ArrayAttr getAsyncOperandsDeviceType(mlir::Operation *accDataClauseOp)
Returns an array of acc:DeviceTypeAttr attributes attached to an acc data clause operation,...
Definition OpenACC.cpp:5135
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:853
Value genCast(OpBuilder &builder, Location loc, Value value, Type dstTy)
Add type casting between arith and index types when needed.
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
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:311
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
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:496
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
detail::DenseArrayAttrImpl< bool > DenseBoolArrayAttr
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.