63#include "llvm/ADT/DenseMap.h"
64#include "llvm/ADT/STLExtras.h"
68#define GEN_PASS_DEF_ACCCOMPUTELOWERING
69#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
73#define DEBUG_TYPE "acc-compute-lowering"
83static bool isOpInComputeRegion(
Operation *op) {
88static bool isOpInSerialRegion(
Operation *op) {
90 return parallelOp.isEffectivelySerial();
92 return kernelsOp.isEffectivelySerial();
96 return computeRegion.isEffectivelySerial();
99 auto attr = funcOp->getDiscardableAttrOfType<SpecializedRoutineAttr>(
101 if (attr && attr.getLevel().getValue() == ParLevel::seq)
111static void materializeConstantLiveInsIntoRegion(
Region ®ion,
115 for (
Value v : liveInValues) {
121 "constants must have a single result");
122 constantLiveIns.push_back(v);
125 if (constantLiveIns.empty())
131 for (
Value v : constantLiveIns) {
134 liveInValues.remove(v);
141static DeviceType getGangWorkerVectorDeviceType(LoopOp loopOp,
142 DeviceType deviceType) {
143 if (deviceType != DeviceType::None &&
144 loopOp.hasAnyGangWorkerVector(deviceType))
146 return DeviceType::None;
149template <
typename ComputeConstructT>
150static DeviceType getParDimsDeviceType(ComputeConstructT computeOp,
151 DeviceType deviceType) {
152 if (deviceType != DeviceType::None &&
153 computeOp.hasAnyGangWorkerVector(deviceType))
155 return DeviceType::None;
167static LogicalResult tryAddSizedLevel(SizedLevelMap &sizedLevelMap,
169 ParLevel level,
Value size,
175 if (level == ParLevel::vector || level == ParLevel::worker) {
177 level == ParLevel::vector
178 ?
"ignoring non-constant vector clause"
179 :
"ignoring non-constant worker clause",
183 accSupport.
emitNYI(loopOp.getLoc(),
184 "non-constant sized parallelism clause");
187 sizedLevelMap[computeOp].push_back({level, *constSize});
192static LogicalResult fillSizedLevelMap(
Operation *op, DeviceType deviceType,
193 SizedLevelMap &sizedLevelMap,
198 if (!computeOp || !isa<KernelsOp>(computeOp))
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)))
214 return failure(
result.wasInterrupted());
222 DeviceType deviceType) {
223 deviceType = getGangWorkerVectorDeviceType(loopOp, deviceType);
225 auto *ctx = loopOp->getContext();
227 if (loopOp.hasVector(deviceType) || loopOp.getVectorValue(deviceType))
229 if (loopOp.hasWorker(deviceType) || loopOp.getWorkerValue(deviceType))
231 if (
auto gangDimValue = loopOp.getGangValue(GangArgType::Dim, deviceType)) {
232 if (
auto gangDimDefOp =
237 }
else if (loopOp.hasGang(deviceType) ||
238 loopOp.getGangValue(GangArgType::Num, deviceType) ||
239 loopOp.getGangValue(GangArgType::Static, deviceType)) {
249static ChunkSizeAttr getChunkSizeFromStatic(LoopOp loopOp,
250 DeviceType deviceType) {
251 DeviceType loopDeviceType = getGangWorkerVectorDeviceType(loopOp, deviceType);
252 Value gangStatic = loopOp.getGangValue(GangArgType::Static, loopDeviceType);
257 return ChunkSizeAttr::get(loopOp->getContext(), chunkSize.value_or(-1));
265template <
typename ComputeConstructT>
267 ComputeConstructT computeOp, DeviceType deviceType,
RewriterBase &rewriter,
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,
276 if (computeOp.isEffectivelySerial())
277 return {ParWidthOp::create(rewriter, loc,
Value(), policy.
seqDim(ctx))};
284 auto numGangs = computeOp.getNumGangsValues(
deviceType);
285 for (
auto [gangDimIdx, gangSize] : llvm::enumerate(numGangs)) {
287 values.push_back(ParWidthOp::create(
296 values.push_back(ParWidthOp::create(
299 indexTy, numWorkers),
305 values.push_back(ParWidthOp::create(
308 indexTy, vectorLength),
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) {
320 return parWidth && parWidth.getParDim() == dim;
326 values.push_back(ParWidthOp::create(rewriter, loc, sizeVal, dim));
331 llvm_unreachable(
"assignKnownLaunchArgs: expected parallel, kernels, or "
343 DeviceType deviceType)
346 LogicalResult matchAndRewrite(LoopOp loopOp,
348 if (loopOp.getUnstructured()) {
352 bool lostIndependence =
353 loopOp.getDefaultOrDeviceTypeParallelism(deviceType) ==
354 LoopParMode::loop_independent &&
355 !isOpInSerialRegion(loopOp) &&
356 (isOpInComputeRegion(loopOp) ||
358 loopOp->getParentOfType<FunctionOpInterface>()));
363 if (lostIndependence)
364 executeRegion->setDiscardableAttr(
366 rewriter.
replaceOp(loopOp, executeRegion);
370 LoopParMode parMode = loopOp.getDefaultOrDeviceTypeParallelism(deviceType);
372 if (parMode == LoopParMode::loop_seq || isOpInSerialRegion(loopOp)) {
379 setParDimsAttr(forOp, GPUParallelDimsAttr::seq(loopOp->getContext()));
381 }
else if (parMode == LoopParMode::loop_auto) {
383 assert(!isOpInSerialRegion(loopOp) &&
384 "Expected loop to be in non-serial region");
390 SmallVector<GPUParallelDimAttr> parDims =
391 getParallelDimensions(loopOp, policy, deviceType);
392 if (!parDims.empty()) {
394 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
396 if (ChunkSizeAttr chunkSize =
397 getChunkSizeFromStatic(loopOp, deviceType))
401 }
else if (!isOpInComputeRegion(loopOp) &&
403 loopOp->getParentOfType<FunctionOpInterface>())) {
412 assert(parMode == LoopParMode::loop_independent &&
413 "Expected loop to be independent");
418 SmallVector<GPUParallelDimAttr> parDims =
419 getParallelDimensions(loopOp, policy, deviceType);
420 if (!parDims.empty()) {
422 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
424 if (ChunkSizeAttr chunkSize =
425 getChunkSizeFromStatic(loopOp, deviceType))
435 DeviceType deviceType;
442template <
typename ComputeConstructT>
446 DeviceType deviceType,
const SizedLevelMap &sizedLevelMap)
447 : OpRewritePattern<ComputeConstructT>(ctx), policy(policy),
448 deviceType(deviceType), sizedLevelMap(sizedLevelMap) {}
450 LogicalResult matchAndRewrite(ComputeConstructT computeOp,
451 PatternRewriter &rewriter)
const override {
454 KernelEnvironmentOp::createAndPopulate(computeOp, deviceType, rewriter);
455 auto launchArgs = assignKnownLaunchArgs(computeOp, deviceType, rewriter,
456 policy, sizedLevelMap);
457 Region ®ion = computeOp.getRegion();
460 materializeConstantLiveInsIntoRegion(region, liveInValues, rewriter);
463 computeOp->getLoc(), launchArgs, liveInValues.getArrayRef(),
464 ComputeConstructT::getOperationName(), region, rewriter, mapping);
465 if (!computeRegion) {
475 DeviceType deviceType;
476 const SizedLevelMap &sizedLevelMap;
483class ACCComputeLowering
486 using ACCComputeLoweringBase::ACCComputeLoweringBase;
488 void getDependentDialects(DialectRegistry ®istry)
const override {
489 ACCComputeLoweringBase::getDependentDialects(registry);
493 void runOnOperation()
override {
494 auto op = getOperation();
497 DefaultACCToGPUMappingPolicy policy;
499 SizedLevelMap sizedLevelMap;
500 OpenACCSupport &accSupport = getAnalysis<OpenACCSupport>();
501 if (
failed(fillSizedLevelMap(op, deviceType, sizedLevelMap, accSupport)))
502 return signalPassFailure();
507 RewritePatternSet loopPatterns(context);
508 loopPatterns.insert<ACCLoopConversion>(context, policy, deviceType);
510 return signalPassFailure();
514 RewritePatternSet computePatterns(context);
516 .insert<ComputeOpConversion<ParallelOp>, ComputeOpConversion<KernelsOp>,
517 ComputeOpConversion<SerialOp>>(context, policy, deviceType,
520 return signalPassFailure();
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
MLIRContext * getContext() const
MLIRContext is the top-level object for a collection of MLIR operations.
RAII guard to reset the insertion point of the builder when destroyed.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation is the basic unit of execution within MLIR.
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
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),...
MLIRContext * getContext()
Return the context this operation is associated with.
unsigned getNumResults()
Return the number of results held by this operation.
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.
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...
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
static WalkResult interrupt()
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)
Specialization of arith.constant op that returns an integer value.
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()
ComputeRegionOp buildComputeRegion(Location loc, ValueRange launchArgs, ValueRange inputArgs, llvm::StringRef origin, Region ®ionToClone, 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.
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,...
mlir::Operation * getEnclosingComputeOp(mlir::Region ®ion)
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 ®istry)
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.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region ®ion)
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 ®ion, 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
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.
void getUsedValuesDefinedAbove(Region ®ion, 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
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...