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.
108
109namespace mlir {
110namespace acc {
111
112std::pair<SmallVector<Value>, Block::iterator>
114 IRMapping &mapping, ValueRange resultsToReplace) {
115 if (!src->hasOneBlock())
116 llvm_unreachable("cloneACCRegionInto: multi-block region not supported "
117 "(requires scf.execute_region)");
118
119 Region *insertRegion = dest->getParent();
120 Block *postInsertBlock = dest->splitBlock(inlinePoint);
121 src->cloneInto(insertRegion, postInsertBlock->getIterator(), mapping);
122
123 SmallVector<Value> replacements;
124 Block *lastNewBlock = &*std::prev(postInsertBlock->getIterator());
125
127 if (auto yieldOp = dyn_cast<acc::YieldOp>(lastNewBlock->getTerminator())) {
128 for (auto [replacement, orig] :
129 llvm::zip(yieldOp.getOperands(), resultsToReplace)) {
131 }
132 replacements.append(yieldOp.getOperands().begin(),
133 yieldOp.getOperands().end());
134 ip = std::prev(yieldOp->getIterator());
135 yieldOp.erase();
136 } else {
137 auto terminatorOp =
138 dyn_cast<acc::TerminatorOp>(lastNewBlock->getTerminator());
139 if (!terminatorOp)
140 llvm_unreachable(
141 "cloneACCRegionInto: expected acc.yield or acc.terminator");
142 ip = std::prev(terminatorOp->getIterator());
143 terminatorOp.erase();
144 }
145
146 lastNewBlock->getOperations().splice(lastNewBlock->end(),
147 postInsertBlock->getOperations());
148 postInsertBlock->erase();
149
150 Block *firstNewBlock = &*std::next(dest->getIterator());
151 dest->getOperations().splice(dest->end(), firstNewBlock->getOperations());
152 firstNewBlock->erase();
153 return {replacements, ip};
154}
155
156/// Wrap a multi-block region with scf.execute_region.
157scf::ExecuteRegionOp
159 Location loc, RewriterBase &rewriter) {
160 SmallVector<Operation *> terminators;
161 for (Block &block : region.getBlocks()) {
162 if (block.empty())
163 continue;
164 Operation *term = block.getTerminator();
165 if (term->getNumSuccessors() == 0)
166 terminators.push_back(term);
167 }
168 SmallVector<Type> resultTypes;
169 if (!terminators.empty())
170 for (Value operand : terminators.front()->getOperands())
171 resultTypes.push_back(operand.getType());
172
173 auto exeRegionOp =
174 scf::ExecuteRegionOp::create(rewriter, loc, TypeRange(resultTypes));
175
176 rewriter.cloneRegionBefore(region, exeRegionOp.getRegion(),
177 exeRegionOp.getRegion().end(), mapping);
178
179 for (Operation *term : terminators) {
180 Operation *blockTerminator = mapping.lookup(term);
181 assert(blockTerminator && "expected terminator to be in mapping");
182 rewriter.setInsertionPoint(blockTerminator);
183 (void)scf::YieldOp::create(rewriter, blockTerminator->getLoc(),
184 blockTerminator->getOperands());
185 rewriter.eraseOp(blockTerminator);
186 }
187
188 return exeRegionOp;
189}
190
191scf::ForOp convertACCLoopToSCFFor(LoopOp loopOp, RewriterBase &rewriter,
192 bool enableCollapse) {
193 assert(!loopOp.getUnstructured() &&
194 "use convertUnstructuredACCLoopToSCFExecuteRegion for unstructured "
195 "loops");
196
197 Location loc = loopOp->getLoc();
198
199 IRMapping mapping;
201
202 OpBuilder::InsertionGuard guard(rewriter);
203 rewriter.setInsertionPoint(loopOp);
204
205 // Normalize all loops: lb=0, step=1, ub=tripCount.
206 // scf.for requires a positive step, but acc.loop may have arbitrary steps
207 // (including negative). Normalizing unconditionally keeps this consistent
208 // with convertACCLoopToSCFParallel and lets later passes fold constants.
209 Value zero = arith::ConstantIndexOp::create(rewriter, loc, 0);
210 Value one = arith::ConstantIndexOp::create(rewriter, loc, 1);
211
212 SmallVector<Value> tripCounts;
213 for (auto [idx, iv] : llvm::enumerate(loopOp.getBody().getArguments())) {
214 bool inclusiveUpperbound = false;
215 if (loopOp.getInclusiveUpperbound().has_value())
216 inclusiveUpperbound =
217 loopOp.getInclusiveUpperboundAttr().asArrayRef()[idx];
218
219 Value tc = calculateTripCount(rewriter, loc, loopOp.getLowerbound()[idx],
220 loopOp.getUpperbound()[idx],
221 loopOp.getStep()[idx], inclusiveUpperbound);
222 tripCounts.push_back(tc);
223 }
224
225 for (auto [idx, iv] : llvm::enumerate(loopOp.getBody().getArguments())) {
226 // For nested loops, insert inside the previous loop's body
227 if (idx > 0)
228 rewriter.setInsertionPointToStart(forOps.back().getBody());
229
230 scf::ForOp forOp =
231 scf::ForOp::create(rewriter, loc, zero, tripCounts[idx], one);
232 forOps.push_back(forOp);
233 mapping.map(iv, forOp.getInductionVar());
234 }
235
236 // Set insertion point inside the innermost loop for IV casts and body cloning
237 rewriter.setInsertionPointToStart(forOps.back().getBody());
238
239 // Handle IV type conversion (index -> original type)
240 SmallVector<Value> scfIVs;
241 for (scf::ForOp forOp : forOps)
242 scfIVs.push_back(forOp.getInductionVar());
243 mapACCLoopIVsToSCFIVs(loopOp, scfIVs, rewriter, mapping);
244
245 // Clone the loop body into the innermost scf.for
246 cloneACCRegionIntoForLoop(&loopOp.getRegion(), forOps.back().getBody(),
247 rewriter.getInsertionPoint(), mapping, rewriter);
248
249 // Denormalize IV uses: original_iv = normalized_iv * orig_step + orig_lb
250 for (size_t idx = 0; idx < forOps.size(); ++idx) {
251 Value iv = forOps[idx].getInductionVar();
252 if (!iv.use_empty()) {
253 rewriter.setInsertionPointToStart(forOps[idx].getBody());
254 normalizeIVUses(rewriter, loc, iv, loopOp.getLowerbound()[idx],
255 loopOp.getStep()[idx]);
256 }
257 }
258
259 // Optionally collapse nested loops
260 if (enableCollapse && forOps.size() > 1) {
261 unsigned numCollapsed = forOps.size();
262 if (failed(coalesceLoops(rewriter, forOps)))
263 loopOp.emitError("failed to collapse acc.loop");
264 else
265 setCollapseCountAttr(forOps.front(), numCollapsed);
266 }
267
268 copyLoopAnnotationAttr(loopOp, forOps.front());
269 return forOps.front();
270}
271
272scf::ParallelOp convertACCLoopToSCFParallel(LoopOp loopOp,
273 RewriterBase &rewriter) {
274 assert(!loopOp.getUnstructured() &&
275 "use convertUnstructuredACCLoopToSCFExecuteRegion for unstructured "
276 "loops");
277 assert(
278 rewriter.getInsertionBlock() &&
279 !loopOp->isProperAncestor(rewriter.getInsertionBlock()->getParentOp()) &&
280 "builder insertion point must not be inside the loop being converted");
281
282 Location loc = loopOp->getLoc();
283
284 SmallVector<Value> lowerBounds, upperBounds, steps;
285
286 // Normalize all loops: lb=0, step=1, ub=tripCount
287 Value lb = arith::ConstantIndexOp::create(rewriter, loc, 0);
288 Value step = arith::ConstantIndexOp::create(rewriter, loc, 1);
289
290 for (auto [idx, iv] : llvm::enumerate(loopOp.getBody().getArguments())) {
291 bool inclusiveUpperbound = false;
292 if (loopOp.getInclusiveUpperbound().has_value())
293 inclusiveUpperbound = loopOp.getInclusiveUpperbound().value()[idx];
294
295 Value ub = calculateTripCount(rewriter, loc, loopOp.getLowerbound()[idx],
296 loopOp.getUpperbound()[idx],
297 loopOp.getStep()[idx], inclusiveUpperbound);
298
299 lowerBounds.push_back(lb);
300 upperBounds.push_back(ub);
301 steps.push_back(step);
302 }
303
304 auto parallelOp =
305 scf::ParallelOp::create(rewriter, loc, lowerBounds, upperBounds, steps);
306
307 // Create IV type conversions
308 IRMapping mapping;
309 rewriter.setInsertionPointToStart(parallelOp.getBody());
310 mapACCLoopIVsToSCFIVs(loopOp, parallelOp.getInductionVars(), rewriter,
311 mapping);
312
313 if (!loopOp.getRegion().hasOneBlock()) {
315 loopOp.getRegion(), mapping, loc, rewriter);
316 if (!exeRegion) {
317 rewriter.eraseOp(parallelOp);
318 return nullptr;
319 }
320 } else {
321 cloneACCRegionIntoForLoop(&loopOp.getRegion(), parallelOp.getBody(),
322 rewriter.getInsertionPoint(), mapping, rewriter);
323 }
324
325 // Denormalize IV uses
326 rewriter.setInsertionPointToStart(parallelOp.getBody());
327 for (auto [idx, iv] : llvm::enumerate(parallelOp.getBody()->getArguments()))
328 if (!iv.use_empty())
329 normalizeIVUses(rewriter, loc, iv, loopOp.getLowerbound()[idx],
330 loopOp.getStep()[idx]);
331
332 setCollapseCountAttr(parallelOp, parallelOp.getNumLoops());
333 copyLoopAnnotationAttr(loopOp, parallelOp);
334 return parallelOp;
335}
336
337scf::ExecuteRegionOp
339 RewriterBase &rewriter) {
340 assert(loopOp.getUnstructured() &&
341 "use convertACCLoopToSCFFor for structured loops");
342 assert(
343 rewriter.getInsertionBlock() &&
344 !loopOp->isProperAncestor(rewriter.getInsertionBlock()->getParentOp()) &&
345 "builder insertion point must not be inside the loop being converted");
346
347 IRMapping mapping;
348 return wrapMultiBlockRegionWithSCFExecuteRegion(loopOp.getRegion(), mapping,
349 loopOp->getLoc(), rewriter);
350}
351
352void setCollapseCountAttr(Operation *op, uint64_t count) {
355 IntegerAttr::get(IntegerType::get(op->getContext(), 64), count));
356}
357
359 if (auto attr =
361 return attr.getValue().getZExtValue();
362 return 1;
363}
364
365} // namespace acc
366} // 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:34
OpListType::iterator iterator
Definition Block.h:165
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:162
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
iterator end()
Definition Block.h:169
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:608
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
constexpr llvm::StringLiteral getLoopAnnotationAttrName()
Canonical name used when attaching LoopAnnotationAttr as a discardable attribute on operations that d...
Definition LLVMAttrs.h:122
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