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