61#include "llvm/ADT/STLExtras.h"
65#define GEN_PASS_DEF_ACCCOMPUTELOWERING
66#include "mlir/Dialect/OpenACC/Transforms/Passes.h.inc"
70#define DEBUG_TYPE "acc-compute-lowering"
81static bool isOpInComputeRegion(
Operation *op) {
86static bool isOpInSerialRegion(
Operation *op) {
88 return parallelOp.isEffectivelySerial();
90 return kernelsOp.isEffectivelySerial();
94 return computeRegion.isEffectivelySerial();
97 auto attr = funcOp->getAttrOfType<SpecializedRoutineAttr>(
99 if (attr && attr.getLevel().getValue() == ParLevel::seq)
109static void materializeConstantLiveInsIntoRegion(
Region ®ion,
113 for (
Value v : liveInValues) {
119 "constants must have a single result");
120 constantLiveIns.push_back(v);
123 if (constantLiveIns.empty())
129 for (
Value v : constantLiveIns) {
132 liveInValues.remove(v);
139static DeviceType getGangWorkerVectorDeviceType(LoopOp loopOp,
140 DeviceType deviceType) {
141 if (deviceType != DeviceType::None &&
142 loopOp.hasAnyGangWorkerVector(deviceType))
144 return DeviceType::None;
147template <
typename ComputeConstructT>
148static DeviceType getParDimsDeviceType(ComputeConstructT computeOp,
149 DeviceType deviceType) {
150 if (deviceType != DeviceType::None &&
151 computeOp.hasAnyGangWorkerVector(deviceType))
153 return DeviceType::None;
160 DeviceType deviceType) {
161 deviceType = getGangWorkerVectorDeviceType(loopOp, deviceType);
163 auto *ctx = loopOp->getContext();
165 if (loopOp.hasVector(deviceType))
167 if (loopOp.hasWorker(deviceType))
169 if (
auto gangDimValue = loopOp.getGangValue(GangArgType::Dim, deviceType)) {
170 if (
auto gangDimDefOp =
175 }
else if (loopOp.hasGang(deviceType)) {
186template <
typename ComputeConstructT>
188assignKnownLaunchArgs(ComputeConstructT computeOp, DeviceType deviceType,
192 auto loc = computeOp->getLoc();
194 if constexpr (std::is_same_v<ComputeConstructT, SerialOp>) {
195 return {ParWidthOp::create(rewriter, loc,
Value(), policy.
seqDim(ctx))};
196 }
else if constexpr (llvm::is_one_of<ComputeConstructT, ParallelOp,
198 if (computeOp.isEffectivelySerial())
199 return {ParWidthOp::create(rewriter, loc,
Value(), policy.
seqDim(ctx))};
201 deviceType = getParDimsDeviceType(computeOp, deviceType);
206 auto numGangs = computeOp.getNumGangsValues(deviceType);
207 for (
auto [gangDimIdx, gangSize] : llvm::enumerate(numGangs)) {
209 values.push_back(ParWidthOp::create(
213 policy.
gangDim(ctx, gangLevel)));
216 Value numWorkers = computeOp.getNumWorkersValue(deviceType);
218 values.push_back(ParWidthOp::create(
221 indexTy, numWorkers),
225 Value vectorLength = computeOp.getVectorLengthValue(deviceType);
227 values.push_back(ParWidthOp::create(
230 indexTy, vectorLength),
235 llvm_unreachable(
"assignKnownLaunchArgs: expected parallel, kernels, or "
247 DeviceType deviceType)
250 LogicalResult matchAndRewrite(LoopOp loopOp,
252 if (loopOp.getUnstructured()) {
257 rewriter.
replaceOp(loopOp, executeRegion);
261 LoopParMode parMode = loopOp.getDefaultOrDeviceTypeParallelism(deviceType);
263 if (parMode == LoopParMode::loop_seq || isOpInSerialRegion(loopOp)) {
270 setParDimsAttr(forOp, GPUParallelDimsAttr::seq(loopOp->getContext()));
272 }
else if (parMode == LoopParMode::loop_auto) {
274 assert(!isOpInSerialRegion(loopOp) &&
275 "Expected loop to be in non-serial region");
282 getParallelDimensions(loopOp, policy,
deviceType);
283 if (!parDims.empty()) {
285 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
289 }
else if (!isOpInComputeRegion(loopOp) &&
291 loopOp->getParentOfType<FunctionOpInterface>())) {
300 assert(parMode == LoopParMode::loop_independent &&
301 "Expected loop to be independent");
307 getParallelDimensions(loopOp, policy,
deviceType);
308 if (!parDims.empty()) {
310 GPUParallelDimsAttr::get(loopOp->getContext(), parDims);
328template <
typename ComputeConstructT>
336 LogicalResult matchAndRewrite(ComputeConstructT computeOp,
340 KernelEnvironmentOp::createAndPopulate(computeOp, deviceType, rewriter);
342 assignKnownLaunchArgs(computeOp, deviceType, rewriter, policy);
346 materializeConstantLiveInsIntoRegion(region, liveInValues, rewriter);
349 computeOp->getLoc(), launchArgs, liveInValues.getArrayRef(),
350 ComputeConstructT::getOperationName(), region, rewriter, mapping);
351 if (!computeRegion) {
361 DeviceType deviceType;
368class ACCComputeLowering
371 using ACCComputeLoweringBase::ACCComputeLoweringBase;
373 void runOnOperation()
override {
374 auto op = getOperation();
377 DefaultACCToGPUMappingPolicy policy;
382 RewritePatternSet loopPatterns(context);
383 loopPatterns.insert<ACCLoopConversion>(context, policy, deviceType);
385 return signalPassFailure();
389 RewritePatternSet computePatterns(context);
391 .insert<ComputeOpConversion<ParallelOp>, ComputeOpConversion<KernelsOp>,
392 ComputeOpConversion<SerialOp>>(context, policy, deviceType);
394 return signalPassFailure();
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
MLIRContext * getContext() const
This is a utility class for mapping one set of IR entities to another.
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'.
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.
ParDimAttrT seqDim(MLIRContext *ctx) const
ParDimAttrT vectorDim(MLIRContext *ctx) const
ParDimAttrT workerDim(MLIRContext *ctx) const
ParDimAttrT gangDim(MLIRContext *ctx, ParLevel level) const
Convenience methods for specific parallelism levels.
::mlir::Pass::Option< mlir::acc::DeviceType > deviceType
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.
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...
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...