//===- LoopUtils.cpp - Misc loop utilities for simplification //-----------===// // // Copyright 2019 The MLIR Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ============================================================================= // // This file implements miscellaneous loop simplification routines. // //===----------------------------------------------------------------------===// #include "mlir/Transforms/LoopUtils.h" #include "mlir/Analysis/LoopAnalysis.h" #include "mlir/IR/AffineExpr.h" #include "mlir/IR/AffineMap.h" #include "mlir/IR/Builders.h" #include "mlir/IR/StandardOps.h" #include "mlir/IR/Statements.h" #include "mlir/IR/StmtVisitor.h" using namespace mlir; /// Returns the upper bound of an unrolled loop with lower bound 'lb' and with /// the specified trip count, stride, and unroll factor. Returns nullptr when /// the trip count can't be expressed as an affine expression. AffineMap *mlir::getUnrolledLoopUpperBound(const ForStmt &forStmt, unsigned unrollFactor, MLFuncBuilder *builder) { auto *lbMap = forStmt.getLowerBoundMap(); // Single result lower bound map only. if (lbMap->getNumResults() != 1) return nullptr; // Sometimes, the trip count cannot be expressed as an affine expression. auto *tripCountExpr = getTripCountExpr(forStmt); if (!tripCountExpr) return nullptr; AffineExpr *newUbExpr; auto *lbExpr = lbMap->getResult(0); int64_t step = forStmt.getStep(); // lbExpr + (count - count % unrollFactor - 1) * step). if (auto *cTripCountExpr = dyn_cast(tripCountExpr)) { uint64_t tripCount = static_cast(cTripCountExpr->getValue()); newUbExpr = builder->getAddExpr( lbExpr, builder->getConstantExpr( (tripCount - tripCount % unrollFactor - 1) * step)); } else { newUbExpr = builder->getAddExpr( lbExpr, builder->getMulExpr( builder->getSubExpr( builder->getSubExpr( tripCountExpr, builder->getModExpr(tripCountExpr, unrollFactor)), 1), step)); } return builder->getAffineMap(lbMap->getNumDims(), lbMap->getNumSymbols(), {newUbExpr}, {}); } /// Returns the lower bound of the cleanup loop when unrolling a loop with lower /// bound 'lb' and with the specified trip count, stride, and unroll factor. /// Returns nullptr when the trip count can't be expressed as an affine /// expression. AffineMap *mlir::getCleanupLoopLowerBound(const ForStmt &forStmt, unsigned unrollFactor, MLFuncBuilder *builder) { auto *lbMap = forStmt.getLowerBoundMap(); // Single result lower bound map only. if (lbMap->getNumResults() != 1) return nullptr; // Sometimes the trip count cannot be expressed as an affine expression. auto *tripCountExpr = getTripCountExpr(forStmt); if (!tripCountExpr) return nullptr; AffineExpr *newLbExpr; auto *lbExpr = lbMap->getResult(0); int64_t step = forStmt.getStep(); // lbExpr + (count - count % unrollFactor) * step); if (auto *cTripCountExpr = dyn_cast(tripCountExpr)) { uint64_t tripCount = static_cast(cTripCountExpr->getValue()); newLbExpr = builder->getAddExpr( lbExpr, builder->getConstantExpr( (tripCount - tripCount % unrollFactor) * step)); } else { newLbExpr = builder->getAddExpr( lbExpr, builder->getMulExpr( builder->getSubExpr( tripCountExpr, builder->getModExpr(tripCountExpr, unrollFactor)), step)); } return builder->getAffineMap(lbMap->getNumDims(), lbMap->getNumSymbols(), {newLbExpr}, {}); } /// Promotes the loop body of a forStmt to its containing block if the forStmt /// was known to have a single iteration. Returns false otherwise. // TODO(bondhugula): extend this for arbitrary affine bounds. bool mlir::promoteIfSingleIteration(ForStmt *forStmt) { Optional tripCount = getConstantTripCount(*forStmt); if (!tripCount.hasValue() || tripCount.getValue() != 1) return false; // TODO(mlir-team): there is no builder for a max. if (forStmt->getLowerBoundMap()->getNumResults() != 1) return false; // Replaces all IV uses to its single iteration value. if (!forStmt->use_empty()) { if (forStmt->hasConstantLowerBound()) { auto *mlFunc = forStmt->findFunction(); MLFuncBuilder topBuilder(&mlFunc->front()); auto constOp = topBuilder.create( forStmt->getLoc(), forStmt->getConstantLowerBound()); forStmt->replaceAllUsesWith(constOp->getResult()); } else { const AffineBound lb = forStmt->getLowerBound(); SmallVector lbOperands(lb.operand_begin(), lb.operand_end()); MLFuncBuilder builder(forStmt->getBlock(), StmtBlock::iterator(forStmt)); auto affineApplyOp = builder.create( forStmt->getLoc(), lb.getMap(), lbOperands); forStmt->replaceAllUsesWith(affineApplyOp->getResult(0)); } } // Move the loop body statements to the loop's containing block. auto *block = forStmt->getBlock(); block->getStatements().splice(StmtBlock::iterator(forStmt), forStmt->getStatements()); forStmt->eraseFromBlock(); return true; } /// Promotes all single iteration for stmt's in the MLFunction, i.e., moves /// their body into the containing StmtBlock. void mlir::promoteSingleIterationLoops(MLFunction *f) { // Gathers all innermost loops through a post order pruned walk. class LoopBodyPromoter : public StmtWalker { public: void visitForStmt(ForStmt *forStmt) { promoteIfSingleIteration(forStmt); } }; LoopBodyPromoter fsw; fsw.walkPostOrder(f); }