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. The tile sizes are in clause order, so the first one belongs
183 // to the innermost tiled IV.
184 rewriter.setInsertionPoint(tileLoop);
187 for (unsigned i = 0; i < numIVs; ++i) {
188 if (i < tileCount) {
189 mlir::Value tileSize =
190 resolveAndCastTileSize(tileSizes[tileCount - 1 - i], defaultTileSize,
191 origSteps[i].getType(), rewriter, loc);
192 mlir::Value scaled =
193 mlir::arith::MulIOp::create(rewriter, loc, origSteps[i], tileSize);
194 scaledSteps.push_back(scaled);
195 tileLoopSteps.push_back(scaled);
196 } else {
197 tileLoopSteps.push_back(origSteps[i]);
198 }
199 }
200
201 // Compute each element-loop upper bound by clamping the tile's far edge,
202 // origIV + scaledStep, to origUB. An ascending loop takes
203 // min(origUB, edge - 1), a descending one max(origUB, edge + 1). The -1 and
204 // +1 apply in the presence of inclusive bounds and are omitted otherwise.
205 rewriter.setInsertionPoint(tileLoop.getBody().getTerminator());
206 llvm::SmallVector<mlir::Value, 3> elemLBs, elemUBs, elemSteps;
208 llvm::SmallVector<bool> elemInclusiveUBs;
209 for (unsigned i = 0; i < tileCount; ++i) {
210 mlir::Value stepped =
211 mlir::arith::AddIOp::create(rewriter, loc, origIVs[i], scaledSteps[i]);
212 mlir::Type stepTy = scaledSteps[i].getType();
213 std::optional<int64_t> constStep = mlir::getConstantIntValue(origSteps[i]);
214
215 // The inclusive edge is one iteration short of the next tile's start, in
216 // whichever direction the loop runs.
217 auto inclusiveEdge = [&](bool descending) -> mlir::Value {
218 if (!inclusiveUBs[i])
219 return stepped;
220 mlir::Value c1 = mlir::arith::ConstantOp::create(
221 rewriter, loc, stepTy, rewriter.getIntegerAttr(stepTy, 1));
222 if (descending)
223 return mlir::arith::AddIOp::create(rewriter, loc, stepped, c1);
224 return mlir::arith::SubIOp::create(rewriter, loc, stepped, c1);
225 };
226
227 mlir::Value elemUB;
228 if (constStep && *constStep < 0) {
229 elemUB = mlir::arith::MaxSIOp::create(rewriter, loc, origUBs[i],
230 inclusiveEdge(/*descending=*/true));
231 } else if (constStep) {
232 elemUB = mlir::arith::MinSIOp::create(
233 rewriter, loc, origUBs[i], inclusiveEdge(/*descending=*/false));
234 } else {
235 // The sign is only known at run time, so compute both and select.
236 mlir::Type origStepTy = origSteps[i].getType();
237 mlir::Value zero = mlir::arith::ConstantOp::create(
238 rewriter, loc, origStepTy, rewriter.getIntegerAttr(origStepTy, 0));
239 mlir::Value isDescending = mlir::arith::CmpIOp::create(
240 rewriter, loc, mlir::arith::CmpIPredicate::slt, origSteps[i], zero);
241 mlir::Value descUB = mlir::arith::MaxSIOp::create(
242 rewriter, loc, origUBs[i], inclusiveEdge(/*descending=*/true));
243 mlir::Value ascUB = mlir::arith::MinSIOp::create(
244 rewriter, loc, origUBs[i], inclusiveEdge(/*descending=*/false));
245 elemUB = mlir::arith::SelectOp::create(rewriter, loc, isDescending,
246 descUB, ascUB);
247 }
248 elemUBs.push_back(elemUB);
249 elemLBs.push_back(origIVs[i]);
250 elemSteps.push_back(origSteps[i]);
251 elemIVTypes.push_back(origIVs[i].getType());
252 elemInclusiveUBs.push_back(inclusiveUBs[i]);
253 }
254
255 // Only attach an inclusiveUpperbound attribute if at least one element
256 // dimension is inclusive.
258 if (llvm::is_contained(elemInclusiveUBs, true))
259 elemInclAttr = rewriter.getDenseBoolArrayAttr(elemInclusiveUBs);
260
261 // Create the element group loop from the unmodified tile loop.
262 mlir::acc::LoopOp elementLoop =
263 createElementGroupLoop(tileLoop, rewriter, elemLBs, elemUBs, elemSteps,
264 elemInclAttr, elemIVTypes, loc);
265
266 // Move the original body into the element loop and remap the tiled IVs to the
267 // element IVs.
269 elementLoop.getBody().getArguments());
270 llvm::SmallVector<mlir::Value, 3> tiledOrigIVs(origIVs.begin(),
271 origIVs.begin() + tileCount);
272 moveOpsAndReplaceIVs(tileLoop, elementLoop, newIVs, tiledOrigIVs, nOps,
273 rewriter);
274
275 // Turn the fused loop into the tile group: scaled steps, gang only.
276 rewriter.startOpModification(tileLoop);
277 tileLoop.getStepMutable().clear();
278 tileLoop.getStepMutable().append(tileLoopSteps);
280 rewriter.finalizeOpModification(tileLoop);
281
282 return tileLoop;
283}
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:34
OpListType::iterator iterator
Definition Block.h:165
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
DenseBoolArrayAttr getDenseBoolArrayAttr(ArrayRef< bool > values)
Tensor-typed DenseArrayAttr getters.
Definition Builders.cpp:159
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:439
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
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:392
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:311
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