MLIR 24.0.0git
ACCComputeLowering.cpp
Go to the documentation of this file.
1//===- ACCComputeLowering.cpp - Lower ACC compute to compute_region -------===//
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 pass decomposes OpenACC compute constructs into a representation that
10// separates the data environment from the compute portion and prepares for
11// parallelism assignment and privatization at the appropriate level.
12//
13// Overview:
14// ---------
15// Each compute construct (`acc.parallel`, `acc.serial`, `acc.kernels`) is
16// lowered to (1) `acc.kernel_environment`, which captures the data environment
17// and (2) `acc.compute_region`, which holds the compute body. Inside the
18// compute region, acc.loop is converted to SCF loops (`scf.parallel` or
19// `scf.for`) with any predetermined parallelism expressed as `par_dims`. This
20// decomposition allows later phases to assign parallelism and handle
21// privatization at the right granularity.
22//
23// Transformations:
24// ----------------
25// 1. Compute constructs: acc.parallel, acc.serial, and acc.kernels are
26// replaced by acc.kernel_environment containing a single acc.compute_region.
27// For acc.parallel / acc.kernels, launch arguments (num_gangs, num_workers,
28// vector_length) become acc.par_width ops (each result is `index`) and are
29// passed as compute_region launch operands. Compute regions with
30// num_gangs(1), num_workers(1), and vector_length(1) and acc serial use a
31// single sequential acc.par_width launch operand.
32//
33// 2. acc.loop: Converted according to context and attributes:
34// - Unstructured: body wrapped in scf.execute_region.
35// - Sequential (serial region, seq clause, or compute region with
36// num_gangs(1), num_workers(1), and vector_length(1)):
37// scf.parallel with par_dims = sequential.
38// - Auto (in parallel/kernels): scf.for with collapse when
39// multi-dimensional.
40// - Orphan (not inside a compute construct): scf.for, no collapse.
41// - Independent (in parallel/kernels): scf.parallel with par_dims from
42// gang/worker/vector mapping (e.g. block_x).
43//
44//===----------------------------------------------------------------------===//
45
48
58#include "mlir/IR/IRMapping.h"
59#include "mlir/IR/Matchers.h"
63#include "llvm/ADT/DenseMap.h"
64#include "llvm/ADT/STLExtras.h"
65
66namespace mlir {
67namespace acc {
68#define GEN_PASS_DEF_ACCCOMPUTELOWERING
69#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
70} // namespace acc
71} // namespace mlir
72
73#define DEBUG_TYPE "acc-compute-lowering"
74
75using namespace mlir;
76using namespace mlir::acc;
77
78namespace {
79//===----------------------------------------------------------------------===//
80// Helper functions
81//===----------------------------------------------------------------------===//
82
83static bool isOpInComputeRegion(Operation *op) {
84 Region *region = op->getBlock()->getParent();
85 return getEnclosingComputeOp(*region) != nullptr;
86}
87
88static bool isOpInSerialRegion(Operation *op) {
89 if (auto parallelOp = op->getParentOfType<ParallelOp>())
90 return parallelOp.isEffectivelySerial();
91 if (auto kernelsOp = op->getParentOfType<KernelsOp>())
92 return kernelsOp.isEffectivelySerial();
93 if (op->getParentOfType<SerialOp>())
94 return true;
95 if (auto computeRegion = op->getParentOfType<ComputeRegionOp>())
96 return computeRegion.isEffectivelySerial();
97 if (auto funcOp = op->getParentOfType<FunctionOpInterface>()) {
98 if (isSpecializedAccRoutine(funcOp)) {
99 auto attr = funcOp->getDiscardableAttrOfType<SpecializedRoutineAttr>(
101 if (attr && attr.getLevel().getValue() == ParLevel::seq)
102 return true;
103 }
104 }
105 return false;
106}
107
108/// Clone defining ops of constant live-in values into `region`, rewrite uses
109/// inside the region to the clones, and remove those values from
110/// `liveInValues` so they are not threaded through `acc.compute_region` ins.
111static void materializeConstantLiveInsIntoRegion(Region &region,
112 SetVector<Value> &liveInValues,
113 RewriterBase &rewriter) {
114 SmallVector<Value> constantLiveIns;
115 for (Value v : liveInValues) {
116 Operation *defOp = v.getDefiningOp();
117 if (defOp && matchPattern(defOp, m_Constant())) {
118 // As per the definition of ConstantLike trait, constants must have a
119 // single result.
120 assert(defOp->getNumResults() == 1 &&
121 "constants must have a single result");
122 constantLiveIns.push_back(v);
123 }
124 }
125 if (constantLiveIns.empty())
126 return;
127
128 OpBuilder::InsertionGuard guard(rewriter);
129 rewriter.setInsertionPointToStart(&region.front());
130
131 for (Value v : constantLiveIns) {
132 Value newV = rewriter.clone(*v.getDefiningOp())->getResult(0);
133 replaceAllUsesInRegionWith(v, newV, region);
134 liveInValues.remove(v);
135 }
136}
137
138/// Return the device type from which gang/worker/vector clauses should be read.
139/// If the requested device type has any such clauses, use that exclusively;
140/// otherwise fall back to the default (DeviceType::None).
141static DeviceType getGangWorkerVectorDeviceType(LoopOp loopOp,
142 DeviceType deviceType) {
143 if (deviceType != DeviceType::None &&
144 loopOp.hasAnyGangWorkerVector(deviceType))
145 return deviceType;
146 return DeviceType::None;
147}
148
149template <typename ComputeConstructT>
150static DeviceType getParDimsDeviceType(ComputeConstructT computeOp,
151 DeviceType deviceType) {
152 if (deviceType != DeviceType::None &&
153 computeOp.hasAnyGangWorkerVector(deviceType))
154 return deviceType;
155 return DeviceType::None;
156}
157
158/// Constant sized gang/worker/vector clauses collected per compute construct.
159struct SizedLevel {
160 ParLevel level;
161 int64_t size;
162};
164
165/// Record a sized clause if `size` is a constant. A non-constant vector or
166/// worker clause is ignored. Other non-constant sizes are NYI.
167static LogicalResult tryAddSizedLevel(SizedLevelMap &sizedLevelMap,
168 Operation *computeOp, LoopOp loopOp,
169 ParLevel level, Value size,
170 OpenACCSupport &accSupport) {
171 if (!size)
172 return success();
173 std::optional<int64_t> constSize = getConstantIntValue(size);
174 if (!constSize) {
175 if (level == ParLevel::vector || level == ParLevel::worker) {
176 accSupport.emitRemark(loopOp,
177 level == ParLevel::vector
178 ? "ignoring non-constant vector clause"
179 : "ignoring non-constant worker clause",
180 DEBUG_TYPE);
181 return success();
182 }
183 accSupport.emitNYI(loopOp.getLoc(),
184 "non-constant sized parallelism clause");
185 return failure();
186 }
187 sizedLevelMap[computeOp].push_back({level, *constSize});
188 return success();
189}
190
191/// Collect constant sized levels from loops in `acc.kernels` regions.
192static LogicalResult fillSizedLevelMap(Operation *op, DeviceType deviceType,
193 SizedLevelMap &sizedLevelMap,
194 OpenACCSupport &accSupport) {
195 WalkResult result = op->walk([&](LoopOp loopOp) {
196 Operation *computeOp =
197 getEnclosingComputeOp(*loopOp->getBlock()->getParent());
198 if (!computeOp || !isa<KernelsOp>(computeOp))
199 return WalkResult::advance();
200 DeviceType loopDeviceType =
201 getGangWorkerVectorDeviceType(loopOp, deviceType);
202 if (failed(tryAddSizedLevel(
203 sizedLevelMap, computeOp, loopOp, ParLevel::vector,
204 loopOp.getVectorValue(loopDeviceType), accSupport)) ||
205 failed(tryAddSizedLevel(
206 sizedLevelMap, computeOp, loopOp, ParLevel::worker,
207 loopOp.getWorkerValue(loopDeviceType), accSupport)) ||
208 failed(tryAddSizedLevel(
209 sizedLevelMap, computeOp, loopOp, ParLevel::gang_dim1,
210 loopOp.getGangValue(GangArgType::Num, loopDeviceType), accSupport)))
211 return WalkResult::interrupt();
212 return WalkResult::advance();
213 });
214 return failure(result.wasInterrupted());
215}
216
217/// Map loop parallelism clauses (gang/worker/vector) to GPU parallel
218/// dimensions using the given mapping policy. Sized clauses (e.g. vector(n))
219/// count as the corresponding level.
221getParallelDimensions(LoopOp loopOp, const ACCToGPUMappingPolicy &policy,
222 DeviceType deviceType) {
223 deviceType = getGangWorkerVectorDeviceType(loopOp, deviceType);
225 auto *ctx = loopOp->getContext();
226
227 if (loopOp.hasVector(deviceType) || loopOp.getVectorValue(deviceType))
228 insertParDim(parDims, policy.vectorDim(ctx));
229 if (loopOp.hasWorker(deviceType) || loopOp.getWorkerValue(deviceType))
230 insertParDim(parDims, policy.workerDim(ctx));
231 if (auto gangDimValue = loopOp.getGangValue(GangArgType::Dim, deviceType)) {
232 if (auto gangDimDefOp =
233 gangDimValue.getDefiningOp<arith::ConstantIntOp>()) {
234 auto gangLevel = getGangParLevel(gangDimDefOp.value());
235 insertParDim(parDims, policy.gangDim(ctx, gangLevel));
236 }
237 } else if (loopOp.hasGang(deviceType) ||
238 loopOp.getGangValue(GangArgType::Num, deviceType) ||
239 loopOp.getGangValue(GangArgType::Static, deviceType)) {
240 insertParDim(parDims, policy.gangDim(ctx, ParLevel::gang_dim1));
241 }
242 return parDims;
243}
244
245/// If \p loopOp has a `gang(static:)` value, return the corresponding
246/// `acc.chunk_size` attribute. A constant size is stored directly; a
247/// non-constant size is recorded as -1, the same encoding as `static:*`.
248/// Returns a null attribute when the clause is absent.
249static ChunkSizeAttr getChunkSizeFromStatic(LoopOp loopOp,
250 DeviceType deviceType) {
251 DeviceType loopDeviceType = getGangWorkerVectorDeviceType(loopOp, deviceType);
252 Value gangStatic = loopOp.getGangValue(GangArgType::Static, loopDeviceType);
253 if (!gangStatic)
254 return {};
255 std::optional<int64_t> chunkSize = getConstantIntValue(gangStatic);
256 // Non-constant static is treated like `static:*` (implementation-chosen).
257 return ChunkSizeAttr::get(loopOp->getContext(), chunkSize.value_or(-1));
258}
259
260/// Build `acc.compute_region` launch operands: one sequential `acc.par_width`
261/// for `acc.serial`, for `acc.parallel` / `acc.kernels` when every num_gangs
262/// operand and num_workers / vector_length are the constant 1, and otherwise
263/// `acc.par_width` from gang/worker/vector (device-type operands first, then
264/// default DeviceType::None).
265template <typename ComputeConstructT>
266static SmallVector<Value> assignKnownLaunchArgs(
267 ComputeConstructT computeOp, DeviceType deviceType, RewriterBase &rewriter,
268 const ACCToGPUMappingPolicy &policy, const SizedLevelMap &sizedLevelMap) {
269 auto *ctx = rewriter.getContext();
270 auto loc = computeOp->getLoc();
272 if constexpr (std::is_same_v<ComputeConstructT, SerialOp>) {
273 return {ParWidthOp::create(rewriter, loc, Value(), policy.seqDim(ctx))};
274 } else if constexpr (llvm::is_one_of<ComputeConstructT, ParallelOp,
275 KernelsOp>::value) {
276 if (computeOp.isEffectivelySerial())
277 return {ParWidthOp::create(rewriter, loc, Value(), policy.seqDim(ctx))};
279 deviceType = getParDimsDeviceType(computeOp, deviceType);
281 SmallVector<Value> values;
282 auto indexTy = rewriter.getIndexType();
284 auto numGangs = computeOp.getNumGangsValues(deviceType);
285 for (auto [gangDimIdx, gangSize] : llvm::enumerate(numGangs)) {
286 auto gangLevel = getGangParLevel(gangDimIdx + 1);
287 values.push_back(ParWidthOp::create(
288 rewriter, loc,
289 getValueOrCreateCastToIndexLike(rewriter, gangSize.getLoc(), indexTy,
290 gangSize),
291 policy.gangDim(ctx, gangLevel)));
292 }
293
294 Value numWorkers = computeOp.getNumWorkersValue(deviceType);
295 if (numWorkers) {
296 values.push_back(ParWidthOp::create(
297 rewriter, loc,
298 getValueOrCreateCastToIndexLike(rewriter, numWorkers.getLoc(),
299 indexTy, numWorkers),
300 policy.workerDim(ctx)));
301 }
303 Value vectorLength = computeOp.getVectorLengthValue(deviceType);
304 if (vectorLength) {
305 values.push_back(ParWidthOp::create(
306 rewriter, loc,
307 getValueOrCreateCastToIndexLike(rewriter, vectorLength.getLoc(),
308 indexTy, vectorLength),
309 policy.vectorDim(ctx)));
310 }
311
312 // Loop-level sized clauses. Skip a dim already set on the construct.
313 // Rematerialize the constant here so it dominates the compute region.
314 auto sizedLevels = sizedLevelMap.find(computeOp.getOperation());
315 if (sizedLevels != sizedLevelMap.end()) {
316 for (const SizedLevel &sizedLevel : sizedLevels->second) {
317 GPUParallelDimAttr dim = policy.map(ctx, sizedLevel.level);
318 bool exists = llvm::any_of(values, [&](Value v) {
319 auto parWidth = v.getDefiningOp<ParWidthOp>();
320 return parWidth && parWidth.getParDim() == dim;
321 });
322 if (exists)
323 continue;
324 Value sizeVal =
325 arith::ConstantIndexOp::create(rewriter, loc, sizedLevel.size);
326 values.push_back(ParWidthOp::create(rewriter, loc, sizeVal, dim));
327 }
328 }
329 return values;
330 } else {
331 llvm_unreachable("assignKnownLaunchArgs: expected parallel, kernels, or "
332 "serial");
333 }
335
336//===----------------------------------------------------------------------===//
337// Loop conversion pattern
338//===----------------------------------------------------------------------===//
339
340class ACCLoopConversion : public OpRewritePattern<LoopOp> {
341public:
342 ACCLoopConversion(MLIRContext *ctx, const ACCToGPUMappingPolicy &policy,
343 DeviceType deviceType)
344 : OpRewritePattern<LoopOp>(ctx), policy(policy), deviceType(deviceType) {}
345
346 LogicalResult matchAndRewrite(LoopOp loopOp,
347 PatternRewriter &rewriter) const override {
348 if (loopOp.getUnstructured()) {
349 // An independent loop is asserted parallel but cannot be parallelized
350 // once it is lowered as a region of blocks. Record that, so the loss
351 // can be reported instead of silently running the loop sequentially.
352 bool lostIndependence =
353 loopOp.getDefaultOrDeviceTypeParallelism(deviceType) ==
354 LoopParMode::loop_independent &&
355 !isOpInSerialRegion(loopOp) &&
356 (isOpInComputeRegion(loopOp) ||
358 loopOp->getParentOfType<FunctionOpInterface>()));
359 auto executeRegion =
361 if (!executeRegion)
362 return failure();
363 if (lostIndependence)
364 executeRegion->setDiscardableAttr(
366 rewriter.replaceOp(loopOp, executeRegion);
367 return success();
368 }
369
370 LoopParMode parMode = loopOp.getDefaultOrDeviceTypeParallelism(deviceType);
371
372 if (parMode == LoopParMode::loop_seq || isOpInSerialRegion(loopOp)) {
373 // Use scf.for with sequential loops, because the loop's parallelism is
374 // already determined.
375 auto forOp =
376 convertACCLoopToSCFFor(loopOp, rewriter, /*enableCollapse=*/true);
377 if (!forOp)
378 return failure();
379 setParDimsAttr(forOp, GPUParallelDimsAttr::seq(loopOp->getContext()));
380 rewriter.replaceOp(loopOp, forOp);
381 } else if (parMode == LoopParMode::loop_auto) {
382 // All loops in serial regions should have already been handled.
383 assert(!isOpInSerialRegion(loopOp) &&
384 "Expected loop to be in non-serial region");
385 // Mark as scf.for to allow auto-parallelization analysis later.
386 auto forOp =
387 convertACCLoopToSCFFor(loopOp, rewriter, /*enableCollapse=*/true);
388 if (!forOp)
389 return failure();
390 SmallVector<GPUParallelDimAttr> parDims =
391 getParallelDimensions(loopOp, policy, deviceType);
392 if (!parDims.empty()) {
393 auto parDimsAttr =
394 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
395 setParDimsAttr(forOp, parDimsAttr);
396 if (ChunkSizeAttr chunkSize =
397 getChunkSizeFromStatic(loopOp, deviceType))
398 setChunkSizeAttr(forOp, chunkSize);
399 }
400 rewriter.replaceOp(loopOp, forOp);
401 } else if (!isOpInComputeRegion(loopOp) &&
403 loopOp->getParentOfType<FunctionOpInterface>())) {
404 // This loop is an orphan `acc loop` but it is not in any sort
405 // of compute region. Thus it is just a sequential non-accelerator loop.
406 auto forOp =
407 convertACCLoopToSCFFor(loopOp, rewriter, /*enableCollapse=*/false);
408 if (!forOp)
409 return failure();
410 rewriter.replaceOp(loopOp, forOp);
411 } else {
412 assert(parMode == LoopParMode::loop_independent &&
413 "Expected loop to be independent");
414 auto parallelOp = convertACCLoopToSCFParallel(loopOp, rewriter);
415 if (!parallelOp)
416 return failure();
417
418 SmallVector<GPUParallelDimAttr> parDims =
419 getParallelDimensions(loopOp, policy, deviceType);
420 if (!parDims.empty()) {
421 auto parDimsAttr =
422 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
423 setParDimsAttr(parallelOp, parDimsAttr);
424 if (ChunkSizeAttr chunkSize =
425 getChunkSizeFromStatic(loopOp, deviceType))
426 setChunkSizeAttr(parallelOp, chunkSize);
427 }
428 rewriter.replaceOp(loopOp, parallelOp);
429 }
430 return success();
431 }
432
433private:
434 const ACCToGPUMappingPolicy &policy;
435 DeviceType deviceType;
436};
437
438//===----------------------------------------------------------------------===//
439// Compute construct conversion pattern
440//===----------------------------------------------------------------------===//
441
442template <typename ComputeConstructT>
443class ComputeOpConversion : public OpRewritePattern<ComputeConstructT> {
444public:
445 ComputeOpConversion(MLIRContext *ctx, const ACCToGPUMappingPolicy &policy,
446 DeviceType deviceType, const SizedLevelMap &sizedLevelMap)
447 : OpRewritePattern<ComputeConstructT>(ctx), policy(policy),
448 deviceType(deviceType), sizedLevelMap(sizedLevelMap) {}
449
450 LogicalResult matchAndRewrite(ComputeConstructT computeOp,
451 PatternRewriter &rewriter) const override {
452 rewriter.setInsertionPoint(computeOp);
453 auto kernelEnv =
454 KernelEnvironmentOp::createAndPopulate(computeOp, deviceType, rewriter);
455 auto launchArgs = assignKnownLaunchArgs(computeOp, deviceType, rewriter,
456 policy, sizedLevelMap);
457 Region &region = computeOp.getRegion();
458 SetVector<Value> liveInValues;
459 getUsedValuesDefinedAbove(region, region, liveInValues);
460 materializeConstantLiveInsIntoRegion(region, liveInValues, rewriter);
461 IRMapping mapping;
462 auto computeRegion = buildComputeRegion(
463 computeOp->getLoc(), launchArgs, liveInValues.getArrayRef(),
464 ComputeConstructT::getOperationName(), region, rewriter, mapping);
465 if (!computeRegion) {
466 rewriter.eraseOp(kernelEnv);
467 return failure();
468 }
469 rewriter.eraseOp(computeOp);
470 return success();
471 }
472
473private:
474 const ACCToGPUMappingPolicy &policy;
475 DeviceType deviceType;
476 const SizedLevelMap &sizedLevelMap;
477};
478
479//===----------------------------------------------------------------------===//
480// Pass implementation
481//===----------------------------------------------------------------------===//
482
483class ACCComputeLowering
484 : public acc::impl::ACCComputeLoweringBase<ACCComputeLowering> {
485public:
486 using ACCComputeLoweringBase::ACCComputeLoweringBase;
487
488 void getDependentDialects(DialectRegistry &registry) const override {
489 ACCComputeLoweringBase::getDependentDialects(registry);
491 }
492
493 void runOnOperation() override {
494 auto op = getOperation();
495 auto *context = op.getContext();
496
497 DefaultACCToGPUMappingPolicy policy;
498 // Collect loop sized levels before loops are rewritten away.
499 SizedLevelMap sizedLevelMap;
500 OpenACCSupport &accSupport = getAnalysis<OpenACCSupport>();
501 if (failed(fillSizedLevelMap(op, deviceType, sizedLevelMap, accSupport)))
502 return signalPassFailure();
503
504 // Part 1: Convert acc.loop to scf.parallel/scf.for while the parent
505 // compute construct is still present (needed to determine conversion
506 // strategy).
507 RewritePatternSet loopPatterns(context);
508 loopPatterns.insert<ACCLoopConversion>(context, policy, deviceType);
509 if (failed(applyPatternsGreedily(op, std::move(loopPatterns))))
510 return signalPassFailure();
511
512 // Part 2: Convert acc.parallel, acc.kernels, and acc.serial to
513 // acc.kernel_environment { acc.compute_region { ... } }.
514 RewritePatternSet computePatterns(context);
515 computePatterns
516 .insert<ComputeOpConversion<ParallelOp>, ComputeOpConversion<KernelsOp>,
517 ComputeOpConversion<SerialOp>>(context, policy, deviceType,
518 sizedLevelMap);
519 if (failed(applyPatternsGreedily(op, std::move(computePatterns))))
520 return signalPassFailure();
521 }
522};
523} // namespace
return success()
#define DEBUG_TYPE
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
UnitAttr getUnitAttr()
Definition Builders.cpp:106
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:59
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
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
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:849
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
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.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
ParDimAttrT seqDim(MLIRContext *ctx) const
virtual ParDimAttrT map(MLIRContext *ctx, ParLevel level) const =0
Map an OpenACC parallelism level to target dimension.
ParDimAttrT vectorDim(MLIRContext *ctx) const
ParDimAttrT workerDim(MLIRContext *ctx) const
ParDimAttrT gangDim(MLIRContext *ctx, ParLevel level) const
Convenience methods for specific parallelism levels.
remark::detail::InFlightRemark emitRemark(Operation *op, std::function< std::string()> messageFn, llvm::StringRef category="openacc")
Emit an OpenACC remark with lazy message generation.
InFlightDiagnostic emitNYI(Location loc, const Twine &message)
Report a case that is not yet supported by the implementation.
::mlir::Pass::Option< mlir::acc::DeviceType > deviceType
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:434
Specialization of arith.constant op that returns an integer value.
Definition Arith.h:34
ParLevel getGangParLevel(int64_t gangDimValue)
Convert a gang dimension value (1, 2, or 3) to the corresponding ParLevel.
void insertParDim(llvm::SmallVector< GPUParallelDimAttr > &parDims, GPUParallelDimAttr parDim)
Insert parDim into parDims while preserving dimension ordering.
static constexpr StringLiteral getSpecializedRoutineAttrName()
Definition OpenACC.h:207
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 setChunkSizeAttr(Operation *op, ChunkSizeAttr attr)
Set the acc.chunk_size attribute on op.
bool isSpecializedAccRoutine(mlir::Operation *op)
Used to check whether this is a specialized accelerator version of acc routine function.
Definition OpenACC.h:219
scf::ParallelOp convertACCLoopToSCFParallel(LoopOp loopOp, RewriterBase &rewriter)
Convert acc.loop to scf.parallel.
static constexpr StringLiteral getUnstructuredIndependentLoopAttrName()
Name for a unit attribute attached to the region an unstructured independent loop was converted to,...
Definition OpenACC.h:244
mlir::Operation * getEnclosingComputeOp(mlir::Region &region)
Used to obtain the enclosing compute construct operation that contains the provided region.
scf::ExecuteRegionOp convertUnstructuredACCLoopToSCFExecuteRegion(LoopOp loopOp, RewriterBase &rewriter)
Convert an unstructured acc.loop to scf.execute_region.
void registerAllExtensions(DialectRegistry &registry)
Register the standard OpenACC models for GPU, LLVM and MemRef IR.
void setParDimsAttr(Operation *op, GPUParallelDimsAttr attr)
Set parallel dimensions on op.
scf::ForOp convertACCLoopToSCFFor(LoopOp loopOp, RewriterBase &rewriter, bool enableCollapse)
Convert a structured acc.loop to scf.for.
ACCParMappingPolicy< mlir::acc::GPUParallelDimAttr > ACCToGPUMappingPolicy
Type alias for the GPU-specific mapping policy.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
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
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region &region)
Replace all uses of orig within the given region with replacement.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
LogicalResult applyPatternsGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:128
Value getValueOrCreateCastToIndexLike(OpBuilder &b, Location loc, Type targetType, Value value)
Create a cast from an index-like value (index or integer) to another index-like value.
Definition Utils.cpp:122
void getUsedValuesDefinedAbove(Region &region, Region &limit, SetVector< Value > &values)
Fill values with a list of values defined at the ancestors of the limit region and used within region...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...