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