MLIR 24.0.0git
OpenACCUtilsLoop.cpp
Go to the documentation of this file.
1//===- OpenACCUtilsLoop.cpp - OpenACC Loop 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 converting OpenACC loops to SCF.
10//
11//===----------------------------------------------------------------------===//
12
14
21#include "mlir/IR/IRMapping.h"
23#include "llvm/ADT/SmallPtrSet.h"
24#include "llvm/Support/ErrorHandling.h"
25
26using namespace mlir;
27
28/// Calculate trip count for a loop: (ub - lb + step) / step
29/// If inclusiveUpperbound is false, subtracts 1 from ub first.
31 Value step, bool inclusiveUpperbound) {
32 Type type = b.getIndexType();
33
34 // Convert original loop arguments to index type
35 lb = getValueOrCreateCastToIndexLike(b, loc, type, lb);
37 step = getValueOrCreateCastToIndexLike(b, loc, type, step);
38
39 if (!inclusiveUpperbound) {
41 ub = b.createOrFold<arith::SubIOp>(loc, ub, one,
42 arith::IntegerOverflowFlags::nsw);
43 }
44
45 Value sub = b.createOrFold<arith::SubIOp>(loc, ub, lb,
46 arith::IntegerOverflowFlags::nsw);
47 Value add = b.createOrFold<arith::AddIOp>(loc, sub, step,
48 arith::IntegerOverflowFlags::nsw);
49 return b.createOrFold<arith::DivSIOp>(loc, add, step);
50}
51
52/// Handle differing types between SCF (index) and ACC loops.
53/// Creates casts from the new SCF IVs to the original ACC IV types and updates
54/// the mapping. The newIVs should correspond 1:1 with the ACC loop's IVs.
55static void mapACCLoopIVsToSCFIVs(acc::LoopOp accLoop, ValueRange newIVs,
56 OpBuilder &b, IRMapping &mapping) {
57 for (auto [origIV, newIV] :
58 llvm::zip(accLoop.getBody().getArguments(), newIVs)) {
60 b, accLoop->getLoc(), origIV.getType(), newIV);
61 mapping.map(origIV, replacementIV);
62 }
63}
64
65/// Normalize IV uses after converting to normalized loop form.
66/// For normalized loops (lb=0, step=1), we need to denormalize the IV:
67/// original_iv = new_iv * orig_step + orig_lb
69 Value origStep) {
70 Type indexType = b.getIndexType();
71 Value lb = getValueOrCreateCastToIndexLike(b, loc, indexType, origLB);
72 Value step = getValueOrCreateCastToIndexLike(b, loc, indexType, origStep);
73
74 // new_iv * step + lb
75 Value scaled =
76 arith::MulIOp::create(b, loc, iv, step, arith::IntegerOverflowFlags::nsw);
77 Value denormalized = arith::AddIOp::create(b, loc, scaled, lb,
78 arith::IntegerOverflowFlags::nsw);
79
80 // Replace uses of iv with denormalized value, except for the ops that
81 // compute the denormalized value itself (muli and addi)
83 exceptions.insert(scaled.getDefiningOp());
84 exceptions.insert(denormalized.getDefiningOp());
85 iv.replaceAllUsesExcept(denormalized, exceptions);
86}
87
88/// Helper used by loop conversion: clone region and return insertion point
89/// only.
91 Block::iterator insertionPoint,
92 IRMapping &mapping,
93 RewriterBase &rewriter) {
94 auto [replacements, ip] =
95 acc::cloneACCRegionInto(src, dest, insertionPoint, mapping, ValueRange{});
96 (void)replacements;
97 return ip;
98}
99
100/// Copy the discardable LLVM loop annotation attribute from an acc.loop to the
101/// lowered SCF op so later SCF to CFG/LLVM lowering can emit !llvm.loop
102/// metadata.
104 if (Attribute ann = from->getDiscardableAttr(LLVM::LoopAnnotationAttr::name))
105 to->setDiscardableAttr(LLVM::LoopAnnotationAttr::name, ann);
106}
107
108namespace mlir {
109namespace acc {
110
111std::pair<SmallVector<Value>, Block::iterator>
113 IRMapping &mapping, ValueRange resultsToReplace) {
114 if (!src->hasOneBlock())
115 llvm_unreachable("cloneACCRegionInto: multi-block region not supported "
116 "(requires scf.execute_region)");
117
118 Region *insertRegion = dest->getParent();
119 Block *postInsertBlock = dest->splitBlock(inlinePoint);
120 src->cloneInto(insertRegion, postInsertBlock->getIterator(), mapping);
121
122 SmallVector<Value> replacements;
123 Block *lastNewBlock = &*std::prev(postInsertBlock->getIterator());
124
126 if (auto yieldOp = dyn_cast<acc::YieldOp>(lastNewBlock->getTerminator())) {
127 for (auto [replacement, orig] :
128 llvm::zip(yieldOp.getOperands(), resultsToReplace)) {
130 }
131 replacements.append(yieldOp.getOperands().begin(),
132 yieldOp.getOperands().end());
133 ip = std::prev(yieldOp->getIterator());
134 yieldOp.erase();
135 } else {
136 auto terminatorOp =
137 dyn_cast<acc::TerminatorOp>(lastNewBlock->getTerminator());
138 if (!terminatorOp)
139 llvm_unreachable(
140 "cloneACCRegionInto: expected acc.yield or acc.terminator");
141 ip = std::prev(terminatorOp->getIterator());
142 terminatorOp.erase();
143 }
144
145 lastNewBlock->getOperations().splice(lastNewBlock->end(),
146 postInsertBlock->getOperations());
147 postInsertBlock->erase();
148
149 Block *firstNewBlock = &*std::next(dest->getIterator());
150 dest->getOperations().splice(dest->end(), firstNewBlock->getOperations());
151 firstNewBlock->erase();
152 return {replacements, ip};
153}
154
155/// Wrap a multi-block region with scf.execute_region.
156scf::ExecuteRegionOp
158 Location loc, RewriterBase &rewriter) {
159 SmallVector<Operation *> terminators;
160 for (Block &block : region.getBlocks()) {
161 if (block.empty())
162 continue;
163 Operation *term = block.getTerminator();
164 if (term->getNumSuccessors() == 0)
165 terminators.push_back(term);
166 }
167 SmallVector<Type> resultTypes;
168 if (!terminators.empty())
169 for (Value operand : terminators.front()->getOperands())
170 resultTypes.push_back(operand.getType());
171
172 auto exeRegionOp =
173 scf::ExecuteRegionOp::create(rewriter, loc, TypeRange(resultTypes));
174
175 rewriter.cloneRegionBefore(region, exeRegionOp.getRegion(),
176 exeRegionOp.getRegion().end(), mapping);
177
178 for (Operation *term : terminators) {
179 Operation *blockTerminator = mapping.lookup(term);
180 assert(blockTerminator && "expected terminator to be in mapping");
181 rewriter.setInsertionPoint(blockTerminator);
182 (void)scf::YieldOp::create(rewriter, blockTerminator->getLoc(),
183 blockTerminator->getOperands());
184 rewriter.eraseOp(blockTerminator);
185 }
186
187 return exeRegionOp;
188}
189
190scf::ForOp convertACCLoopToSCFFor(LoopOp loopOp, RewriterBase &rewriter,
191 bool enableCollapse) {
192 assert(!loopOp.getUnstructured() &&
193 "use convertUnstructuredACCLoopToSCFExecuteRegion for unstructured "
194 "loops");
195
196 Location loc = loopOp->getLoc();
197
198 IRMapping mapping;
200
201 OpBuilder::InsertionGuard guard(rewriter);
202 rewriter.setInsertionPoint(loopOp);
203
204 // Normalize all loops: lb=0, step=1, ub=tripCount.
205 // scf.for requires a positive step, but acc.loop may have arbitrary steps
206 // (including negative). Normalizing unconditionally keeps this consistent
207 // with convertACCLoopToSCFParallel and lets later passes fold constants.
208 Value zero = arith::ConstantIndexOp::create(rewriter, loc, 0);
209 Value one = arith::ConstantIndexOp::create(rewriter, loc, 1);
210
211 SmallVector<Value> tripCounts;
212 for (auto [idx, iv] : llvm::enumerate(loopOp.getBody().getArguments())) {
213 bool inclusiveUpperbound = false;
214 if (loopOp.getInclusiveUpperbound().has_value())
215 inclusiveUpperbound =
216 loopOp.getInclusiveUpperboundAttr().asArrayRef()[idx];
217
218 Value tc = calculateTripCount(rewriter, loc, loopOp.getLowerbound()[idx],
219 loopOp.getUpperbound()[idx],
220 loopOp.getStep()[idx], inclusiveUpperbound);
221 tripCounts.push_back(tc);
222 }
223
224 for (auto [idx, iv] : llvm::enumerate(loopOp.getBody().getArguments())) {
225 // For nested loops, insert inside the previous loop's body
226 if (idx > 0)
227 rewriter.setInsertionPointToStart(forOps.back().getBody());
228
229 scf::ForOp forOp =
230 scf::ForOp::create(rewriter, loc, zero, tripCounts[idx], one);
231 forOps.push_back(forOp);
232 mapping.map(iv, forOp.getInductionVar());
233 }
234
235 // Set insertion point inside the innermost loop for IV casts and body cloning
236 rewriter.setInsertionPointToStart(forOps.back().getBody());
237
238 // Handle IV type conversion (index -> original type)
239 SmallVector<Value> scfIVs;
240 for (scf::ForOp forOp : forOps)
241 scfIVs.push_back(forOp.getInductionVar());
242 mapACCLoopIVsToSCFIVs(loopOp, scfIVs, rewriter, mapping);
243
244 // Clone the loop body into the innermost scf.for
245 cloneACCRegionIntoForLoop(&loopOp.getRegion(), forOps.back().getBody(),
246 rewriter.getInsertionPoint(), mapping, rewriter);
247
248 // Denormalize IV uses: original_iv = normalized_iv * orig_step + orig_lb
249 for (size_t idx = 0; idx < forOps.size(); ++idx) {
250 Value iv = forOps[idx].getInductionVar();
251 if (!iv.use_empty()) {
252 rewriter.setInsertionPointToStart(forOps[idx].getBody());
253 normalizeIVUses(rewriter, loc, iv, loopOp.getLowerbound()[idx],
254 loopOp.getStep()[idx]);
255 }
256 }
257
258 // Optionally collapse nested loops
259 if (enableCollapse && forOps.size() > 1) {
260 unsigned numCollapsed = forOps.size();
261 if (failed(coalesceLoops(rewriter, forOps)))
262 loopOp.emitError("failed to collapse acc.loop");
263 else
264 setCollapseCountAttr(forOps.front(), numCollapsed);
265 }
266
267 copyLoopAnnotationAttr(loopOp, forOps.front());
268 return forOps.front();
269}
270
271scf::ParallelOp convertACCLoopToSCFParallel(LoopOp loopOp,
272 RewriterBase &rewriter) {
273 assert(!loopOp.getUnstructured() &&
274 "use convertUnstructuredACCLoopToSCFExecuteRegion for unstructured "
275 "loops");
276 assert(
277 rewriter.getInsertionBlock() &&
278 !loopOp->isProperAncestor(rewriter.getInsertionBlock()->getParentOp()) &&
279 "builder insertion point must not be inside the loop being converted");
280
281 Location loc = loopOp->getLoc();
282
283 SmallVector<Value> lowerBounds, upperBounds, steps;
284
285 // Normalize all loops: lb=0, step=1, ub=tripCount
286 Value lb = arith::ConstantIndexOp::create(rewriter, loc, 0);
287 Value step = arith::ConstantIndexOp::create(rewriter, loc, 1);
288
289 for (auto [idx, iv] : llvm::enumerate(loopOp.getBody().getArguments())) {
290 bool inclusiveUpperbound = false;
291 if (loopOp.getInclusiveUpperbound().has_value())
292 inclusiveUpperbound = loopOp.getInclusiveUpperbound().value()[idx];
293
294 Value ub = calculateTripCount(rewriter, loc, loopOp.getLowerbound()[idx],
295 loopOp.getUpperbound()[idx],
296 loopOp.getStep()[idx], inclusiveUpperbound);
297
298 lowerBounds.push_back(lb);
299 upperBounds.push_back(ub);
300 steps.push_back(step);
301 }
302
303 auto parallelOp =
304 scf::ParallelOp::create(rewriter, loc, lowerBounds, upperBounds, steps);
305
306 // Create IV type conversions
307 IRMapping mapping;
308 rewriter.setInsertionPointToStart(parallelOp.getBody());
309 mapACCLoopIVsToSCFIVs(loopOp, parallelOp.getInductionVars(), rewriter,
310 mapping);
311
312 if (!loopOp.getRegion().hasOneBlock()) {
314 loopOp.getRegion(), mapping, loc, rewriter);
315 if (!exeRegion) {
316 rewriter.eraseOp(parallelOp);
317 return nullptr;
318 }
319 } else {
320 cloneACCRegionIntoForLoop(&loopOp.getRegion(), parallelOp.getBody(),
321 rewriter.getInsertionPoint(), mapping, rewriter);
322 }
323
324 // Denormalize IV uses
325 rewriter.setInsertionPointToStart(parallelOp.getBody());
326 for (auto [idx, iv] : llvm::enumerate(parallelOp.getBody()->getArguments()))
327 if (!iv.use_empty())
328 normalizeIVUses(rewriter, loc, iv, loopOp.getLowerbound()[idx],
329 loopOp.getStep()[idx]);
330
331 setCollapseCountAttr(parallelOp, parallelOp.getNumLoops());
332 copyLoopAnnotationAttr(loopOp, parallelOp);
333 return parallelOp;
334}
335
336scf::ExecuteRegionOp
338 RewriterBase &rewriter) {
339 assert(loopOp.getUnstructured() &&
340 "use convertACCLoopToSCFFor for structured loops");
341 assert(
342 rewriter.getInsertionBlock() &&
343 !loopOp->isProperAncestor(rewriter.getInsertionBlock()->getParentOp()) &&
344 "builder insertion point must not be inside the loop being converted");
345
346 IRMapping mapping;
347 return wrapMultiBlockRegionWithSCFExecuteRegion(loopOp.getRegion(), mapping,
348 loopOp->getLoc(), rewriter);
349}
350
351void setCollapseCountAttr(Operation *op, uint64_t count) {
354 IntegerAttr::get(IntegerType::get(op->getContext(), 64), count));
355}
356
358 if (auto attr =
360 return attr.getValue().getZExtValue();
361 return 1;
362}
363
364} // namespace acc
365} // namespace mlir
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static void mapACCLoopIVsToSCFIVs(acc::LoopOp accLoop, ValueRange newIVs, OpBuilder &b, IRMapping &mapping)
Handle differing types between SCF (index) and ACC loops.
static void copyLoopAnnotationAttr(Operation *from, Operation *to)
Copy the discardable LLVM loop annotation attribute from an acc.loop to the lowered SCF op so later S...
static Block::iterator cloneACCRegionIntoForLoop(Region *src, Block *dest, Block::iterator insertionPoint, IRMapping &mapping, RewriterBase &rewriter)
Helper used by loop conversion: clone region and return insertion point only.
#define add(a, b)
Attributes are known-constant values of operations.
Definition Attributes.h:25
Block represents an ordered list of Operations.
Definition Block.h:33
OpListType::iterator iterator
Definition Block.h:164
void erase()
Unlink this Block from its parent region and delete it.
Definition Block.cpp:66
Block * splitBlock(iterator splitBefore)
Split the block into two blocks before the specified operation or iterator.
Definition Block.cpp:323
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
OpListType & getOperations()
Definition Block.h:161
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
iterator end()
Definition Block.h:168
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
auto lookup(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:72
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
Block::iterator getInsertionPoint() const
Returns the current insertion point of the builder.
Definition Builders.h:448
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void cloneRegionBefore(Region &region, Region &parent, Region::iterator before, IRMapping &mapping)
Clone the blocks that belong to "region" before the given position in another region "parent".
Definition Builders.cpp:598
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
Definition Builders.h:445
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Attribute getDiscardableAttr(StringRef name)
Access a discardable attribute by name, returns a null Attribute if the discardable attribute does no...
Definition Operation.h:485
unsigned getNumSuccessors()
Definition Operation.h:758
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
Definition Operation.h:512
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
AttrClass getDiscardableAttrOfType(StringRef name)
Access a discardable attribute by name and cast it to AttrClass.
Definition Operation.h:493
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
void cloneInto(Region *dest, IRMapping &mapper)
Clone the internal blocks from this region into dest.
Definition Region.cpp:70
iterator end()
Definition Region.h:56
BlockListType & getBlocks()
Definition Region.h:45
bool hasOneBlock()
Return true if this region has exactly one block.
Definition Region.h:68
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
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
bool use_empty() const
Returns true if this value has no uses.
Definition Value.h:208
void replaceAllUsesExcept(Value newValue, const SmallPtrSetImpl< Operation * > &exceptions)
Replace all uses of 'this' value with 'newValue', updating anything in the IR that uses 'this' to use...
Definition Value.cpp:71
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
uint64_t getCollapseCount(Operation *op)
Number of original loops collapsed into op, or 1 when op carries no collapse_count attribute.
void setCollapseCountAttr(Operation *op, uint64_t count)
Record on a collapsed loop how many original loops were folded into it.
Value calculateTripCount(OpBuilder &b, Location loc, Value lb, Value ub, Value step, bool inclusiveUpperbound)
Calculate trip count for a loop: (ub - lb + step) / step.
scf::ParallelOp convertACCLoopToSCFParallel(LoopOp loopOp, RewriterBase &rewriter)
Convert acc.loop to scf.parallel.
scf::ExecuteRegionOp wrapMultiBlockRegionWithSCFExecuteRegion(Region &region, IRMapping &mapping, Location loc, RewriterBase &rewriter)
Wrap a multi-block region in an scf.execute_region.
scf::ExecuteRegionOp convertUnstructuredACCLoopToSCFExecuteRegion(LoopOp loopOp, RewriterBase &rewriter)
Convert an unstructured acc.loop to scf.execute_region.
static constexpr StringLiteral getCollapseCountAttrName()
Name for an attribute attached to a loop indicating the number of loops collapsed to create that loop...
Definition OpenACC.h:212
std::pair< llvm::SmallVector< Value >, Block::iterator > cloneACCRegionInto(Region *src, Block *dest, Block::iterator inlinePoint, IRMapping &mapping, ValueRange resultsToReplace)
Clone an ACC region into a destination block at the given insertion point.
void normalizeIVUses(OpBuilder &b, Location loc, Value iv, Value origLB, Value origStep)
Normalize IV uses after converting to normalized loop form (lb=0, step=1).
scf::ForOp convertACCLoopToSCFFor(LoopOp loopOp, RewriterBase &rewriter, bool enableCollapse)
Convert a structured acc.loop to scf.for.
Include the generated interface declarations.
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region &region)
Replace all uses of orig within the given region with replacement.
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
LogicalResult coalesceLoops(MutableArrayRef< scf::ForOp > loops)
Replace a perfect nest of "for" loops with a single linearized loop.
Definition Utils.cpp:1087