62#include "llvm/ADT/DenseMap.h"
63#include "llvm/ADT/STLExtras.h"
67#define GEN_PASS_DEF_ACCCOMPUTELOWERING
68#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
72#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;
166static LogicalResult tryAddSizedLevel(SizedLevelMap &sizedLevelMap,
168 ParLevel level,
Value size,
174 accSupport.
emitNYI(loopOp.getLoc(),
175 "non-constant sized parallelism clause");
178 sizedLevelMap[computeOp].push_back({level, *constSize});
183static LogicalResult fillSizedLevelMap(
Operation *op, DeviceType deviceType,
184 SizedLevelMap &sizedLevelMap,
189 if (!computeOp || !isa<KernelsOp>(computeOp))
191 DeviceType loopDeviceType =
192 getGangWorkerVectorDeviceType(loopOp, deviceType);
193 if (failed(tryAddSizedLevel(
194 sizedLevelMap, computeOp, loopOp, ParLevel::vector,
195 loopOp.getVectorValue(loopDeviceType), accSupport)) ||
196 failed(tryAddSizedLevel(
197 sizedLevelMap, computeOp, loopOp, ParLevel::worker,
198 loopOp.getWorkerValue(loopDeviceType), accSupport)) ||
199 failed(tryAddSizedLevel(
200 sizedLevelMap, computeOp, loopOp, ParLevel::gang_dim1,
201 loopOp.getGangValue(GangArgType::Num, loopDeviceType), accSupport)))
205 return failure(
result.wasInterrupted());
213 DeviceType deviceType) {
214 deviceType = getGangWorkerVectorDeviceType(loopOp, deviceType);
216 auto *ctx = loopOp->getContext();
218 if (loopOp.hasVector(deviceType) || loopOp.getVectorValue(deviceType))
220 if (loopOp.hasWorker(deviceType) || loopOp.getWorkerValue(deviceType))
222 if (
auto gangDimValue = loopOp.getGangValue(GangArgType::Dim, deviceType)) {
223 if (
auto gangDimDefOp =
228 }
else if (loopOp.hasGang(deviceType) ||
229 loopOp.getGangValue(GangArgType::Num, deviceType)) {
240template <
typename ComputeConstructT>
242 ComputeConstructT computeOp, DeviceType deviceType,
RewriterBase &rewriter,
245 auto loc = computeOp->getLoc();
247 if constexpr (std::is_same_v<ComputeConstructT, SerialOp>) {
248 return {ParWidthOp::create(rewriter, loc,
Value(), policy.
seqDim(ctx))};
249 }
else if constexpr (llvm::is_one_of<ComputeConstructT, ParallelOp,
251 if (computeOp.isEffectivelySerial())
252 return {ParWidthOp::create(rewriter, loc,
Value(), policy.
seqDim(ctx))};
254 deviceType = getParDimsDeviceType(computeOp, deviceType);
259 auto numGangs = computeOp.getNumGangsValues(deviceType);
260 for (
auto [gangDimIdx, gangSize] : llvm::enumerate(numGangs)) {
262 values.push_back(ParWidthOp::create(
266 policy.
gangDim(ctx, gangLevel)));
269 Value numWorkers = computeOp.getNumWorkersValue(deviceType);
271 values.push_back(ParWidthOp::create(
274 indexTy, numWorkers),
280 values.push_back(ParWidthOp::create(
283 indexTy, vectorLength),
289 auto sizedLevels = sizedLevelMap.find(computeOp.getOperation());
290 if (sizedLevels != sizedLevelMap.end()) {
291 for (
const SizedLevel &sizedLevel : sizedLevels->second) {
292 GPUParallelDimAttr dim = policy.
map(ctx, sizedLevel.level);
293 bool exists = llvm::any_of(values, [&](
Value v) {
295 return parWidth && parWidth.getParDim() == dim;
301 values.push_back(ParWidthOp::create(rewriter, loc, sizeVal, dim));
306 llvm_unreachable(
"assignKnownLaunchArgs: expected parallel, kernels, or "
321 LogicalResult matchAndRewrite(LoopOp loopOp,
323 if (loopOp.getUnstructured()) {
328 rewriter.
replaceOp(loopOp, executeRegion);
332 LoopParMode parMode = loopOp.getDefaultOrDeviceTypeParallelism(deviceType);
334 if (parMode == LoopParMode::loop_seq || isOpInSerialRegion(loopOp)) {
341 setParDimsAttr(forOp, GPUParallelDimsAttr::seq(loopOp->getContext()));
343 }
else if (parMode == LoopParMode::loop_auto) {
345 assert(!isOpInSerialRegion(loopOp) &&
346 "Expected loop to be in non-serial region");
353 getParallelDimensions(loopOp, policy, deviceType);
354 if (!parDims.empty()) {
356 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
360 }
else if (!isOpInComputeRegion(loopOp) &&
362 loopOp->getParentOfType<FunctionOpInterface>())) {
371 assert(parMode == LoopParMode::loop_independent &&
372 "Expected loop to be independent");
377 SmallVector<GPUParallelDimAttr> parDims =
378 getParallelDimensions(loopOp, policy, deviceType);
379 if (!parDims.empty()) {
381 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
392 DeviceType deviceType;
399template <
typename ComputeConstructT>
403 DeviceType deviceType,
const SizedLevelMap &sizedLevelMap)
404 : OpRewritePattern<ComputeConstructT>(ctx), policy(policy),
405 deviceType(deviceType), sizedLevelMap(sizedLevelMap) {}
407 LogicalResult matchAndRewrite(ComputeConstructT computeOp,
408 PatternRewriter &rewriter)
const override {
411 KernelEnvironmentOp::createAndPopulate(computeOp, deviceType, rewriter);
412 auto launchArgs = assignKnownLaunchArgs(computeOp, deviceType, rewriter,
413 policy, sizedLevelMap);
414 Region ®ion = computeOp.getRegion();
417 materializeConstantLiveInsIntoRegion(region, liveInValues, rewriter);
420 computeOp->getLoc(), launchArgs, liveInValues.getArrayRef(),
421 ComputeConstructT::getOperationName(), region, rewriter, mapping);
422 if (!computeRegion) {
432 DeviceType deviceType;
433 const SizedLevelMap &sizedLevelMap;
440class ACCComputeLowering
443 using ACCComputeLoweringBase::ACCComputeLoweringBase;
445 void runOnOperation()
override {
446 auto op = getOperation();
449 DefaultACCToGPUMappingPolicy policy;
451 SizedLevelMap sizedLevelMap;
452 OpenACCSupport &accSupport = getAnalysis<OpenACCSupport>();
453 if (
failed(fillSizedLevelMap(op, deviceType, sizedLevelMap, accSupport)))
454 return signalPassFailure();
459 RewritePatternSet loopPatterns(context);
460 loopPatterns.insert<ACCLoopConversion>(context, policy, deviceType);
462 return signalPassFailure();
466 RewritePatternSet computePatterns(context);
468 .insert<ComputeOpConversion<ParallelOp>, ComputeOpConversion<KernelsOp>,
469 ComputeOpConversion<SerialOp>>(context, policy, deviceType,
472 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.
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.
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.
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 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...