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 zero = arith::ConstantOp::create(
517 boundsBuilder, loc, boundsBuilder.getZeroAttr(diff.getType()));
518 Value nonEmpty = arith::CmpIOp::create(boundsBuilder, loc,
519 forOp.getUnsignedCmp()
520 ? arith::CmpIPredicate::ult
521 : arith::CmpIPredicate::slt,
522 lowerBound, upperBound);
523 diff = arith::SelectOp::create(boundsBuilder, loc, nonEmpty, diff, zero);
524
525 Value tripCount = ceilDivPositive(boundsBuilder, loc, diff, step);
526 Value unrollFactorCst = arith::ConstantOp::create(
527 boundsBuilder, loc,
528 boundsBuilder.getIntegerAttr(tripCount.getType(), unrollFactor));
529 Value tripCountRem =
530 arith::RemSIOp::create(boundsBuilder, loc, tripCount, unrollFactorCst);
531 // Compute tripCountEvenMultiple = tripCount - (tripCount % unrollFactor)
532 Value tripCountEvenMultiple =
533 arith::SubIOp::create(boundsBuilder, loc, tripCount, tripCountRem);
534 // Compute upperBoundUnrolled = UpperBound + tripCountEvenMultiple * step
535 upperBoundUnrolled = arith::AddIOp::create(
536 boundsBuilder, loc, lowerBound,
537 arith::MulIOp::create(boundsBuilder, loc, tripCountEvenMultiple, step));
538 // Compute stepUnrolled = step * unrollFactor
539 stepUnrolled =
540 arith::MulIOp::create(boundsBuilder, loc, step, unrollFactorCst);
541 }
542
543 UnrolledLoopInfo resultLoops;
544
545 // Create epilogue clean up loop starting at 'upperBoundUnrolled'.
546 if (generateEpilogueLoop) {
547 auto splitLoops = splitForOpAtPoint(rewriter, forOp, upperBoundUnrolled);
548 if (failed(splitLoops))
549 return failure();
550 forOp = splitLoops->first;
551 scf::ForOp epilogueForOp = splitLoops->second;
552 if (!shouldPromoteIfSingleIteration ||
553 epilogueForOp.promoteIfSingleIteration(rewriter).failed())
554 resultLoops.epilogueLoopOp = epilogueForOp;
555 } else {
556 forOp.setUpperBound(upperBoundUnrolled);
557 }
558
559 // Create unrolled loop.
560 forOp.setStep(stepUnrolled);
561
562 auto iterArgs = ValueRange(forOp.getRegionIterArgs());
563 auto yieldedValues = forOp.getBody()->getTerminator()->getOperands();
564
566 forOp.getBody(), forOp.getInductionVar(), unrollFactor,
567 [&](unsigned i, Value iv, OpBuilder b) {
568 // iv' = iv + step * i;
569 auto stride = arith::MulIOp::create(
570 b, loc, step,
571 arith::ConstantOp::create(b, loc,
572 b.getIntegerAttr(iv.getType(), i)));
573 return arith::AddIOp::create(b, loc, iv, stride);
574 },
575 annotateFn, iterArgs, yieldedValues);
576 // Promote the loop body up if this has turned into a single iteration loop
577 // and `shouldPromoteIfSingleIteration` is true.
578 if (!shouldPromoteIfSingleIteration ||
579 forOp.promoteIfSingleIteration(rewriter).failed())
580 resultLoops.mainLoopOp = forOp;
581 return resultLoops;
582}
583
584/// Unrolls this loop completely.
585LogicalResult mlir::loopUnrollFull(scf::ForOp forOp) {
586 IRRewriter rewriter(forOp.getContext());
587 std::optional<APInt> mayBeConstantTripCount = forOp.getStaticTripCount();
588 if (!mayBeConstantTripCount.has_value())
589 return failure();
590 const APInt &tripCount = *mayBeConstantTripCount;
591 if (tripCount.isZero())
592 return success();
593 if (tripCount.isOne())
594 return forOp.promoteIfSingleIteration(rewriter);
595 return loopUnrollByFactor(forOp, tripCount.getZExtValue());
596}
597
598/// Check if bounds of all inner loops are defined outside of `forOp`
599/// and return false if not.
600static bool areInnerBoundsInvariant(scf::ForOp forOp) {
601 auto walkResult = forOp.walk([&](scf::ForOp innerForOp) {
602 if (!forOp.isDefinedOutsideOfLoop(innerForOp.getLowerBound()) ||
603 !forOp.isDefinedOutsideOfLoop(innerForOp.getUpperBound()) ||
604 !forOp.isDefinedOutsideOfLoop(innerForOp.getStep()))
605 return WalkResult::interrupt();
606
607 return WalkResult::advance();
608 });
609 return !walkResult.wasInterrupted();
610}
611
612/// Unrolls and jams this loop by the specified factor.
613LogicalResult mlir::loopUnrollJamByFactor(scf::ForOp forOp,
614 uint64_t unrollJamFactor) {
615 assert(unrollJamFactor > 0 && "unroll jam factor should be positive");
616
617 if (unrollJamFactor == 1)
618 return success();
619
620 // If any control operand of any inner loop of `forOp` is defined within
621 // `forOp`, no unroll jam.
622 if (!areInnerBoundsInvariant(forOp)) {
623 LDBG() << "failed to unroll and jam: inner bounds are not invariant";
624 return failure();
625 }
626
627 // Currently, for operations with results are not supported.
628 if (forOp->getNumResults() > 0) {
629 LDBG() << "failed to unroll and jam: unsupported loop with results";
630 return failure();
631 }
632
633 // Currently, only constant trip count that divided by the unroll factor is
634 // supported.
635 std::optional<APInt> tripCount = forOp.getStaticTripCount();
636 if (!tripCount.has_value()) {
637 // If the trip count is dynamic, do not unroll & jam.
638 LDBG() << "failed to unroll and jam: trip count could not be determined";
639 return failure();
640 }
641 uint64_t tripCountValue = tripCount->getZExtValue();
642 if (tripCountValue == 0)
643 return success();
644 if (unrollJamFactor > tripCountValue) {
645 LDBG() << "unroll and jam factor is greater than trip count, set factor to "
646 "trip "
647 "count";
648 unrollJamFactor = tripCountValue;
649 } else if (tripCountValue % unrollJamFactor != 0) {
650 LDBG() << "failed to unroll and jam: unsupported trip count that is not a "
651 "multiple of unroll jam factor";
652 return failure();
653 }
654
655 // Nothing in the loop body other than the terminator.
656 if (llvm::hasSingleElement(forOp.getBody()->getOperations()))
657 return success();
658
659 // Gather all sub-blocks to jam upon the loop being unrolled.
661 jbg.walk(forOp);
662 auto &subBlocks = jbg.subBlocks;
663
664 // Collect inner loops.
665 SmallVector<scf::ForOp> innerLoops;
666 forOp.walk([&](scf::ForOp innerForOp) { innerLoops.push_back(innerForOp); });
667
668 // `operandMaps[i - 1]` carries old->new operand mapping for the ith unrolled
669 // iteration. There are (`unrollJamFactor` - 1) iterations.
670 SmallVector<IRMapping> operandMaps(unrollJamFactor - 1);
671
672 // For any loop with iter_args, replace it with a new loop that has
673 // `unrollJamFactor` copies of its iterOperands, iter_args and yield
674 // operands.
675 SmallVector<scf::ForOp> newInnerLoops;
676 IRRewriter rewriter(forOp.getContext());
677 for (scf::ForOp oldForOp : innerLoops) {
678 SmallVector<Value> dupIterOperands, dupYieldOperands;
679 ValueRange oldIterOperands = oldForOp.getInits();
680 ValueRange oldIterArgs = oldForOp.getRegionIterArgs();
681 ValueRange oldYieldOperands =
682 cast<scf::YieldOp>(oldForOp.getBody()->getTerminator()).getOperands();
683 // Get additional iterOperands, iterArgs, and yield operands. We will
684 // fix iterOperands and yield operands after cloning of sub-blocks.
685 for (unsigned i = unrollJamFactor - 1; i >= 1; --i) {
686 dupIterOperands.append(oldIterOperands.begin(), oldIterOperands.end());
687 dupYieldOperands.append(oldYieldOperands.begin(), oldYieldOperands.end());
688 }
689 // Create a new loop with additional iterOperands, iter_args and yield
690 // operands. This new loop will take the loop body of the original loop.
691 bool forOpReplaced = oldForOp == forOp;
692 scf::ForOp newForOp =
693 cast<scf::ForOp>(*oldForOp.replaceWithAdditionalYields(
694 rewriter, dupIterOperands, /*replaceInitOperandUsesInLoop=*/false,
695 [&](OpBuilder &b, Location loc, ArrayRef<BlockArgument> newBbArgs) {
696 return dupYieldOperands;
697 }));
698 newInnerLoops.push_back(newForOp);
699 // `forOp` has been replaced with a new loop.
700 if (forOpReplaced)
701 forOp = newForOp;
702 // Update `operandMaps` for `newForOp` iterArgs and results.
703 ValueRange newIterArgs = newForOp.getRegionIterArgs();
704 unsigned oldNumIterArgs = oldIterArgs.size();
705 ValueRange newResults = newForOp.getResults();
706 unsigned oldNumResults = newResults.size() / unrollJamFactor;
707 assert(oldNumIterArgs == oldNumResults &&
708 "oldNumIterArgs must be the same as oldNumResults");
709 for (unsigned i = unrollJamFactor - 1; i >= 1; --i) {
710 for (unsigned j = 0; j < oldNumIterArgs; ++j) {
711 // `newForOp` has `unrollJamFactor` - 1 new sets of iterArgs and
712 // results. Update `operandMaps[i - 1]` to map old iterArgs and results
713 // to those in the `i`th new set.
714 operandMaps[i - 1].map(newIterArgs[j],
715 newIterArgs[i * oldNumIterArgs + j]);
716 operandMaps[i - 1].map(newResults[j],
717 newResults[i * oldNumResults + j]);
718 }
719 }
720 }
721
722 // Scale the step of loop being unroll-jammed by the unroll-jam factor.
723 rewriter.setInsertionPoint(forOp);
724 int64_t step = forOp.getConstantStep()->getSExtValue();
725 auto newStep = rewriter.createOrFold<arith::MulIOp>(
726 forOp.getLoc(), forOp.getStep(),
727 rewriter.createOrFold<arith::ConstantOp>(
728 forOp.getLoc(), rewriter.getIndexAttr(unrollJamFactor)));
729 forOp.setStep(newStep);
730 auto forOpIV = forOp.getInductionVar();
731
732 // Unroll and jam (appends unrollJamFactor - 1 additional copies).
733 for (unsigned i = unrollJamFactor - 1; i >= 1; --i) {
734 for (auto &subBlock : subBlocks) {
735 // Builder to insert unroll-jammed bodies. Insert right at the end of
736 // sub-block.
737 OpBuilder builder(subBlock.first->getBlock(), std::next(subBlock.second));
738
739 // If the induction variable is used, create a remapping to the value for
740 // this unrolled instance.
741 if (!forOpIV.use_empty()) {
742 // iv' = iv + i * step, i = 1 to unrollJamFactor-1.
743 auto ivTag = builder.createOrFold<arith::ConstantOp>(
744 forOp.getLoc(), builder.getIndexAttr(step * i));
745 auto ivUnroll =
746 builder.createOrFold<arith::AddIOp>(forOp.getLoc(), forOpIV, ivTag);
747 operandMaps[i - 1].map(forOpIV, ivUnroll);
748 }
749 // Clone the sub-block being unroll-jammed.
750 for (auto it = subBlock.first; it != std::next(subBlock.second); ++it)
751 builder.clone(*it, operandMaps[i - 1]);
752 }
753 // Fix iterOperands and yield op operands of newly created loops.
754 for (auto newForOp : newInnerLoops) {
755 unsigned oldNumIterOperands =
756 newForOp.getNumRegionIterArgs() / unrollJamFactor;
757 unsigned numControlOperands = newForOp.getNumControlOperands();
758 auto yieldOp = cast<scf::YieldOp>(newForOp.getBody()->getTerminator());
759 unsigned oldNumYieldOperands = yieldOp.getNumOperands() / unrollJamFactor;
760 assert(oldNumIterOperands == oldNumYieldOperands &&
761 "oldNumIterOperands must be the same as oldNumYieldOperands");
762 for (unsigned j = 0; j < oldNumIterOperands; ++j) {
763 // The `i`th duplication of an old iterOperand or yield op operand
764 // needs to be replaced with a mapped value from `operandMaps[i - 1]`
765 // if such mapped value exists.
766 newForOp.setOperand(numControlOperands + i * oldNumIterOperands + j,
767 operandMaps[i - 1].lookupOrDefault(
768 newForOp.getOperand(numControlOperands + j)));
769 yieldOp.setOperand(
770 i * oldNumYieldOperands + j,
771 operandMaps[i - 1].lookupOrDefault(yieldOp.getOperand(j)));
772 }
773 }
774 }
775
776 // Promote the loop body up if this has turned into a single iteration loop.
777 (void)forOp.promoteIfSingleIteration(rewriter);
778 return success();
779}
780
782 Location loc, OpFoldResult lb,
784 OpFoldResult step) {
785 Range normalizedLoopBounds;
786 normalizedLoopBounds.offset = rewriter.getIndexAttr(0);
787 normalizedLoopBounds.stride = rewriter.getIndexAttr(1);
788 AffineExpr s0, s1, s2;
789 bindSymbols(rewriter.getContext(), s0, s1, s2);
790 AffineExpr e = (s1 - s0).ceilDiv(s2);
791 normalizedLoopBounds.size =
792 affine::makeComposedFoldedAffineApply(rewriter, loc, e, {lb, ub, step});
793 return normalizedLoopBounds;
794}
795
798 OpFoldResult step) {
799 if (getType(lb).isIndex()) {
800 return emitNormalizedLoopBoundsForIndexType(rewriter, loc, lb, ub, step);
801 }
802 // For non-index types, generate `arith` instructions
803 // Check if the loop is already known to have a constant zero lower bound or
804 // a constant one step.
805 bool isZeroBased = false;
806 if (auto lbCst = getConstantIntValue(lb))
807 isZeroBased = lbCst.value() == 0;
808
809 bool isStepOne = false;
810 if (auto stepCst = getConstantIntValue(step))
811 isStepOne = stepCst.value() == 1;
812
813 Type rangeType = getType(lb);
814 assert(rangeType == getType(ub) && rangeType == getType(step) &&
815 "expected matching types");
816
817 // Compute the number of iterations the loop executes: ceildiv(ub - lb, step)
818 // assuming the step is strictly positive. Update the bounds and the step
819 // of the loop to go from 0 to the number of iterations, if necessary.
820 if (isZeroBased && isStepOne)
821 return {lb, ub, step};
822
823 OpFoldResult diff = ub;
824 if (!isZeroBased) {
825 diff = rewriter.createOrFold<arith::SubIOp>(
826 loc, getValueOrCreateConstantIntOp(rewriter, loc, ub),
827 getValueOrCreateConstantIntOp(rewriter, loc, lb));
828 }
829 OpFoldResult newUpperBound = diff;
830 if (!isStepOne) {
831 newUpperBound = rewriter.createOrFold<arith::CeilDivSIOp>(
832 loc, getValueOrCreateConstantIntOp(rewriter, loc, diff),
833 getValueOrCreateConstantIntOp(rewriter, loc, step));
834 }
835
836 OpFoldResult newLowerBound = rewriter.getZeroAttr(rangeType);
837 OpFoldResult newStep = rewriter.getOneAttr(rangeType);
838
839 return {newLowerBound, newUpperBound, newStep};
840}
841
843 Location loc,
844 Value normalizedIv,
845 OpFoldResult origLb,
846 OpFoldResult origStep) {
847 AffineExpr d0, s0, s1;
848 bindSymbols(rewriter.getContext(), s0, s1);
849 bindDims(rewriter.getContext(), d0);
850 AffineExpr e = d0 * s1 + s0;
852 rewriter, loc, e, ArrayRef<OpFoldResult>{normalizedIv, origLb, origStep});
853 Value denormalizedIvVal =
854 getValueOrCreateConstantIndexOp(rewriter, loc, denormalizedIv);
855 SmallPtrSet<Operation *, 1> preservedUses;
856 // If an `affine.apply` operation is generated for denormalization, the use
857 // of `origLb` in those ops must not be replaced. These arent not generated
858 // when `origLb == 0` and `origStep == 1`.
859 if (!isZeroInteger(origLb) || !isOneInteger(origStep)) {
860 if (Operation *preservedUse = denormalizedIvVal.getDefiningOp()) {
861 preservedUses.insert(preservedUse);
862 }
863 }
864 rewriter.replaceAllUsesExcept(normalizedIv, denormalizedIvVal, preservedUses);
865}
866
868 Value normalizedIv, OpFoldResult origLb,
869 OpFoldResult origStep) {
870 if (getType(origLb).isIndex()) {
871 return denormalizeInductionVariableForIndexType(rewriter, loc, normalizedIv,
872 origLb, origStep);
873 }
874 Value denormalizedIv;
876 bool isStepOne = isOneInteger(origStep);
877 bool isZeroBased = isZeroInteger(origLb);
878
879 Value scaled = normalizedIv;
880 if (!isStepOne) {
881 Value origStepValue =
882 getValueOrCreateConstantIntOp(rewriter, loc, origStep);
883 scaled = arith::MulIOp::create(rewriter, loc, normalizedIv, origStepValue);
884 preserve.insert(scaled.getDefiningOp());
885 }
886 denormalizedIv = scaled;
887 if (!isZeroBased) {
888 Value origLbValue = getValueOrCreateConstantIntOp(rewriter, loc, origLb);
889 denormalizedIv = arith::AddIOp::create(rewriter, loc, scaled, origLbValue);
890 preserve.insert(denormalizedIv.getDefiningOp());
891 }
892
893 rewriter.replaceAllUsesExcept(normalizedIv, denormalizedIv, preserve);
894}
895
897 ArrayRef<OpFoldResult> values) {
898 assert(!values.empty() && "unexecpted empty array");
899 AffineExpr s0, s1;
900 bindSymbols(rewriter.getContext(), s0, s1);
901 AffineExpr mul = s0 * s1;
902 OpFoldResult products = rewriter.getIndexAttr(1);
903 for (auto v : values) {
905 rewriter, loc, mul, ArrayRef<OpFoldResult>{products, v});
906 }
907 return products;
908}
909
910/// Helper function to multiply a sequence of values.
912 ArrayRef<Value> values) {
913 assert(!values.empty() && "unexpected empty list");
914 if (getType(values.front()).isIndex()) {
916 OpFoldResult product = getProductOfIndexes(rewriter, loc, ofrs);
917 return getValueOrCreateConstantIndexOp(rewriter, loc, product);
918 }
919 std::optional<Value> productOf;
920 for (auto v : values) {
921 auto vOne = getConstantIntValue(v);
922 if (vOne && vOne.value() == 1)
923 continue;
924 if (productOf)
925 productOf = arith::MulIOp::create(rewriter, loc, productOf.value(), v)
926 .getResult();
927 else
928 productOf = v;
929 }
930 if (!productOf) {
931 productOf = arith::ConstantOp::create(
932 rewriter, loc, rewriter.getOneAttr(getType(values.front())))
933 .getResult();
934 }
935 return productOf.value();
936}
937
938/// For each original loop, the value of the
939/// induction variable can be obtained by dividing the induction variable of
940/// the linearized loop by the total number of iterations of the loops nested
941/// in it modulo the number of iterations in this loop (remove the values
942/// related to the outer loops):
943/// iv_i = floordiv(iv_linear, product-of-loop-ranges-until-i) mod range_i.
944/// Compute these iteratively from the innermost loop by creating a "running
945/// quotient" of division by the range.
946static std::pair<SmallVector<Value>, SmallPtrSet<Operation *, 2>>
948 Value linearizedIv, ArrayRef<Value> ubs) {
949
950 if (linearizedIv.getType().isIndex()) {
951 Operation *delinearizedOp = affine::AffineDelinearizeIndexOp::create(
952 rewriter, loc, linearizedIv, ubs);
953 auto resultVals = llvm::map_to_vector(
954 delinearizedOp->getResults(), [](OpResult r) -> Value { return r; });
955 return {resultVals, SmallPtrSet<Operation *, 2>{delinearizedOp}};
956 }
957
958 SmallVector<Value> delinearizedIvs(ubs.size());
959 SmallPtrSet<Operation *, 2> preservedUsers;
960
961 llvm::BitVector isUbOne(ubs.size());
962 for (auto [index, ub] : llvm::enumerate(ubs)) {
963 auto ubCst = getConstantIntValue(ub);
964 if (ubCst && ubCst.value() == 1)
965 isUbOne.set(index);
966 }
967
968 // Prune the lead ubs that are all ones.
969 unsigned numLeadingOneUbs = 0;
970 for (auto [index, ub] : llvm::enumerate(ubs)) {
971 if (!isUbOne.test(index)) {
972 break;
973 }
974 delinearizedIvs[index] = arith::ConstantOp::create(
975 rewriter, loc, rewriter.getZeroAttr(ub.getType()));
976 numLeadingOneUbs++;
977 }
978
979 Value previous = linearizedIv;
980 for (unsigned i = numLeadingOneUbs, e = ubs.size(); i < e; ++i) {
981 unsigned idx = ubs.size() - (i - numLeadingOneUbs) - 1;
982 if (i != numLeadingOneUbs && !isUbOne.test(idx + 1)) {
983 previous = arith::DivSIOp::create(rewriter, loc, previous, ubs[idx + 1]);
984 preservedUsers.insert(previous.getDefiningOp());
985 }
986 Value iv = previous;
987 if (i != e - 1) {
988 if (!isUbOne.test(idx)) {
989 iv = arith::RemSIOp::create(rewriter, loc, previous, ubs[idx]);
990 preservedUsers.insert(iv.getDefiningOp());
991 } else {
992 iv = arith::ConstantOp::create(
993 rewriter, loc, rewriter.getZeroAttr(ubs[idx].getType()));
994 }
995 }
996 delinearizedIvs[idx] = iv;
997 }
998 return {delinearizedIvs, preservedUsers};
999}
1000
1001LogicalResult mlir::coalesceLoops(RewriterBase &rewriter,
1003 if (loops.size() < 2)
1004 return failure();
1005
1006 scf::ForOp innermost = loops.back();
1007 scf::ForOp outermost = loops.front();
1008
1009 // Bail out if any loop has a known zero step, as normalization
1010 // would result in a division by zero.
1011 for (auto loop : loops) {
1012 if (auto step = getConstantIntValue(loop.getStep())) {
1013 if (step.value() == 0) {
1014 return failure();
1015 }
1016 }
1017 }
1018 // 1. Make sure all loops iterate from 0 to upperBound with step 1. This
1019 // allows the following code to assume upperBound is the number of iterations.
1020 for (auto loop : loops) {
1021 OpBuilder::InsertionGuard g(rewriter);
1022 rewriter.setInsertionPoint(outermost);
1023 Value lb = loop.getLowerBound();
1024 Value ub = loop.getUpperBound();
1025 Value step = loop.getStep();
1026 auto newLoopRange =
1027 emitNormalizedLoopBounds(rewriter, loop.getLoc(), lb, ub, step);
1028
1029 rewriter.modifyOpInPlace(loop, [&]() {
1030 loop.setLowerBound(getValueOrCreateConstantIntOp(rewriter, loop.getLoc(),
1031 newLoopRange.offset));
1032 loop.setUpperBound(getValueOrCreateConstantIntOp(rewriter, loop.getLoc(),
1033 newLoopRange.size));
1034 loop.setStep(getValueOrCreateConstantIntOp(rewriter, loop.getLoc(),
1035 newLoopRange.stride));
1036 });
1037 rewriter.setInsertionPointToStart(innermost.getBody());
1038 denormalizeInductionVariable(rewriter, loop.getLoc(),
1039 loop.getInductionVar(), lb, step);
1040 }
1041
1042 // 2. Emit code computing the upper bound of the coalesced loop as product
1043 // of the number of iterations of all loops.
1044 OpBuilder::InsertionGuard g(rewriter);
1045 rewriter.setInsertionPoint(outermost);
1046 Location loc = outermost.getLoc();
1047 SmallVector<Value> upperBounds = llvm::map_to_vector(
1048 loops, [](auto loop) { return loop.getUpperBound(); });
1049 Value upperBound = getProductOfIntsOrIndexes(rewriter, loc, upperBounds);
1050 outermost.setUpperBound(upperBound);
1051
1052 // Insert delinearization at the start of the outermost loop body.
1053 rewriter.setInsertionPointToStart(outermost.getBody());
1054 auto [delinearizeIvs, preservedUsers] = delinearizeInductionVariable(
1055 rewriter, loc, outermost.getInductionVar(), upperBounds);
1056 rewriter.replaceAllUsesExcept(outermost.getInductionVar(), delinearizeIvs[0],
1057 preservedUsers);
1058
1059 for (int i = loops.size() - 1; i > 0; --i) {
1060 auto outerLoop = loops[i - 1];
1061 auto innerLoop = loops[i];
1062
1063 Operation *innerTerminator = innerLoop.getBody()->getTerminator();
1064 auto yieldedVals = llvm::to_vector(innerTerminator->getOperands());
1065 assert(llvm::equal(outerLoop.getRegionIterArgs(), innerLoop.getInitArgs()));
1066 for (Value &yieldedVal : yieldedVals) {
1067 // The yielded value may be the induction variable of the inner loop,
1068 // which is about to be inlined and whose block argument is about to
1069 // be destroyed. Use its replacement value instead.
1070 if (yieldedVal == innerLoop.getInductionVar()) {
1071 yieldedVal = delinearizeIvs[i];
1072 continue;
1073 }
1074 // The yielded value may be an iteration argument of the inner loop
1075 // which is about to be inlined.
1076 auto iter = llvm::find(innerLoop.getRegionIterArgs(), yieldedVal);
1077 if (iter != innerLoop.getRegionIterArgs().end()) {
1078 unsigned iterArgIndex = iter - innerLoop.getRegionIterArgs().begin();
1079 // `outerLoop` iter args identical to the `innerLoop` init args.
1080 assert(iterArgIndex < innerLoop.getInitArgs().size());
1081 yieldedVal = innerLoop.getInitArgs()[iterArgIndex];
1082 }
1083 }
1084 rewriter.eraseOp(innerTerminator);
1085
1086 SmallVector<Value> innerBlockArgs;
1087 innerBlockArgs.push_back(delinearizeIvs[i]);
1088 llvm::append_range(innerBlockArgs, outerLoop.getRegionIterArgs());
1089 rewriter.inlineBlockBefore(innerLoop.getBody(), outerLoop.getBody(),
1090 Block::iterator(innerLoop), innerBlockArgs);
1091 rewriter.replaceOp(innerLoop, yieldedVals);
1092 }
1093 return success();
1094}
1095
1097 if (loops.empty()) {
1098 return failure();
1099 }
1100 IRRewriter rewriter(loops.front().getContext());
1101 return coalesceLoops(rewriter, loops);
1102}
1103
1104LogicalResult mlir::coalescePerfectlyNestedSCFForLoops(scf::ForOp op) {
1105 LogicalResult result(failure());
1107 getPerfectlyNestedLoops(loops, op);
1108
1109 // Look for a band of loops that can be coalesced, i.e. perfectly nested
1110 // loops with bounds defined above some loop.
1111
1112 // 1. For each loop, find above which parent loop its bounds operands are
1113 // defined.
1114 SmallVector<unsigned> operandsDefinedAbove(loops.size());
1115 for (unsigned i = 0, e = loops.size(); i < e; ++i) {
1116 operandsDefinedAbove[i] = i;
1117 for (unsigned j = 0; j < i; ++j) {
1118 SmallVector<Value> boundsOperands = {loops[i].getLowerBound(),
1119 loops[i].getUpperBound(),
1120 loops[i].getStep()};
1121 if (areValuesDefinedAbove(boundsOperands, loops[j].getRegion())) {
1122 operandsDefinedAbove[i] = j;
1123 break;
1124 }
1125 }
1126 }
1127
1128 // 2. For each inner loop check that the iter_args for the immediately outer
1129 // loop are the init for the immediately inner loop and that the yields of the
1130 // return of the inner loop is the yield for the immediately outer loop. Keep
1131 // track of where the chain starts from for each loop.
1132 SmallVector<unsigned> iterArgChainStart(loops.size());
1133 iterArgChainStart[0] = 0;
1134 for (unsigned i = 1, e = loops.size(); i < e; ++i) {
1135 // By default set the start of the chain to itself.
1136 iterArgChainStart[i] = i;
1137 auto outerloop = loops[i - 1];
1138 auto innerLoop = loops[i];
1139 if (outerloop.getNumRegionIterArgs() != innerLoop.getNumRegionIterArgs()) {
1140 continue;
1141 }
1142 if (!llvm::equal(outerloop.getRegionIterArgs(), innerLoop.getInitArgs())) {
1143 continue;
1144 }
1145 auto outerloopTerminator = outerloop.getBody()->getTerminator();
1146 if (!llvm::equal(outerloopTerminator->getOperands(),
1147 innerLoop.getResults())) {
1148 continue;
1149 }
1150 iterArgChainStart[i] = iterArgChainStart[i - 1];
1151 }
1152
1153 // 3. Identify bands of loops such that the operands of all of them are
1154 // defined above the first loop in the band. Traverse the nest bottom-up
1155 // so that modifications don't invalidate the inner loops.
1156 for (unsigned end = loops.size(); end > 0; --end) {
1157 unsigned start = 0;
1158 for (; start < end - 1; ++start) {
1159 auto maxPos =
1160 *std::max_element(std::next(operandsDefinedAbove.begin(), start),
1161 std::next(operandsDefinedAbove.begin(), end));
1162 if (maxPos > start)
1163 continue;
1164 if (iterArgChainStart[end - 1] > start)
1165 continue;
1166 auto band = llvm::MutableArrayRef(loops.data() + start, end - start);
1167 if (succeeded(coalesceLoops(band)))
1168 result = success();
1169 break;
1170 }
1171 // If a band was found and transformed, keep looking at the loops above
1172 // the outermost transformed loop.
1173 if (start != end - 1)
1174 end = start + 1;
1175 }
1176 return result;
1177}
1178
1180 RewriterBase &rewriter, scf::ParallelOp loops,
1181 ArrayRef<std::vector<unsigned>> combinedDimensions) {
1182 OpBuilder::InsertionGuard g(rewriter);
1183 rewriter.setInsertionPoint(loops);
1184 Location loc = loops.getLoc();
1185
1186 // Presort combined dimensions.
1187 auto sortedDimensions = llvm::to_vector<3>(combinedDimensions);
1188 for (auto &dims : sortedDimensions)
1189 llvm::sort(dims);
1190
1191 // Normalize ParallelOp's iteration pattern.
1192 SmallVector<Value, 3> normalizedUpperBounds;
1193 for (unsigned i = 0, e = loops.getNumLoops(); i < e; ++i) {
1194 OpBuilder::InsertionGuard g2(rewriter);
1195 rewriter.setInsertionPoint(loops);
1196 Value lb = loops.getLowerBound()[i];
1197 Value ub = loops.getUpperBound()[i];
1198 Value step = loops.getStep()[i];
1199 auto newLoopRange = emitNormalizedLoopBounds(rewriter, loc, lb, ub, step);
1200 normalizedUpperBounds.push_back(getValueOrCreateConstantIntOp(
1201 rewriter, loops.getLoc(), newLoopRange.size));
1202
1203 rewriter.setInsertionPointToStart(loops.getBody());
1204 denormalizeInductionVariable(rewriter, loc, loops.getInductionVars()[i], lb,
1205 step);
1206 }
1207
1208 // Combine iteration spaces.
1209 SmallVector<Value, 3> lowerBounds, upperBounds, steps;
1210 auto cst0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
1211 auto cst1 = arith::ConstantIndexOp::create(rewriter, loc, 1);
1212 for (auto &sortedDimension : sortedDimensions) {
1213 Value newUpperBound = arith::ConstantIndexOp::create(rewriter, loc, 1);
1214 for (auto idx : sortedDimension) {
1215 newUpperBound = arith::MulIOp::create(rewriter, loc, newUpperBound,
1216 normalizedUpperBounds[idx]);
1217 }
1218 lowerBounds.push_back(cst0);
1219 steps.push_back(cst1);
1220 upperBounds.push_back(newUpperBound);
1221 }
1222
1223 // Create new ParallelLoop with conversions to the original induction values.
1224 // The loop below uses divisions to get the relevant range of values in the
1225 // new induction value that represent each range of the original induction
1226 // value. The remainders then determine based on that range, which iteration
1227 // of the original induction value this represents. This is a normalized value
1228 // that is un-normalized already by the previous logic.
1229 auto newPloop = scf::ParallelOp::create(
1230 rewriter, loc, lowerBounds, upperBounds, steps,
1231 [&](OpBuilder &insideBuilder, Location, ValueRange ploopIVs) {
1232 for (unsigned i = 0, e = combinedDimensions.size(); i < e; ++i) {
1233 Value previous = ploopIVs[i];
1234 unsigned numberCombinedDimensions = combinedDimensions[i].size();
1235 // Iterate over all except the last induction value.
1236 for (unsigned j = numberCombinedDimensions - 1; j > 0; --j) {
1237 unsigned idx = combinedDimensions[i][j];
1238
1239 // Determine the current induction value's current loop iteration
1240 Value iv = arith::RemSIOp::create(insideBuilder, loc, previous,
1241 normalizedUpperBounds[idx]);
1242 replaceAllUsesInRegionWith(loops.getBody()->getArgument(idx), iv,
1243 loops.getRegion());
1244
1245 // Remove the effect of the current induction value to prepare for
1246 // the next value.
1247 previous = arith::DivSIOp::create(insideBuilder, loc, previous,
1248 normalizedUpperBounds[idx]);
1249 }
1250
1251 // The final induction value is just the remaining value.
1252 unsigned idx = combinedDimensions[i][0];
1253 replaceAllUsesInRegionWith(loops.getBody()->getArgument(idx),
1254 previous, loops.getRegion());
1255 }
1256 });
1257
1258 // Replace the old loop with the new loop.
1259 loops.getBody()->back().erase();
1260 newPloop.getBody()->getOperations().splice(
1261 Block::iterator(newPloop.getBody()->back()),
1262 loops.getBody()->getOperations());
1263 loops.erase();
1264}
1265
1266// Hoist the ops within `outer` that appear before `inner`.
1267// Such ops include the ops that have been introduced by parametric tiling.
1268// Ops that come from triangular loops (i.e. that belong to the program slice
1269// rooted at `outer`) and ops that have side effects cannot be hoisted.
1270// Return failure when any op fails to hoist.
1271static LogicalResult hoistOpsBetween(scf::ForOp outer, scf::ForOp inner) {
1272 SetVector<Operation *> forwardSlice;
1274 options.filter = [&inner](Operation *op) {
1275 return op != inner.getOperation();
1276 };
1277 getForwardSlice(outer.getInductionVar(), &forwardSlice, options);
1278 LogicalResult status = success();
1280 for (auto &op : outer.getBody()->without_terminator()) {
1281 // Stop when encountering the inner loop.
1282 if (&op == inner.getOperation())
1283 break;
1284 // Skip over non-hoistable ops.
1285 if (forwardSlice.count(&op) > 0) {
1286 status = failure();
1287 continue;
1288 }
1289 // Skip intermediate scf::ForOp, these are not considered a failure.
1290 if (isa<scf::ForOp>(op))
1291 continue;
1292 // Skip other ops with regions.
1293 if (op.getNumRegions() > 0) {
1294 status = failure();
1295 continue;
1296 }
1297 // Skip if op has side effects.
1298 // TODO: loads to immutable memory regions are ok.
1299 if (!isMemoryEffectFree(&op)) {
1300 status = failure();
1301 continue;
1302 }
1303 toHoist.push_back(&op);
1304 }
1305 auto *outerForOp = outer.getOperation();
1306 for (auto *op : toHoist)
1307 op->moveBefore(outerForOp);
1308 return status;
1309}
1310
1311// Traverse the interTile and intraTile loops and try to hoist ops such that
1312// bands of perfectly nested loops are isolated.
1313// Return failure if either perfect interTile or perfect intraTile bands cannot
1314// be formed.
1315static LogicalResult tryIsolateBands(const TileLoops &tileLoops) {
1316 LogicalResult status = success();
1317 const Loops &interTile = tileLoops.first;
1318 const Loops &intraTile = tileLoops.second;
1319 auto size = interTile.size();
1320 assert(size == intraTile.size());
1321 if (size <= 1)
1322 return success();
1323 for (unsigned s = 1; s < size; ++s)
1324 status = succeeded(status) ? hoistOpsBetween(intraTile[0], intraTile[s])
1325 : failure();
1326 for (unsigned s = 1; s < size; ++s)
1327 status = succeeded(status) ? hoistOpsBetween(interTile[0], interTile[s])
1328 : failure();
1329 return status;
1330}
1331
1332/// Collect perfectly nested loops starting from `rootForOps`. Loops are
1333/// perfectly nested if each loop is the first and only non-terminator operation
1334/// in the parent loop. Collect at most `maxLoops` loops and append them to
1335/// `forOps`.
1336template <typename T>
1338 SmallVectorImpl<T> &forOps, T rootForOp,
1339 unsigned maxLoops = std::numeric_limits<unsigned>::max()) {
1340 for (unsigned i = 0; i < maxLoops; ++i) {
1341 forOps.push_back(rootForOp);
1342 Block &body = rootForOp.getRegion().front();
1343 if (body.begin() != std::prev(body.end(), 2))
1344 return;
1345
1346 rootForOp = dyn_cast<T>(&body.front());
1347 if (!rootForOp)
1348 return;
1349 }
1350}
1351
1352static Loops stripmineSink(scf::ForOp forOp, Value factor,
1353 ArrayRef<scf::ForOp> targets) {
1354 assert(!forOp.getUnsignedCmp() && "unsigned loops are not supported");
1355 auto originalStep = forOp.getStep();
1356 auto iv = forOp.getInductionVar();
1357
1358 OpBuilder b(forOp);
1359 forOp.setStep(arith::MulIOp::create(b, forOp.getLoc(), originalStep, factor));
1360
1361 Loops innerLoops;
1362 for (auto t : targets) {
1363 assert(!t.getUnsignedCmp() && "unsigned loops are not supported");
1364
1365 // Save information for splicing ops out of t when done
1366 auto begin = t.getBody()->begin();
1367 auto nOps = t.getBody()->getOperations().size();
1368
1369 // Insert newForOp before the terminator of `t`.
1370 auto b = OpBuilder::atBlockTerminator((t.getBody()));
1371 Value stepped = arith::AddIOp::create(b, t.getLoc(), iv, forOp.getStep());
1372 Value ub =
1373 arith::MinSIOp::create(b, t.getLoc(), forOp.getUpperBound(), stepped);
1374
1375 // Splice [begin, begin + nOps - 1) into `newForOp` and replace uses.
1376 auto newForOp = scf::ForOp::create(b, t.getLoc(), iv, ub, originalStep);
1377 newForOp.getBody()->getOperations().splice(
1378 newForOp.getBody()->getOperations().begin(),
1379 t.getBody()->getOperations(), begin, std::next(begin, nOps - 1));
1380 replaceAllUsesInRegionWith(iv, newForOp.getInductionVar(),
1381 newForOp.getRegion());
1382
1383 innerLoops.push_back(newForOp);
1384 }
1385
1386 return innerLoops;
1387}
1388
1390 ArrayRef<Value> sizes,
1391 ArrayRef<scf::ForOp> targets) {
1393 SmallVector<scf::ForOp, 8> currentTargets(targets);
1394 for (auto it : llvm::zip(forOps, sizes)) {
1395 auto step = stripmineSink(std::get<0>(it), std::get<1>(it), currentTargets);
1396 res.push_back(step);
1397 currentTargets = step;
1398 }
1399 return res;
1400}
1401
1403 scf::ForOp target) {
1405 for (auto loops : tile(forOps, sizes, ArrayRef<scf::ForOp>(target)))
1406 res.push_back(llvm::getSingleElement(loops));
1407 return res;
1408}
1409
1411 // Collect perfectly nested loops. If more size values provided than nested
1412 // loops available, truncate `sizes`.
1414 forOps.reserve(sizes.size());
1415 getPerfectlyNestedLoopsImpl(forOps, rootForOp, sizes.size());
1416 if (forOps.size() < sizes.size())
1417 sizes = sizes.take_front(forOps.size());
1418
1419 return ::tile(forOps, sizes, forOps.back());
1420}
1421
1423 scf::ForOp root) {
1424 getPerfectlyNestedLoopsImpl(nestedLoops, root);
1425}
1426
1428 ArrayRef<int64_t> sizes) {
1429 // Collect perfectly nested loops. If more size values provided than nested
1430 // loops available, truncate `sizes`.
1432 forOps.reserve(sizes.size());
1433 getPerfectlyNestedLoopsImpl(forOps, rootForOp, sizes.size());
1434 if (forOps.size() < sizes.size())
1435 sizes = sizes.take_front(forOps.size());
1436
1437 // The strip-mining transformation splices loop bodies into a new inner loop
1438 // without threading iter_args. If any of the collected loops carries
1439 // iter_args, the splice would produce invalid IR (yielded values from the
1440 // inner scope used in the outer terminator). Skip the transformation in
1441 // that case.
1442 if (llvm::any_of(forOps,
1443 [](scf::ForOp op) { return !op.getInitArgs().empty(); }))
1444 return {};
1445
1446 // Compute the tile sizes such that i-th outer loop executes size[i]
1447 // iterations. Given that the loop current executes
1448 // numIterations = ceildiv((upperBound - lowerBound), step)
1449 // iterations, we need to tile with size ceildiv(numIterations, size[i]).
1450 SmallVector<Value, 4> tileSizes;
1451 tileSizes.reserve(sizes.size());
1452 for (unsigned i = 0, e = sizes.size(); i < e; ++i) {
1453 assert(sizes[i] > 0 && "expected strictly positive size for strip-mining");
1454
1455 auto forOp = forOps[i];
1456 OpBuilder builder(forOp);
1457 auto loc = forOp.getLoc();
1458 Value diff = arith::SubIOp::create(builder, loc, forOp.getUpperBound(),
1459 forOp.getLowerBound());
1460 Value numIterations = ceilDivPositive(builder, loc, diff, forOp.getStep());
1461 Value iterationsPerBlock =
1462 ceilDivPositive(builder, loc, numIterations, sizes[i]);
1463 tileSizes.push_back(iterationsPerBlock);
1464 }
1465
1466 // Call parametric tiling with the given sizes.
1467 auto intraTile = tile(forOps, tileSizes, forOps.back());
1468 TileLoops tileLoops = std::make_pair(forOps, intraTile);
1469
1470 // TODO: for now we just ignore the result of band isolation.
1471 // In the future, mapping decisions may be impacted by the ability to
1472 // isolate perfectly nested bands.
1473 (void)tryIsolateBands(tileLoops);
1474
1475 return tileLoops;
1476}
1477
1479 scf::ForallOp source,
1480 RewriterBase &rewriter) {
1481 unsigned numTargetOuts = target.getNumResults();
1482 unsigned numSourceOuts = source.getNumResults();
1483
1484 // Create fused shared_outs.
1485 SmallVector<Value> fusedOuts;
1486 llvm::append_range(fusedOuts, target.getOutputs());
1487 llvm::append_range(fusedOuts, source.getOutputs());
1488
1489 // Create a new scf.forall op after the source loop.
1490 rewriter.setInsertionPointAfter(source);
1491 scf::ForallOp fusedLoop = scf::ForallOp::create(
1492 rewriter, source.getLoc(), source.getMixedLowerBound(),
1493 source.getMixedUpperBound(), source.getMixedStep(), fusedOuts,
1494 source.getMapping());
1495
1496 // Map control operands.
1497 IRMapping mapping;
1498 mapping.map(target.getInductionVars(), fusedLoop.getInductionVars());
1499 mapping.map(source.getInductionVars(), fusedLoop.getInductionVars());
1500
1501 // Map shared outs.
1502 mapping.map(target.getRegionIterArgs(),
1503 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1504 mapping.map(source.getRegionIterArgs(),
1505 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1506
1507 // Append everything except the terminator into the fused operation.
1508 rewriter.setInsertionPointToStart(fusedLoop.getBody());
1509 for (Operation &op : target.getBody()->without_terminator())
1510 rewriter.clone(op, mapping);
1511 for (Operation &op : source.getBody()->without_terminator())
1512 rewriter.clone(op, mapping);
1513
1514 // Fuse the old terminator in_parallel ops into the new one.
1515 scf::InParallelOp targetTerm = target.getTerminator();
1516 scf::InParallelOp sourceTerm = source.getTerminator();
1517 scf::InParallelOp fusedTerm = fusedLoop.getTerminator();
1518 rewriter.setInsertionPointToStart(fusedTerm.getBody());
1519 for (Operation &op : targetTerm.getYieldingOps())
1520 rewriter.clone(op, mapping);
1521 for (Operation &op : sourceTerm.getYieldingOps())
1522 rewriter.clone(op, mapping);
1523
1524 // Replace old loops by substituting their uses by results of the fused loop.
1525 rewriter.replaceOp(target, fusedLoop.getResults().take_front(numTargetOuts));
1526 rewriter.replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1527
1528 return fusedLoop;
1529}
1530
1532 scf::ForOp source,
1533 RewriterBase &rewriter) {
1534 assert(source.getUnsignedCmp() == target.getUnsignedCmp() &&
1535 "incompatible signedness");
1536 unsigned numTargetOuts = target.getNumResults();
1537 unsigned numSourceOuts = source.getNumResults();
1538
1539 // Create fused init_args, with target's init_args before source's init_args.
1540 SmallVector<Value> fusedInitArgs;
1541 llvm::append_range(fusedInitArgs, target.getInitArgs());
1542 llvm::append_range(fusedInitArgs, source.getInitArgs());
1543
1544 // Create a new scf.for op after the source loop (with scf.yield terminator
1545 // (without arguments) only in case its init_args is empty).
1546 rewriter.setInsertionPointAfter(source);
1547 scf::ForOp fusedLoop = scf::ForOp::create(
1548 rewriter, source.getLoc(), source.getLowerBound(), source.getUpperBound(),
1549 source.getStep(), fusedInitArgs, /*bodyBuilder=*/nullptr,
1550 source.getUnsignedCmp());
1551
1552 // Map original induction variables and operands to those of the fused loop.
1553 IRMapping mapping;
1554 mapping.map(target.getInductionVar(), fusedLoop.getInductionVar());
1555 mapping.map(target.getRegionIterArgs(),
1556 fusedLoop.getRegionIterArgs().take_front(numTargetOuts));
1557 mapping.map(source.getInductionVar(), fusedLoop.getInductionVar());
1558 mapping.map(source.getRegionIterArgs(),
1559 fusedLoop.getRegionIterArgs().take_back(numSourceOuts));
1560
1561 // Merge target's body into the new (fused) for loop and then source's body.
1562 rewriter.setInsertionPointToStart(fusedLoop.getBody());
1563 for (Operation &op : target.getBody()->without_terminator())
1564 rewriter.clone(op, mapping);
1565 for (Operation &op : source.getBody()->without_terminator())
1566 rewriter.clone(op, mapping);
1567
1568 // Build fused yield results by appropriately mapping original yield operands.
1569 SmallVector<Value> yieldResults;
1570 for (Value operand : target.getBody()->getTerminator()->getOperands())
1571 yieldResults.push_back(mapping.lookupOrDefault(operand));
1572 for (Value operand : source.getBody()->getTerminator()->getOperands())
1573 yieldResults.push_back(mapping.lookupOrDefault(operand));
1574 if (!yieldResults.empty())
1575 scf::YieldOp::create(rewriter, source.getLoc(), yieldResults);
1576
1577 // Replace old loops by substituting their uses by results of the fused loop.
1578 rewriter.replaceOp(target, fusedLoop.getResults().take_front(numTargetOuts));
1579 rewriter.replaceOp(source, fusedLoop.getResults().take_back(numSourceOuts));
1580
1581 return fusedLoop;
1582}
1583
1584FailureOr<scf::ForallOp> mlir::normalizeForallOp(RewriterBase &rewriter,
1585 scf::ForallOp forallOp) {
1586 SmallVector<OpFoldResult> lbs = forallOp.getMixedLowerBound();
1587 SmallVector<OpFoldResult> ubs = forallOp.getMixedUpperBound();
1588 SmallVector<OpFoldResult> steps = forallOp.getMixedStep();
1589
1590 if (forallOp.isNormalized())
1591 return forallOp;
1592
1593 OpBuilder::InsertionGuard g(rewriter);
1594 auto loc = forallOp.getLoc();
1595 rewriter.setInsertionPoint(forallOp);
1597 for (auto [lb, ub, step] : llvm::zip_equal(lbs, ubs, steps)) {
1598 Range normalizedLoopParams =
1599 emitNormalizedLoopBounds(rewriter, loc, lb, ub, step);
1600 newUbs.push_back(normalizedLoopParams.size);
1601 }
1602 (void)foldDynamicIndexList(newUbs);
1603
1604 // Use the normalized builder since the lower bounds are always 0 and the
1605 // steps are always 1.
1606 auto normalizedForallOp = scf::ForallOp::create(
1607 rewriter, loc, newUbs, forallOp.getOutputs(), forallOp.getMapping(),
1608 [](OpBuilder &, Location, ValueRange) {});
1609
1610 rewriter.inlineRegionBefore(forallOp.getBodyRegion(),
1611 normalizedForallOp.getBodyRegion(),
1612 normalizedForallOp.getBodyRegion().begin());
1613 // Remove the original empty block in the new loop.
1614 rewriter.eraseBlock(&normalizedForallOp.getBodyRegion().back());
1615
1616 rewriter.setInsertionPointToStart(normalizedForallOp.getBody());
1617 // Update the users of the original loop variables.
1618 for (auto [idx, iv] :
1619 llvm::enumerate(normalizedForallOp.getInductionVars())) {
1620 auto origLb = getValueOrCreateConstantIndexOp(rewriter, loc, lbs[idx]);
1621 auto origStep = getValueOrCreateConstantIndexOp(rewriter, loc, steps[idx]);
1622 denormalizeInductionVariable(rewriter, loc, iv, origLb, origStep);
1623 }
1624
1625 rewriter.replaceOp(forallOp, normalizedForallOp);
1626 return normalizedForallOp;
1627}
1628
1631 assert(!loops.empty() && "unexpected empty loop nest");
1632 if (loops.size() == 1)
1633 return isa_and_nonnull<scf::ForOp>(loops.front().getOperation());
1634 for (auto [outerLoop, innerLoop] :
1635 llvm::zip_equal(loops.drop_back(), loops.drop_front())) {
1636 auto outerFor = dyn_cast_or_null<scf::ForOp>(outerLoop.getOperation());
1637 auto innerFor = dyn_cast_or_null<scf::ForOp>(innerLoop.getOperation());
1638 if (!outerFor || !innerFor)
1639 return false;
1640 auto outerBBArgs = outerFor.getRegionIterArgs();
1641 auto innerIterArgs = innerFor.getInitArgs();
1642 if (outerBBArgs.size() != innerIterArgs.size())
1643 return false;
1644
1645 for (auto [outerBBArg, innerIterArg] :
1646 llvm::zip_equal(outerBBArgs, innerIterArgs)) {
1647 if (!llvm::hasSingleElement(outerBBArg.getUses()) ||
1648 innerIterArg != outerBBArg)
1649 return false;
1650 }
1651
1652 ValueRange outerYields =
1653 cast<scf::YieldOp>(outerFor.getBody()->getTerminator())->getOperands();
1654 ValueRange innerResults = innerFor.getResults();
1655 if (outerYields.size() != innerResults.size())
1656 return false;
1657 for (auto [outerYield, innerResult] :
1658 llvm::zip_equal(outerYields, innerResults)) {
1659 if (!llvm::hasSingleElement(innerResult.getUses()) ||
1660 outerYield != innerResult)
1661 return false;
1662 }
1663 }
1664 return true;
1665}
1666
1668mlir::getConstLoopBounds(mlir::LoopLikeOpInterface loopOp) {
1669 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1670 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1671 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1672 if (!loBnds || !upBnds || !steps)
1673 return {};
1675 for (auto [lb, ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1676 auto lbCst = getConstantIntValue(lb);
1677 auto ubCst = getConstantIntValue(ub);
1678 auto stepCst = getConstantIntValue(step);
1679 if (!lbCst || !ubCst || !stepCst)
1680 return {};
1681 loopRanges.emplace_back(*lbCst, *ubCst, *stepCst);
1682 }
1683 return loopRanges;
1684}
1685
1687mlir::getConstLoopTripCounts(mlir::LoopLikeOpInterface loopOp) {
1688 std::optional<SmallVector<OpFoldResult>> loBnds = loopOp.getLoopLowerBounds();
1689 std::optional<SmallVector<OpFoldResult>> upBnds = loopOp.getLoopUpperBounds();
1690 std::optional<SmallVector<OpFoldResult>> steps = loopOp.getLoopSteps();
1691 if (!loBnds || !upBnds || !steps)
1692 return {};
1694 for (auto [lb, ub, step] : llvm::zip(*loBnds, *upBnds, *steps)) {
1695 // TODO(#178506): Signedness is not handled correctly here.
1696 std::optional<llvm::APInt> numIter = constantTripCount(
1697 lb, ub, step, /*isSigned=*/true, scf::computeUbMinusLb);
1698 if (!numIter)
1699 return {};
1700 tripCounts.push_back(*numIter);
1701 }
1702 return tripCounts;
1703}
1704
1705FailureOr<scf::ParallelOp> mlir::parallelLoopUnrollByFactors(
1706 scf::ParallelOp op, ArrayRef<uint64_t> unrollFactors,
1707 RewriterBase &rewriter,
1708 function_ref<void(unsigned, Operation *, OpBuilder)> annotateFn,
1709 IRMapping *clonedToSrcOpsMap) {
1710 const unsigned numLoops = op.getNumLoops();
1711 assert(llvm::none_of(unrollFactors, [](uint64_t f) { return f == 0; }) &&
1712 "Expected positive unroll factors");
1713 assert((!unrollFactors.empty() && (unrollFactors.size() <= numLoops)) &&
1714 "Expected non-empty unroll factors of size <= to the number of loops");
1715
1716 // Bail out if no valid unroll factors were provided
1717 if (llvm::all_of(unrollFactors, [](uint64_t f) { return f == 1; }))
1718 return rewriter.notifyMatchFailure(
1719 op, "Unrolling not applied if all factors are 1");
1720
1721 // Return if the loop body is empty.
1722 if (llvm::hasSingleElement(op.getBody()->getOperations()))
1723 return rewriter.notifyMatchFailure(op, "Cannot unroll an empty loop body");
1724
1725 // If the provided unroll factors do not cover all the loop dims, they are
1726 // applied to the inner loop dimensions.
1727 const unsigned firstLoopDimIdx = numLoops - unrollFactors.size();
1728
1729 // Make sure that the unroll factors divide the iteration space evenly
1730 // TODO: Support unrolling loops with dynamic iteration spaces.
1732 if (tripCounts.empty())
1733 return rewriter.notifyMatchFailure(
1734 op, "Failed to compute constant trip counts for the loop. Note that "
1735 "dynamic loop sizes are not supported.");
1736
1737 for (unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1738 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1739 if (tripCounts[dimIdx].urem(unrollFactor) != 0)
1740 return rewriter.notifyMatchFailure(
1741 op, "Unroll factors don't divide the iteration space evenly");
1742 }
1743
1744 std::optional<SmallVector<OpFoldResult>> maybeFoldSteps = op.getLoopSteps();
1745 if (!maybeFoldSteps)
1746 return rewriter.notifyMatchFailure(op, "Failed to retrieve loop steps");
1748 for (auto step : *maybeFoldSteps)
1749 steps.push_back(static_cast<size_t>(*getConstantIntValue(step)));
1750
1751 for (unsigned dimIdx = firstLoopDimIdx; dimIdx < numLoops; dimIdx++) {
1752 const uint64_t unrollFactor = unrollFactors[dimIdx - firstLoopDimIdx];
1753 if (unrollFactor == 1)
1754 continue;
1755 const size_t origStep = steps[dimIdx];
1756 const int64_t newStep = origStep * unrollFactor;
1757 IRMapping clonedToSrcOpsMap;
1758
1759 ValueRange iterArgs = ValueRange(op.getRegionIterArgs());
1760 auto yieldedValues = op.getBody()->getTerminator()->getOperands();
1761
1763 op.getBody(), op.getInductionVars()[dimIdx], unrollFactor,
1764 [&](unsigned i, Value iv, OpBuilder b) {
1765 // iv' = iv + step * i;
1766 const AffineExpr expr = b.getAffineDimExpr(0) + (origStep * i);
1767 const auto map =
1768 b.getDimIdentityMap().dropResult(0).insertResult(expr, 0);
1769 return affine::AffineApplyOp::create(b, iv.getLoc(), map,
1770 ValueRange{iv});
1771 },
1772 /*annotateFn*/ annotateFn, iterArgs, yieldedValues, &clonedToSrcOpsMap);
1773
1774 // Update loop step
1775 auto prevInsertPoint = rewriter.saveInsertionPoint();
1776 rewriter.setInsertionPoint(op);
1777 op.getStepMutable()[dimIdx].assign(
1778 arith::ConstantIndexOp::create(rewriter, op.getLoc(), newStep));
1779 rewriter.restoreInsertionPoint(prevInsertPoint);
1780 }
1781 return op;
1782}
return success()
static OpFoldResult getProductOfIndexes(RewriterBase &rewriter, Location loc, ArrayRef< OpFoldResult > values)
Definition Utils.cpp:896
static LogicalResult tryIsolateBands(const TileLoops &tileLoops)
Definition Utils.cpp:1315
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:1337
static LogicalResult hoistOpsBetween(scf::ForOp outer, scf::ForOp inner)
Definition Utils.cpp:1271
static Range emitNormalizedLoopBoundsForIndexType(RewriterBase &rewriter, Location loc, OpFoldResult lb, OpFoldResult ub, OpFoldResult step)
Definition Utils.cpp:781
static Loops stripmineSink(scf::ForOp forOp, Value factor, ArrayRef< scf::ForOp > targets)
Definition Utils.cpp:1352
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:911
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:947
static void denormalizeInductionVariableForIndexType(RewriterBase &rewriter, Location loc, Value normalizedIv, OpFoldResult origLb, OpFoldResult origStep)
Definition Utils.cpp:842
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:600
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:308
Block represents an ordered list of Operations.
Definition Block.h:34
OpListType::iterator iterator
Definition Block.h:165
unsigned getNumArguments()
Definition Block.h:153
Operation & front()
Definition Block.h:178
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgListType getArguments()
Definition Block.h:112
iterator end()
Definition Block.h:169
iterator begin()
Definition Block.h:168
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:256
This is a value defined by a result of an operation.
Definition Value.h:456
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:434
operand_type_range getOperandTypes()
Definition Operation.h:424
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:731
result_type_range getResultTypes()
Definition Operation.h:455
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:405
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
result_range getResults()
Definition Operation.h:442
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:206
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:392
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
Type getType() const
Return the type of this value.
Definition Value.h:105
bool 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
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:85
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:434
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:117
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:1422
bool isPerfectlyNestedForLoops(MutableArrayRef< LoopLikeOpInterface > loops)
Check if the provided loops are perfectly nested for-loops.
Definition Utils.cpp:1629
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:311
LogicalResult coalescePerfectlyNestedSCFForLoops(scf::ForOp op)
Walk an affine.for to find a band to coalesce.
Definition Utils.cpp:1104
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
Definition AffineExpr.h:313
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:585
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:1687
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:1668
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:1179
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:128
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:1410
LogicalResult loopUnrollJamByFactor(scf::ForOp forOp, uint64_t unrollFactor)
Unrolls and jams this scf.for operation by the specified unroll factor.
Definition Utils.cpp:613
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:327
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:1705
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:1389
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:867
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:1478
LogicalResult coalesceLoops(MutableArrayRef< scf::ForOp > loops)
Replace a perfect nest of "for" loops with a single linearized loop.
Definition Utils.cpp:1096
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:1531
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155
TileLoops extractFixedOuterLoops(scf::ForOp rootFOrOp, ArrayRef< int64_t > sizes)
Definition Utils.cpp:1427
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:796
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:1584
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.