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