MLIR 24.0.0git
OpenACCUtilsTiling.cpp
Go to the documentation of this file.
1//===- OpenACCUtilsTiling.cpp - OpenACC Loop Tiling 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 contains utility functions for tiling OpenACC loops.
10//
11//===----------------------------------------------------------------------===//
12
14
20#include "llvm/ADT/STLExtras.h"
21
22// Resolve unknown tile sizes (represented as -1 for tile(*)) to the default.
23// Returns a value with the same type as targetType.
25 int32_t defaultTileSize,
26 mlir::Type targetType,
27 mlir::RewriterBase &rewriter,
28 mlir::Location loc) {
29 auto constVal = mlir::getConstantIntValue(tileSize);
30 if (constVal && *constVal < 0) {
31 // Create constant with the target type directly
32 return mlir::arith::ConstantOp::create(
33 rewriter, loc, targetType,
34 rewriter.getIntegerAttr(targetType, defaultTileSize));
35 }
36 return mlir::getValueOrCreateCastToIndexLike(rewriter, loc, targetType,
37 tileSize);
38}
39
40// Remove vector/worker attributes from loop
41static void removeWorkerVectorFromLoop(mlir::acc::LoopOp loop) {
42 if (loop.hasVector() || loop.getVectorValue()) {
43 loop.removeVectorAttr();
44 loop.removeVectorOperandsDeviceTypeAttr();
45 } else if (loop.hasWorker() || loop.getWorkerValue()) {
46 loop.removeWorkerAttr();
47 loop.removeWorkerNumOperandsDeviceTypeAttr();
48 }
49}
50
51// Create a new ACC loop with new steps, lb, ub from original loop
52static mlir::acc::LoopOp createACCLoopFromOriginal(
53 mlir::acc::LoopOp origLoop, mlir::RewriterBase &rewriter,
55 mlir::DenseBoolArrayAttr inclusiveUBAttr,
56 mlir::acc::CombinedConstructsTypeAttr combinedAttr, mlir::Location loc) {
57 mlir::ArrayAttr collapseAttr = mlir::ArrayAttr{};
58 mlir::ArrayAttr collapseDeviceTypeAttr = mlir::ArrayAttr{};
59 auto newLoop = mlir::acc::LoopOp::create(
60 rewriter, loc, origLoop->getResultTypes(), lb, ub, step, inclusiveUBAttr,
61 collapseAttr, collapseDeviceTypeAttr, origLoop.getGangOperands(),
62 origLoop.getGangOperandsArgTypeAttr(),
63 origLoop.getGangOperandsSegmentsAttr(),
64 origLoop.getGangOperandsDeviceTypeAttr(), origLoop.getWorkerNumOperands(),
65 origLoop.getWorkerNumOperandsDeviceTypeAttr(),
66 origLoop.getVectorOperands(), origLoop.getVectorOperandsDeviceTypeAttr(),
67 origLoop.getSeqAttr(), origLoop.getIndependentAttr(),
68 origLoop.getAuto_Attr(), origLoop.getGangAttr(), origLoop.getWorkerAttr(),
69 origLoop.getVectorAttr(), mlir::ValueRange{}, mlir::DenseI32ArrayAttr{},
70 mlir::ArrayAttr{}, origLoop.getCacheOperands(),
71 origLoop.getPrivateOperands(), origLoop.getFirstprivateOperands(),
72 origLoop.getReductionOperands(), combinedAttr);
73 return newLoop;
74}
75
76// Move ops from source to target Loop and replace uses of IVs
77static void moveOpsAndReplaceIVs(mlir::acc::LoopOp sourceLoop,
78 mlir::acc::LoopOp targetLoop,
81 size_t nOps, mlir::RewriterBase &rewriter) {
82 // nOps includes the terminator; move all ops except the terminator:
83 // [begin, begin + nOps - 1)
84 mlir::Block::iterator begin = sourceLoop.getBody().begin();
85 mlir::Block::iterator end = std::next(begin, nOps - 1);
86
87 // Notify the rewriter about all ops being moved (and their nested ops).
88 // Directly moved ops have their parent block changed (rewriter fingerprint
89 // tracking invalidated). Nested ops may have operands replaced by
90 // replaceAllUsesInRegionWith below.
92 for (mlir::Block::iterator it = begin; it != end; ++it)
93 it->walk([&](mlir::Operation *op) {
94 movedOps.push_back(op);
95 rewriter.startOpModification(op);
96 });
97
98 targetLoop.getBody().getOperations().splice(
99 targetLoop.getBody().getOperations().begin(),
100 sourceLoop.getBody().getOperations(), begin, end);
101
102 // Replace uses of origIV with newIV
103 for (auto [i, newIV] : llvm::enumerate(newIVs))
104 mlir::replaceAllUsesInRegionWith(origIVs[i], newIV, targetLoop.getRegion());
105
106 for (mlir::Operation *op : movedOps)
107 rewriter.finalizeOpModification(op);
108}
109
110// Create a single "element group" loop nested in `tileLoop`, carrying
111// `ivTypes.size()` induction variables so the whole element space is one
112// multi-IV loop. The element group carries vector or worker but not gang.
113static mlir::acc::LoopOp
114createElementGroupLoop(mlir::acc::LoopOp tileLoop, mlir::RewriterBase &rewriter,
116 mlir::ValueRange steps,
117 mlir::DenseBoolArrayAttr inclusiveUBAttr,
119 mlir::acc::LoopOp elementLoop = createACCLoopFromOriginal(
120 tileLoop, rewriter, lbs, ubs, steps, inclusiveUBAttr,
121 mlir::acc::CombinedConstructsTypeAttr{}, loc);
122
123 // Drop gang from the element group, keeping vector/worker. The operand
124 // values must be cleared too, not just the attributes.
125 rewriter.startOpModification(elementLoop);
126 if (tileLoop.hasGang() ||
127 tileLoop.getGangValue(mlir::acc::GangArgType::Num) ||
128 tileLoop.getGangValue(mlir::acc::GangArgType::Dim) ||
129 tileLoop.getGangValue(mlir::acc::GangArgType::Static)) {
130 elementLoop.removeGangAttr();
131 elementLoop.removeGangOperandsArgTypeAttr();
132 elementLoop.removeGangOperandsSegmentsAttr();
133 elementLoop.removeGangOperandsDeviceTypeAttr();
134 elementLoop.getGangOperandsMutable().clear();
135 }
136 if (tileLoop.hasVector() || tileLoop.getVectorValue()) {
137 elementLoop.removeWorkerAttr();
138 elementLoop.removeWorkerNumOperandsDeviceTypeAttr();
139 elementLoop.getWorkerNumOperandsMutable().clear();
140 }
141 rewriter.finalizeOpModification(elementLoop);
142
143 // Create the element loop body: one block argument per IV plus a terminator.
144 mlir::Block *blk = rewriter.createBlock(&elementLoop.getRegion(),
145 elementLoop.getRegion().begin());
146 rewriter.setInsertionPointToEnd(blk);
147 mlir::acc::YieldOp::create(rewriter, loc);
148 for (mlir::Type ivType : ivTypes)
149 elementLoop.getBody().addArgument(ivType, loc);
150
151 return elementLoop;
152}
153
154mlir::acc::LoopOp
155mlir::acc::tileACCLoops(mlir::acc::LoopOp tileLoop,
156 const llvm::SmallVector<mlir::Value> &tileSizes,
157 int32_t defaultTileSize, mlir::RewriterBase &rewriter) {
158 // Tile a single fused acc.loop that carries all associated induction
159 // variables. This keeps the tile iterations as one multi-IV "tile group"
160 // loop and the in-tile iterations as one multi-IV "element group" loop, each
161 // spanning all of its induction variables.
162 const mlir::Location loc = tileLoop.getLoc();
163 const unsigned tileCount = tileSizes.size();
164
165 llvm::SmallVector<mlir::Value, 3> origIVs(tileLoop.getBody().getArguments());
166 llvm::SmallVector<mlir::Value, 3> origUBs(tileLoop.getUpperbound());
167 llvm::SmallVector<mlir::Value, 3> origSteps(tileLoop.getStep());
168 const unsigned numIVs = origIVs.size();
169 const size_t nOps = tileLoop.getBody().getOperations().size();
170
171 // Original inclusive-UB flags (default false when the attribute is absent).
172 llvm::SmallVector<bool> inclusiveUBs;
173 for (unsigned i = 0; i < numIVs; ++i) {
174 if (tileLoop.getInclusiveUpperboundAttr())
175 inclusiveUBs.push_back(
176 tileLoop.getInclusiveUpperboundAttr().asArrayRef()[i]);
177 else
178 inclusiveUBs.push_back(false);
179 }
180
181 // Scale each tiled dimension's step by its tile size to form the tile group
182 // loop steps.
183 rewriter.setInsertionPoint(tileLoop);
186 for (unsigned i = 0; i < numIVs; ++i) {
187 if (i < tileCount) {
189 tileSizes[i], defaultTileSize, origSteps[i].getType(), rewriter, loc);
190 mlir::Value scaled =
191 mlir::arith::MulIOp::create(rewriter, loc, origSteps[i], tileSize);
192 scaledSteps.push_back(scaled);
193 tileLoopSteps.push_back(scaled);
194 } else {
195 tileLoopSteps.push_back(origSteps[i]);
196 }
197 }
198
199 // Compute the element-loop upper bounds min(origUB, origIV + scaledStep).
200 rewriter.setInsertionPoint(tileLoop.getBody().getTerminator());
201 llvm::SmallVector<mlir::Value, 3> elemLBs, elemUBs, elemSteps;
203 llvm::SmallVector<bool> elemInclusiveUBs;
204 for (unsigned i = 0; i < tileCount; ++i) {
205 mlir::Value stepped =
206 mlir::arith::AddIOp::create(rewriter, loc, origIVs[i], scaledSteps[i]);
207 mlir::Value newUB = stepped;
208 if (inclusiveUBs[i]) {
209 // Inclusive UB: min(origUB, origIV + (scaledStep - 1)).
210 mlir::Value c1 = mlir::arith::ConstantOp::create(
211 rewriter, loc, scaledSteps[i].getType(),
212 rewriter.getIntegerAttr(scaledSteps[i].getType(), 1));
213 newUB = mlir::arith::SubIOp::create(rewriter, loc, stepped, c1);
214 }
215 elemUBs.push_back(
216 mlir::arith::MinSIOp::create(rewriter, loc, origUBs[i], newUB));
217 elemLBs.push_back(origIVs[i]);
218 elemSteps.push_back(origSteps[i]);
219 elemIVTypes.push_back(origIVs[i].getType());
220 elemInclusiveUBs.push_back(inclusiveUBs[i]);
221 }
222
223 // Only attach an inclusiveUpperbound attribute if at least one element
224 // dimension is inclusive.
226 if (llvm::is_contained(elemInclusiveUBs, true))
227 elemInclAttr = rewriter.getDenseBoolArrayAttr(elemInclusiveUBs);
228
229 // Create the element group loop from the unmodified tile loop.
230 mlir::acc::LoopOp elementLoop =
231 createElementGroupLoop(tileLoop, rewriter, elemLBs, elemUBs, elemSteps,
232 elemInclAttr, elemIVTypes, loc);
233
234 // Move the original body into the element loop and remap the tiled IVs to the
235 // element IVs.
237 elementLoop.getBody().getArguments());
238 llvm::SmallVector<mlir::Value, 3> tiledOrigIVs(origIVs.begin(),
239 origIVs.begin() + tileCount);
240 moveOpsAndReplaceIVs(tileLoop, elementLoop, newIVs, tiledOrigIVs, nOps,
241 rewriter);
242
243 // Turn the fused loop into the tile group: scaled steps, gang only.
244 rewriter.startOpModification(tileLoop);
245 tileLoop.getStepMutable().clear();
246 tileLoop.getStepMutable().append(tileLoopSteps);
248 rewriter.finalizeOpModification(tileLoop);
249
250 return tileLoop;
251}
static void removeWorkerVectorFromLoop(mlir::acc::LoopOp loop)
static void moveOpsAndReplaceIVs(mlir::acc::LoopOp sourceLoop, mlir::acc::LoopOp targetLoop, llvm::ArrayRef< mlir::Value > newIVs, llvm::ArrayRef< mlir::Value > origIVs, size_t nOps, mlir::RewriterBase &rewriter)
static mlir::Value resolveAndCastTileSize(mlir::Value tileSize, int32_t defaultTileSize, mlir::Type targetType, mlir::RewriterBase &rewriter, mlir::Location loc)
static mlir::acc::LoopOp createACCLoopFromOriginal(mlir::acc::LoopOp origLoop, mlir::RewriterBase &rewriter, mlir::ValueRange lb, mlir::ValueRange ub, mlir::ValueRange step, mlir::DenseBoolArrayAttr inclusiveUBAttr, mlir::acc::CombinedConstructsTypeAttr combinedAttr, mlir::Location loc)
static mlir::acc::LoopOp createElementGroupLoop(mlir::acc::LoopOp tileLoop, mlir::RewriterBase &rewriter, mlir::ValueRange lbs, mlir::ValueRange ubs, mlir::ValueRange steps, mlir::DenseBoolArrayAttr inclusiveUBAttr, llvm::ArrayRef< mlir::Type > ivTypes, mlir::Location loc)
Block represents an ordered list of Operations.
Definition Block.h:33
OpListType::iterator iterator
Definition Block.h:164
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:233
DenseBoolArrayAttr getDenseBoolArrayAttr(ArrayRef< bool > values)
Tensor-typed DenseArrayAttr getters.
Definition Builders.cpp:155
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
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:435
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:400
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:438
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void finalizeOpModification(Operation *op)
This method is used to signal the end of an in-place modification of the given operation.
virtual void startOpModification(Operation *op)
This method is used to notify the rewriter that an in-place operation modification is about to happen...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
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
mlir::acc::LoopOp tileACCLoops(mlir::acc::LoopOp tileLoop, const llvm::SmallVector< mlir::Value > &tileSizes, int32_t defaultTileSize, mlir::RewriterBase &rewriter)
Tile a single fused acc.loop that carries all associated induction variables (one IV per tile dimensi...
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.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
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
detail::DenseArrayAttrImpl< int32_t > DenseI32ArrayAttr
detail::DenseArrayAttrImpl< bool > DenseBoolArrayAttr