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