MLIR 24.0.0git
OpenACCUtilsCG.cpp
Go to the documentation of this file.
1//===- OpenACCUtilsCG.cpp - OpenACC Code Generation Utilities -------------===//
2//
3// Part of the LLVM 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//
9// This file implements utility functions for OpenACC code generation.
10//
11//===----------------------------------------------------------------------===//
12
14
22#include "mlir/IR/BuiltinOps.h"
23#include "mlir/IR/IRMapping.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/ADT/TypeSwitch.h"
29#include "llvm/Support/MathExtras.h"
30
31namespace mlir {
32namespace acc {
33
34std::optional<DataLayout> getDataLayout(Operation *op, bool allowDefault) {
35 if (!op)
36 return std::nullopt;
37
38 // Walk up the parent chain to find the nearest operation with an explicit
39 // data layout spec. Check ModuleOp explicitly since it does not actually
40 // implement DataLayoutOpInterface as a trait (it just has the same methods).
41 Operation *current = op;
42 while (current) {
43 // Check for ModuleOp with explicit data layout spec
44 if (auto mod = llvm::dyn_cast<ModuleOp>(current)) {
45 if (mod.getDataLayoutSpec())
46 return DataLayout(mod);
47 } else if (auto dataLayoutOp =
48 llvm::dyn_cast<DataLayoutOpInterface>(current)) {
49 // Check other DataLayoutOpInterface implementations
50 if (dataLayoutOp.getDataLayoutSpec())
51 return DataLayout(dataLayoutOp);
52 }
53 current = current->getParentOp();
54 }
55
56 // No explicit data layout found; return default if allowed
57 if (allowDefault) {
58 // Check if op itself is a ModuleOp
59 if (auto mod = llvm::dyn_cast<ModuleOp>(op))
60 return DataLayout(mod);
61 // Otherwise check parents
62 if (auto mod = op->getParentOfType<ModuleOp>())
63 return DataLayout(mod);
64 }
65
66 return std::nullopt;
67}
68
69ComputeRegionOp buildComputeRegion(Location loc, ValueRange launchArgs,
70 ValueRange inputArgs, llvm::StringRef origin,
71 Region &regionToClone,
72 RewriterBase &rewriter, IRMapping &mapping,
73 ValueRange output,
74 FlatSymbolRefAttr kernelFuncName,
75 FlatSymbolRefAttr kernelModuleName,
76 Value stream, ValueRange inputArgsToMap) {
77 SmallVector<Type> resultTypes;
78 for (auto val : output)
79 resultTypes.push_back(val.getType());
80 auto computeRegion =
81 ComputeRegionOp::create(rewriter, loc, resultTypes, launchArgs, inputArgs,
82 stream, origin, kernelFuncName, kernelModuleName);
83
84 assert(!regionToClone.getBlocks().empty() &&
85 "empty region for acc.compute_region");
86 OpBuilder::InsertionGuard guard(rewriter);
87
88 ValueRange mapKeys = inputArgsToMap.empty() ? inputArgs : inputArgsToMap;
89 assert(mapKeys.size() == inputArgs.size() &&
90 "inputArgsToMap must have same size as inputArgs when provided");
91
92 Type indexType = rewriter.getIndexType();
93 Block *entryBlock = rewriter.createBlock(&computeRegion.getRegion());
94 for (size_t i = 0; i < launchArgs.size(); ++i)
95 entryBlock->addArgument(indexType, loc);
96 for (Value input : inputArgs)
97 entryBlock->addArgument(input.getType(), loc);
98 for (size_t i = 0; i < inputArgs.size(); ++i)
99 mapping.map(mapKeys[i], entryBlock->getArgument(launchArgs.size() + i));
100 rewriter.setInsertionPointToStart(entryBlock);
101 if (regionToClone.getBlocks().size() == 1) {
102 for (auto &op : regionToClone.front().getOperations()) {
103 if (op.hasTrait<OpTrait::IsTerminator>())
104 break;
105 rewriter.clone(op, mapping);
106 }
107 SmallVector<Value> yieldOperands;
108 for (auto val : output)
109 yieldOperands.push_back(mapping.lookup(val));
110 rewriter.setInsertionPointToEnd(entryBlock);
111 YieldOp::create(rewriter, loc, yieldOperands);
112 } else {
114 regionToClone, mapping, loc, rewriter);
115 if (!exeRegion) {
116 rewriter.eraseOp(computeRegion);
117 return nullptr;
118 }
120 llvm::to_vector(exeRegion.getOps<scf::YieldOp>()));
121 assert(!yieldOps.empty() &&
122 "multi-block region must contain at least one scf.yield");
123 assert(llvm::all_of(yieldOps,
124 [&output](scf::YieldOp yieldOp) {
125 return yieldOp.getNumOperands() ==
126 static_cast<int64_t>(output.size()) &&
127 llvm::all_of(
128 llvm::zip(yieldOp.getOperands(), output),
129 [](auto pair) {
130 return std::get<0>(pair).getType() ==
131 std::get<1>(pair).getType();
132 });
133 }) &&
134 "each scf.yield operand count and types must match output");
135 rewriter.setInsertionPointToEnd(entryBlock);
136 YieldOp::create(rewriter, loc, exeRegion.getResults());
137 }
138
139 return computeRegion;
140}
141
144 GPUParallelDimAttr parDim) {
145 return llvm::lower_bound(
146 parDims, parDim,
147 [](const GPUParallelDimAttr &lhs, const GPUParallelDimAttr &rhs) {
148 return lhs.getOrder() > rhs.getOrder();
149 });
150}
151
153 GPUParallelDimAttr parDim) {
155 if (lb == parDims.end() || *lb != parDim)
156 parDims.insert(lb, parDim);
157}
158
160 GPUParallelDimAttr parDim) {
162 if (lb != parDims.end() && *lb == parDim)
163 parDims.erase(lb);
164}
165
166#define ACC_OP_WITH_PAR_DIMS_LIST \
167 PrivatizeOp, ReductionAccumulateOp, ReductionAccumulateArrayOp, \
168 ReductionCombineOp
169
170GPUParallelDimsAttr getParDimsAttr(Operation *op) {
173 [](auto parOp) { return parOp.getParDimsAttr(); })
174 .Default([](Operation *op) -> GPUParallelDimsAttr {
175 if (Attribute attr =
176 op->getDiscardableAttr(GPUParallelDimsAttr::name)) {
177 GPUParallelDimsAttr parDimsAttr = dyn_cast<GPUParallelDimsAttr>(attr);
178 assert(parDimsAttr && "acc.par_dims must be a GPUParallelDimsAttr");
179 return parDimsAttr;
180 }
181 return nullptr;
182 });
183}
184
185bool hasParDimsAttr(Operation *op) { return getParDimsAttr(op) != nullptr; }
186
188 if (GPUParallelDimsAttr parDimsAttr = getParDimsAttr(op))
189 return parDimsAttr.isSeq();
190 return false;
191}
192
193void setParDimsAttr(Operation *op, GPUParallelDimsAttr attr) {
194 assert(!hasParDimsAttr(op) && "parallel dimensions attribute is already set");
197 [&](auto parOp) { parOp.setParDimsAttr(attr); })
198 .Default([&](Operation *op) {
199 op->setDiscardableAttr(GPUParallelDimsAttr::name, attr);
200 });
201}
202
203void updateParDimsAttr(Operation *op, GPUParallelDimsAttr attr) {
204 assert(hasParDimsAttr(op) &&
205 "expected parallel dimensions attribute to already be set");
208 [&](auto parOp) { parOp.setParDimsAttr(attr); })
209 .Default([&](Operation *op) {
210 op->setDiscardableAttr(GPUParallelDimsAttr::name, attr);
211 });
212}
213
214#undef ACC_OP_WITH_PAR_DIMS_LIST
215
217 return op->hasDiscardableAttrOfType<GPUBlockRedundantAttr>(
218 GPUBlockRedundantAttr::name);
219}
220
222 op->setDiscardableAttr(GPUBlockRedundantAttr::name,
223 GPUBlockRedundantAttr::get(op->getContext()));
224}
225
227 assert(hasParDimsAttr(from) &&
228 "expected parallel dimensions attribute to already be set");
230}
231
232ActiveParDimsAttr getActiveParDimsAttr(Operation *op) {
233 return op->getDiscardableAttrOfType<ActiveParDimsAttr>(
234 ActiveParDimsAttr::name);
235}
236
238 return getActiveParDimsAttr(op) != nullptr;
239}
240
241void setActiveParDimsAttr(Operation *op, ActiveParDimsAttr attr) {
242 op->setDiscardableAttr(ActiveParDimsAttr::name, attr);
243}
244
246 setActiveParDimsAttr(op, ActiveParDimsAttr::get(op->getContext(), dims));
247}
248
250 assert(alignment > 0 && llvm::isPowerOf2_64(alignment) &&
251 "alignment must be a power of two");
252 return (offset + alignment - 1) & ~(alignment - 1);
253}
254
256 int64_t aligned = alignOffset(bytesUsed_, alignment);
257 if (aligned + bytes > maxTotalBytes_) {
258 return false;
259 }
260 bytesUsed_ = aligned + bytes;
261 return true;
262}
263
265 int64_t total = 0;
266 region.walk([&](GPUSharedMemoryOp op) {
267 int64_t upperBound = op.getStaticUpperBoundBytes();
268 total = SharedMemoryBudget::alignOffset(total) + upperBound;
269 });
270 return total;
271}
272
273PrivatizeOp getPrivatizeOp(PrivateLocalOp privateLocal,
274 ComputeRegionOp computeRegion) {
275 Value value = privateLocal.getPrivatized();
276 if (BlockArgument blockArg = dyn_cast<BlockArgument>(value)) {
277 auto owner = dyn_cast<ComputeRegionOp>(blockArg.getOwner()->getParentOp());
278 value = (owner ? owner : computeRegion).getOperand(blockArg);
279 }
280 PrivatizeOp privatizeOp = value.getDefiningOp<PrivatizeOp>();
281 assert(privatizeOp && "expected privatize op to be the defining op");
282 return privatizeOp;
283}
284
285static bool isThreadXPrivatize(PrivatizeOp privatize) {
286 if (GPUParallelDimsAttr parDimsAttr = privatize.getParDimsAttr())
287 return llvm::any_of(parDimsAttr.getArray(),
288 [](GPUParallelDimAttr d) { return d.isThreadX(); });
289 return false;
290}
291
292MemRefType getPrivateBaseMemRefType(Type baseTy, ModuleOp module) {
293 auto memrefTy = cast<PointerLikeType>(baseTy).getAsMemRefType(module);
294 assert(memrefTy && "private base type must be convertible to memref");
295 return memrefTy;
296}
297
299collectPrivateLocalParDims(PrivateLocalOp privateLocal,
300 ComputeRegionOp computeRegion) {
302 // Walk the enclosing scf.parallel loops, but stop at the compute region
303 // boundary: loops outside the compute region do not contribute parallel
304 // dimensions to this privatization.
305 auto parentLoop = privateLocal->getParentOfType<scf::ParallelOp>();
306 while (parentLoop && computeRegion->isProperAncestor(parentLoop)) {
307 if (GPUParallelDimsAttr parDimsAttr = getParDimsAttr(parentLoop))
308 for (GPUParallelDimAttr parDim : parDimsAttr.getArray())
309 insertParDim(parDims, parDim);
310 parentLoop = parentLoop->getParentOfType<scf::ParallelOp>();
311 }
312 if (GPUParallelDimsAttr parDimsAttr = getParDimsAttr(computeRegion))
313 for (GPUParallelDimAttr parDim : parDimsAttr.getArray())
314 insertParDim(parDims, parDim);
315 if (parDims.empty()) {
316 for (GPUParallelDimAttr parDim : computeRegion.getLaunchParDims()) {
317 if (parDim.isAnyBlock())
318 insertParDim(parDims, parDim);
319 }
320 }
321
322 for (Operation *user : privateLocal.getResult().getUsers()) {
323 if (auto accumulateOp = dyn_cast<ReductionAccumulateOp>(user)) {
324 if (accumulateOp.getMemref() == privateLocal.getResult())
325 for (GPUParallelDimAttr parDim : accumulateOp.getParDims().getArray())
326 insertParDim(parDims, parDim);
327 }
328 if (auto combineOp = dyn_cast<ReductionCombineOp>(user)) {
329 if (combineOp.getSrcMemref() == privateLocal.getResult())
330 for (GPUParallelDimAttr parDim : getReductionCombineParDims(combineOp))
331 insertParDim(parDims, parDim);
332 }
333 if (auto combineRegionOp = dyn_cast<ReductionCombineRegionOp>(user)) {
334 if (combineRegionOp.getSrcVar() == privateLocal.getResult())
335 for (GPUParallelDimAttr parDim :
336 getReductionCombineParDims(combineRegionOp))
337 insertParDim(parDims, parDim);
338 }
339 }
340 return parDims;
341}
342
343static FailureOr<std::optional<int64_t>> getWorkerPrivateSharedMemoryNumCopies(
344 PrivateLocalOp privateLocal, ComputeRegionOp computeRegion,
345 bool isWorkerPrivate, OpenACCSupport *support) {
346 if (!isWorkerPrivate)
347 return std::optional<int64_t>(1);
348
349 GPUParallelDimAttr threadY =
350 GPUParallelDimAttr::threadYDim(privateLocal.getContext());
351 std::optional<Value> workerArg = computeRegion.getKnownLaunchArg(threadY);
352 if (!workerArg)
353 return std::optional<int64_t>();
354
355 auto workerArgConst = workerArg->getDefiningOp<arith::ConstantIndexOp>();
356 if (workerArgConst)
357 return std::optional<int64_t>(workerArgConst.value());
358
359 FailureOr<int64_t> workerArgBound =
361 *workerArg);
362 if (succeeded(workerArgBound))
363 return std::optional<int64_t>(*workerArgBound);
364
365 if (support) {
366 (void)support->emitNYI(privateLocal.getLoc(),
367 "worker-private variables in shared memory "
368 "require compile-time constant num_workers");
369 return failure();
370 }
371 return std::optional<int64_t>();
372}
373
375 auto funcOp = op->getParentOfType<FunctionOpInterface>();
376 return funcOp && isSpecializedAccRoutine(funcOp);
377}
378
380 PrivateLocalOp privateLocal, ComputeRegionOp computeRegion, ModuleOp module,
381 const ACCToGPUMappingPolicy &policy, OpenACCSupport *support) {
382 if (isInsideACCSpecializedRoutine(computeRegion))
383 return false;
384
385 if (isThreadXPrivatize(getPrivatizeOp(privateLocal, computeRegion)))
386 return false;
387
388 bool isReductionAccumulator =
389 llvm::any_of(privateLocal.getResult().getUsers(), [](Operation *user) {
390 return isa<ReductionAccumulateOp>(user);
391 });
392
394 collectPrivateLocalParDims(privateLocal, computeRegion);
395 bool isGangPrivate =
396 llvm::any_of(parDims, [&](auto parDim) { return policy.isGang(parDim); });
397 bool isWorkerPrivate = llvm::any_of(
398 parDims, [&](auto parDim) { return policy.isWorker(parDim); });
399 bool isVectorPrivate = llvm::any_of(
400 parDims, [&](auto parDim) { return policy.isVector(parDim); });
401
402 auto baseTy = getPrivateBaseMemRefType(
403 cast<PrivateType>(privateLocal.getPrivatized().getType()).getBaseTy(),
404 module);
405
406 bool isBlockLevelPrivate =
407 !isVectorPrivate &&
408 (isGangPrivate ||
409 (isWorkerPrivate && baseTy.getRank() > 0 && !isReductionAccumulator));
410 if (!isBlockLevelPrivate)
411 return false;
412
413 for (int64_t dim : baseTy.getShape())
414 if (dim == ShapedType::kDynamic)
415 return false;
416
417 auto resultMemRefTy = dyn_cast<MemRefType>(privateLocal.getType());
418 if (!resultMemRefTy || !resultMemRefTy.getLayout().isIdentity() ||
419 resultMemRefTy.getMemorySpace())
420 return false;
421
422 if (isGangPrivate && isWorkerPrivate && !isReductionAccumulator)
423 return false;
424
425 FailureOr<std::optional<int64_t>> numCopies =
426 getWorkerPrivateSharedMemoryNumCopies(privateLocal, computeRegion,
427 isWorkerPrivate, support);
428 if (failed(numCopies))
429 return failure();
430 return numCopies->has_value();
431}
432
434 PrivateLocalOp privateLocal, ComputeRegionOp computeRegion, ModuleOp module,
435 const ACCToGPUMappingPolicy &policy, OpenACCSupport *support) {
436 FailureOr<bool> isCandidate = isPrivateLocalSharedMemoryCandidate(
437 privateLocal, computeRegion, module, policy);
438 if (failed(isCandidate) || !*isCandidate)
439 return std::nullopt;
440
442 collectPrivateLocalParDims(privateLocal, computeRegion);
443 bool isWorkerPrivate = llvm::any_of(
444 parDims, [&](auto parDim) { return policy.isWorker(parDim); });
445
446 FailureOr<std::optional<int64_t>> numCopies =
448 privateLocal, computeRegion, isWorkerPrivate, /*support=*/nullptr);
449 if (failed(numCopies) || !numCopies->has_value())
450 return std::nullopt;
451
452 auto baseTy = getPrivateBaseMemRefType(
453 cast<PrivateType>(privateLocal.getPrivatized().getType()).getBaseTy(),
454 module);
455 std::optional<TypeSizeAndAlignment> elementSizeAndAlignment =
456 getTypeSizeAndAlignment(baseTy.getElementType(), module, support);
457 if (!elementSizeAndAlignment)
458 return std::nullopt;
459
460 int64_t numElements = 1;
461 for (int64_t dim : baseTy.getShape())
462 numElements *= dim;
463 return elementSizeAndAlignment->first.getFixedValue() * numElements *
464 numCopies->value();
465}
466
467bool hasAttachPoint(Operation *mapEntryOp) {
468 if (!mapEntryOp)
469 return false;
470 if (auto mapInfo = dyn_cast<MapInfoOp>(mapEntryOp))
471 return mapInfo.getVarPtrPtr() != nullptr;
472 if (isa<AttachOp>(mapEntryOp))
473 return true;
474 if (std::optional<DataClause> clause = getDataClause(mapEntryOp)) {
475 if (*clause == DataClause::acc_attach || *clause == DataClause::acc_detach)
476 return true;
477 }
478 return getVarPtrPtr(mapEntryOp) != nullptr;
479}
480
481DataDescKind getDataDescKind(Operation *mapEntryOp) {
482 if (auto mapInfo = dyn_cast<MapInfoOp>(mapEntryOp))
483 return mapInfo.getDescKind();
484 return DataDescKind::none;
485}
486
487Value getDesc(Operation *mapEntryOp) {
488 auto mapInfo = dyn_cast<MapInfoOp>(mapEntryOp);
489 if (!mapInfo)
490 return {};
491 if (Value desc = mapInfo.getDesc())
492 return desc;
493 // When the mapped var is itself the descriptor, map_info omits a redundant
494 // `desc` operand; recover it from `var` whenever a descriptor kind is set.
495 if (mapInfo.getDescKind() != DataDescKind::none)
496 return mapInfo.getVar();
497 return {};
498}
499
500std::optional<int64_t> getMapElementSize(Operation *mapEntryOp) {
501 if (auto mapInfo = dyn_cast<MapInfoOp>(mapEntryOp))
502 if (auto attr = mapInfo.getElementSizeAttr())
503 return attr.getInt();
504 return std::nullopt;
505}
506
508 if (auto mapInfo = dyn_cast<MapInfoOp>(mapEntryOp))
509 return mapInfo.getSize();
510 return {};
511}
512
513std::optional<MapFlags> getMapFlags(Operation *mapEntryOp) {
514 if (auto mapInfo = dyn_cast<MapInfoOp>(mapEntryOp))
515 return mapInfo.getMapFlags();
516 return std::nullopt;
517}
518
521 for (OpOperand &use : entryResult.getUses()) {
522 Operation *op = use.getOwner();
523 if (!isa<ACC_DATA_EXIT_OPS>(op))
524 continue;
525 Value accVar;
527 [&](auto exit) { accVar = exit.getAccVar(); });
528 // The entry result can also be used as another operand, such as async.
529 if (accVar == entryResult)
530 exitOps.push_back(op);
531 }
532 return exitOps;
533}
534
536 SmallVector<Operation *> exitOps = getPairedDataExitOps(entryResult);
537 return exitOps.empty() ? nullptr : exitOps.front();
538}
539
540static std::optional<DataClause> getExitDataClause(Operation *exitOp) {
542 .Case<ACC_DATA_EXIT_OPS>([&](auto exit) { return exit.getDataClause(); })
543 .Default([&](Operation *) { return std::nullopt; });
544}
545
547 auto getMappedVar = [](Operation *op) {
548 Value var = getVar(op);
549 return var ? var : getVarPtr(op);
550 };
551 Value var = getMappedVar(entryOp);
552 if (!var)
553 return false;
554
555 auto copiesOutOnly = [&](Value sibling) {
556 Operation *siblingOp = sibling.getDefiningOp();
557 if (!siblingOp || getMappedVar(siblingOp) != var)
558 return false;
559 if (std::optional<MapFlags> siblingFlags = getMapFlags(siblingOp))
560 return bitEnumContainsAny(*siblingFlags, MapFlags::from) &&
561 !bitEnumContainsAny(*siblingFlags, MapFlags::to);
562 std::optional<DataClause> siblingClause = getDataClause(siblingOp);
563 return siblingClause && (*siblingClause == DataClause::acc_copyout ||
564 *siblingClause == DataClause::acc_copyout_zero);
565 };
566
567 Value entryResult = entryOp->getResult(0);
568 auto mapsSameVarOnConstruct = [&](auto construct) {
569 return llvm::any_of(construct.getDataClauseOperands(), [&](Value sibling) {
570 return sibling != entryResult && copiesOutOnly(sibling);
571 });
572 };
573 return llvm::any_of(entryResult.getUsers(), [&](Operation *user) {
574 return llvm::TypeSwitch<Operation *, bool>(user)
575 .Case<KernelEnvironmentOp, KernelsOp, ParallelOp, SerialOp, DataOp>(
576 mapsSameVarOnConstruct)
577 .Default(false);
578 });
579}
580
581static DataClauseModifier getEntryModifiers(Operation *entryOp) {
583 .Case<ACC_DATA_ENTRY_OPS>(
584 [&](auto entry) { return entry.getModifiers(); })
585 .Default([&](Operation *) { return DataClauseModifier::none; });
586}
587
588MapFlags computePrivatizeMapFlags(PrivatizeOp privatizeOp,
589 const ACCToGPUMappingPolicy &policy) {
590 MapFlags flags = MapFlags::private_;
591
592 // Storage is private without being replicated per parallel level when the
593 // privatization does not name any parallel dimension.
594 GPUParallelDimsAttr parDims = privatizeOp.getParDimsAttr();
595 if (!parDims)
596 return flags;
597
598 for (GPUParallelDimAttr parDim : parDims.getArray()) {
599 if (policy.isGang(parDim))
600 flags = flags | MapFlags::gang_private;
601 else if (policy.isWorker(parDim))
602 flags = flags | MapFlags::worker_private;
603 else if (policy.isVector(parDim))
604 flags = flags | MapFlags::vector_private;
605 }
606 return flags;
607}
608
609MapFlags computeDataClauseMapFlags(Operation *entryOp, bool ptrAndObj) {
610 MapFlags flags = MapFlags::none;
611 std::optional<DataClause> enterClause = getDataClause(entryOp);
612 if (!enterClause)
613 return flags;
614
615 switch (*enterClause) {
616 case DataClause::acc_create:
617 case DataClause::acc_copyout:
618 case DataClause::acc_present:
619 case DataClause::acc_private:
620 case DataClause::acc_firstprivate:
621 case DataClause::acc_delete:
622 case DataClause::acc_update_host:
623 case DataClause::acc_update_self:
624 case DataClause::acc_declare_device_resident:
625 if (*enterClause == DataClause::acc_declare_device_resident)
626 flags = flags | MapFlags::device_resident;
627 if (*enterClause == DataClause::acc_present)
628 flags = flags | MapFlags::present;
629 if (*enterClause == DataClause::acc_private ||
630 *enterClause == DataClause::acc_firstprivate)
631 flags = flags | MapFlags::private_;
632 if (*enterClause == DataClause::acc_firstprivate)
633 flags = flags | MapFlags::to;
634 break;
635 case DataClause::acc_deviceptr:
636 flags = flags | MapFlags::devptr;
637 break;
638 case DataClause::acc_create_zero:
639 case DataClause::acc_copyout_zero:
640 flags = flags | MapFlags::init_zero;
641 break;
642 case DataClause::acc_copy:
643 case DataClause::acc_copyin:
644 case DataClause::acc_copyin_readonly:
645 case DataClause::acc_reduction:
646 case DataClause::acc_update_device:
647 flags = flags | MapFlags::to;
648 break;
649 case DataClause::acc_no_create:
650 flags = flags | MapFlags::no_create;
651 break;
652 case DataClause::acc_attach:
653 break;
654 default:
655 break;
656 }
657 if (*enterClause == DataClause::acc_reduction)
658 flags = flags | MapFlags::reduction;
659
660 std::optional<DataClause> exitClause;
661 if (Operation *exitOp = findCorrespondingDataExit(entryOp->getResult(0)))
662 exitClause = getExitDataClause(exitOp);
663 if (exitClause) {
664 switch (*exitClause) {
665 case DataClause::acc_copy:
666 case DataClause::acc_reduction:
667 case DataClause::acc_copyout:
668 case DataClause::acc_copyout_zero:
669 case DataClause::acc_update_host:
670 case DataClause::acc_update_self:
671 flags = flags | MapFlags::from;
672 break;
673 case DataClause::acc_declare_device_resident:
674 flags = flags | MapFlags::device_resident;
675 break;
676 case DataClause::acc_present:
677 flags = flags | MapFlags::present;
678 break;
679 // `delete` only decrements the dynamic reference counter, so it must not
680 // request a forced unmap: the device copy has to survive while an
681 // enclosing region still references it. Only `finalize` zeroes the counter,
682 // and that is handled from the exit_data op below.
683 case DataClause::acc_delete:
684 break;
685 // An exit that repeats its entry clause only releases the device copy.
686 case DataClause::acc_create:
687 case DataClause::acc_create_zero:
688 case DataClause::acc_copyin:
689 case DataClause::acc_copyin_readonly:
690 if (hasCopyOutSibling(entryOp))
691 flags = flags | MapFlags::from;
692 break;
693 default:
694 break;
695 }
696 if (*exitClause == DataClause::acc_reduction)
697 flags = flags | MapFlags::reduction;
698 }
699
700 if (ptrAndObj)
701 flags = flags | MapFlags::ptr_and_obj;
702 if (getImplicitFlag(entryOp))
703 flags = flags | MapFlags::implicit;
704 if (bitEnumContainsAny(getEntryModifiers(entryOp), DataClauseModifier::zero))
705 flags = flags | MapFlags::init_zero;
706
707 for (OpOperand &use : entryOp->getResult(0).getUses()) {
708 if (auto exitDataOp = dyn_cast<ExitDataOp>(use.getOwner())) {
709 if (exitDataOp.getFinalize())
710 flags = flags | MapFlags::delete_;
711 }
712 if (auto updateOp = dyn_cast<UpdateOp>(use.getOwner())) {
713 if (updateOp.getIfPresent())
714 flags = flags | MapFlags::if_present;
715 }
716 }
717
718 return flags;
719}
720
721int64_t computeMapInfoSizeBytes(Value var, Type varType, DataDescKind descKind,
722 ValueRange bounds, const DataLayout &dataLayout,
723 OpenACCSupport *support) {
724 // Bounds-driven and descriptor-driven maps report size 0: the extents and
725 // element size already state the size, and restating it here could disagree.
726 if (!bounds.empty() || descKind != DataDescKind::none)
727 return 0;
728
729 ModuleOp module;
730 if (Operation *def = var.getDefiningOp())
731 module = def->getParentOfType<ModuleOp>();
732 else if (Region *region = var.getParentRegion())
733 if (Operation *parent = region->getParentOp())
734 module = parent->getParentOfType<ModuleOp>();
735 if (!module)
736 return -1;
737
738 auto tryUtilsSize = [&](Type ty) -> std::optional<int64_t> {
739 std::optional<TypeSizeAndAlignment> sizeAndAlign =
740 getTypeSizeAndAlignment(ty, module, dataLayout, support, var);
741 if (!sizeAndAlign || sizeAndAlign->first.isScalable())
742 return std::nullopt;
743 return static_cast<int64_t>(sizeAndAlign->first.getFixedValue());
744 };
745 if (std::optional<int64_t> size = tryUtilsSize(varType))
746 return *size;
747 if (std::optional<int64_t> size = tryUtilsSize(var.getType()))
748 return *size;
749
750 return -1;
751}
752
754 OpBuilder &builder) {
755 if (shape.size() != bounds.size())
756 return;
757 for (auto [boundValue, extent] : llvm::zip_equal(bounds, shape)) {
758 auto bound = boundValue.getDefiningOp<DataBoundsOp>();
759 if (!bound || bound.getSourceExtent() || extent < 0)
760 continue;
761 OpBuilder::InsertionGuard guard(builder);
762 builder.setInsertionPoint(bound);
763 Value sourceExtent =
764 arith::ConstantIndexOp::create(builder, bound.getLoc(), extent);
765 bound.getSourceExtentMutable().assign(sourceExtent);
766 }
767}
768
769} // namespace acc
770} // namespace mlir
#define ACC_OP_WITH_PAR_DIMS_LIST
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument getArgument(unsigned i)
Definition Block.h:153
OpListType & getOperations()
Definition Block.h:161
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
IndexType getIndexType()
Definition Builders.cpp:59
The main mechanism for performing data layout queries.
A symbol reference with a reference path containing a single element.
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
auto lookup(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:72
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
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
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
This class represents an operand of an operation.
Definition Value.h:254
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool hasDiscardableAttrOfType(NameT &&name)
Definition Operation.h:506
Attribute getDiscardableAttr(StringRef name)
Access a discardable attribute by name, returns a null Attribute if the discardable attribute does no...
Definition Operation.h:485
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
Definition Operation.h:512
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
AttrClass getDiscardableAttrOfType(StringRef name)
Access a discardable attribute by name and cast it to AttrClass.
Definition Operation.h:493
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
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
BlockListType & getBlocks()
Definition Region.h:45
RetT walk(FnT &&callback)
Walk all nested operations, blocks or regions (including this region), depending on the type of callb...
Definition Region.h:312
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
static FailureOr< int64_t > computeConstantBound(presburger::BoundType type, const Variable &var, const StopConditionFn &stopCondition=nullptr, ValueBoundsOptions options={})
Compute a constant bound for the given variable.
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
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
user_range getUsers() const
Definition Value.h:218
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition Value.cpp:39
virtual bool isWorker(ParDimAttrT attr) const =0
Check if the attribute represents worker parallelism.
virtual bool isVector(ParDimAttrT attr) const =0
Check if the attribute represents vector parallelism.
virtual bool isGang(ParDimAttrT attr) const =0
Check if the attribute represents gang parallelism (any gang dimension).
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
bool tryAllocate(int64_t bytes, int64_t alignment=kDefaultAlignmentBytes)
Reserve bytes, rounding the current offset up to alignment first.
static int64_t alignOffset(int64_t offset, int64_t alignment=kDefaultAlignmentBytes)
Round offset up to the next multiple of alignment, which must be a power of two.
Specialization of arith.constant op that returns an integer of index type.
Definition Arith.h:93
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
#define ACC_DATA_ENTRY_OPS
Definition OpenACC.h:49
#define ACC_DATA_EXIT_OPS
Definition OpenACC.h:59
MapFlags computePrivatizeMapFlags(PrivatizeOp privatizeOp, const ACCToGPUMappingPolicy &policy)
Compute the private and parallel-level map flags for privatized storage.
SmallVector< Operation * > getPairedDataExitOps(Value entryResult)
Returns the data exit operations paired with the data entry result entryResult, which take it as thei...
GPUParallelDimsAttr getParDimsAttr(Operation *op)
Obtain the parallel dimensions carried by op, if any.
static std::optional< DataClause > getExitDataClause(Operation *exitOp)
std::optional< DataLayout > getDataLayout(Operation *op, bool allowDefault=true)
Get the data layout for an operation.
std::optional< int64_t > getMapElementSize(Operation *mapEntryOp)
Returns element size in bytes from acc.map_info, if present.
MemRefType getPrivateBaseMemRefType(Type baseTy, ModuleOp module)
Returns the ranked MemRef type used to allocate privatized storage.
SmallVector< GPUParallelDimAttr > getReductionCombineParDims(ReductionCombineOp op)
Returns the parallel dimensions that participate in op's combine step.
void setActiveParDimsAttr(Operation *op, ActiveParDimsAttr attr)
Set active parallel dimensions on op.
mlir::Value getVar(mlir::Operation *accDataClauseOp)
Used to obtain the var from a data clause operation.
Definition OpenACC.cpp:5368
void insertParDim(llvm::SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Insert parDim into parDims while preserving dimension ordering.
bool hasActiveParDimsAttr(Operation *op)
Return whether op carries active parallel dimensions.
std::optional< mlir::acc::DataClause > getDataClause(mlir::Operation *accDataEntryOp)
Used to obtain the dataClause from a data entry operation.
Definition OpenACC.cpp:5477
bool hasParDimsAttr(Operation *op)
Return whether op carries parallel dimensions.
static Operation * findCorrespondingDataExit(Value entryResult)
MapFlags computeDataClauseMapFlags(Operation *entryOp, bool ptrAndObj)
Fold enter (+ paired exit) data-clause semantics into offload map flags.
bool hasCopyOutSibling(Operation *entryOp)
True when another data clause of the same construct maps the same variable with a copy-back and no co...
Value getMapSize(Operation *mapEntryOp)
Returns the optional size operand from acc.map_info, or null.
ComputeRegionOp buildComputeRegion(Location loc, ValueRange launchArgs, ValueRange inputArgs, llvm::StringRef origin, Region &regionToClone, RewriterBase &rewriter, IRMapping &mapping, ValueRange output={}, FlatSymbolRefAttr kernelFuncName={}, FlatSymbolRefAttr kernelModuleName={}, Value stream={}, ValueRange inputArgsToMap={})
Build an acc.compute_region operation by cloning a source region.
void setGPUBlockRedundantAttr(Operation *op)
Mark op with the acc.gpu_block_redundant attribute.
static FailureOr< std::optional< int64_t > > getWorkerPrivateSharedMemoryNumCopies(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion, bool isWorkerPrivate, OpenACCSupport *support)
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
Definition OpenACC.h:201
static bool isInsideACCSpecializedRoutine(Operation *op)
FailureOr< bool > isPrivateLocalSharedMemoryCandidate(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion, ModuleOp module, const ACCToGPUMappingPolicy &policy, OpenACCSupport *support=nullptr)
True when privateLocal may be placed in shared memory.
int64_t sumExistingSharedMemoryBytes(Region &region)
Sum aligned static_upper_bound_bytes for all acc.gpu_shared_memory in region.
scf::ExecuteRegionOp wrapMultiBlockRegionWithSCFExecuteRegion(Region &region, IRMapping &mapping, Location loc, RewriterBase &rewriter)
Wrap a multi-block region in an scf.execute_region.
void updateParDimsAttr(Operation *op, GPUParallelDimsAttr attr)
Update parallel dimensions on op.
DataDescKind getDataDescKind(Operation *mapEntryOp)
Returns descriptor kind from acc.map_info, or none for other ops.
bool getImplicitFlag(mlir::Operation *accDataEntryOp)
Used to find out whether data operation is implicit.
Definition OpenACC.cpp:5487
Value getDesc(Operation *mapEntryOp)
Returns descriptor value from acc.map_info.
void populateSourceExtents(ValueRange bounds, ArrayRef< int64_t > shape, OpBuilder &builder)
Record known extents of the source array on bounds that may describe a section.
mlir::Value getVarPtrPtr(mlir::Operation *accDataClauseOp)
Used to obtain the varPtrPtr from a data clause operation.
Definition OpenACC.cpp:5409
PrivatizeOp getPrivatizeOp(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion)
Resolve the acc.privatize operation associated with a private local.
bool hasSeqParDims(Operation *op)
Return whether op carries sequential parallel dimensions.
void copyParDimsAttr(Operation *from, Operation *to)
Copy parallel dimensions from from to to.
bool hasGPUBlockRedundantAttr(Operation *op)
Return whether op is marked with the acc.gpu_block_redundant attribute, i.e.
void removeParDim(llvm::SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Remove parDim from parDims if present.
void setParDimsAttr(Operation *op, GPUParallelDimsAttr attr)
Set parallel dimensions on op.
ActiveParDimsAttr getActiveParDimsAttr(Operation *op)
Obtain the active parallel dimensions carried by op, if any.
std::optional< int64_t > getPrivateLocalSharedMemoryUpperBoundBytes(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion, ModuleOp module, const ACCToGPUMappingPolicy &policy, OpenACCSupport *support=nullptr)
Upper-bound byte size for a shared-memory private_local candidate, or std::nullopt when not eligible ...
static bool isThreadXPrivatize(PrivatizeOp privatize)
std::optional< MapFlags > getMapFlags(Operation *mapEntryOp)
Returns offload map-type flags from acc.map_info, if present.
int64_t computeMapInfoSizeBytes(Value var, Type varType, DataDescKind descKind, ValueRange bounds, const DataLayout &dataLayout, OpenACCSupport *support=nullptr)
Compute total mapped byte size for acc.map_info.
static SmallVector< GPUParallelDimAttr >::iterator findParDim(SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
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
SmallVector< GPUParallelDimAttr > collectPrivateLocalParDims(PrivateLocalOp privateLocal, ComputeRegionOp computeRegion)
Collect parallel dimensions that govern privatization of privateLocal.
bool hasAttachPoint(Operation *mapEntryOp)
Returns true when mapEntryOp carries an attach point (varPtrPtr).
ACCParMappingPolicy< mlir::acc::GPUParallelDimAttr > ACCToGPUMappingPolicy
Type alias for the GPU-specific mapping policy.
static DataClauseModifier getEntryModifiers(Operation *entryOp)
std::optional< TypeSizeAndAlignment > getTypeSizeAndAlignment(Type ty, ModuleOp module, const DataLayout &dl, OpenACCSupport *support=nullptr, Value var={})
Returns the size and ABI alignment in bytes.
Include the generated interface declarations.