| //===- WrapInZeroTripCheck.cpp - Loop transforms to add zero-trip-check ---===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Dialect/SCF/IR/SCF.h" | |
| #include "mlir/Dialect/SCF/Transforms/Transforms.h" | |
| #include "mlir/IR/IRMapping.h" | |
| #include "mlir/IR/PatternMatch.h" | |
| using namespace mlir; | |
| /// Create zero-trip-check around a `while` op and return the new loop op in the | |
| /// check. The while loop is rotated to avoid evaluating the condition twice. | |
| /// | |
| /// Given an example below: | |
| /// | |
| /// scf.while (%arg0 = %init) : (i32) -> i64 { | |
| /// %val = .., %arg0 : i64 | |
| /// %cond = arith.cmpi .., %arg0 : i32 | |
| /// scf.condition(%cond) %val : i64 | |
| /// } do { | |
| /// ^bb0(%arg1: i64): | |
| /// %next = .., %arg1 : i32 | |
| /// scf.yield %next : i32 | |
| /// } | |
| /// | |
| /// First clone before block to the front of the loop: | |
| /// | |
| /// %pre_val = .., %init : i64 | |
| /// %pre_cond = arith.cmpi .., %init : i32 | |
| /// scf.while (%arg0 = %init) : (i32) -> i64 { | |
| /// %val = .., %arg0 : i64 | |
| /// %cond = arith.cmpi .., %arg0 : i32 | |
| /// scf.condition(%cond) %val : i64 | |
| /// } do { | |
| /// ^bb0(%arg1: i64): | |
| /// %next = .., %arg1 : i32 | |
| /// scf.yield %next : i32 | |
| /// } | |
| /// | |
| /// Create `if` op with the condition, rotate and move the loop into the else | |
| /// branch: | |
| /// | |
| /// %pre_val = .., %init : i64 | |
| /// %pre_cond = arith.cmpi .., %init : i32 | |
| /// scf.if %pre_cond -> i64 { | |
| /// %res = scf.while (%arg1 = %va0) : (i64) -> i64 { | |
| /// // Original after block | |
| /// %next = .., %arg1 : i32 | |
| /// // Original before block | |
| /// %val = .., %next : i64 | |
| /// %cond = arith.cmpi .., %next : i32 | |
| /// scf.condition(%cond) %val : i64 | |
| /// } do { | |
| /// ^bb0(%arg2: i64): | |
| /// %scf.yield %arg2 : i32 | |
| /// } | |
| /// scf.yield %res : i64 | |
| /// } else { | |
| /// scf.yield %pre_val : i64 | |
| /// } | |
| FailureOr<scf::WhileOp> mlir::scf::wrapWhileLoopInZeroTripCheck( | |
| scf::WhileOp whileOp, RewriterBase &rewriter, bool forceCreateCheck) { | |
| // If the loop is in do-while form (after block only passes through values), | |
| // there is no need to create a zero-trip-check as before block is always run. | |
| if (!forceCreateCheck && isa<scf::YieldOp>(whileOp.getAfterBody()->front())) { | |
| return whileOp; | |
| } | |
| OpBuilder::InsertionGuard insertion_guard(rewriter); | |
| IRMapping mapper; | |
| Block *beforeBlock = whileOp.getBeforeBody(); | |
| // Clone before block before the loop for zero-trip-check. | |
| for (auto [arg, init] : | |
| llvm::zip_equal(beforeBlock->getArguments(), whileOp.getInits())) { | |
| mapper.map(arg, init); | |
| } | |
| rewriter.setInsertionPoint(whileOp); | |
| for (auto &op : *beforeBlock) { | |
| if (isa<scf::ConditionOp>(op)) { | |
| break; | |
| } | |
| // Safe to clone everything as in a single block all defs have been cloned | |
| // and added to mapper in order. | |
| rewriter.insert(op.clone(mapper)); | |
| } | |
| scf::ConditionOp condOp = whileOp.getConditionOp(); | |
| Value clonedCondition = mapper.lookupOrDefault(condOp.getCondition()); | |
| SmallVector<Value> clonedCondArgs = llvm::map_to_vector( | |
| condOp.getArgs(), [&](Value arg) { return mapper.lookupOrDefault(arg); }); | |
| // Create rotated while loop. | |
| auto newLoopOp = scf::WhileOp::create( | |
| rewriter, whileOp.getLoc(), whileOp.getResultTypes(), clonedCondArgs, | |
| [&](OpBuilder &builder, Location loc, ValueRange args) { | |
| // Rotate and move the loop body into before block. | |
| auto newBlock = builder.getBlock(); | |
| rewriter.mergeBlocks(whileOp.getAfterBody(), newBlock, args); | |
| auto yieldOp = cast<scf::YieldOp>(newBlock->getTerminator()); | |
| rewriter.mergeBlocks(whileOp.getBeforeBody(), newBlock, | |
| yieldOp.getResults()); | |
| rewriter.eraseOp(yieldOp); | |
| }, | |
| [&](OpBuilder &builder, Location loc, ValueRange args) { | |
| // Pass through values. | |
| scf::YieldOp::create(builder, loc, args); | |
| }); | |
| // Create zero-trip-check and move the while loop in. | |
| auto ifOp = scf::IfOp::create( | |
| rewriter, whileOp.getLoc(), clonedCondition, | |
| [&](OpBuilder &builder, Location loc) { | |
| // Then runs the while loop. | |
| rewriter.moveOpBefore(newLoopOp, builder.getInsertionBlock(), | |
| builder.getInsertionPoint()); | |
| scf::YieldOp::create(builder, loc, newLoopOp.getResults()); | |
| }, | |
| [&](OpBuilder &builder, Location loc) { | |
| // Else returns the results from precondition. | |
| scf::YieldOp::create(builder, loc, clonedCondArgs); | |
| }); | |
| rewriter.replaceOp(whileOp, ifOp); | |
| return newLoopOp; | |
| } |