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