MLIR 24.0.0git
Utils.cpp
Go to the documentation of this file.
1//===- Utils.cpp ---- Misc utilities for loop transformation ----------===//
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 implements miscellaneous loop transformation routines.
10//
11//===----------------------------------------------------------------------===//
12
20#include "mlir/IR/IRMapping.h"
25#include "llvm/ADT/APInt.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/ADT/SmallVectorExtras.h"
29#include "llvm/Support/DebugLog.h"
30#include <cstdint>
31
32using namespace mlir;
33
34#define DEBUG_TYPE "scf-utils"
35
37 RewriterBase &rewriter, MutableArrayRef<scf::ForOp> loopNest,
38 ValueRange newIterOperands, const NewYieldValuesFn &newYieldValuesFn,
39 bool replaceIterOperandsUsesInLoop) {
40 if (loopNest.empty())
41 return {};
42 // This method is recursive (to make it more readable). Adding an
43 // assertion here to limit the recursion. (See
44 // https://discourse.llvm.org/t/rfc-update-to-mlir-developer-policy-on-recursion/62235)
45 assert(loopNest.size() <= 10 &&
46 "exceeded recursion limit when yielding value from loop nest");
47
48 // To yield a value from a perfectly nested loop nest, the following
49 // pattern needs to be created, i.e. starting with
50 //
51 // ```mlir
52 // scf.for .. {
53 // scf.for .. {
54 // scf.for .. {
55 // %value = ...
56 // }
57 // }
58 // }
59 // ```
60 //
61 // needs to be modified to
62 //
63 // ```mlir
64 // %0 = scf.for .. iter_args(%arg0 = %init) {
65 // %1 = scf.for .. iter_args(%arg1 = %arg0) {
66 // %2 = scf.for .. iter_args(%arg2 = %arg1) {
67 // %value = ...
68 // scf.yield %value
69 // }
70 // scf.yield %2
71 // }
72 // scf.yield %1
73 // }
74 // ```
75 //
76 // The inner most loop is handled using the `replaceWithAdditionalYields`
77 // that works on a single loop.
78 if (loopNest.size() == 1) {
79 auto innerMostLoop =
80 cast<scf::ForOp>(*loopNest.back().replaceWithAdditionalYields(
81 rewriter, newIterOperands, replaceIterOperandsUsesInLoop,
82 newYieldValuesFn));
83 return {innerMostLoop};
84 }
85 // The outer loops are modified by calling this method recursively
86 // - The return value of the inner loop is the value yielded by this loop.
87 // - The region iter args of this loop are the init_args for the inner loop.
88 SmallVector<scf::ForOp> newLoopNest;
90 [&](OpBuilder &innerBuilder, Location loc,
92 newLoopNest = replaceLoopNestWithNewYields(rewriter, loopNest.drop_front(),
93 innerNewBBArgs, newYieldValuesFn,
94 replaceIterOperandsUsesInLoop);
95 return llvm::map_to_vector(
96 newLoopNest.front().getResults().take_back(innerNewBBArgs.size()),
97 [](OpResult r) -> Value { return r; });
98 };
99 scf::ForOp outerMostLoop =
100 cast<scf::ForOp>(*loopNest.front().replaceWithAdditionalYields(
101 rewriter, newIterOperands, replaceIterOperandsUsesInLoop, fn));
102 newLoopNest.insert(newLoopNest.begin(), outerMostLoop);
103 return newLoopNest;
104}
105
106/// Outline a region with a single block into a new FuncOp.
107/// Assumes the FuncOp result types is the type of the yielded operands of the
108/// single block. This constraint makes it easy to determine the result.
109/// This method also clones the `arith::ConstantIndexOp` at the start of
110/// `outlinedFuncBody` to alloc simple canonicalizations. If `callOp` is
111/// provided, it will be set to point to the operation that calls the outlined
112/// function.
113// TODO: support more than single-block regions.
114// TODO: more flexible constant handling.
115FailureOr<func::FuncOp> mlir::outlineSingleBlockRegion(RewriterBase &rewriter,
116 Location loc,
117 Region &region,
118 StringRef funcName,
119 func::CallOp *callOp) {
120 assert(!funcName.empty() && "funcName cannot be empty");
121 if (!region.hasOneBlock())
122 return failure();
123
124 Block *originalBlock = &region.front();
125 Operation *originalTerminator = originalBlock->getTerminator();
126
127 // Outline before current function.
128 OpBuilder::InsertionGuard g(rewriter);
129 rewriter.setInsertionPoint(region.getParentOfType<FunctionOpInterface>());
130
131 SetVector<Value> captures;
132 getUsedValuesDefinedAbove(region, captures);
133
134 ValueRange outlinedValues(captures.getArrayRef());
135 SmallVector<Type> outlinedFuncArgTypes;
136 SmallVector<Location> outlinedFuncArgLocs;
137 // Region's arguments are exactly the first block's arguments as per
138 // Region::getArguments().
139 // Func's arguments are cat(regions's arguments, captures arguments).
140 for (BlockArgument arg : region.getArguments()) {
141 outlinedFuncArgTypes.push_back(arg.getType());
142 outlinedFuncArgLocs.push_back(arg.getLoc());
143 }
144 for (Value value : outlinedValues) {
145 outlinedFuncArgTypes.push_back(value.getType());
146 outlinedFuncArgLocs.push_back(value.getLoc());
147 }
148 FunctionType outlinedFuncType =
149 FunctionType::get(rewriter.getContext(), outlinedFuncArgTypes,
150 originalTerminator->getOperandTypes());
151 auto outlinedFunc =
152 func::FuncOp::create(rewriter, loc, funcName, outlinedFuncType);
153 Block *outlinedFuncBody = outlinedFunc.addEntryBlock();
154
155 // Merge blocks while replacing the original block operands.
156 // Warning: `mergeBlocks` erases the original block, reconstruct it later.
157 int64_t numOriginalBlockArguments = originalBlock->getNumArguments();
158 auto outlinedFuncBlockArgs = outlinedFuncBody->getArguments();
159 {
160 OpBuilder::InsertionGuard g(rewriter);
161 rewriter.setInsertionPointToEnd(outlinedFuncBody);
162 rewriter.mergeBlocks(
163 originalBlock, outlinedFuncBody,
164 outlinedFuncBlockArgs.take_front(numOriginalBlockArguments));
165 // Explicitly set up a new ReturnOp terminator.
166 rewriter.setInsertionPointToEnd(outlinedFuncBody);
167 func::ReturnOp::create(rewriter, loc, originalTerminator->getResultTypes(),
168 originalTerminator->getOperands());
169 }
170
171 // Reconstruct the block that was deleted and add a
172 // terminator(call_results).
173 Block *newBlock = rewriter.createBlock(
174 &region, region.begin(),
175 TypeRange{outlinedFuncArgTypes}.take_front(numOriginalBlockArguments),
176 ArrayRef<Location>(outlinedFuncArgLocs)
177 .take_front(numOriginalBlockArguments));
178 {
179 OpBuilder::InsertionGuard g(rewriter);
180 rewriter.setInsertionPointToEnd(newBlock);
181 SmallVector<Value> callValues;
182 llvm::append_range(callValues, newBlock->getArguments());
183 llvm::append_range(callValues, outlinedValues);
184 auto call = func::CallOp::create(rewriter, loc, outlinedFunc, callValues);
185 if (callOp)
186 *callOp = call;
187
188 // `originalTerminator` was moved to `outlinedFuncBody` and is still valid.
189 // Clone `originalTerminator` to take the callOp results then erase it from
190 // `outlinedFuncBody`.
191 IRMapping bvm;
192 bvm.map(originalTerminator->getOperands(), call->getResults());
193 rewriter.clone(*originalTerminator, bvm);
194 rewriter.eraseOp(originalTerminator);
195 }
196
197 // Lastly, explicit RAUW outlinedValues, only for uses within `outlinedFunc`.
198 // Clone the `arith::ConstantIndexOp` at the start of `outlinedFuncBody`.
199 for (auto it : llvm::zip(outlinedValues, outlinedFuncBlockArgs.take_back(
200 outlinedValues.size()))) {
201 Value orig = std::get<0>(it);
202 Value repl = std::get<1>(it);
203 {
204 OpBuilder::InsertionGuard g(rewriter);
205 rewriter.setInsertionPointToStart(outlinedFuncBody);
207 repl = rewriter.clone(*cst)->getResult(0);
208 }
209 }
210 orig.replaceUsesWithIf(repl, [&](OpOperand &opOperand) {
211 return outlinedFunc->isProperAncestor(opOperand.getOwner());
212 });
213 }
214
215 return outlinedFunc;
216}
217
218LogicalResult mlir::outlineIfOp(RewriterBase &b, scf::IfOp ifOp,
219 func::FuncOp *thenFn, StringRef thenFnName,
220 func::FuncOp *elseFn, StringRef elseFnName) {
221 IRRewriter rewriter(b);
222 Location loc = ifOp.getLoc();
223 FailureOr<func::FuncOp> outlinedFuncOpOrFailure;
224 if (thenFn && !ifOp.getThenRegion().empty()) {
225 outlinedFuncOpOrFailure = outlineSingleBlockRegion(
226 rewriter, loc, ifOp.getThenRegion(), thenFnName);
227 if (failed(outlinedFuncOpOrFailure))
228 return failure();
229 *thenFn = *outlinedFuncOpOrFailure;
230 }
231 if (elseFn && !ifOp.getElseRegion().empty()) {
232 outlinedFuncOpOrFailure = outlineSingleBlockRegion(
233 rewriter, loc, ifOp.getElseRegion(), elseFnName);
234 if (failed(outlinedFuncOpOrFailure))
235 return failure();
236 *elseFn = *outlinedFuncOpOrFailure;
237 }
238 return success();
239}
240
243 assert(rootOp != nullptr && "Root operation must not be a nullptr.");
244 bool rootEnclosesPloops = false;
245 for (Region &region : rootOp->getRegions()) {
246 for (Block &block : region.getBlocks()) {
247 for (Operation &op : block) {
248 bool enclosesPloops = getInnermostParallelLoops(&op, result);
249 rootEnclosesPloops |= enclosesPloops;
250 if (auto ploop = dyn_cast<scf::ParallelOp>(op)) {
251 rootEnclosesPloops = true;
252
253 // Collect parallel loop if it is an innermost one.
254 if (!enclosesPloops)
255 result.push_back(ploop);
256 }
257 }
258 }
259 }
260 return rootEnclosesPloops;
261}
262
263// Build the IR that performs ceil division of a positive value by a constant:
264// ceildiv(a, B) = divis(a + (B-1), B)
265// where divis is rounding-to-zero division.
266static Value ceilDivPositive(OpBuilder &builder, Location loc, Value dividend,
267 int64_t divisor) {
268 assert(divisor > 0 && "expected positive divisor");
269 assert(dividend.getType().isIntOrIndex() &&
270 "expected integer or index-typed value");
271
272 Value divisorMinusOneCst = arith::ConstantOp::create(
273 builder, loc, builder.getIntegerAttr(dividend.getType(), divisor - 1));
274 Value divisorCst = arith::ConstantOp::create(
275 builder, loc, builder.getIntegerAttr(dividend.getType(), divisor));
276 Value sum = arith::AddIOp::create(builder, loc, dividend, divisorMinusOneCst);
277 return arith::DivUIOp::create(builder, loc, sum, divisorCst);
278}
279
280// Build the IR that performs ceil division of a positive value by another
281// positive value:
282// ceildiv(a, b) = divis(a + (b - 1), b)
283// where divis is rounding-to-zero division.
284static Value ceilDivPositive(OpBuilder &builder, Location loc, Value dividend,
285 Value divisor) {
286 assert(dividend.getType().isIntOrIndex() &&
287 "expected integer or index-typed value");
288 Value cstOne = arith::ConstantOp::create(
289 builder, loc, builder.getOneAttr(dividend.getType()));
290 Value divisorMinusOne = arith::SubIOp::create(builder, loc, divisor, cstOne);
291 Value sum = arith::AddIOp::create(builder, loc, dividend, divisorMinusOne);
292 return arith::DivUIOp::create(builder, loc, sum, divisor);
293}
294
296 Block *loopBodyBlock, Value iv, uint64_t unrollFactor,
297 function_ref<Value(unsigned, Value, OpBuilder)> ivRemapFn,
298 function_ref<void(unsigned, Operation *, OpBuilder)> annotateFn,
299 ValueRange iterArgs, ValueRange yieldedValues,
300 IRMapping *clonedToSrcOpsMap) {
301
302 // Check if the op was cloned from another source op, and return it if found
303 // (or the same op if not found)
304 auto findOriginalSrcOp =
305 [](Operation *op, const IRMapping &clonedToSrcOpsMap) -> Operation * {
306 Operation *srcOp = op;
307 // If the source op derives from another op: traverse the chain to find the
308 // original source op
309 while (srcOp && clonedToSrcOpsMap.contains(srcOp))
310 srcOp = clonedToSrcOpsMap.lookup(srcOp);
311 return srcOp;
312 };
313
314 // Builder to insert unrolled bodies just before the terminator of the body of
315 // the loop.
316 auto builder = OpBuilder::atBlockTerminator(loopBodyBlock);
317
318 static const auto noopAnnotateFn = [](unsigned, Operation *, OpBuilder) {};
319 if (!annotateFn)
320 annotateFn = noopAnnotateFn;
321
322 // Keep a pointer to the last non-terminator operation in the original block
323 // so that we know what to clone (since we are doing this in-place).
324 Block::iterator srcBlockEnd = std::prev(loopBodyBlock->end(), 2);
325
326 // Unroll the contents of the loop body (append unrollFactor - 1 additional
327 // copies).
328 SmallVector<Value, 4> lastYielded(yieldedValues);
329
330 for (unsigned i = 1; i < unrollFactor; i++) {
331 // Prepare operand map.
332 IRMapping operandMap;
333 operandMap.map(iterArgs, lastYielded);
334
335 // If the induction variable is used, create a remapping to the value for
336 // this unrolled instance.
337 if (!iv.use_empty()) {
338 Value ivUnroll = ivRemapFn(i, iv, builder);
339 operandMap.map(iv, ivUnroll);
340 }
341
342 // Clone the original body of 'forOp'.
343 for (auto it = loopBodyBlock->begin(); it != std::next(srcBlockEnd); it++) {
344 Operation *srcOp = &(*it);
345 Operation *clonedOp = builder.clone(*srcOp, operandMap);
346 annotateFn(i, clonedOp, builder);
347 if (clonedToSrcOpsMap)
348 clonedToSrcOpsMap->map(clonedOp,
349 findOriginalSrcOp(srcOp, *clonedToSrcOpsMap));
350 }
351
352 // Update yielded values.
353 for (unsigned i = 0, e = lastYielded.size(); i < e; i++)
354 lastYielded[i] = operandMap.lookupOrDefault(yieldedValues[i]);
355 }
356
357 // Make sure we annotate the Ops in the original body. We do this last so that
358 // any annotations are not copied into the cloned Ops above.
359 for (auto it = loopBodyBlock->begin(); it != std::next(srcBlockEnd); it++)
360 annotateFn(0, &*it, builder);
361
362 // Update operands of the yield statement.
363 loopBodyBlock->getTerminator()->setOperands(lastYielded);
364}
365
366/// Splits `forOp` into two consecutive loops at `splitPoint`.
367FailureOr<std::pair<scf::ForOp, scf::ForOp>>
368mlir::splitForOpAtPoint(RewriterBase &rewriter, scf::ForOp forOp,
369 Value splitPoint) {
370 if (splitPoint.getType() != forOp.getLowerBound().getType())
371 return failure();
372
373 // Reject statically known violations of the split preconditions.
374 bool isUnsigned = forOp.getUnsignedCmp();
375 Value lbVal = forOp.getLowerBound();
376 Value ubVal = forOp.getUpperBound();
377 Value stepVal = forOp.getStep();
378 auto checkSplitPoint = [&](auto getBound) -> LogicalResult {
379 auto lb = getBound(lbVal);
380 auto ub = getBound(ubVal);
381 auto step = getBound(stepVal);
382 auto split = getBound(splitPoint);
383 if ((lb && split && *lb > *split) || (split && ub && *split >= *ub) ||
384 (step && *step <= 0))
385 return failure();
386 if (lb && step && split && (*split - *lb) % *step != 0)
387 return failure();
388 return success();
389 };
390 if (failed(isUnsigned ? checkSplitPoint(getConstantUIntValue)
391 : checkSplitPoint(getConstantIntValue)))
392 return failure();
393
394 OpBuilder::InsertionGuard guard(rewriter);
395 rewriter.setInsertionPointAfter(forOp);
396 auto firstForOp = cast<scf::ForOp>(rewriter.clone(*forOp));
397 auto secondForOp = cast<scf::ForOp>(rewriter.clone(*forOp));
398 rewriter.modifyOpInPlace(firstForOp,
399 [&] { firstForOp.setUpperBound(splitPoint); });
400 rewriter.modifyOpInPlace(secondForOp,
401 [&] { secondForOp.setLowerBound(splitPoint); });
402
403 // Chain iter-args across the split:
404 // - `secondForOp` is initialized from `firstForOp`'s results.
405 // - Users of `forOp`'s results are redirected to `secondForOp`'s results,
406 // so downstream code observes the final carried values.
407 rewriter.modifyOpInPlace(secondForOp, [&] {
408 secondForOp->setOperands(secondForOp.getNumControlOperands(),
409 secondForOp.getInitArgs().size(),
410 firstForOp.getResults());
411 });
412 rewriter.replaceOp(forOp, secondForOp.getResults());
413
414 return std::pair<scf::ForOp, scf::ForOp>{firstForOp, secondForOp};
415}
416
417/// Unrolls 'forOp' by 'unrollFactor', returns the unrolled main loop and the
418/// epilogue loop, if the loop is unrolled.
419FailureOr<UnrolledLoopInfo> mlir::loopUnrollByFactor(
420 scf::ForOp forOp, uint64_t unrollFactor,
421 function_ref<void(unsigned, Operation *, OpBuilder)> annotateFn,
422 bool shouldPromoteIfSingleIteration) {
423 assert(unrollFactor > 0 && "expected positive unroll factor");
424
425 // Return if the loop body is empty.
426 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
427 return UnrolledLoopInfo{forOp, std::nullopt};
428
429 // Compute tripCount = ceilDiv((upperBound - lowerBound), step) and populate
430 // 'upperBoundUnrolled' and 'stepUnrolled' for static and dynamic cases.
431 OpBuilder boundsBuilder(forOp);
432 IRRewriter rewriter(forOp.getContext());
433 auto loc = forOp.getLoc();
434 Value step = forOp.getStep();
435 Value upperBoundUnrolled;
436 Value stepUnrolled;
437 bool generateEpilogueLoop = true;
438
439 std::optional<APInt> constTripCount = forOp.getStaticTripCount();
440 // A static trip count does not imply constant bounds: it is also known when
441 // the lower and the upper bound are the same value (zero iterations), when
442 // the lower bound is zero and the upper bound is the step (one iteration),
443 // and when the upper bound is a constant offset from a non-constant lower
444 // bound. The computation below reads all three bounds as constants, so fall
445 // back to the dynamic case unless they are.
446 if (constTripCount && !(getConstantAPIntValue(forOp.getLowerBound()) &&
447 getConstantAPIntValue(forOp.getUpperBound()) &&
449 constTripCount = std::nullopt;
450 if (constTripCount) {
451 // Constant loop bounds computation.
452 bool isUnsignedLoop = forOp.getUnsignedCmp();
453 // For unsigned loops, bounds must be zero-extended: narrow integer types
454 // (e.g. i1, i2, i3) may have bit patterns that are negative in a signed
455 // context (e.g., i1 value 1 has getSExtValue() == -1, getZExtValue() == 1).
456 // Zero-extension is only safe when the unsigned value fits in int64_t, i.e.
457 // the type's bitwidth is < 64. Bail out for 64-bit unsigned loops.
458 if (isUnsignedLoop) {
459 if (auto intTy = dyn_cast<IntegerType>(forOp.getUpperBound().getType()))
460 if (intTy.getWidth() >= 64)
461 return failure();
462 }
463 auto getLoopBound = [&](Value v) -> int64_t {
464 auto apInt = getConstantAPIntValue(v);
465 assert(apInt && "expected constant loop bound");
466 return isUnsignedLoop ? static_cast<int64_t>(apInt->first.getZExtValue())
467 : apInt->first.getSExtValue();
468 };
469 int64_t lbCst = getLoopBound(forOp.getLowerBound());
470 int64_t ubCst = getLoopBound(forOp.getUpperBound());
471 int64_t stepCst = getLoopBound(step);
472 if (unrollFactor == 1) {
473 if (shouldPromoteIfSingleIteration && constTripCount->isOne() &&
474 failed(forOp.promoteIfSingleIteration(rewriter)))
475 return failure();
476 return UnrolledLoopInfo{forOp, std::nullopt};
477 }
478
479 uint64_t tripCount = constTripCount->getZExtValue();
480 uint64_t tripCountEvenMultiple = tripCount - tripCount % unrollFactor;
481 int64_t upperBoundUnrolledCst = lbCst + tripCountEvenMultiple * stepCst;
482 int64_t stepUnrolledCst = stepCst * unrollFactor;
483
484 // Create constant for 'upperBoundUnrolled' and set epilogue loop flag.
485 generateEpilogueLoop = upperBoundUnrolledCst < ubCst;
486 if (generateEpilogueLoop)
487 upperBoundUnrolled = arith::ConstantOp::create(
488 boundsBuilder, loc,
489 boundsBuilder.getIntegerAttr(forOp.getUpperBound().getType(),
490 upperBoundUnrolledCst));
491 else
492 upperBoundUnrolled = forOp.getUpperBound();
493
494 // Create constant for 'stepUnrolled'. When the main loop has zero
495 // iterations (tripCountEvenMultiple == 0), keep the original step.
496 // stepCst * unrollFactor may produce a value that, when truncated to the
497 // bound type's bitwidth during IntegerAttr construction, wraps to zero; a
498 // zero step causes constantTripCount to return nullopt instead of 0, which
499 // prevents the zero-trip main loop from being elided.
500 bool mainLoopHasNoIter = (tripCountEvenMultiple == 0);
501 bool stepUnchanged = (stepCst == stepUnrolledCst);
502 stepUnrolled =
503 (mainLoopHasNoIter || stepUnchanged)
504 ? step
505 : arith::ConstantOp::create(boundsBuilder, loc,
506 boundsBuilder.getIntegerAttr(
507 step.getType(), stepUnrolledCst));
508 } else {
509 // Dynamic loop bounds computation.
510 // TODO: Add dynamic asserts for negative lb/ub/step, or
511 // consider using ceilDiv from AffineApplyExpander.
512 auto lowerBound = forOp.getLowerBound();
513 auto upperBound = forOp.getUpperBound();
514 Value diff =
515 arith::SubIOp::create(boundsBuilder, loc, upperBound, lowerBound);
516 Value tripCount = ceilDivPositive(boundsBuilder, loc, diff, step);
517 Value unrollFactorCst = arith::ConstantOp::create(
518 boundsBuilder, loc,
519 boundsBuilder.getIntegerAttr(tripCount.getType(), unrollFactor));
520 Value tripCountRem =
521 arith::RemSIOp::create(boundsBuilder, loc, tripCount, unrollFactorCst);
522 // Compute tripCountEvenMultiple = tripCount - (tripCount % unrollFactor)
523 Value tripCountEvenMultiple =
524 arith::SubIOp::create(boundsBuilder, loc, tripCount, tripCountRem);
525 // Compute upperBoundUnrolled = lowerBound + tripCountEvenMultiple * step
526 upperBoundUnrolled = arith::AddIOp::create(
527 boundsBuilder, loc, lowerBound,
528 arith::MulIOp::create(boundsBuilder, loc, tripCountEvenMultiple, step));
529 // Scale 'step' by 'unrollFactor'.
530 stepUnrolled =
531 arith::MulIOp::create(boundsBuilder, loc, step, unrollFactorCst);
532 }
533
534 UnrolledLoopInfo resultLoops;
535
536 // Create epilogue clean up loop starting at 'upperBoundUnrolled'.
537 if (generateEpilogueLoop) {
538 auto splitLoops = splitForOpAtPoint(rewriter, forOp, upperBoundUnrolled);
539 if (failed(splitLoops))
540 return failure();
541 forOp = splitLoops->first;
542 scf::ForOp epilogueForOp = splitLoops->second;
543 if (!shouldPromoteIfSingleIteration ||
544 epilogueForOp.promoteIfSingleIteration(rewriter).failed())
545 resultLoops.epilogueLoopOp = epilogueForOp;
546 } else {
547 forOp.setUpperBound(upperBoundUnrolled);
548 }
549
550 // Create unrolled loop.
551 forOp.setStep(stepUnrolled);
552
553 auto iterArgs = ValueRange(forOp.getRegionIterArgs());
554 auto yieldedValues = forOp.getBody()->getTerminator()->getOperands();
555
557 forOp.getBody(), forOp.getInductionVar(), unrollFactor,
558 [&](unsigned i, Value iv, OpBuilder b) {
559 // iv' = iv + step * i;
560 auto stride = arith::MulIOp::create(
561 b, loc, step,
562 arith::ConstantOp::create(b, loc,
563 b.getIntegerAttr(iv.getType(), i)));
564 return arith::AddIOp::create(b, loc, iv, stride);
565 },
566 annotateFn, iterArgs, yieldedValues);
567 // Promote the loop body up if this has turned into a single iteration loop
568 // and `shouldPromoteIfSingleIteration` is true.
569 if (!shouldPromoteIfSingleIteration ||
570 forOp.promoteIfSingleIteration(rewriter).failed())
571 resultLoops.mainLoopOp = forOp;
572 return resultLoops;
573}
574
575/// Unrolls this loop completely.
576LogicalResult mlir::loopUnrollFull(scf::ForOp forOp) {
577 IRRewriter rewriter(forOp.getContext());
578 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
579 if (!mayBeConstantTripCount.has_value())
580 return failure();
581 const APInt &tripCount = *mayBeConstantTripCount;
582 if (tripCount.isZero())
583 return success();
584 if (tripCount.isOne())
585 return forOp.promoteIfSingleIteration(rewriter);
586 return loopUnrollByFactor(forOp, tripCount.getZExtValue());
587}
588
589/// Check if bounds of all inner loops are defined outside of `forOp`
590/// and return false if not.
591static bool areInnerBoundsInvariant(scf::ForOp forOp) {
592 auto walkResult = forOp.walk([&](scf::ForOp innerForOp) {
593 if (!forOp.isDefinedOutsideOfLoop(innerForOp.getLowerBound()) ||
594 !forOp.isDefinedOutsideOfLoop(innerForOp.getUpperBound()) ||
595 !forOp.isDefinedOutsideOfLoop(innerForOp.getStep()))
596 return WalkResult::interrupt();
597
598 return WalkResult::advance();
599 });
600 return !walkResult.wasInterrupted();
601}
602
603/// Unrolls and jams this loop by the specified factor.
604LogicalResult mlir::loopUnrollJamByFactor(scf::ForOp forOp,
605 uint64_t unrollJamFactor) {
606 assert(unrollJamFactor > 0 && "unroll jam factor should be positive");
607
608 if (unrollJamFactor == 1)
609 return success();
610
611 // If any control operand of any inner loop of `forOp` is defined within
612 // `forOp`, no unroll jam.
613 if (!areInnerBoundsInvariant(forOp)) {
614 LDBG() << "failed to unroll and jam: inner bounds are not invariant";
615 return failure();
616 }
617
618 // Currently, for operations with results are not supported.
619 if (forOp->getNumResults() > 0) {
620 LDBG() << "failed to unroll and jam: unsupported loop with results";
621 return failure();
622 }
623
624 // Currently, only constant trip count that divided by the unroll factor is
625 // supported.
626 std::optional<APInt> tripCount = forOp.getStaticTripCount();
627 if (!tripCount.has_value()) {
628 // If the trip count is dynamic, do not unroll & jam.
629 LDBG() << "failed to unroll and jam: trip count could not be determined";
630 return failure();
631 }
632 uint64_t tripCountValue = tripCount->getZExtValue();
633 if (tripCountValue == 0)
634 return success();
635 if (unrollJamFactor > tripCountValue) {
636 LDBG() << "unroll and jam factor is greater than trip count, set factor to "
637 "trip "
638 "count";
639 unrollJamFactor = tripCountValue;
640 } else if (tripCountValue % unrollJamFactor != 0) {
641 LDBG() << "failed to unroll and jam: unsupported trip count that is not a "
642 "multiple of unroll jam factor";
643 return failure();
644 }
645
646 // Nothing in the loop body other than the terminator.
647 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
648 return success();
649
650 // Gather all sub-blocks to jam upon the loop being unrolled.
652 jbg.walk(forOp);
653 auto &subBlocks = jbg.subBlocks;
654
655 // Collect inner loops.
656 SmallVector<scf::ForOp> innerLoops;
657 forOp.walk([&](scf::ForOp innerForOp) { innerLoops.push_back(innerForOp); });
658
659 // `operandMaps[i - 1]` carries old->new operand mapping for the ith unrolled
660 // iteration. There are (`unrollJamFactor` - 1) iterations.
661 SmallVector<IRMapping> operandMaps(unrollJamFactor - 1);
662
663 // For any loop with iter_args, replace it with a new loop that has
664 // `unrollJamFactor` copies of its iterOperands, iter_args and yield
665 // operands.
666 SmallVector<scf::ForOp> newInnerLoops;
667 IRRewriter rewriter(forOp.getContext());
668 for (scf::ForOp oldForOp : innerLoops) {
669 SmallVector<Value> dupIterOperands, dupYieldOperands;
670 ValueRange oldIterOperands = oldForOp.getInits();
671 ValueRange oldIterArgs = oldForOp.getRegionIterArgs();
672 ValueRange oldYieldOperands =
673 cast<scf::YieldOp>(oldForOp.getBody()->getTerminator()).getOperands();
674 // Get additional iterOperands, iterArgs, and yield operands. We will
675 // fix iterOperands and yield operands after cloning of sub-blocks.
676 for (unsigned i = unrollJamFactor - 1; i >= 1; --i) {
677 dupIterOperands.append(oldIterOperands.begin(), oldIterOperands.end());
678 dupYieldOperands.append(oldYieldOperands.begin(), oldYieldOperands.end());
679 }
680 // Create a new loop with additional iterOperands, iter_args and yield
681 // operands. This new loop will take the loop body of the original loop.
682 bool forOpReplaced = oldForOp == forOp;
683 scf::ForOp newForOp =
684 cast<scf::ForOp>(*oldForOp.replaceWithAdditionalYields(
685 rewriter, dupIterOperands, /*replaceInitOperandUsesInLoop=*/false,
686 [&](OpBuilder &b, Location loc, ArrayRef<BlockArgument> newBbArgs) {
687 return dupYieldOperands;
688 }));
689 newInnerLoops.push_back(newForOp);
690 // `forOp` has been replaced with a new loop.
691 if (forOpReplaced)
692 forOp = newForOp;
693 // Update `operandMaps` for `newForOp` iterArgs and results.
694 ValueRange newIterArgs = newForOp.getRegionIterArgs();
695 unsigned oldNumIterArgs = oldIterArgs.size();
696 ValueRange newResults = newForOp.getResults();
697 unsigned oldNumResults = newResults.size() / unrollJamFactor;
698 assert(oldNumIterArgs == oldNumResults &&
699 "oldNumIterArgs must be the same as oldNumResults");
700 for (unsigned i = unrollJamFactor - 1; i >= 1; --i) {
701 for (unsigned j = 0; j < oldNumIterArgs; ++j) {
702 // `newForOp` has `unrollJamFactor` - 1 new sets of iterArgs and
703 // results. Update `operandMaps[i - 1]` to map old iterArgs and results
704 // to those in the `i`th new set.
705 operandMaps[i - 1].map(newIterArgs[j],
706 newIterArgs[i * oldNumIterArgs + j]);
707 operandMaps[i - 1].map(newResults[j],
708 newResults[i * oldNumResults + j]);
709 }
710 }
711 }
712
713 // Scale the step of loop being unroll-jammed by the unroll-jam factor.
714 rewriter.setInsertionPoint(forOp);
715 int64_t step = forOp.getConstantStep()->getSExtValue();
716 auto newStep = rewriter.createOrFold<arith::MulIOp>(
717 forOp.getLoc(), forOp.getStep(),
718 rewriter.createOrFold<arith::ConstantOp>(
719 forOp.getLoc(), rewriter.getIndexAttr(unrollJamFactor)));
720 forOp.setStep(newStep);
721 auto forOpIV = forOp.getInductionVar();
722
723 // Unroll and jam (appends unrollJamFactor - 1 additional copies).
724 for (unsigned i = unrollJamFactor - 1; i >= 1; --i) {
725 for (auto &subBlock : subBlocks) {
726 // Builder to insert unroll-jammed bodies. Insert right at the end of
727 // sub-block.
728 OpBuilder builder(subBlock.first->getBlock(), std::next(subBlock.second));
729
730 // If the induction variable is used, create a remapping to the value for
731 // this unrolled instance.
732 if (!forOpIV.use_empty()) {
733 // iv' = iv + i * step, i = 1 to unrollJamFactor-1.
734 auto ivTag = builder.createOrFold<arith::ConstantOp>(
735 forOp.getLoc(), builder.getIndexAttr(step * i));
736 auto ivUnroll =
737 builder.createOrFold<arith::AddIOp>(forOp.getLoc(), forOpIV, ivTag);
738 operandMaps[i - 1].map(forOpIV, ivUnroll);
739 }
740 // Clone the sub-block being unroll-jammed.
741 for (auto it = subBlock.first; it != std::next(subBlock.second); ++it)
742 builder.clone(*it, operandMaps[i - 1]);
743 }
744 // Fix iterOperands and yield op operands of newly created loops.
745 for (auto newForOp : newInnerLoops) {
746 unsigned oldNumIterOperands =
747 newForOp.getNumRegionIterArgs() / unrollJamFactor;
748 unsigned numControlOperands = newForOp.getNumControlOperands();
749 auto yieldOp = cast<scf::YieldOp>(newForOp.getBody()->getTerminator());
750 unsigned oldNumYieldOperands = yieldOp.getNumOperands() / unrollJamFactor;
751 assert(oldNumIterOperands == oldNumYieldOperands &&
752 "oldNumIterOperands must be the same as oldNumYieldOperands");
753 for (unsigned j = 0; j < oldNumIterOperands; ++j) {
754 // The `i`th duplication of an old iterOperand or yield op operand
755 // needs to be replaced with a mapped value from `operandMaps[i - 1]`
756 // if such mapped value exists.
757 newForOp.setOperand(numControlOperands + i * oldNumIterOperands + j,
758 operandMaps[i - 1].lookupOrDefault(
759 newForOp.getOperand(numControlOperands + j)));
760 yieldOp.setOperand(
761 i * oldNumYieldOperands + j,
762 operandMaps[i - 1].lookupOrDefault(yieldOp.getOperand(j)));
763 }
764 }
765 }
766
767 // Promote the loop body up if this has turned into a single iteration loop.
768 (void)forOp.promoteIfSingleIteration(rewriter);
769 return success();
770}
771
773 Location loc, OpFoldResult lb,
775 OpFoldResult step) {
776 Range normalizedLoopBounds;
777 normalizedLoopBounds.offset = rewriter.getIndexAttr(0);
778 normalizedLoopBounds.stride = rewriter.getIndexAttr(1);
779 AffineExpr s0, s1, s2;
780 bindSymbols(rewriter.getContext(), s0, s1, s2);
781 AffineExpr e = (s1 - s0).ceilDiv(s2);
782 normalizedLoopBounds.size =
783 affine::makeComposedFoldedAffineApply(rewriter, loc, e, {lb, ub, step});
784 return normalizedLoopBounds;
785}
786
789 OpFoldResult step) {
790 if (getType(lb).isIndex()) {
791 return emitNormalizedLoopBoundsForIndexType(rewriter, loc, lb, ub, step);
792 }
793 // For non-index types, generate `arith` instructions
794 // Check if the loop is already known to have a constant zero lower bound or
795 // a constant one step.
796 bool isZeroBased = false;
797 if (auto lbCst = getConstantIntValue(lb))
798 isZeroBased = lbCst.value() == 0;
799
800 bool isStepOne = false;
801 if (auto stepCst = getConstantIntValue(step))
802 isStepOne = stepCst.value() == 1;
803
804 Type rangeType = getType(lb);
805 assert(rangeType == getType(ub) && rangeType == getType(step) &&
806 "expected matching types");
807
808 // Compute the number of iterations the loop executes: ceildiv(ub - lb, step)
809 // assuming the step is strictly positive. Update the bounds and the step
810 // of the loop to go from 0 to the number of iterations, if necessary.
811 if (isZeroBased && isStepOne)
812 return {lb, ub, step};
813
814 OpFoldResult diff = ub;
815 if (!isZeroBased) {
816 diff = rewriter.createOrFold<arith::SubIOp>(
817 loc, getValueOrCreateConstantIntOp(rewriter, loc, ub),
818 getValueOrCreateConstantIntOp(rewriter, loc, lb));
819 }
820 OpFoldResult newUpperBound = diff;
821 if (!isStepOne) {
822 newUpperBound = rewriter.createOrFold<arith::CeilDivSIOp>(
823 loc, getValueOrCreateConstantIntOp(rewriter, loc, diff),
824 getValueOrCreateConstantIntOp(rewriter, loc, step));
825 }
826
827 OpFoldResult newLowerBound = rewriter.getZeroAttr(rangeType);
828 OpFoldResult newStep = rewriter.getOneAttr(rangeType);
829
830 return {newLowerBound, newUpperBound, newStep};
831}
832
834 Location loc,
835 Value normalizedIv,
836 OpFoldResult origLb,
837 OpFoldResult origStep) {
838 AffineExpr d0, s0, s1;
839 bindSymbols(rewriter.getContext(), s0, s1);
840 bindDims(rewriter.getContext(), d0);
841 AffineExpr e = d0 * s1 + s0;
843 rewriter, loc, e, ArrayRef<OpFoldResult>{normalizedIv, origLb, origStep});
844 Value denormalizedIvVal =
845 getValueOrCreateConstantIndexOp(rewriter, loc, denormalizedIv);
846 SmallPtrSet<Operation *, 1> preservedUses;
847 // If an `affine.apply` operation is generated for denormalization, the use
848 // of `origLb` in those ops must not be replaced. These arent not generated
849 // when `origLb == 0` and `origStep == 1`.
850 if (!isZeroInteger(origLb) || !isOneInteger(origStep)) {
851 if (Operation *preservedUse = denormalizedIvVal.getDefiningOp()) {
852 preservedUses.insert(preservedUse);
853 }
854 }
855 rewriter.replaceAllUsesExcept(normalizedIv, denormalizedIvVal, preservedUses);
856}
857
859 Value normalizedIv, OpFoldResult origLb,
860 OpFoldResult origStep) {
861 if (getType(origLb).isIndex()) {
862 return denormalizeInductionVariableForIndexType(rewriter, loc, normalizedIv,
863 origLb, origStep);
864 }
865 Value denormalizedIv;
867 bool isStepOne = isOneInteger(origStep);
868 bool isZeroBased = isZeroInteger(origLb);
869
870 Value scaled = normalizedIv;
871 if (!isStepOne) {
872 Value origStepValue =
873 getValueOrCreateConstantIntOp(rewriter, loc, origStep);
874 scaled = arith::MulIOp::create(rewriter, loc, normalizedIv, origStepValue);
875 preserve.insert(scaled.getDefiningOp());
876 }
877 denormalizedIv = scaled;
878 if (!isZeroBased) {
879 Value origLbValue = getValueOrCreateConstantIntOp(rewriter, loc, origLb);
880 denormalizedIv = arith::AddIOp::create(rewriter, loc, scaled, origLbValue);
881 preserve.insert(denormalizedIv.getDefiningOp());
882 }
883
884 rewriter.replaceAllUsesExcept(normalizedIv, denormalizedIv, preserve);
885}
886
888 ArrayRef<OpFoldResult> values) {
889 assert(!values.empty() && "unexecpted empty array");
890 AffineExpr s0, s1;
891 bindSymbols(rewriter.getContext(), s0, s1);
892 AffineExpr mul = s0 * s1;
893 OpFoldResult products = rewriter.getIndexAttr(1);
894 for (auto v : values) {
896 rewriter, loc, mul, ArrayRef<OpFoldResult>{products, v});
897 }
898 return products;
899}
900
901/// Helper function to multiply a sequence of values.
903 ArrayRef<Value> values) {
904 assert(!values.empty() && "unexpected empty list");
905 if (getType(values.front()).isIndex()) {
907 OpFoldResult product = getProductOfIndexes(rewriter, loc, ofrs);
908 return getValueOrCreateConstantIndexOp(rewriter, loc, product);
909 }
910 std::optional<Value> productOf;
911 for (auto v : values) {
912 auto vOne = getConstantIntValue(v);
913 if (vOne && vOne.value() == 1)
914 continue;
915 if (productOf)
916 productOf = arith::MulIOp::create(rewriter, loc, productOf.value(), v)
917 .getResult();
918 else
919 productOf = v;
920 }
921 if (!productOf) {
922 productOf = arith::ConstantOp::create(
923 rewriter, loc, rewriter.getOneAttr(getType(values.front())))
924 .getResult();
925 }
926 return productOf.value();
927}
928
929/// For each original loop, the value of the
930/// induction variable can be obtained by dividing the induction variable of
931/// the linearized loop by the total number of iterations of the loops nested
932/// in it modulo the number of iterations in this loop (remove the values
933/// related to the outer loops):
934/// iv_i = floordiv(iv_linear, product-of-loop-ranges-until-i) mod range_i.
935/// Compute these iteratively from the innermost loop by creating a "running
936/// quotient" of division by the range.
937static std::pair<SmallVector<Value>, SmallPtrSet<Operation *, 2>>
939 Value linearizedIv, ArrayRef<Value> ubs) {
940
941 if (linearizedIv.getType().isIndex()) {
942 Operation *delinearizedOp = affine::AffineDelinearizeIndexOp::create(
943 rewriter, loc, linearizedIv, ubs);
944 auto resultVals = llvm::map_to_vector(
945 delinearizedOp->getResults(), [](OpResult r) -> Value { return r; });
946 return {resultVals, SmallPtrSet<Operation *, 2>{delinearizedOp}};
947 }
948
949 SmallVector<Value> delinearizedIvs(ubs.size());
950 SmallPtrSet<Operation *, 2> preservedUsers;
951
952 llvm::BitVector isUbOne(ubs.size());
953 for (auto [index, ub] : llvm::enumerate(ubs)) {
954 auto ubCst = getConstantIntValue(ub);
955 if (ubCst && ubCst.value() == 1)
956 isUbOne.set(index);
957 }
958
959 // Prune the lead ubs that are all ones.
960 unsigned numLeadingOneUbs = 0;
961 for (auto [index, ub] : llvm::enumerate(ubs)) {
962 if (!isUbOne.test(index)) {
963 break;
964 }
965 delinearizedIvs[index] = arith::ConstantOp::create(
966 rewriter, loc, rewriter.getZeroAttr(ub.getType()));
967 numLeadingOneUbs++;
968 }
969
970 Value previous = linearizedIv;
971 for (unsigned i = numLeadingOneUbs, e = ubs.size(); i < e; ++i) {
972 unsigned idx = ubs.size() - (i - numLeadingOneUbs) - 1;
973 if (i != numLeadingOneUbs && !isUbOne.test(idx + 1)) {
974 previous = arith::DivSIOp::create(rewriter, loc, previous, ubs[idx + 1]);
975 preservedUsers.insert(previous.getDefiningOp());
976 }
977 Value iv = previous;
978 if (i != e - 1) {
979 if (!isUbOne.test(idx)) {
980 iv = arith::RemSIOp::create(rewriter, loc, previous, ubs[idx]);
981 preservedUsers.insert(iv.getDefiningOp());
982 } else {
983 iv = arith::ConstantOp::create(
984 rewriter, loc, rewriter.getZeroAttr(ubs[idx].getType()));
985 }
986 }
987 delinearizedIvs[idx] = iv;
988 }
989 return {delinearizedIvs, preservedUsers};
990}
991
992LogicalResult mlir::coalesceLoops(RewriterBase &rewriter,
994 if (loops.size() < 2)
995 return failure();
996
997 scf::ForOp innermost = loops.back();
998 scf::ForOp outermost = loops.front();
999
1000 // Bail out if any loop has a known zero step, as normalization
1001 // would result in a division by zero.
1002 for (auto loop : loops) {
1003 if (auto step = getConstantIntValue(loop.getStep())) {
1004 if (step.value() == 0) {
1005 return failure();
1006 }
1007 }
1008 }
1009 // 1. Make sure all loops iterate from 0 to upperBound with step 1. This
1010 // allows the following code to assume upperBound is the number of iterations.
1011 for (auto loop : loops) {
1012 OpBuilder::InsertionGuard g(rewriter);
1013 rewriter.setInsertionPoint(outermost);
1014 Value lb = loop.getLowerBound();
1015 Value ub = loop.getUpperBound();
1016 Value step = loop.getStep();
1017 auto newLoopRange =
1018 emitNormalizedLoopBounds(rewriter, loop.getLoc(), lb, ub, step);
1019
1020 rewriter.modifyOpInPlace(loop, [&]() {
1021 loop.setLowerBound(getValueOrCreateConstantIntOp(rewriter, loop.getLoc(),
1022 newLoopRange.offset));
1023 loop.setUpperBound(getValueOrCreateConstantIntOp(rewriter, loop.getLoc(),
1024 newLoopRange.size));
1025 loop.setStep(getValueOrCreateConstantIntOp(rewriter, loop.getLoc(),
1026 newLoopRange.stride));
1027 });
1028 rewriter.setInsertionPointToStart(innermost.getBody());
1029 denormalizeInductionVariable(rewriter, loop.getLoc(),
1030 loop.getInductionVar(), lb, step);
1031 }
1032
1033 // 2. Emit code computing the upper bound of the coalesced loop as product
1034 // of the number of iterations of all loops.
1035 OpBuilder::InsertionGuard g(rewriter);
1036 rewriter.setInsertionPoint(outermost);
1037 Location loc = outermost.getLoc();
1038 SmallVector<Value> upperBounds = llvm::map_to_vector(
1039 loops, [](auto loop) { return loop.getUpperBound(); });
1040 Value upperBound = getProductOfIntsOrIndexes(rewriter, loc, upperBounds);
1041 outermost.setUpperBound(upperBound);
1042
1043 // Insert delinearization at the start of the outermost loop body.
1044 rewriter.setInsertionPointToStart(outermost.getBody());
1045 auto [delinearizeIvs, preservedUsers] = delinearizeInductionVariable(
1046 rewriter, loc, outermost.getInductionVar(), upperBounds);
1047 rewriter.replaceAllUsesExcept(outermost.getInductionVar(), delinearizeIvs[0],
1048 preservedUsers);
1049
1050 for (int i = loops.size() - 1; i > 0; --i) {
1051 auto outerLoop = loops[i - 1];
1052 auto innerLoop = loops[i];
1053
1054 Operation *innerTerminator = innerLoop.getBody()->getTerminator();
1055 auto yieldedVals = llvm::to_vector(innerTerminator->getOperands());
1056 assert(llvm::equal(outerLoop.getRegionIterArgs(), innerLoop.getInitArgs()));
1057 for (Value &yieldedVal : yieldedVals) {
1058 // The yielded value may be the induction variable of the inner loop,
1059 // which is about to be inlined and whose block argument is about to
1060 // be destroyed. Use its replacement value instead.
1061 if (yieldedVal == innerLoop.getInductionVar()) {
1062 yieldedVal = delinearizeIvs[i];
1063 continue;
1064 }
1065 // The yielded value may be an iteration argument of the inner loop
1066 // which is about to be inlined.
1067 auto iter = llvm::find(innerLoop.getRegionIterArgs(), yieldedVal);
1068 if (iter != innerLoop.getRegionIterArgs().end()) {
1069 unsigned iterArgIndex = iter - innerLoop.getRegionIterArgs().begin();
1070 // `outerLoop` iter args identical to the `innerLoop` init args.
1071 assert(iterArgIndex < innerLoop.getInitArgs().size());
1072 yieldedVal = innerLoop.getInitArgs()[iterArgIndex];
1073 }
1074 }
1075 rewriter.eraseOp(innerTerminator);
1076
1077 SmallVector<Value> innerBlockArgs;
1078 innerBlockArgs.push_back(delinearizeIvs[i]);
1079 llvm::append_range(innerBlockArgs, outerLoop.getRegionIterArgs());
1080 rewriter.inlineBlockBefore(innerLoop.getBody(), outerLoop.getBody(),
1081 Block::iterator(innerLoop), innerBlockArgs);
1082 rewriter.replaceOp(innerLoop, yieldedVals);
1083 }
1084 return success();
1085}
1086
1088 if (loops.empty()) {
1089 return failure();
1090 }
1091 IRRewriter rewriter(loops.front().getContext());
1092 return coalesceLoops(rewriter, loops);
1093}
1094
1095LogicalResult mlir::coalescePerfectlyNestedSCFForLoops(scf::ForOp op) {
1096 LogicalResult result(failure());
1098 getPerfectlyNestedLoops(loops, op);
1099
1100 // Look for a band of loops that can be coalesced, i.e. perfectly nested
1101 // loops with bounds defined above some loop.
1102
1103 // 1. For each loop, find above which parent loop its bounds operands are
1104 // defined.
1105 SmallVector<unsigned> operandsDefinedAbove(loops.size());
1106 for (unsigned i = 0, e = loops.size(); i < e; ++i) {
1107 operandsDefinedAbove[i] = i;
1108 for (unsigned j = 0; j < i; ++j) {
1109 SmallVector<Value> boundsOperands = {loops[i].getLowerBound(),
1110 loops[i].getUpperBound(),
1111 loops[i].getStep()};
1112 if (areValuesDefinedAbove(boundsOperands, loops[j].getRegion())) {
1113 operandsDefinedAbove[i] = j;
1114 break;
1115 }
1116 }
1117 }
1118
1119 // 2. For each inner loop check that the iter_args for the immediately outer
1120 // loop are the init for the immediately inner loop and that the yields of the
1121 // return of the inner loop is the yield for the immediately outer loop. Keep
1122 // track of where the chain starts from for each loop.
1123 SmallVector<unsigned> iterArgChainStart(loops.size());
1124 iterArgChainStart[0] = 0;
1125 for (unsigned i = 1, e = loops.size(); i < e; ++i) {
1126 // By default set the start of the chain to itself.
1127 iterArgChainStart[i] = i;
1128 auto outerloop = loops[i - 1];
1129 auto innerLoop = loops[i];
1130 if (outerloop.getNumRegionIterArgs() != innerLoop.getNumRegionIterArgs()) {
1131 continue;
1132 }
1133 if (!llvm::equal(outerloop.getRegionIterArgs(), innerLoop.getInitArgs())) {
1134 continue;
1135 }
1136 auto outerloopTerminator = outerloop.getBody()->getTerminator();
1137 if (!llvm::equal(outerloopTerminator->getOperands(),
1138 innerLoop.getResults())) {
1139 continue;
1140 }
1141 iterArgChainStart[i] = iterArgChainStart[i - 1];
1142 }
1143
1144 // 3. Identify bands of loops such that the operands of all of them are
1145 // defined above the first loop in the band. Traverse the nest bottom-up
1146 // so that modifications don't invalidate the inner loops.
1147 for (unsigned end = loops.size(); end > 0; --end) {
1148 unsigned start = 0;
1149 for (; start < end - 1; ++start) {
1150 auto maxPos =
1151 *std::max_element(std::next(operandsDefinedAbove.begin(), start),
1152 std::next(operandsDefinedAbove.begin(), end));
1153 if (maxPos > start)
1154 continue;
1155 if (iterArgChainStart[end - 1] > start)
1156 continue;
1157 auto band = llvm::MutableArrayRef(loops.data() + start, end - start);
1158 if (succeeded(coalesceLoops(band)))
1159 result = success();
1160 break;
1161 }
1162 // If a band was found and transformed, keep looking at the loops above
1163 // the outermost transformed loop.
1164 if (start != end - 1)
1165 end = start + 1;
1166 }
1167 return result;
1168}
1169
1171 RewriterBase &rewriter, scf::ParallelOp loops,
1172 ArrayRef<std::vector<unsigned>> combinedDimensions) {
1173 OpBuilder::InsertionGuard g(rewriter);
1174 rewriter.setInsertionPoint(loops);
1175 Location loc = loops.getLoc();
1176
1177 // Presort combined dimensions.
1178 auto sortedDimensions = llvm::to_vector<3>(combinedDimensions);
1179 for (auto &dims : sortedDimensions)
1180 llvm::sort(dims);
1181
1182 // Normalize ParallelOp's iteration pattern.
1183 SmallVector<Value, 3> normalizedUpperBounds;
1184 for (unsigned i = 0, e = loops.getNumLoops(); i < e; ++i) {
1185 OpBuilder::InsertionGuard g2(rewriter);
1186 rewriter.setInsertionPoint(loops);
1187 Value lb = loops.getLowerBound()[i];
1188 Value ub = loops.getUpperBound()[i];
1189 Value step = loops.getStep()[i];
1190 auto newLoopRange = emitNormalizedLoopBounds(rewriter, loc, lb, ub, step);
1191 normalizedUpperBounds.push_back(getValueOrCreateConstantIntOp(
1192 rewriter, loops.getLoc(), newLoopRange.size));
1193
1194 rewriter.setInsertionPointToStart(loops.getBody());
1195 denormalizeInductionVariable(rewriter, loc, loops.getInductionVars()[i], lb,
1196 step);
1197 }
1198
1199 // Combine iteration spaces.
1200 SmallVector<Value, 3> lowerBounds, upperBounds, steps;
1201 auto cst0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
1202 auto cst1 = arith::ConstantIndexOp::create(rewriter, loc, 1);
1203 for (auto &sortedDimension : sortedDimensions) {
1204 Value newUpperBound = arith::ConstantIndexOp::create(rewriter, loc, 1);
1205 for (auto idx : sortedDimension) {
1206 newUpperBound = arith::MulIOp::create(rewriter, loc, newUpperBound,
1207 normalizedUpperBounds[idx]);
1208 }
1209 lowerBounds.push_back(cst0);
1210 steps.push_back(cst1);
1211 upperBounds.push_back(newUpperBound);
1212 }
1213
1214 // Create new ParallelLoop with conversions to the original induction values.
1215 // The loop below uses divisions to get the relevant range of values in the
1216 // new induction value that represent each range of the original induction
1217 // value. The remainders then determine based on that range, which iteration
1218 // of the original induction value this represents. This is a normalized value
1219 // that is un-normalized already by the previous logic.
1220 auto newPloop = scf::ParallelOp::create(
1221 rewriter, loc, lowerBounds, upperBounds, steps,
1222 [&](OpBuilder &insideBuilder, Location, ValueRange ploopIVs) {
1223 for (unsigned i = 0, e = combinedDimensions.size(); i < e; ++i) {
1224 Value previous = ploopIVs[i];
1225 unsigned numberCombinedDimensions = combinedDimensions[i].size();
1226 // Iterate over all except the last induction value.
1227 for (unsigned j = numberCombinedDimensions - 1; j > 0; --j) {
1228 unsigned idx = combinedDimensions[i][j];
1229
1230 // Determine the current induction value's current loop iteration
1231 Value iv = arith::RemSIOp::create(insideBuilder, loc, previous,
1232 normalizedUpperBounds[idx]);
1233 replaceAllUsesInRegionWith(loops.getBody()->getArgument(idx), iv,
1234 loops.getRegion());
1235
1236 // Remove the effect of the current induction value to prepare for
1237 // the next value.
1238 previous = arith::DivSIOp::create(insideBuilder, loc, previous,
1239 normalizedUpperBounds[idx]);
1240 }
1241
1242 // The final induction value is just the remaining value.
1243 unsigned idx = combinedDimensions[i][0];
1244 replaceAllUsesInRegionWith(loops.getBody()->getArgument(idx),
1245 previous, loops.getRegion());
1246 }
1247 });
1248
1249 // Replace the old loop with the new loop.
1250 loops.getBody()->back().erase();
1251 newPloop.getBody()->getOperations().splice(
1252 Block::iterator(newPloop.getBody()->back()),
1253 loops.getBody()->getOperations());
1254 loops.erase();
1255}
1256
1257// Hoist the ops within `outer` that appear before `inner`.
1258// Such ops include the ops that have been introduced by parametric tiling.
1259// Ops that come from triangular loops (i.e. that belong to the program slice
1260// rooted at `outer`) and ops that have side effects cannot be hoisted.
1261// Return failure when any op fails to hoist.
1262static LogicalResult hoistOpsBetween(scf::ForOp outer, scf::ForOp inner) {
1263 SetVector<Operation *> forwardSlice;
1265 options.filter = [&inner](Operation *op) {
1266 return op != inner.getOperation();
1267 };
1268 getForwardSlice(outer.getInductionVar(), &forwardSlice, options);
1269 LogicalResult status = success();
1271 for (auto &op : outer.getBody()->without_terminator()) {
1272 // Stop when encountering the inner loop.
1273 if (&op == inner.getOperation())
1274 break;
1275 // Skip over non-hoistable ops.
1276 if (forwardSlice.count(&op) > 0) {
1277 status = failure();
1278 continue;
1279 }
1280 // Skip intermediate scf::ForOp, these are not considered a failure.
1281 if (isa<scf::ForOp>(op))
1282 continue;
1283 // Skip other ops with regions.
1284 if (op.getNumRegions() > 0) {
1285 status = failure();
1286 continue;
1287 }
1288 // Skip if op has side effects.
1289 // TODO: loads to immutable memory regions are ok.
1290 if (!isMemoryEffectFree(&op)) {
1291 status = failure();
1292 continue;
1293 }
1294 toHoist.push_back(&op);
1295 }
1296 auto *outerForOp = outer.getOperation();
1297 for (auto *op : toHoist)
1298 op->moveBefore(outerForOp);
1299 return status;
1300}
1301
1302// Traverse the interTile and intraTile loops and try to hoist ops such that
1303// bands of perfectly nested loops are isolated.
1304// Return failure if either perfect interTile or perfect intraTile bands cannot
1305// be formed.
1306static LogicalResult tryIsolateBands(const TileLoops &tileLoops) {
1307 LogicalResult status = success();
1308 const Loops &interTile = tileLoops.first;
1309 const Loops &intraTile = tileLoops.second;
1310 auto size = interTile.size();
1311 assert(size == intraTile.size());
1312 if (size <= 1)
1313 return success();
1314 for (unsigned s = 1; s < size; ++s)
1315 status = succeeded(status) ? hoistOpsBetween(intraTile[0], intraTile[s])
1316 : failure();
1317 for (unsigned s = 1; s < size; ++s)
1318 status = succeeded(status) ? hoistOpsBetween(interTile[0], interTile[s])
1319 : failure();
1320 return status;
1321}
1322
1323/// Collect perfectly nested loops starting from `rootForOps`. Loops are
1324/// perfectly nested if each loop is the first and only non-terminator operation
1325/// in the parent loop. Collect at most `maxLoops` loops and append them to
1326/// `forOps`.
1327template <typename T>
1329 SmallVectorImpl<T> &forOps, T rootForOp,
1330 unsigned maxLoops = std::numeric_limits<unsigned>::max()) {
1331 for (unsigned i = 0; i < maxLoops; ++i) {
1332 forOps.push_back(rootForOp);
1333 Block &body = rootForOp.getRegion().front();
1334 if (body.begin() != std::prev(body.end(), 2))
1335 return;
1336
1337 rootForOp = dyn_cast<T>(&body.front());
1338 if (!rootForOp)
1339 return;
1340 }
1341}
1342
1343static Loops stripmineSink(scf::ForOp forOp, Value factor,
1344 ArrayRef<scf::ForOp> targets) {
1345 assert(!forOp.getUnsignedCmp() && "unsigned loops are not supported");
1346 auto originalStep = forOp.getStep();
1347 auto iv = forOp.getInductionVar();
1348
1349 OpBuilder b(forOp);
1350 forOp.setStep(arith::MulIOp::create(b, forOp.getLoc(), originalStep, factor));
1351
1352 Loops innerLoops;
1353 for (auto t : targets) {
1354 assert(!t.getUnsignedCmp() && "unsigned loops are not supported");
1355
1356 // Save information for splicing ops out of t when done
1357 auto begin = t.getBody()->begin();
1358 auto nOps = t.getBody()->getOperations().size();
1359
1360 // Insert newForOp before the terminator of `t`.
1361 auto b = OpBuilder::atBlockTerminator((t.getBody()));
1362 Value stepped = arith::AddIOp::create(b, t.getLoc(), iv, forOp.getStep());
1363 Value ub =
1364 arith::MinSIOp::create(b, t.getLoc(), forOp.getUpperBound(), stepped);
1365
1366 // Splice [begin, begin + nOps - 1) into `newForOp` and replace uses.
1367 auto newForOp = scf::ForOp::create(b, t.getLoc(), iv, ub, originalStep);
1368 newForOp.getBody()->getOperations().splice(
1369 newForOp.getBody()->getOperations().begin(),
1370 t.getBody()->getOperations(), begin, std::next(begin, nOps - 1));
1371 replaceAllUsesInRegionWith(iv, newForOp.getInductionVar(),
1372 newForOp.getRegion());
1373
1374 innerLoops.push_back(newForOp);
1375 }
1376
1377 return innerLoops;
1378}
1379
1381 ArrayRef<Value> sizes,
1382 ArrayRef<scf::ForOp> targets) {
1384 SmallVector<scf::ForOp, 8> currentTargets(targets);
1385 for (auto it : llvm::zip(forOps, sizes)) {
1386 auto step = stripmineSink(std::get<0>(it), std::get<1>(it), currentTargets);
1387 res.push_back(step);
1388 currentTargets = step;
1389 }
1390 return res;
1391}
1392
1394 scf::ForOp target) {
1396 for (auto loops : tile(forOps, sizes, ArrayRef<scf::ForOp>(target)))
1397 res.push_back(llvm::getSingleElement(loops));
1398 return res;
1399}
1400
1402 // Collect perfectly nested loops. If more size values provided than nested
1403 // loops available, truncate `sizes`.
1405 forOps.reserve(sizes.size());
1406 getPerfectlyNestedLoopsImpl(forOps, rootForOp, sizes.size());
1407 if (forOps.size() < sizes.size())
1408 sizes = sizes.take_front(forOps.size());
1409
1410 return ::tile(forOps, sizes, forOps.back());
1411}
1412
1414 scf::ForOp root) {
1415 getPerfectlyNestedLoopsImpl(nestedLoops, root);
1416}
1417
1419 ArrayRef<int64_t> sizes) {
1420 // Collect perfectly nested loops. If more size values provided than nested
1421 // loops available, truncate `sizes`.
1423 forOps.reserve(sizes.size());
1424 getPerfectlyNestedLoopsImpl(forOps, rootForOp, sizes.size());
1425 if (forOps.size() < sizes.size())
1426 sizes = sizes.take_front(forOps.size());
1427
1428 // The strip-mining transformation splices loop bodies into a new inner loop
1429 // without threading iter_args. If any of the collected loops carries
1430 // iter_args, the splice would produce invalid IR (yielded values from the
1431 // inner scope used in the outer terminator). Skip the transformation in
1432 // that case.
1433 if (llvm::any_of(forOps,
1434 [](scf::ForOp op) { return !op.getInitArgs().empty(); }))
1435 return {};
1436
1437 // Compute the tile sizes such that i-th outer loop executes size[i]
1438 // iterations. Given that the loop current executes
1439 // numIterations = ceildiv((upperBound - lowerBound), step)
1440 // iterations, we need to tile with size ceildiv(numIterations, size[i]).
1441 SmallVector<Value, 4> tileSizes;
1442 tileSizes.reserve(sizes.size());
1443 for (unsigned i = 0, e = sizes.size(); i < e; ++i) {
1444 assert(sizes[i] > 0 && "expected strictly positive size for strip-mining");
1445
1446 auto forOp = forOps[i];
1447 OpBuilder builder(forOp);
1448 auto loc = forOp.getLoc();
1449 Value diff = arith::SubIOp::create(builder, loc, forOp.getUpperBound(),
1450 forOp.getLowerBound());
1451 Value numIterations = ceilDivPositive(builder, loc, diff, forOp.getStep());
1452 Value iterationsPerBlock =
1453 ceilDivPositive(builder, loc, numIterations, sizes[i]);
1454 tileSizes.push_back(iterationsPerBlock);
1455 }
1456
1457 // Call parametric tiling with the given sizes.
1458 auto intraTile = tile(forOps, tileSizes, forOps.back());
1459 TileLoops tileLoops = std::make_pair(forOps, intraTile);
1460
1461 // TODO: for now we just ignore the result of band isolation.
1462 // In the future, mapping decisions may be impacted by the ability to
1463 // isolate perfectly nested bands.
1464 (void)tryIsolateBands(tileLoops);
1465
1466 return tileLoops;
1467}
1468
1470 scf::ForallOp source,
1471 RewriterBase &rewriter) {
1472 unsigned numTargetOuts = target.getNumResults();
1473 unsigned numSourceOuts = source.getNumResults();
1474
1475 // Create fused shared_outs.
1476 SmallVector<Value> fusedOuts;
1477 llvm::append_range(fusedOuts, target.getOutputs());
1478 llvm::append_range(fusedOuts, source.getOutputs());
1479
1480 // Create a new scf.forall op after the source loop.
1481 rewriter.setInsertionPointAfter(source);
1482 scf::ForallOp fusedLoop = scf::ForallOp::create(
1483 rewriter, source.getLoc(), source.getMixedLowerBound(),
1484 source.getMixedUpperBound(), source.getMixedStep(), fusedOuts,
1485 source.getMapping());
1486
1487 // Map control operands.
1488 IRMapping mapping;
1489 mapping.map(target.getInductionVars(), fusedLoop.getInductionVars());
1490 mapping.map(source.getInductionVars(), fusedLoop.getInductionVars());
1491
1492 // Map shared outs.
1493 mapping.map(target.getRegionIterArgs(),
1494 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1495 mapping.map(source.getRegionIterArgs(),
1496 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1497
1498 // Append everything except the terminator into the fused operation.
1499 rewriter.setInsertionPointToStart(fusedLoop.getBody());
1500 for (Operation &op : target.getBody()->without_terminator())
1501 rewriter.clone(op, mapping);
1502 for (Operation &op : source.getBody()->without_terminator())
1503 rewriter.clone(op, mapping);
1504
1505 // Fuse the old terminator in_parallel ops into the new one.
1506 scf::InParallelOp targetTerm = target.getTerminator();
1507 scf::InParallelOp sourceTerm = source.getTerminator();
1508 scf::InParallelOp fusedTerm = fusedLoop.getTerminator();
1509 rewriter.setInsertionPointToStart(fusedTerm.getBody());
1510 for (Operation &op : targetTerm.getYieldingOps())
1511 rewriter.clone(op, mapping);
1512 for (Operation &op : sourceTerm.getYieldingOps())
1513 rewriter.clone(op, mapping);
1514
1515 // Replace old loops by substituting their uses by results of the fused loop.
1516 rewriter.replaceOp(target, fusedLoop.getResults().take_front(numTargetOuts));
1517 rewriter.replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1518
1519 return fusedLoop;
1520}
1521
1523 scf::ForOp source,
1524 RewriterBase &rewriter) {
1525 assert(source.getUnsignedCmp() == target.getUnsignedCmp() &&
1526 "incompatible signedness");
1527 unsigned numTargetOuts = target.getNumResults();
1528 unsigned numSourceOuts = source.getNumResults();
1529
1530 // Create fused init_args, with target's init_args before source's init_args.
1531 SmallVector<Value> fusedInitArgs;
1532 llvm::append_range(fusedInitArgs, target.getInitArgs());
1533 llvm::append_range(fusedInitArgs, source.getInitArgs());
1534
1535 // Create a new scf.for op after the source loop (with scf.yield terminator
1536 // (without arguments) only in case its init_args is empty).
1537 rewriter.setInsertionPointAfter(source);
1538 scf::ForOp fusedLoop = scf::ForOp::create(
1539 rewriter, source.getLoc(), source.getLowerBound(), source.getUpperBound(),
1540 source.getStep(), fusedInitArgs, /*bodyBuilder=*/nullptr,
1541 source.getUnsignedCmp());
1542
1543 // Map original induction variables and operands to those of the fused loop.
1544 IRMapping mapping;
1545 mapping.map(target.getInductionVar(), fusedLoop.getInductionVar());
1546 mapping.map(target.getRegionIterArgs(),
1547 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1548 mapping.map(source.getInductionVar(), fusedLoop.getInductionVar());
1549 mapping.map(source.getRegionIterArgs(),
1550 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1551
1552 // Merge target's body into the new (fused) for loop and then source's body.
1553 rewriter.setInsertionPointToStart(fusedLoop.getBody());
1554 for (Operation &op : target.getBody()->without_terminator())
1555 rewriter.clone(op, mapping);
1556 for (Operation &op : source.getBody()->without_terminator())
1557 rewriter.clone(op, mapping);
1558
1559 // Build fused yield results by appropriately mapping original yield operands.
1560 SmallVector<Value> yieldResults;
1561 for (Value operand : target.getBody()->getTerminator()->getOperands())
1562 yieldResults.push_back(mapping.lookupOrDefault(operand));
1563 for (Value operand : source.getBody()->getTerminator()->getOperands())
1564 yieldResults.push_back(mapping.lookupOrDefault(operand));
1565 if (!yieldResults.empty())
1566 scf::YieldOp::create(rewriter, source.getLoc(), yieldResults);
1567
1568 // Replace old loops by substituting their uses by results of the fused loop.
1569 rewriter.replaceOp(target, fusedLoop.getResults().take_front(numTargetOuts));
1570 rewriter.replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1571
1572 return fusedLoop;
1573}
1574
1575FailureOr<scf::ForallOp> mlir::normalizeForallOp(RewriterBase &rewriter,
1576 scf::ForallOp forallOp) {
1577 SmallVector<OpFoldResult> lbs = forallOp.getMixedLowerBound();
1578 SmallVector<OpFoldResult> ubs = forallOp.getMixedUpperBound();
1579 SmallVector<OpFoldResult> steps = forallOp.getMixedStep();
1580
1581 if (forallOp.isNormalized())
1582 return forallOp;
1583
1584 OpBuilder::InsertionGuard g(rewriter);
1585 auto loc = forallOp.getLoc();
1586 rewriter.setInsertionPoint(forallOp);
1588 for (auto [lb, ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
1589 Range normalizedLoopParams =
1590 emitNormalizedLoopBounds(rewriter, loc, lb, ub, step);
1591 newUbs.push_back(normalizedLoopParams.size);
1592 }
1593 (void)foldDynamicIndexList(newUbs);
1594
1595 // Use the normalized builder since the lower bounds are always 0 and the
1596 // steps are always 1.
1597 auto normalizedForallOp = scf::ForallOp::create(
1598 rewriter, loc, newUbs, forallOp.getOutputs(), forallOp.getMapping(),
1599 [](OpBuilder &, Location, ValueRange) {});
1600
1601 rewriter.inlineRegionBefore(forallOp.getBodyRegion(),
1602 normalizedForallOp.getBodyRegion(),
1603 normalizedForallOp.getBodyRegion().begin());
1604 // Remove the original empty block in the new loop.
1605 rewriter.eraseBlock(&normalizedForallOp.getBodyRegion().back());
1606
1607 rewriter.setInsertionPointToStart(normalizedForallOp.getBody());
1608 // Update the users of the original loop variables.
1609 for (auto [idx, iv] :
1610 llvm::enumerate(normalizedForallOp.getInductionVars())) {
1611 auto origLb = getValueOrCreateConstantIndexOp(rewriter, loc, lbs[idx]);
1612 auto origStep = getValueOrCreateConstantIndexOp(rewriter, loc, steps[idx]);
1613 denormalizeInductionVariable(rewriter, loc, iv, origLb, origStep);
1614 }
1615
1616 rewriter.replaceOp(forallOp, normalizedForallOp);
1617 return normalizedForallOp;
1618}
1619
1622 assert(!loops.empty() && "unexpected empty loop nest");
1623 if (loops.size() == 1)
1624 return isa_and_nonnull<scf::ForOp>(loops.front().getOperation());
1625 for (auto [outerLoop, innerLoop] :
1626 llvm::zip_equal(loops.drop_back(), loops.drop_front())) {
1627 auto outerFor = dyn_cast_or_null<scf::ForOp>(outerLoop.getOperation());
1628 auto innerFor = dyn_cast_or_null<scf::ForOp>(innerLoop.getOperation());
1629 if (!outerFor || !innerFor)
1630 return false;
1631 auto outerBBArgs = outerFor.getRegionIterArgs();
1632 auto innerIterArgs = innerFor.getInitArgs();
1633 if (outerBBArgs.size() != innerIterArgs.size())
1634 return false;
1635
1636 for (auto [outerBBArg, innerIterArg] :
1637 llvm::zip_equal(outerBBArgs, innerIterArgs)) {
1638 if (!llvm::hasSingleElement(outerBBArg.getUses()) ||
1639 innerIterArg != outerBBArg)
1640 return false;
1641 }
1642
1643 ValueRange outerYields =
1644 cast<scf::YieldOp>(outerFor.getBody()->getTerminator())->getOperands();
1645 ValueRange innerResults = innerFor.getResults();
1646 if (outerYields.size() != innerResults.size())
1647 return false;
1648 for (auto [outerYield, innerResult] :
1649 llvm::zip_equal(outerYields, innerResults)) {
1650 if (!llvm::hasSingleElement(innerResult.getUses()) ||
1651 outerYield != innerResult)
1652 return false;
1653 }
1654 }
1655 return true;
1656}
1657
1659mlir::getConstLoopBounds(mlir::LoopLikeOpInterface loopOp) {
1660 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1661 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1662 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1663 if (!loBnds || !upBnds || !steps)
1664 return {};
1666 for (auto [lb, ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1667 auto lbCst = getConstantIntValue(lb);
1668 auto ubCst = getConstantIntValue(ub);
1669 auto stepCst = getConstantIntValue(step);
1670 if (!lbCst || !ubCst || !stepCst)
1671 return {};
1672 loopRanges.emplace_back(*lbCst, *ubCst, *stepCst);
1673 }
1674 return loopRanges;
1675}
1676
1678mlir::getConstLoopTripCounts(mlir::LoopLikeOpInterface loopOp) {
1679 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1680 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1681 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1682 if (!loBnds || !upBnds || !steps)
1683 return {};
1685 for (auto [lb, ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1686 // TODO(#178506): Signedness is not handled correctly here.
1687 std::optional<llvm::APInt> numIter = constantTripCount(
1688 lb, ub, step, /*isSigned=*/true, scf::computeUbMinusLb);
1689 if (!numIter)
1690 return {};
1691 tripCounts.push_back(*numIter);
1692 }
1693 return tripCounts;
1694}
1695
1696FailureOr<scf::ParallelOp> mlir::parallelLoopUnrollByFactors(
1697 scf::ParallelOp op, ArrayRef<uint64_t> unrollFactors,
1698 RewriterBase &rewriter,
1699 function_ref<void(unsigned, Operation *, OpBuilder)> annotateFn,
1700 IRMapping *clonedToSrcOpsMap) {
1701 const unsigned numLoops = op.getNumLoops();
1702 assert(llvm::none_of(unrollFactors, [](uint64_t f) { return f == 0; }) &&
1703 "Expected positive unroll factors");
1704 assert((!unrollFactors.empty() && (unrollFactors.size() <= numLoops)) &&
1705 "Expected non-empty unroll factors of size <= to the number of loops");
1706
1707 // Bail out if no valid unroll factors were provided
1708 if (llvm::all_of(unrollFactors, [](uint64_t f) { return f == 1; }))
1709 return rewriter.notifyMatchFailure(
1710 op, "Unrolling not applied if all factors are 1");
1711
1712 // Return if the loop body is empty.
1713 if (llvm::hasSingleElement(op.getBody()->getOperations()))
1714 return rewriter.notifyMatchFailure(op, "Cannot unroll an empty loop body");
1715
1716 // If the provided unroll factors do not cover all the loop dims, they are
1717 // applied to the inner loop dimensions.
1718 const unsigned firstLoopDimIdx = numLoops - unrollFactors.size();
1719
1720 // Make sure that the unroll factors divide the iteration space evenly
1721 // TODO: Support unrolling loops with dynamic iteration spaces.
1723 if (tripCounts.empty())
1724 return rewriter.notifyMatchFailure(
1725 op, "Failed to compute constant trip counts for the loop. Note that "
1726 "dynamic loop sizes are not supported.");
1727
1728 for (unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1729 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1730 if (tripCounts[dimIdx].urem(unrollFactor) != 0)
1731 return rewriter.notifyMatchFailure(
1732 op, "Unroll factors don't divide the iteration space evenly");
1733 }
1734
1735 std::optional<SmallVector<OpFoldResult>> maybeFoldSteps = op.getLoopSteps();
1736 if (!maybeFoldSteps)
1737 return rewriter.notifyMatchFailure(op, "Failed to retrieve loop steps");
1739 for (auto step : *maybeFoldSteps)
1740 steps.push_back(static_cast<size_t>(*getConstantIntValue(step)));
1741
1742 for (unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1743 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1744 if (unrollFactor == 1)
1745 continue;
1746 const size_t origStep = steps[dimIdx];
1747 const int64_t newStep = origStep * unrollFactor;
1748 IRMapping clonedToSrcOpsMap;
1749
1750 ValueRange iterArgs = ValueRange(op.getRegionIterArgs());
1751 auto yieldedValues = op.getBody()->getTerminator()->getOperands();
1752
1754 op.getBody(), op.getInductionVars()[dimIdx], unrollFactor,
1755 [&](unsigned i, Value iv, OpBuilder b) {
1756 // iv' = iv + step * i;
1757 const AffineExpr expr = b.getAffineDimExpr(0) + (origStep * i);
1758 const auto map =
1759 b.getDimIdentityMap().dropResult(0).insertResult(expr, 0);
1760 return affine::AffineApplyOp::create(b, iv.getLoc(), map,
1761 ValueRange{iv});
1762 },
1763 /*annotateFn*/ annotateFn, iterArgs, yieldedValues, &clonedToSrcOpsMap);
1764
1765 // Update loop step
1766 auto prevInsertPoint = rewriter.saveInsertionPoint();
1767 rewriter.setInsertionPoint(op);
1768 op.getStepMutable()[dimIdx].assign(
1769 arith::ConstantIndexOp::create(rewriter, op.getLoc(), newStep));
1770 rewriter.restoreInsertionPoint(prevInsertPoint);
1771 }
1772 return op;
1773}
return success()
static OpFoldResult getProductOfIndexes(RewriterBase &rewriter, Location loc, ArrayRef< OpFoldResult > values)
Definition Utils.cpp:887
static LogicalResult tryIsolateBands(const TileLoops &tileLoops)
Definition Utils.cpp:1306
static void getPerfectlyNestedLoopsImpl(SmallVectorImpl< T > &forOps, T rootForOp, unsigned maxLoops=std::numeric_limits< unsigned >::max())
Collect perfectly nested loops starting from rootForOps.
Definition Utils.cpp:1328
static LogicalResult hoistOpsBetween(scf::ForOp outer, scf::ForOp inner)
Definition Utils.cpp:1262
static Range emitNormalizedLoopBoundsForIndexType(RewriterBase &rewriter, Location loc, OpFoldResult lb, OpFoldResult ub, OpFoldResult step)
Definition Utils.cpp:772
static Loops stripmineSink(scf::ForOp forOp, Value factor, ArrayRef< scf::ForOp > targets)
Definition Utils.cpp:1343
static Value ceilDivPositive(OpBuilder &builder, Location loc, Value dividend, int64_t divisor)
Definition Utils.cpp:266
static Value getProductOfIntsOrIndexes(RewriterBase &rewriter, Location loc, ArrayRef< Value > values)
Helper function to multiply a sequence of values.
Definition Utils.cpp:902
static std::pair< SmallVector< Value >, SmallPtrSet< Operation *, 2 > > delinearizeInductionVariable(RewriterBase &rewriter, Location loc, Value linearizedIv, ArrayRef< Value > ubs)
For each original loop, the value of the induction variable can be obtained by dividing the induction...
Definition Utils.cpp:938
static void denormalizeInductionVariableForIndexType(RewriterBase &rewriter, Location loc, Value normalizedIv, OpFoldResult origLb, OpFoldResult origStep)
Definition Utils.cpp:833
static bool areInnerBoundsInvariant(scf::ForOp forOp)
Check if bounds of all inner loops are defined outside of forOp and return false if not.
Definition Utils.cpp:591
static int64_t product(ArrayRef< int64_t > vals)
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static llvm::ManagedStatic< PassManagerOptions > options
#define mul(a, b)
Base type for affine expression.
Definition AffineExpr.h:68
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:33
OpListType::iterator iterator
Definition Block.h:164
unsigned getNumArguments()
Definition Block.h:152
Operation & front()
Definition Block.h:177
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:111
iterator end()
Definition Block.h:168
iterator begin()
Definition Block.h:167
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:116
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:237
TypedAttr getZeroAttr(Type type)
Definition Builders.cpp:333
MLIRContext * getContext() const
Definition Builders.h:56
TypedAttr getOneAttr(Type type)
Definition Builders.cpp:351
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
Definition IRMapping.h:65
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
bool contains(T from) const
Checks to see if a mapping for 'from' exists.
Definition IRMapping.h:51
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
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
InsertPoint saveInsertionPoint() const
Return a saved insertion point.
Definition Builders.h:388
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
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
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
static OpBuilder atBlockTerminator(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the block terminator.
Definition Builders.h:255
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
void restoreInsertionPoint(InsertPoint ip)
Restore the insert point to a previously saved point.
Definition Builders.h:393
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Definition Builders.h:528
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:415
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
This is a value defined by a result of an operation.
Definition Value.h:454
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
operand_type_range getOperandTypes()
Definition Operation.h:422
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:729
result_type_range getResultTypes()
Definition Operation.h:453
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
result_range getResults()
Definition Operation.h:440
Operation * clone(IRMapping &mapper, const CloneOptions &options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
BlockArgListType getArguments()
Definition Region.h:94
iterator begin()
Definition Region.h:55
ParentT getParentOfType()
Find the first parent operation of the given type, or nullptr if there is no ancestor operation.
Definition Region.h:221
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 eraseBlock(Block *block)
This method erases all operations in a block.
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.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues={})
Inline the operations of block 'source' into block 'dest' before the given position.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region &region, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIndex() const
Definition Types.cpp:56
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
Definition Types.cpp:114
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 replaceUsesWithIf(Value newValue, function_ref< bool(OpOperand &)> shouldReplace)
Replace all uses of 'this' value with 'newValue' if the given callback returns true.
Definition Value.cpp:91
Type getType() const
Return the type of this value.
Definition Value.h:105
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
Specialization of arith.constant op that returns an integer of index type.
Definition Arith.h:93
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
std::optional< llvm::APSInt > computeUbMinusLb(Value lb, Value ub, bool isSigned)
Helper function to compute the difference between two values.
Definition SCF.cpp:116
Include the generated interface declarations.
void getPerfectlyNestedLoops(SmallVectorImpl< scf::ForOp > &nestedLoops, scf::ForOp root)
Get perfectly nested sequence of loops starting at root of loop nest (the first op being another Affi...
Definition Utils.cpp:1413
bool isPerfectlyNestedForLoops(MutableArrayRef< LoopLikeOpInterface > loops)
Check if the provided loops are perfectly nested for-loops.
Definition Utils.cpp:1620
FailureOr< UnrolledLoopInfo > loopUnrollByFactor(scf::ForOp forOp, uint64_t unrollFactor, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn=nullptr, bool shouldPromoteIfSingleIteration=true)
Unrolls this for operation by the specified unroll factor.
Definition Utils.cpp:419
LogicalResult outlineIfOp(RewriterBase &b, scf::IfOp ifOp, func::FuncOp *thenFn, StringRef thenFnName, func::FuncOp *elseFn, StringRef elseFnName)
Outline the then and/or else regions of ifOp as follows:
Definition Utils.cpp:218
void replaceAllUsesInRegionWith(Value orig, Value replacement, Region &region)
Replace all uses of orig within the given region with replacement.
SmallVector< scf::ForOp > replaceLoopNestWithNewYields(RewriterBase &rewriter, MutableArrayRef< scf::ForOp > loopNest, ValueRange newIterOperands, const NewYieldValuesFn &newYieldValuesFn, bool replaceIterOperandsUsesInLoop=true)
Update a perfectly nested loop nest to yield new values from the innermost loop and propagating it up...
Definition Utils.cpp:36
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
std::function< SmallVector< Value >( OpBuilder &b, Location loc, ArrayRef< BlockArgument > newBbArgs)> NewYieldValuesFn
A function that returns the additional yielded values during replaceWithAdditionalYields.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:310
LogicalResult coalescePerfectlyNestedSCFForLoops(scf::ForOp op)
Walk an affine.for to find a band to coalesce.
Definition Utils.cpp:1095
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
Definition AffineExpr.h:311
void generateUnrolledLoop(Block *loopBodyBlock, Value iv, uint64_t unrollFactor, function_ref< Value(unsigned, Value, OpBuilder)> ivRemapFn, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn, ValueRange iterArgs, ValueRange yieldedValues, IRMapping *clonedToSrcOpsMap=nullptr)
Generate unrolled copies of an scf loop's 'loopBodyBlock', with 'iterArgs' and 'yieldedValues' as the...
Definition Utils.cpp:295
Value getValueOrCreateConstantIntOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:105
LogicalResult loopUnrollFull(scf::ForOp forOp)
Unrolls this loop completely.
Definition Utils.cpp:576
llvm::SmallVector< llvm::APInt > getConstLoopTripCounts(mlir::LoopLikeOpInterface loopOp)
Get constant trip counts for each of the induction variables of the given loop operation.
Definition Utils.cpp:1678
std::pair< Loops, Loops > TileLoops
Definition Utils.h:167
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
llvm::SmallVector< std::tuple< int64_t, int64_t, int64_t > > getConstLoopBounds(mlir::LoopLikeOpInterface loopOp)
Get constant loop bounds and steps for each of the induction variables of the given loop operation,...
Definition Utils.cpp:1659
void collapseParallelLoops(RewriterBase &rewriter, scf::ParallelOp loops, ArrayRef< std::vector< unsigned > > combinedDimensions)
Take the ParallelLoop and for each set of dimension indices, combine them into a single dimension.
Definition Utils.cpp:1170
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:125
FailureOr< std::pair< scf::ForOp, scf::ForOp > > splitForOpAtPoint(RewriterBase &rewriter, scf::ForOp forOp, Value splitPoint)
Splits forOp into two consecutive loops at splitPoint: first: [lowerBound, splitPoint) second: [split...
Definition Utils.cpp:368
std::optional< std::pair< APInt, bool > > getConstantAPIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
SliceOptions ForwardSliceOptions
Loops tilePerfectlyNested(scf::ForOp rootForOp, ArrayRef< Value > sizes)
Tile a nest of scf::ForOp loops rooted at rootForOp with the given (parametric) sizes.
Definition Utils.cpp:1401
LogicalResult loopUnrollJamByFactor(scf::ForOp forOp, uint64_t unrollFactor)
Unrolls and jams this scf.for operation by the specified unroll factor.
Definition Utils.cpp:604
bool getInnermostParallelLoops(Operation *rootOp, SmallVectorImpl< scf::ParallelOp > &result)
Get a list of innermost parallel loops contained in rootOp.
Definition Utils.cpp:241
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Definition AffineExpr.h:325
FailureOr< scf::ParallelOp > parallelLoopUnrollByFactors(scf::ParallelOp op, ArrayRef< uint64_t > unrollFactors, RewriterBase &rewriter, function_ref< void(unsigned, Operation *, OpBuilder)> annotateFn=nullptr, IRMapping *clonedToSrcOpsMap=nullptr)
Unroll this scf::Parallel loop by the specified unroll factors.
Definition Utils.cpp:1696
void getUsedValuesDefinedAbove(Region &region, Region &limit, SetVector< Value > &values)
Fill values with a list of values defined at the ancestors of the limit region and used within region...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
Definition Utils.cpp:1380
FailureOr< func::FuncOp > outlineSingleBlockRegion(RewriterBase &rewriter, Location loc, Region &region, StringRef funcName, func::CallOp *callOp=nullptr)
Outline a region with a single block into a new FuncOp.
Definition Utils.cpp:115
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
bool areValuesDefinedAbove(Range values, Region &limit)
Check if all values in the provided range are defined above the limit region.
Definition RegionUtils.h:26
void denormalizeInductionVariable(RewriterBase &rewriter, Location loc, Value normalizedIv, OpFoldResult origLb, OpFoldResult origStep)
Get back the original induction variable values after loop normalization.
Definition Utils.cpp:858
std::optional< uint64_t > getConstantUIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer zero-extended to 64 bits.
scf::ForallOp fuseIndependentSiblingForallLoops(scf::ForallOp target, scf::ForallOp source, RewriterBase &rewriter)
Given two scf.forall loops, target and source, fuses target into source.
Definition Utils.cpp:1469
LogicalResult coalesceLoops(MutableArrayRef< scf::ForOp > loops)
Replace a perfect nest of "for" loops with a single linearized loop.
Definition Utils.cpp:1087
scf::ForOp fuseIndependentSiblingForLoops(scf::ForOp target, scf::ForOp source, RewriterBase &rewriter)
Given two scf.for loops, target and source, fuses target into source.
Definition Utils.cpp:1522
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
TileLoops extractFixedOuterLoops(scf::ForOp rootFOrOp, ArrayRef< int64_t > sizes)
Definition Utils.cpp:1418
Range emitNormalizedLoopBounds(RewriterBase &rewriter, Location loc, OpFoldResult lb, OpFoldResult ub, OpFoldResult step)
Materialize bounds and step of a zero-based and unit-step loop derived by normalizing the specified b...
Definition Utils.cpp:787
SmallVector< scf::ForOp, 8 > Loops
Tile a nest of standard for loops rooted at rootForOp by finding such parametric tile sizes that the ...
Definition Utils.h:166
bool isOneInteger(OpFoldResult v)
Return true if v is an IntegerAttr with value 1.
std::optional< APInt > constantTripCount(OpFoldResult lb, OpFoldResult ub, OpFoldResult step, bool isSigned, llvm::function_ref< std::optional< llvm::APSInt >(Value, Value, bool)> computeUbMinusLb)
Return the number of iterations for a loop with a lower bound lb, upper bound ub and step step,...
LogicalResult foldDynamicIndexList(SmallVectorImpl< OpFoldResult > &ofrs, bool onlyNonNegative=false, bool onlyNonZero=false)
Returns "success" when any of the elements in ofrs is a constant value.
FailureOr< scf::ForallOp > normalizeForallOp(RewriterBase &rewriter, scf::ForallOp forallOp)
Normalize an scf.forall operation.
Definition Utils.cpp:1575
void getForwardSlice(Operation *op, SetVector< Operation * > *forwardSlice, const ForwardSliceOptions &options={})
Fills forwardSlice with the computed forward slice (i.e.
void walk(Operation *op)
SmallVector< std::pair< Block::iterator, Block::iterator > > subBlocks
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...
OpFoldResult stride
OpFoldResult size
OpFoldResult offset
std::optional< scf::ForOp > epilogueLoopOp
Definition Utils.h:109
std::optional< scf::ForOp > mainLoopOp
Definition Utils.h:108
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.