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 13 #include "mlir/Dialect/SCF/Utils/Utils.h" 14 #include "mlir/Analysis/SliceAnalysis.h" 15 #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" 16 #include "mlir/Dialect/Func/IR/FuncOps.h" 17 #include "mlir/Dialect/SCF/SCF.h" 18 #include "mlir/IR/BlockAndValueMapping.h" 19 #include "mlir/IR/BuiltinOps.h" 20 #include "mlir/IR/PatternMatch.h" 21 #include "mlir/Support/MathExtras.h" 22 #include "mlir/Transforms/RegionUtils.h" 23 #include "llvm/ADT/STLExtras.h" 24 #include "llvm/ADT/SetVector.h" 25 #include "llvm/ADT/SmallPtrSet.h" 26 27 using namespace mlir; 28 29 namespace { 30 // This structure is to pass and return sets of loop parameters without 31 // confusing the order. 32 struct LoopParams { 33 Value lowerBound; 34 Value upperBound; 35 Value step; 36 }; 37 } // namespace 38 39 scf::ForOp 40 mlir::replaceLoopWithNewYields(OpBuilder &builder, scf::ForOp loop, 41 ValueRange newIterOperands, 42 const NewYieldValueFn &newYieldValuesFn) { 43 // Create a new loop before the existing one, with the extra operands. 44 OpBuilder::InsertionGuard g(builder); 45 builder.setInsertionPoint(loop); 46 auto operands = llvm::to_vector(loop.getIterOperands()); 47 operands.append(newIterOperands.begin(), newIterOperands.end()); 48 scf::ForOp newLoop = builder.create<scf::ForOp>( 49 loop.getLoc(), loop.getLowerBound(), loop.getUpperBound(), loop.getStep(), 50 operands, [](OpBuilder &, Location, Value, ValueRange) {}); 51 52 Block *loopBody = loop.getBody(); 53 Block *newLoopBody = newLoop.getBody(); 54 55 // Move the body of the original loop to the new loop. 56 newLoopBody->getOperations().splice(newLoopBody->end(), 57 loopBody->getOperations()); 58 59 // Generate the new yield values to use by using the callback and append the 60 // yield values to the scf.yield operation. 61 auto yield = cast<scf::YieldOp>(newLoopBody->getTerminator()); 62 ArrayRef<BlockArgument> newBBArgs = 63 newLoopBody->getArguments().take_back(newIterOperands.size()); 64 { 65 OpBuilder::InsertionGuard g(builder); 66 builder.setInsertionPoint(yield); 67 SmallVector<Value> newYieldedValues = 68 newYieldValuesFn(builder, loop.getLoc(), newBBArgs); 69 assert(newIterOperands.size() == newYieldedValues.size() && 70 "expected as many new yield values as new iter operands"); 71 yield.getResultsMutable().append(newYieldedValues); 72 } 73 74 // Remap the BlockArguments from the original loop to the new loop 75 // BlockArguments. 76 ArrayRef<BlockArgument> bbArgs = loopBody->getArguments(); 77 for (auto it : 78 llvm::zip(bbArgs, newLoopBody->getArguments().take_front(bbArgs.size()))) 79 std::get<0>(it).replaceAllUsesWith(std::get<1>(it)); 80 81 // Replace all uses of `newIterOperands` with the corresponding basic block 82 // arguments. 83 for (auto it : llvm::zip(newIterOperands, newBBArgs)) { 84 std::get<0>(it).replaceUsesWithIf(std::get<1>(it), [&](OpOperand &use) { 85 Operation *user = use.getOwner(); 86 return newLoop->isProperAncestor(user); 87 }); 88 } 89 90 // Replace all uses of the original loop with corresponding values from the 91 // new loop. 92 loop.replaceAllUsesWith( 93 newLoop.getResults().take_front(loop.getNumResults())); 94 95 // Add a fake yield to the original loop body that just returns the 96 // BlockArguments corresponding to the iter_args. This makes it a no-op loop. 97 // The loop is dead. The caller is expected to erase it. 98 builder.setInsertionPointToEnd(loopBody); 99 builder.create<scf::YieldOp>(loop->getLoc(), loop.getRegionIterArgs()); 100 101 return newLoop; 102 } 103 104 /// Outline a region with a single block into a new FuncOp. 105 /// Assumes the FuncOp result types is the type of the yielded operands of the 106 /// single block. This constraint makes it easy to determine the result. 107 /// This method also clones the `arith::ConstantIndexOp` at the start of 108 /// `outlinedFuncBody` to alloc simple canonicalizations. 109 // TODO: support more than single-block regions. 110 // TODO: more flexible constant handling. 111 FailureOr<func::FuncOp> mlir::outlineSingleBlockRegion(RewriterBase &rewriter, 112 Location loc, 113 Region ®ion, 114 StringRef funcName) { 115 assert(!funcName.empty() && "funcName cannot be empty"); 116 if (!region.hasOneBlock()) 117 return failure(); 118 119 Block *originalBlock = ®ion.front(); 120 Operation *originalTerminator = originalBlock->getTerminator(); 121 122 // Outline before current function. 123 OpBuilder::InsertionGuard g(rewriter); 124 rewriter.setInsertionPoint(region.getParentOfType<func::FuncOp>()); 125 126 SetVector<Value> captures; 127 getUsedValuesDefinedAbove(region, captures); 128 129 ValueRange outlinedValues(captures.getArrayRef()); 130 SmallVector<Type> outlinedFuncArgTypes; 131 SmallVector<Location> outlinedFuncArgLocs; 132 // Region's arguments are exactly the first block's arguments as per 133 // Region::getArguments(). 134 // Func's arguments are cat(regions's arguments, captures arguments). 135 for (BlockArgument arg : region.getArguments()) { 136 outlinedFuncArgTypes.push_back(arg.getType()); 137 outlinedFuncArgLocs.push_back(arg.getLoc()); 138 } 139 for (Value value : outlinedValues) { 140 outlinedFuncArgTypes.push_back(value.getType()); 141 outlinedFuncArgLocs.push_back(value.getLoc()); 142 } 143 FunctionType outlinedFuncType = 144 FunctionType::get(rewriter.getContext(), outlinedFuncArgTypes, 145 originalTerminator->getOperandTypes()); 146 auto outlinedFunc = 147 rewriter.create<func::FuncOp>(loc, funcName, outlinedFuncType); 148 Block *outlinedFuncBody = outlinedFunc.addEntryBlock(); 149 150 // Merge blocks while replacing the original block operands. 151 // Warning: `mergeBlocks` erases the original block, reconstruct it later. 152 int64_t numOriginalBlockArguments = originalBlock->getNumArguments(); 153 auto outlinedFuncBlockArgs = outlinedFuncBody->getArguments(); 154 { 155 OpBuilder::InsertionGuard g(rewriter); 156 rewriter.setInsertionPointToEnd(outlinedFuncBody); 157 rewriter.mergeBlocks( 158 originalBlock, outlinedFuncBody, 159 outlinedFuncBlockArgs.take_front(numOriginalBlockArguments)); 160 // Explicitly set up a new ReturnOp terminator. 161 rewriter.setInsertionPointToEnd(outlinedFuncBody); 162 rewriter.create<func::ReturnOp>(loc, originalTerminator->getResultTypes(), 163 originalTerminator->getOperands()); 164 } 165 166 // Reconstruct the block that was deleted and add a 167 // terminator(call_results). 168 Block *newBlock = rewriter.createBlock( 169 ®ion, region.begin(), 170 TypeRange{outlinedFuncArgTypes}.take_front(numOriginalBlockArguments), 171 ArrayRef<Location>(outlinedFuncArgLocs) 172 .take_front(numOriginalBlockArguments)); 173 { 174 OpBuilder::InsertionGuard g(rewriter); 175 rewriter.setInsertionPointToEnd(newBlock); 176 SmallVector<Value> callValues; 177 llvm::append_range(callValues, newBlock->getArguments()); 178 llvm::append_range(callValues, outlinedValues); 179 Operation *call = 180 rewriter.create<func::CallOp>(loc, outlinedFunc, callValues); 181 182 // `originalTerminator` was moved to `outlinedFuncBody` and is still valid. 183 // Clone `originalTerminator` to take the callOp results then erase it from 184 // `outlinedFuncBody`. 185 BlockAndValueMapping bvm; 186 bvm.map(originalTerminator->getOperands(), call->getResults()); 187 rewriter.clone(*originalTerminator, bvm); 188 rewriter.eraseOp(originalTerminator); 189 } 190 191 // Lastly, explicit RAUW outlinedValues, only for uses within `outlinedFunc`. 192 // Clone the `arith::ConstantIndexOp` at the start of `outlinedFuncBody`. 193 for (auto it : llvm::zip(outlinedValues, outlinedFuncBlockArgs.take_back( 194 outlinedValues.size()))) { 195 Value orig = std::get<0>(it); 196 Value repl = std::get<1>(it); 197 { 198 OpBuilder::InsertionGuard g(rewriter); 199 rewriter.setInsertionPointToStart(outlinedFuncBody); 200 if (Operation *cst = orig.getDefiningOp<arith::ConstantIndexOp>()) { 201 BlockAndValueMapping bvm; 202 repl = rewriter.clone(*cst, bvm)->getResult(0); 203 } 204 } 205 orig.replaceUsesWithIf(repl, [&](OpOperand &opOperand) { 206 return outlinedFunc->isProperAncestor(opOperand.getOwner()); 207 }); 208 } 209 210 return outlinedFunc; 211 } 212 213 LogicalResult mlir::outlineIfOp(RewriterBase &b, scf::IfOp ifOp, 214 func::FuncOp *thenFn, StringRef thenFnName, 215 func::FuncOp *elseFn, StringRef elseFnName) { 216 IRRewriter rewriter(b); 217 Location loc = ifOp.getLoc(); 218 FailureOr<func::FuncOp> outlinedFuncOpOrFailure; 219 if (thenFn && !ifOp.getThenRegion().empty()) { 220 outlinedFuncOpOrFailure = outlineSingleBlockRegion( 221 rewriter, loc, ifOp.getThenRegion(), thenFnName); 222 if (failed(outlinedFuncOpOrFailure)) 223 return failure(); 224 *thenFn = *outlinedFuncOpOrFailure; 225 } 226 if (elseFn && !ifOp.getElseRegion().empty()) { 227 outlinedFuncOpOrFailure = outlineSingleBlockRegion( 228 rewriter, loc, ifOp.getElseRegion(), elseFnName); 229 if (failed(outlinedFuncOpOrFailure)) 230 return failure(); 231 *elseFn = *outlinedFuncOpOrFailure; 232 } 233 return success(); 234 } 235 236 bool mlir::getInnermostParallelLoops(Operation *rootOp, 237 SmallVectorImpl<scf::ParallelOp> &result) { 238 assert(rootOp != nullptr && "Root operation must not be a nullptr."); 239 bool rootEnclosesPloops = false; 240 for (Region ®ion : rootOp->getRegions()) { 241 for (Block &block : region.getBlocks()) { 242 for (Operation &op : block) { 243 bool enclosesPloops = getInnermostParallelLoops(&op, result); 244 rootEnclosesPloops |= enclosesPloops; 245 if (auto ploop = dyn_cast<scf::ParallelOp>(op)) { 246 rootEnclosesPloops = true; 247 248 // Collect parallel loop if it is an innermost one. 249 if (!enclosesPloops) 250 result.push_back(ploop); 251 } 252 } 253 } 254 } 255 return rootEnclosesPloops; 256 } 257 258 // Build the IR that performs ceil division of a positive value by a constant: 259 // ceildiv(a, B) = divis(a + (B-1), B) 260 // where divis is rounding-to-zero division. 261 static Value ceilDivPositive(OpBuilder &builder, Location loc, Value dividend, 262 int64_t divisor) { 263 assert(divisor > 0 && "expected positive divisor"); 264 assert(dividend.getType().isIndex() && "expected index-typed value"); 265 266 Value divisorMinusOneCst = 267 builder.create<arith::ConstantIndexOp>(loc, divisor - 1); 268 Value divisorCst = builder.create<arith::ConstantIndexOp>(loc, divisor); 269 Value sum = builder.create<arith::AddIOp>(loc, dividend, divisorMinusOneCst); 270 return builder.create<arith::DivSIOp>(loc, sum, divisorCst); 271 } 272 273 // Build the IR that performs ceil division of a positive value by another 274 // positive value: 275 // ceildiv(a, b) = divis(a + (b - 1), b) 276 // where divis is rounding-to-zero division. 277 static Value ceilDivPositive(OpBuilder &builder, Location loc, Value dividend, 278 Value divisor) { 279 assert(dividend.getType().isIndex() && "expected index-typed value"); 280 281 Value cstOne = builder.create<arith::ConstantIndexOp>(loc, 1); 282 Value divisorMinusOne = builder.create<arith::SubIOp>(loc, divisor, cstOne); 283 Value sum = builder.create<arith::AddIOp>(loc, dividend, divisorMinusOne); 284 return builder.create<arith::DivSIOp>(loc, sum, divisor); 285 } 286 287 /// Helper to replace uses of loop carried values (iter_args) and loop 288 /// yield values while promoting single iteration scf.for ops. 289 static void replaceIterArgsAndYieldResults(scf::ForOp forOp) { 290 // Replace uses of iter arguments with iter operands (initial values). 291 auto iterOperands = forOp.getIterOperands(); 292 auto iterArgs = forOp.getRegionIterArgs(); 293 for (auto e : llvm::zip(iterOperands, iterArgs)) 294 std::get<1>(e).replaceAllUsesWith(std::get<0>(e)); 295 296 // Replace uses of loop results with the values yielded by the loop. 297 auto outerResults = forOp.getResults(); 298 auto innerResults = forOp.getBody()->getTerminator()->getOperands(); 299 for (auto e : llvm::zip(outerResults, innerResults)) 300 std::get<0>(e).replaceAllUsesWith(std::get<1>(e)); 301 } 302 303 /// Promotes the loop body of a forOp to its containing block if the forOp 304 /// it can be determined that the loop has a single iteration. 305 LogicalResult mlir::promoteIfSingleIteration(scf::ForOp forOp) { 306 auto lbCstOp = forOp.getLowerBound().getDefiningOp<arith::ConstantIndexOp>(); 307 auto ubCstOp = forOp.getUpperBound().getDefiningOp<arith::ConstantIndexOp>(); 308 auto stepCstOp = forOp.getStep().getDefiningOp<arith::ConstantIndexOp>(); 309 if (!lbCstOp || !ubCstOp || !stepCstOp || lbCstOp.value() < 0 || 310 ubCstOp.value() < 0 || stepCstOp.value() < 0) 311 return failure(); 312 int64_t tripCount = 313 mlir::ceilDiv(ubCstOp.value() - lbCstOp.value(), stepCstOp.value()); 314 if (tripCount != 1) 315 return failure(); 316 auto iv = forOp.getInductionVar(); 317 iv.replaceAllUsesWith(lbCstOp); 318 319 replaceIterArgsAndYieldResults(forOp); 320 321 // Move the loop body operations, except for its terminator, to the loop's 322 // containing block. 323 auto *parentBlock = forOp->getBlock(); 324 forOp.getBody()->getTerminator()->erase(); 325 parentBlock->getOperations().splice(Block::iterator(forOp), 326 forOp.getBody()->getOperations()); 327 forOp.erase(); 328 return success(); 329 } 330 331 /// Generates unrolled copies of scf::ForOp 'loopBodyBlock', with 332 /// associated 'forOpIV' by 'unrollFactor', calling 'ivRemapFn' to remap 333 /// 'forOpIV' for each unrolled body. If specified, annotates the Ops in each 334 /// unrolled iteration using annotateFn. 335 static void generateUnrolledLoop( 336 Block *loopBodyBlock, Value forOpIV, uint64_t unrollFactor, 337 function_ref<Value(unsigned, Value, OpBuilder)> ivRemapFn, 338 function_ref<void(unsigned, Operation *, OpBuilder)> annotateFn, 339 ValueRange iterArgs, ValueRange yieldedValues) { 340 // Builder to insert unrolled bodies just before the terminator of the body of 341 // 'forOp'. 342 auto builder = OpBuilder::atBlockTerminator(loopBodyBlock); 343 344 if (!annotateFn) 345 annotateFn = [](unsigned, Operation *, OpBuilder) {}; 346 347 // Keep a pointer to the last non-terminator operation in the original block 348 // so that we know what to clone (since we are doing this in-place). 349 Block::iterator srcBlockEnd = std::prev(loopBodyBlock->end(), 2); 350 351 // Unroll the contents of 'forOp' (append unrollFactor - 1 additional copies). 352 SmallVector<Value, 4> lastYielded(yieldedValues); 353 354 for (unsigned i = 1; i < unrollFactor; i++) { 355 BlockAndValueMapping operandMap; 356 357 // Prepare operand map. 358 operandMap.map(iterArgs, lastYielded); 359 360 // If the induction variable is used, create a remapping to the value for 361 // this unrolled instance. 362 if (!forOpIV.use_empty()) { 363 Value ivUnroll = ivRemapFn(i, forOpIV, builder); 364 operandMap.map(forOpIV, ivUnroll); 365 } 366 367 // Clone the original body of 'forOp'. 368 for (auto it = loopBodyBlock->begin(); it != std::next(srcBlockEnd); it++) { 369 Operation *clonedOp = builder.clone(*it, operandMap); 370 annotateFn(i, clonedOp, builder); 371 } 372 373 // Update yielded values. 374 for (unsigned i = 0, e = lastYielded.size(); i < e; i++) 375 lastYielded[i] = operandMap.lookup(yieldedValues[i]); 376 } 377 378 // Make sure we annotate the Ops in the original body. We do this last so that 379 // any annotations are not copied into the cloned Ops above. 380 for (auto it = loopBodyBlock->begin(); it != std::next(srcBlockEnd); it++) 381 annotateFn(0, &*it, builder); 382 383 // Update operands of the yield statement. 384 loopBodyBlock->getTerminator()->setOperands(lastYielded); 385 } 386 387 /// Unrolls 'forOp' by 'unrollFactor', returns success if the loop is unrolled. 388 LogicalResult mlir::loopUnrollByFactor( 389 scf::ForOp forOp, uint64_t unrollFactor, 390 function_ref<void(unsigned, Operation *, OpBuilder)> annotateFn) { 391 assert(unrollFactor > 0 && "expected positive unroll factor"); 392 393 // Return if the loop body is empty. 394 if (llvm::hasSingleElement(forOp.getBody()->getOperations())) 395 return success(); 396 397 // Compute tripCount = ceilDiv((upperBound - lowerBound), step) and populate 398 // 'upperBoundUnrolled' and 'stepUnrolled' for static and dynamic cases. 399 OpBuilder boundsBuilder(forOp); 400 auto loc = forOp.getLoc(); 401 auto step = forOp.getStep(); 402 Value upperBoundUnrolled; 403 Value stepUnrolled; 404 bool generateEpilogueLoop = true; 405 406 auto lbCstOp = forOp.getLowerBound().getDefiningOp<arith::ConstantIndexOp>(); 407 auto ubCstOp = forOp.getUpperBound().getDefiningOp<arith::ConstantIndexOp>(); 408 auto stepCstOp = forOp.getStep().getDefiningOp<arith::ConstantIndexOp>(); 409 if (lbCstOp && ubCstOp && stepCstOp) { 410 // Constant loop bounds computation. 411 int64_t lbCst = lbCstOp.value(); 412 int64_t ubCst = ubCstOp.value(); 413 int64_t stepCst = stepCstOp.value(); 414 assert(lbCst >= 0 && ubCst >= 0 && stepCst >= 0 && 415 "expected positive loop bounds and step"); 416 int64_t tripCount = mlir::ceilDiv(ubCst - lbCst, stepCst); 417 418 if (unrollFactor == 1) { 419 if (tripCount == 1 && failed(promoteIfSingleIteration(forOp))) 420 return failure(); 421 return success(); 422 } 423 424 int64_t tripCountEvenMultiple = tripCount - (tripCount % unrollFactor); 425 int64_t upperBoundUnrolledCst = lbCst + tripCountEvenMultiple * stepCst; 426 assert(upperBoundUnrolledCst <= ubCst); 427 int64_t stepUnrolledCst = stepCst * unrollFactor; 428 429 // Create constant for 'upperBoundUnrolled' and set epilogue loop flag. 430 generateEpilogueLoop = upperBoundUnrolledCst < ubCst; 431 if (generateEpilogueLoop) 432 upperBoundUnrolled = boundsBuilder.create<arith::ConstantIndexOp>( 433 loc, upperBoundUnrolledCst); 434 else 435 upperBoundUnrolled = ubCstOp; 436 437 // Create constant for 'stepUnrolled'. 438 stepUnrolled = stepCst == stepUnrolledCst 439 ? step 440 : boundsBuilder.create<arith::ConstantIndexOp>( 441 loc, stepUnrolledCst); 442 } else { 443 // Dynamic loop bounds computation. 444 // TODO: Add dynamic asserts for negative lb/ub/step, or 445 // consider using ceilDiv from AffineApplyExpander. 446 auto lowerBound = forOp.getLowerBound(); 447 auto upperBound = forOp.getUpperBound(); 448 Value diff = 449 boundsBuilder.create<arith::SubIOp>(loc, upperBound, lowerBound); 450 Value tripCount = ceilDivPositive(boundsBuilder, loc, diff, step); 451 Value unrollFactorCst = 452 boundsBuilder.create<arith::ConstantIndexOp>(loc, unrollFactor); 453 Value tripCountRem = 454 boundsBuilder.create<arith::RemSIOp>(loc, tripCount, unrollFactorCst); 455 // Compute tripCountEvenMultiple = tripCount - (tripCount % unrollFactor) 456 Value tripCountEvenMultiple = 457 boundsBuilder.create<arith::SubIOp>(loc, tripCount, tripCountRem); 458 // Compute upperBoundUnrolled = lowerBound + tripCountEvenMultiple * step 459 upperBoundUnrolled = boundsBuilder.create<arith::AddIOp>( 460 loc, lowerBound, 461 boundsBuilder.create<arith::MulIOp>(loc, tripCountEvenMultiple, step)); 462 // Scale 'step' by 'unrollFactor'. 463 stepUnrolled = 464 boundsBuilder.create<arith::MulIOp>(loc, step, unrollFactorCst); 465 } 466 467 // Create epilogue clean up loop starting at 'upperBoundUnrolled'. 468 if (generateEpilogueLoop) { 469 OpBuilder epilogueBuilder(forOp->getContext()); 470 epilogueBuilder.setInsertionPoint(forOp->getBlock(), 471 std::next(Block::iterator(forOp))); 472 auto epilogueForOp = cast<scf::ForOp>(epilogueBuilder.clone(*forOp)); 473 epilogueForOp.setLowerBound(upperBoundUnrolled); 474 475 // Update uses of loop results. 476 auto results = forOp.getResults(); 477 auto epilogueResults = epilogueForOp.getResults(); 478 479 for (auto e : llvm::zip(results, epilogueResults)) { 480 std::get<0>(e).replaceAllUsesWith(std::get<1>(e)); 481 } 482 epilogueForOp->setOperands(epilogueForOp.getNumControlOperands(), 483 epilogueForOp.getNumIterOperands(), results); 484 (void)promoteIfSingleIteration(epilogueForOp); 485 } 486 487 // Create unrolled loop. 488 forOp.setUpperBound(upperBoundUnrolled); 489 forOp.setStep(stepUnrolled); 490 491 auto iterArgs = ValueRange(forOp.getRegionIterArgs()); 492 auto yieldedValues = forOp.getBody()->getTerminator()->getOperands(); 493 494 generateUnrolledLoop( 495 forOp.getBody(), forOp.getInductionVar(), unrollFactor, 496 [&](unsigned i, Value iv, OpBuilder b) { 497 // iv' = iv + step * i; 498 auto stride = b.create<arith::MulIOp>( 499 loc, step, b.create<arith::ConstantIndexOp>(loc, i)); 500 return b.create<arith::AddIOp>(loc, iv, stride); 501 }, 502 annotateFn, iterArgs, yieldedValues); 503 // Promote the loop body up if this has turned into a single iteration loop. 504 (void)promoteIfSingleIteration(forOp); 505 return success(); 506 } 507 508 /// Return the new lower bound, upper bound, and step in that order. Insert any 509 /// additional bounds calculations before the given builder and any additional 510 /// conversion back to the original loop induction value inside the given Block. 511 static LoopParams normalizeLoop(OpBuilder &boundsBuilder, 512 OpBuilder &insideLoopBuilder, Location loc, 513 Value lowerBound, Value upperBound, Value step, 514 Value inductionVar) { 515 // Check if the loop is already known to have a constant zero lower bound or 516 // a constant one step. 517 bool isZeroBased = false; 518 if (auto ubCst = lowerBound.getDefiningOp<arith::ConstantIndexOp>()) 519 isZeroBased = ubCst.value() == 0; 520 521 bool isStepOne = false; 522 if (auto stepCst = step.getDefiningOp<arith::ConstantIndexOp>()) 523 isStepOne = stepCst.value() == 1; 524 525 // Compute the number of iterations the loop executes: ceildiv(ub - lb, step) 526 // assuming the step is strictly positive. Update the bounds and the step 527 // of the loop to go from 0 to the number of iterations, if necessary. 528 // TODO: introduce support for negative steps or emit dynamic asserts 529 // on step positivity, whatever gets implemented first. 530 if (isZeroBased && isStepOne) 531 return {/*lowerBound=*/lowerBound, /*upperBound=*/upperBound, 532 /*step=*/step}; 533 534 Value diff = boundsBuilder.create<arith::SubIOp>(loc, upperBound, lowerBound); 535 Value newUpperBound = ceilDivPositive(boundsBuilder, loc, diff, step); 536 537 Value newLowerBound = 538 isZeroBased ? lowerBound 539 : boundsBuilder.create<arith::ConstantIndexOp>(loc, 0); 540 Value newStep = 541 isStepOne ? step : boundsBuilder.create<arith::ConstantIndexOp>(loc, 1); 542 543 // Insert code computing the value of the original loop induction variable 544 // from the "normalized" one. 545 Value scaled = 546 isStepOne 547 ? inductionVar 548 : insideLoopBuilder.create<arith::MulIOp>(loc, inductionVar, step); 549 Value shifted = 550 isZeroBased 551 ? scaled 552 : insideLoopBuilder.create<arith::AddIOp>(loc, scaled, lowerBound); 553 554 SmallPtrSet<Operation *, 2> preserve{scaled.getDefiningOp(), 555 shifted.getDefiningOp()}; 556 inductionVar.replaceAllUsesExcept(shifted, preserve); 557 return {/*lowerBound=*/newLowerBound, /*upperBound=*/newUpperBound, 558 /*step=*/newStep}; 559 } 560 561 /// Transform a loop with a strictly positive step 562 /// for %i = %lb to %ub step %s 563 /// into a 0-based loop with step 1 564 /// for %ii = 0 to ceildiv(%ub - %lb, %s) step 1 { 565 /// %i = %ii * %s + %lb 566 /// Insert the induction variable remapping in the body of `inner`, which is 567 /// expected to be either `loop` or another loop perfectly nested under `loop`. 568 /// Insert the definition of new bounds immediate before `outer`, which is 569 /// expected to be either `loop` or its parent in the loop nest. 570 static void normalizeLoop(scf::ForOp loop, scf::ForOp outer, scf::ForOp inner) { 571 OpBuilder builder(outer); 572 OpBuilder innerBuilder = OpBuilder::atBlockBegin(inner.getBody()); 573 auto loopPieces = normalizeLoop(builder, innerBuilder, loop.getLoc(), 574 loop.getLowerBound(), loop.getUpperBound(), 575 loop.getStep(), loop.getInductionVar()); 576 577 loop.setLowerBound(loopPieces.lowerBound); 578 loop.setUpperBound(loopPieces.upperBound); 579 loop.setStep(loopPieces.step); 580 } 581 582 void mlir::coalesceLoops(MutableArrayRef<scf::ForOp> loops) { 583 if (loops.size() < 2) 584 return; 585 586 scf::ForOp innermost = loops.back(); 587 scf::ForOp outermost = loops.front(); 588 589 // 1. Make sure all loops iterate from 0 to upperBound with step 1. This 590 // allows the following code to assume upperBound is the number of iterations. 591 for (auto loop : loops) 592 normalizeLoop(loop, outermost, innermost); 593 594 // 2. Emit code computing the upper bound of the coalesced loop as product 595 // of the number of iterations of all loops. 596 OpBuilder builder(outermost); 597 Location loc = outermost.getLoc(); 598 Value upperBound = outermost.getUpperBound(); 599 for (auto loop : loops.drop_front()) 600 upperBound = 601 builder.create<arith::MulIOp>(loc, upperBound, loop.getUpperBound()); 602 outermost.setUpperBound(upperBound); 603 604 builder.setInsertionPointToStart(outermost.getBody()); 605 606 // 3. Remap induction variables. For each original loop, the value of the 607 // induction variable can be obtained by dividing the induction variable of 608 // the linearized loop by the total number of iterations of the loops nested 609 // in it modulo the number of iterations in this loop (remove the values 610 // related to the outer loops): 611 // iv_i = floordiv(iv_linear, product-of-loop-ranges-until-i) mod range_i. 612 // Compute these iteratively from the innermost loop by creating a "running 613 // quotient" of division by the range. 614 Value previous = outermost.getInductionVar(); 615 for (unsigned i = 0, e = loops.size(); i < e; ++i) { 616 unsigned idx = loops.size() - i - 1; 617 if (i != 0) 618 previous = builder.create<arith::DivSIOp>(loc, previous, 619 loops[idx + 1].getUpperBound()); 620 621 Value iv = (i == e - 1) ? previous 622 : builder.create<arith::RemSIOp>( 623 loc, previous, loops[idx].getUpperBound()); 624 replaceAllUsesInRegionWith(loops[idx].getInductionVar(), iv, 625 loops.back().getRegion()); 626 } 627 628 // 4. Move the operations from the innermost just above the second-outermost 629 // loop, delete the extra terminator and the second-outermost loop. 630 scf::ForOp second = loops[1]; 631 innermost.getBody()->back().erase(); 632 outermost.getBody()->getOperations().splice( 633 Block::iterator(second.getOperation()), 634 innermost.getBody()->getOperations()); 635 second.erase(); 636 } 637 638 void mlir::collapseParallelLoops( 639 scf::ParallelOp loops, ArrayRef<std::vector<unsigned>> combinedDimensions) { 640 OpBuilder outsideBuilder(loops); 641 Location loc = loops.getLoc(); 642 643 // Presort combined dimensions. 644 auto sortedDimensions = llvm::to_vector<3>(combinedDimensions); 645 for (auto &dims : sortedDimensions) 646 std::sort(dims.begin(), dims.end()); 647 648 // Normalize ParallelOp's iteration pattern. 649 SmallVector<Value, 3> normalizedLowerBounds, normalizedSteps, 650 normalizedUpperBounds; 651 for (unsigned i = 0, e = loops.getNumLoops(); i < e; ++i) { 652 OpBuilder insideLoopBuilder = OpBuilder::atBlockBegin(loops.getBody()); 653 auto resultBounds = 654 normalizeLoop(outsideBuilder, insideLoopBuilder, loc, 655 loops.getLowerBound()[i], loops.getUpperBound()[i], 656 loops.getStep()[i], loops.getBody()->getArgument(i)); 657 658 normalizedLowerBounds.push_back(resultBounds.lowerBound); 659 normalizedUpperBounds.push_back(resultBounds.upperBound); 660 normalizedSteps.push_back(resultBounds.step); 661 } 662 663 // Combine iteration spaces. 664 SmallVector<Value, 3> lowerBounds, upperBounds, steps; 665 auto cst0 = outsideBuilder.create<arith::ConstantIndexOp>(loc, 0); 666 auto cst1 = outsideBuilder.create<arith::ConstantIndexOp>(loc, 1); 667 for (unsigned i = 0, e = sortedDimensions.size(); i < e; ++i) { 668 Value newUpperBound = outsideBuilder.create<arith::ConstantIndexOp>(loc, 1); 669 for (auto idx : sortedDimensions[i]) { 670 newUpperBound = outsideBuilder.create<arith::MulIOp>( 671 loc, newUpperBound, normalizedUpperBounds[idx]); 672 } 673 lowerBounds.push_back(cst0); 674 steps.push_back(cst1); 675 upperBounds.push_back(newUpperBound); 676 } 677 678 // Create new ParallelLoop with conversions to the original induction values. 679 // The loop below uses divisions to get the relevant range of values in the 680 // new induction value that represent each range of the original induction 681 // value. The remainders then determine based on that range, which iteration 682 // of the original induction value this represents. This is a normalized value 683 // that is un-normalized already by the previous logic. 684 auto newPloop = outsideBuilder.create<scf::ParallelOp>( 685 loc, lowerBounds, upperBounds, steps, 686 [&](OpBuilder &insideBuilder, Location, ValueRange ploopIVs) { 687 for (unsigned i = 0, e = combinedDimensions.size(); i < e; ++i) { 688 Value previous = ploopIVs[i]; 689 unsigned numberCombinedDimensions = combinedDimensions[i].size(); 690 // Iterate over all except the last induction value. 691 for (unsigned j = numberCombinedDimensions - 1; j > 0; --j) { 692 unsigned idx = combinedDimensions[i][j]; 693 694 // Determine the current induction value's current loop iteration 695 Value iv = insideBuilder.create<arith::RemSIOp>( 696 loc, previous, normalizedUpperBounds[idx]); 697 replaceAllUsesInRegionWith(loops.getBody()->getArgument(idx), iv, 698 loops.getRegion()); 699 700 // Remove the effect of the current induction value to prepare for 701 // the next value. 702 previous = insideBuilder.create<arith::DivSIOp>( 703 loc, previous, normalizedUpperBounds[idx]); 704 } 705 706 // The final induction value is just the remaining value. 707 unsigned idx = combinedDimensions[i][0]; 708 replaceAllUsesInRegionWith(loops.getBody()->getArgument(idx), 709 previous, loops.getRegion()); 710 } 711 }); 712 713 // Replace the old loop with the new loop. 714 loops.getBody()->back().erase(); 715 newPloop.getBody()->getOperations().splice( 716 Block::iterator(newPloop.getBody()->back()), 717 loops.getBody()->getOperations()); 718 loops.erase(); 719 } 720 721 // Hoist the ops within `outer` that appear before `inner`. 722 // Such ops include the ops that have been introduced by parametric tiling. 723 // Ops that come from triangular loops (i.e. that belong to the program slice 724 // rooted at `outer`) and ops that have side effects cannot be hoisted. 725 // Return failure when any op fails to hoist. 726 static LogicalResult hoistOpsBetween(scf::ForOp outer, scf::ForOp inner) { 727 SetVector<Operation *> forwardSlice; 728 getForwardSlice( 729 outer.getInductionVar(), &forwardSlice, 730 [&inner](Operation *op) { return op != inner.getOperation(); }); 731 LogicalResult status = success(); 732 SmallVector<Operation *, 8> toHoist; 733 for (auto &op : outer.getBody()->without_terminator()) { 734 // Stop when encountering the inner loop. 735 if (&op == inner.getOperation()) 736 break; 737 // Skip over non-hoistable ops. 738 if (forwardSlice.count(&op) > 0) { 739 status = failure(); 740 continue; 741 } 742 // Skip intermediate scf::ForOp, these are not considered a failure. 743 if (isa<scf::ForOp>(op)) 744 continue; 745 // Skip other ops with regions. 746 if (op.getNumRegions() > 0) { 747 status = failure(); 748 continue; 749 } 750 // Skip if op has side effects. 751 // TODO: loads to immutable memory regions are ok. 752 if (!MemoryEffectOpInterface::hasNoEffect(&op)) { 753 status = failure(); 754 continue; 755 } 756 toHoist.push_back(&op); 757 } 758 auto *outerForOp = outer.getOperation(); 759 for (auto *op : toHoist) 760 op->moveBefore(outerForOp); 761 return status; 762 } 763 764 // Traverse the interTile and intraTile loops and try to hoist ops such that 765 // bands of perfectly nested loops are isolated. 766 // Return failure if either perfect interTile or perfect intraTile bands cannot 767 // be formed. 768 static LogicalResult tryIsolateBands(const TileLoops &tileLoops) { 769 LogicalResult status = success(); 770 const Loops &interTile = tileLoops.first; 771 const Loops &intraTile = tileLoops.second; 772 auto size = interTile.size(); 773 assert(size == intraTile.size()); 774 if (size <= 1) 775 return success(); 776 for (unsigned s = 1; s < size; ++s) 777 status = succeeded(status) ? hoistOpsBetween(intraTile[0], intraTile[s]) 778 : failure(); 779 for (unsigned s = 1; s < size; ++s) 780 status = succeeded(status) ? hoistOpsBetween(interTile[0], interTile[s]) 781 : failure(); 782 return status; 783 } 784 785 /// Collect perfectly nested loops starting from `rootForOps`. Loops are 786 /// perfectly nested if each loop is the first and only non-terminator operation 787 /// in the parent loop. Collect at most `maxLoops` loops and append them to 788 /// `forOps`. 789 template <typename T> 790 static void getPerfectlyNestedLoopsImpl( 791 SmallVectorImpl<T> &forOps, T rootForOp, 792 unsigned maxLoops = std::numeric_limits<unsigned>::max()) { 793 for (unsigned i = 0; i < maxLoops; ++i) { 794 forOps.push_back(rootForOp); 795 Block &body = rootForOp.getRegion().front(); 796 if (body.begin() != std::prev(body.end(), 2)) 797 return; 798 799 rootForOp = dyn_cast<T>(&body.front()); 800 if (!rootForOp) 801 return; 802 } 803 } 804 805 static Loops stripmineSink(scf::ForOp forOp, Value factor, 806 ArrayRef<scf::ForOp> targets) { 807 auto originalStep = forOp.getStep(); 808 auto iv = forOp.getInductionVar(); 809 810 OpBuilder b(forOp); 811 forOp.setStep(b.create<arith::MulIOp>(forOp.getLoc(), originalStep, factor)); 812 813 Loops innerLoops; 814 for (auto t : targets) { 815 // Save information for splicing ops out of t when done 816 auto begin = t.getBody()->begin(); 817 auto nOps = t.getBody()->getOperations().size(); 818 819 // Insert newForOp before the terminator of `t`. 820 auto b = OpBuilder::atBlockTerminator((t.getBody())); 821 Value stepped = b.create<arith::AddIOp>(t.getLoc(), iv, forOp.getStep()); 822 Value less = b.create<arith::CmpIOp>(t.getLoc(), arith::CmpIPredicate::slt, 823 forOp.getUpperBound(), stepped); 824 Value ub = b.create<arith::SelectOp>(t.getLoc(), less, 825 forOp.getUpperBound(), stepped); 826 827 // Splice [begin, begin + nOps - 1) into `newForOp` and replace uses. 828 auto newForOp = b.create<scf::ForOp>(t.getLoc(), iv, ub, originalStep); 829 newForOp.getBody()->getOperations().splice( 830 newForOp.getBody()->getOperations().begin(), 831 t.getBody()->getOperations(), begin, std::next(begin, nOps - 1)); 832 replaceAllUsesInRegionWith(iv, newForOp.getInductionVar(), 833 newForOp.getRegion()); 834 835 innerLoops.push_back(newForOp); 836 } 837 838 return innerLoops; 839 } 840 841 // Stripmines a `forOp` by `factor` and sinks it under a single `target`. 842 // Returns the new for operation, nested immediately under `target`. 843 template <typename SizeType> 844 static scf::ForOp stripmineSink(scf::ForOp forOp, SizeType factor, 845 scf::ForOp target) { 846 // TODO: Use cheap structural assertions that targets are nested under 847 // forOp and that targets are not nested under each other when DominanceInfo 848 // exposes the capability. It seems overkill to construct a whole function 849 // dominance tree at this point. 850 auto res = stripmineSink(forOp, factor, ArrayRef<scf::ForOp>(target)); 851 assert(res.size() == 1 && "Expected 1 inner forOp"); 852 return res[0]; 853 } 854 855 SmallVector<Loops, 8> mlir::tile(ArrayRef<scf::ForOp> forOps, 856 ArrayRef<Value> sizes, 857 ArrayRef<scf::ForOp> targets) { 858 SmallVector<SmallVector<scf::ForOp, 8>, 8> res; 859 SmallVector<scf::ForOp, 8> currentTargets(targets.begin(), targets.end()); 860 for (auto it : llvm::zip(forOps, sizes)) { 861 auto step = stripmineSink(std::get<0>(it), std::get<1>(it), currentTargets); 862 res.push_back(step); 863 currentTargets = step; 864 } 865 return res; 866 } 867 868 Loops mlir::tile(ArrayRef<scf::ForOp> forOps, ArrayRef<Value> sizes, 869 scf::ForOp target) { 870 SmallVector<scf::ForOp, 8> res; 871 for (auto loops : tile(forOps, sizes, ArrayRef<scf::ForOp>(target))) { 872 assert(loops.size() == 1); 873 res.push_back(loops[0]); 874 } 875 return res; 876 } 877 878 Loops mlir::tilePerfectlyNested(scf::ForOp rootForOp, ArrayRef<Value> sizes) { 879 // Collect perfectly nested loops. If more size values provided than nested 880 // loops available, truncate `sizes`. 881 SmallVector<scf::ForOp, 4> forOps; 882 forOps.reserve(sizes.size()); 883 getPerfectlyNestedLoopsImpl(forOps, rootForOp, sizes.size()); 884 if (forOps.size() < sizes.size()) 885 sizes = sizes.take_front(forOps.size()); 886 887 return ::tile(forOps, sizes, forOps.back()); 888 } 889 890 void mlir::getPerfectlyNestedLoops(SmallVectorImpl<scf::ForOp> &nestedLoops, 891 scf::ForOp root) { 892 getPerfectlyNestedLoopsImpl(nestedLoops, root); 893 } 894 895 TileLoops mlir::extractFixedOuterLoops(scf::ForOp rootForOp, 896 ArrayRef<int64_t> sizes) { 897 // Collect perfectly nested loops. If more size values provided than nested 898 // loops available, truncate `sizes`. 899 SmallVector<scf::ForOp, 4> forOps; 900 forOps.reserve(sizes.size()); 901 getPerfectlyNestedLoopsImpl(forOps, rootForOp, sizes.size()); 902 if (forOps.size() < sizes.size()) 903 sizes = sizes.take_front(forOps.size()); 904 905 // Compute the tile sizes such that i-th outer loop executes size[i] 906 // iterations. Given that the loop current executes 907 // numIterations = ceildiv((upperBound - lowerBound), step) 908 // iterations, we need to tile with size ceildiv(numIterations, size[i]). 909 SmallVector<Value, 4> tileSizes; 910 tileSizes.reserve(sizes.size()); 911 for (unsigned i = 0, e = sizes.size(); i < e; ++i) { 912 assert(sizes[i] > 0 && "expected strictly positive size for strip-mining"); 913 914 auto forOp = forOps[i]; 915 OpBuilder builder(forOp); 916 auto loc = forOp.getLoc(); 917 Value diff = builder.create<arith::SubIOp>(loc, forOp.getUpperBound(), 918 forOp.getLowerBound()); 919 Value numIterations = ceilDivPositive(builder, loc, diff, forOp.getStep()); 920 Value iterationsPerBlock = 921 ceilDivPositive(builder, loc, numIterations, sizes[i]); 922 tileSizes.push_back(iterationsPerBlock); 923 } 924 925 // Call parametric tiling with the given sizes. 926 auto intraTile = tile(forOps, tileSizes, forOps.back()); 927 TileLoops tileLoops = std::make_pair(forOps, intraTile); 928 929 // TODO: for now we just ignore the result of band isolation. 930 // In the future, mapping decisions may be impacted by the ability to 931 // isolate perfectly nested bands. 932 (void)tryIsolateBands(tileLoops); 933 934 return tileLoops; 935 } 936