17669a259SRiver Riddle //===- CSE.cpp - Common Sub-expression Elimination ------------------------===// 27669a259SRiver Riddle // 330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information. 556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67669a259SRiver Riddle // 756222a06SMehdi Amini //===----------------------------------------------------------------------===// 87669a259SRiver Riddle // 97669a259SRiver Riddle // This transformation pass performs a simple common sub-expression elimination 101834ad4aSRiver Riddle // algorithm on operations within a region. 117669a259SRiver Riddle // 127669a259SRiver Riddle //===----------------------------------------------------------------------===// 137669a259SRiver Riddle 14*039b969bSMichele Scuttari #include "PassDetail.h" 1557818885SStephen Neuendorffer #include "mlir/IR/Dominance.h" 1636d3efeaSRiver Riddle #include "mlir/Interfaces/SideEffectInterfaces.h" 1748ccae24SRiver Riddle #include "mlir/Pass/Pass.h" 18*039b969bSMichele Scuttari #include "mlir/Transforms/Passes.h" 197669a259SRiver Riddle #include "llvm/ADT/DenseMapInfo.h" 207669a259SRiver Riddle #include "llvm/ADT/Hashing.h" 217669a259SRiver Riddle #include "llvm/ADT/ScopedHashTable.h" 227669a259SRiver Riddle #include "llvm/Support/Allocator.h" 237669a259SRiver Riddle #include "llvm/Support/RecyclingAllocator.h" 247669a259SRiver Riddle #include <deque> 251834ad4aSRiver Riddle 267669a259SRiver Riddle using namespace mlir; 277669a259SRiver Riddle 287669a259SRiver Riddle namespace { 2999b87c97SRiver Riddle struct SimpleOperationInfo : public llvm::DenseMapInfo<Operation *> { 3099b87c97SRiver Riddle static unsigned getHashValue(const Operation *opC) { 310be5d1a9SMehdi Amini return OperationEquivalence::computeHash( 320be5d1a9SMehdi Amini const_cast<Operation *>(opC), 330be5d1a9SMehdi Amini /*hashOperands=*/OperationEquivalence::directHashValue, 340be5d1a9SMehdi Amini /*hashResults=*/OperationEquivalence::ignoreHashValue, 350be5d1a9SMehdi Amini OperationEquivalence::IgnoreLocations); 367669a259SRiver Riddle } 3799b87c97SRiver Riddle static bool isEqual(const Operation *lhsC, const Operation *rhsC) { 3899b87c97SRiver Riddle auto *lhs = const_cast<Operation *>(lhsC); 3999b87c97SRiver Riddle auto *rhs = const_cast<Operation *>(rhsC); 407669a259SRiver Riddle if (lhs == rhs) 417669a259SRiver Riddle return true; 427669a259SRiver Riddle if (lhs == getTombstoneKey() || lhs == getEmptyKey() || 437669a259SRiver Riddle rhs == getTombstoneKey() || rhs == getEmptyKey()) 447669a259SRiver Riddle return false; 450be5d1a9SMehdi Amini return OperationEquivalence::isEquivalentTo( 460be5d1a9SMehdi Amini const_cast<Operation *>(lhsC), const_cast<Operation *>(rhsC), 470be5d1a9SMehdi Amini /*mapOperands=*/OperationEquivalence::exactValueMatch, 480be5d1a9SMehdi Amini /*mapResults=*/OperationEquivalence::ignoreValueEquivalence, 490be5d1a9SMehdi Amini OperationEquivalence::IgnoreLocations); 507669a259SRiver Riddle } 517669a259SRiver Riddle }; 52be0a7e9fSMehdi Amini } // namespace 53a250643eSChris Lattner 54a250643eSChris Lattner namespace { 55a250643eSChris Lattner /// Simple common sub-expression elimination. 56*039b969bSMichele Scuttari struct CSE : public CSEBase<CSE> { 577669a259SRiver Riddle /// Shared implementation of operation elimination and scoped map definitions. 587669a259SRiver Riddle using AllocatorTy = llvm::RecyclingAllocator< 597669a259SRiver Riddle llvm::BumpPtrAllocator, 6099b87c97SRiver Riddle llvm::ScopedHashTableVal<Operation *, Operation *>>; 6199b87c97SRiver Riddle using ScopedMapTy = llvm::ScopedHashTable<Operation *, Operation *, 627669a259SRiver Riddle SimpleOperationInfo, AllocatorTy>; 637669a259SRiver Riddle 6402da9643SValentin Clement /// Cache holding MemoryEffects information between two operations. The first 6502da9643SValentin Clement /// operation is stored has the key. The second operation is stored inside a 6602da9643SValentin Clement /// pair in the value. The pair also hold the MemoryEffects between those 6702da9643SValentin Clement /// two operations. If the MemoryEffects is nullptr then we assume there is 6802da9643SValentin Clement /// no operation with MemoryEffects::Write between the two operations. 6902da9643SValentin Clement using MemEffectsCache = 7002da9643SValentin Clement DenseMap<Operation *, std::pair<Operation *, MemoryEffects::Effect *>>; 7102da9643SValentin Clement 72a250643eSChris Lattner /// Represents a single entry in the depth first traversal of a CFG. 73a250643eSChris Lattner struct CFGStackNode { 74a250643eSChris Lattner CFGStackNode(ScopedMapTy &knownValues, DominanceInfoNode *node) 75671e30a1SMehdi Amini : scope(knownValues), node(node), childIterator(node->begin()) {} 76a250643eSChris Lattner 77a250643eSChris Lattner /// Scope for the known values. 78a250643eSChris Lattner ScopedMapTy::ScopeTy scope; 79a250643eSChris Lattner 80a250643eSChris Lattner DominanceInfoNode *node; 813fa989d4SNicolai Hähnle DominanceInfoNode::const_iterator childIterator; 82a250643eSChris Lattner 83a250643eSChris Lattner /// If this node has been fully processed yet or not. 84671e30a1SMehdi Amini bool processed = false; 85a250643eSChris Lattner }; 867669a259SRiver Riddle 87b67cab4cSRiver Riddle /// Attempt to eliminate a redundant operation. Returns success if the 88b67cab4cSRiver Riddle /// operation was marked for removal, failure otherwise. 899c61c76bSAndrew Young LogicalResult simplifyOperation(ScopedMapTy &knownValues, Operation *op, 909c61c76bSAndrew Young bool hasSSADominance); 911e344ce4SChris Lattner void simplifyBlock(ScopedMapTy &knownValues, Block *bb, bool hasSSADominance); 926134231aSChris Lattner void simplifyRegion(ScopedMapTy &knownValues, Region ®ion); 93a250643eSChris Lattner 942b61b797SRiver Riddle void runOnOperation() override; 95a250643eSChris Lattner 96a250643eSChris Lattner private: 9702da9643SValentin Clement void replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op, 9802da9643SValentin Clement Operation *existing, bool hasSSADominance); 9902da9643SValentin Clement 10002da9643SValentin Clement /// Check if there is side-effecting operations other than the given effect 10102da9643SValentin Clement /// between the two operations. 10202da9643SValentin Clement bool hasOtherSideEffectingOpInBetween(Operation *fromOp, Operation *toOp); 10302da9643SValentin Clement 104a250643eSChris Lattner /// Operations marked as dead and to be erased. 10599b87c97SRiver Riddle std::vector<Operation *> opsToErase; 1061e344ce4SChris Lattner DominanceInfo *domInfo = nullptr; 10702da9643SValentin Clement MemEffectsCache memEffectsCache; 108a250643eSChris Lattner }; 109be0a7e9fSMehdi Amini } // namespace 110a250643eSChris Lattner 111*039b969bSMichele Scuttari void CSE::replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op, 11202da9643SValentin Clement Operation *existing, bool hasSSADominance) { 1137669a259SRiver Riddle // If we find one then replace all uses of the current operation with the 1149c61c76bSAndrew Young // existing one and mark it for deletion. We can only replace an operand in 1159c61c76bSAndrew Young // an operation if it has not been visited yet. 1169c61c76bSAndrew Young if (hasSSADominance) { 1179c61c76bSAndrew Young // If the region has SSA dominance, then we are guaranteed to have not 1189c61c76bSAndrew Young // visited any use of the current operation. 1198089f937SRiver Riddle op->replaceAllUsesWith(existing); 1207669a259SRiver Riddle opsToErase.push_back(op); 1219c61c76bSAndrew Young } else { 1229c61c76bSAndrew Young // When the region does not have SSA dominance, we need to check if we 1239c61c76bSAndrew Young // have visited a use before replacing any use. 1249c61c76bSAndrew Young for (auto it : llvm::zip(op->getResults(), existing->getResults())) { 1259c61c76bSAndrew Young std::get<0>(it).replaceUsesWithIf( 1269c61c76bSAndrew Young std::get<1>(it), [&](OpOperand &operand) { 1279c61c76bSAndrew Young return !knownValues.count(operand.getOwner()); 1289c61c76bSAndrew Young }); 1299c61c76bSAndrew Young } 1309c61c76bSAndrew Young 1319c61c76bSAndrew Young // There may be some remaining uses of the operation. 1329c61c76bSAndrew Young if (op->use_empty()) 1339c61c76bSAndrew Young opsToErase.push_back(op); 1349c61c76bSAndrew Young } 1357669a259SRiver Riddle 1367669a259SRiver Riddle // If the existing operation has an unknown location and the current 1377669a259SRiver Riddle // operation doesn't, then set the existing op's location to that of the 1387669a259SRiver Riddle // current op. 13902da9643SValentin Clement if (existing->getLoc().isa<UnknownLoc>() && !op->getLoc().isa<UnknownLoc>()) 1407669a259SRiver Riddle existing->setLoc(op->getLoc()); 14102da9643SValentin Clement 14202da9643SValentin Clement ++numCSE; 1437669a259SRiver Riddle } 14433a64540SRiver Riddle 145*039b969bSMichele Scuttari bool CSE::hasOtherSideEffectingOpInBetween(Operation *fromOp, Operation *toOp) { 14602da9643SValentin Clement assert(fromOp->getBlock() == toOp->getBlock()); 14702da9643SValentin Clement assert( 14802da9643SValentin Clement isa<MemoryEffectOpInterface>(fromOp) && 14902da9643SValentin Clement cast<MemoryEffectOpInterface>(fromOp).hasEffect<MemoryEffects::Read>() && 15002da9643SValentin Clement isa<MemoryEffectOpInterface>(toOp) && 15102da9643SValentin Clement cast<MemoryEffectOpInterface>(toOp).hasEffect<MemoryEffects::Read>()); 15202da9643SValentin Clement Operation *nextOp = fromOp->getNextNode(); 15302da9643SValentin Clement auto result = 15402da9643SValentin Clement memEffectsCache.try_emplace(fromOp, std::make_pair(fromOp, nullptr)); 15502da9643SValentin Clement if (result.second) { 15602da9643SValentin Clement auto memEffectsCachePair = result.first->second; 15702da9643SValentin Clement if (memEffectsCachePair.second == nullptr) { 15802da9643SValentin Clement // No MemoryEffects::Write has been detected until the cached operation. 15902da9643SValentin Clement // Continue looking from the cached operation to toOp. 16002da9643SValentin Clement nextOp = memEffectsCachePair.first; 16102da9643SValentin Clement } else { 16202da9643SValentin Clement // MemoryEffects::Write has been detected before so there is no need to 16302da9643SValentin Clement // check further. 16402da9643SValentin Clement return true; 16502da9643SValentin Clement } 16602da9643SValentin Clement } 16702da9643SValentin Clement while (nextOp && nextOp != toOp) { 16802da9643SValentin Clement auto nextOpMemEffects = dyn_cast<MemoryEffectOpInterface>(nextOp); 16902da9643SValentin Clement // TODO: Do we need to handle other effects generically? 17002da9643SValentin Clement // If the operation does not implement the MemoryEffectOpInterface we 17102da9643SValentin Clement // conservatively assumes it writes. 17202da9643SValentin Clement if ((nextOpMemEffects && 17302da9643SValentin Clement nextOpMemEffects.hasEffect<MemoryEffects::Write>()) || 17402da9643SValentin Clement !nextOpMemEffects) { 17502da9643SValentin Clement result.first->second = 17602da9643SValentin Clement std::make_pair(nextOp, MemoryEffects::Write::get()); 17702da9643SValentin Clement return true; 17802da9643SValentin Clement } 17902da9643SValentin Clement nextOp = nextOp->getNextNode(); 18002da9643SValentin Clement } 18102da9643SValentin Clement result.first->second = std::make_pair(toOp, nullptr); 18202da9643SValentin Clement return false; 18302da9643SValentin Clement } 18402da9643SValentin Clement 18502da9643SValentin Clement /// Attempt to eliminate a redundant operation. 186*039b969bSMichele Scuttari LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op, 187*039b969bSMichele Scuttari bool hasSSADominance) { 18802da9643SValentin Clement // Don't simplify terminator operations. 18902da9643SValentin Clement if (op->hasTrait<OpTrait::IsTerminator>()) 19002da9643SValentin Clement return failure(); 19102da9643SValentin Clement 19202da9643SValentin Clement // If the operation is already trivially dead just add it to the erase list. 19302da9643SValentin Clement if (isOpTriviallyDead(op)) { 19402da9643SValentin Clement opsToErase.push_back(op); 19502da9643SValentin Clement ++numDCE; 19602da9643SValentin Clement return success(); 19702da9643SValentin Clement } 19802da9643SValentin Clement 19902da9643SValentin Clement // Don't simplify operations with nested blocks. We don't currently model 20002da9643SValentin Clement // equality comparisons correctly among other things. It is also unclear 20102da9643SValentin Clement // whether we would want to CSE such operations. 20202da9643SValentin Clement if (op->getNumRegions() != 0) 20302da9643SValentin Clement return failure(); 20402da9643SValentin Clement 20502da9643SValentin Clement // Some simple use case of operation with memory side-effect are dealt with 20602da9643SValentin Clement // here. Operations with no side-effect are done after. 20702da9643SValentin Clement if (!MemoryEffectOpInterface::hasNoEffect(op)) { 20802da9643SValentin Clement auto memEffects = dyn_cast<MemoryEffectOpInterface>(op); 20902da9643SValentin Clement // TODO: Only basic use case for operations with MemoryEffects::Read can be 21002da9643SValentin Clement // eleminated now. More work needs to be done for more complicated patterns 21102da9643SValentin Clement // and other side-effects. 21202da9643SValentin Clement if (!memEffects || !memEffects.onlyHasEffect<MemoryEffects::Read>()) 21302da9643SValentin Clement return failure(); 21402da9643SValentin Clement 21502da9643SValentin Clement // Look for an existing definition for the operation. 21602da9643SValentin Clement if (auto *existing = knownValues.lookup(op)) { 21702da9643SValentin Clement if (existing->getBlock() == op->getBlock() && 21802da9643SValentin Clement !hasOtherSideEffectingOpInBetween(existing, op)) { 21902da9643SValentin Clement // The operation that can be deleted has been reach with no 22002da9643SValentin Clement // side-effecting operations in between the existing operation and 22102da9643SValentin Clement // this one so we can remove the duplicate. 22202da9643SValentin Clement replaceUsesAndDelete(knownValues, op, existing, hasSSADominance); 22302da9643SValentin Clement return success(); 22402da9643SValentin Clement } 22502da9643SValentin Clement } 22602da9643SValentin Clement knownValues.insert(op, op); 22702da9643SValentin Clement return failure(); 22802da9643SValentin Clement } 22902da9643SValentin Clement 23002da9643SValentin Clement // Look for an existing definition for the operation. 23102da9643SValentin Clement if (auto *existing = knownValues.lookup(op)) { 23202da9643SValentin Clement replaceUsesAndDelete(knownValues, op, existing, hasSSADominance); 23333a64540SRiver Riddle ++numCSE; 234b67cab4cSRiver Riddle return success(); 235c3424c3cSRiver Riddle } 236c3424c3cSRiver Riddle 2377669a259SRiver Riddle // Otherwise, we add this operation to the known values map. 2387669a259SRiver Riddle knownValues.insert(op, op); 239b67cab4cSRiver Riddle return failure(); 2407669a259SRiver Riddle } 2417669a259SRiver Riddle 242*039b969bSMichele Scuttari void CSE::simplifyBlock(ScopedMapTy &knownValues, Block *bb, 2431e344ce4SChris Lattner bool hasSSADominance) { 2441e344ce4SChris Lattner for (auto &op : *bb) { 245276fae1bSAlex Zinenko // If the operation is simplified, we don't process any held regions. 2461e344ce4SChris Lattner if (succeeded(simplifyOperation(knownValues, &op, hasSSADominance))) 247c3424c3cSRiver Riddle continue; 248c3424c3cSRiver Riddle 2491e344ce4SChris Lattner // Most operations don't have regions, so fast path that case. 2501e344ce4SChris Lattner if (op.getNumRegions() == 0) 2511e344ce4SChris Lattner continue; 2521e344ce4SChris Lattner 253b67cab4cSRiver Riddle // If this operation is isolated above, we can't process nested regions with 254b67cab4cSRiver Riddle // the given 'knownValues' map. This would cause the insertion of implicit 255b67cab4cSRiver Riddle // captures in explicit capture only regions. 2561e344ce4SChris Lattner if (op.mightHaveTrait<OpTrait::IsIsolatedFromAbove>()) { 257b67cab4cSRiver Riddle ScopedMapTy nestedKnownValues; 2586134231aSChris Lattner for (auto ®ion : op.getRegions()) 2596134231aSChris Lattner simplifyRegion(nestedKnownValues, region); 260b67cab4cSRiver Riddle continue; 261b67cab4cSRiver Riddle } 262b67cab4cSRiver Riddle 263b67cab4cSRiver Riddle // Otherwise, process nested regions normally. 2646134231aSChris Lattner for (auto ®ion : op.getRegions()) 2656134231aSChris Lattner simplifyRegion(knownValues, region); 266cdbfd484SRiver Riddle } 26702da9643SValentin Clement // Clear the MemoryEffects cache since its usage is by block only. 26802da9643SValentin Clement memEffectsCache.clear(); 269cdbfd484SRiver Riddle } 270cdbfd484SRiver Riddle 271*039b969bSMichele Scuttari void CSE::simplifyRegion(ScopedMapTy &knownValues, Region ®ion) { 272276fae1bSAlex Zinenko // If the region is empty there is nothing to do. 273276fae1bSAlex Zinenko if (region.empty()) 274cdbfd484SRiver Riddle return; 275cdbfd484SRiver Riddle 2766134231aSChris Lattner bool hasSSADominance = domInfo->hasSSADominance(®ion); 2776134231aSChris Lattner 278276fae1bSAlex Zinenko // If the region only contains one block, then simplify it directly. 279412ae15dSChris Lattner if (region.hasOneBlock()) { 280c3424c3cSRiver Riddle ScopedMapTy::ScopeTy scope(knownValues); 2811e344ce4SChris Lattner simplifyBlock(knownValues, ®ion.front(), hasSSADominance); 282cdbfd484SRiver Riddle return; 283c3424c3cSRiver Riddle } 284cdbfd484SRiver Riddle 28562828865SStephen Neuendorffer // If the region does not have dominanceInfo, then skip it. 28662828865SStephen Neuendorffer // TODO: Regions without SSA dominance should define a different 28762828865SStephen Neuendorffer // traversal order which is appropriate and can be used here. 2889c61c76bSAndrew Young if (!hasSSADominance) 28962828865SStephen Neuendorffer return; 29062828865SStephen Neuendorffer 2917669a259SRiver Riddle // Note, deque is being used here because there was significant performance 2927669a259SRiver Riddle // gains over vector when the container becomes very large due to the 2937669a259SRiver Riddle // specific access patterns. If/when these performance issues are no 2947669a259SRiver Riddle // longer a problem we can change this to vector. For more information see 2957669a259SRiver Riddle // the llvm mailing list discussion on this: 2967669a259SRiver Riddle // http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20120116/135228.html 2977669a259SRiver Riddle std::deque<std::unique_ptr<CFGStackNode>> stack; 2987669a259SRiver Riddle 299276fae1bSAlex Zinenko // Process the nodes of the dom tree for this region. 30079f53b0cSJacques Pienaar stack.emplace_back(std::make_unique<CFGStackNode>( 3011e344ce4SChris Lattner knownValues, domInfo->getRootNode(®ion))); 3027669a259SRiver Riddle 3037669a259SRiver Riddle while (!stack.empty()) { 3047669a259SRiver Riddle auto ¤tNode = stack.back(); 3057669a259SRiver Riddle 3067669a259SRiver Riddle // Check to see if we need to process this node. 3077669a259SRiver Riddle if (!currentNode->processed) { 3087669a259SRiver Riddle currentNode->processed = true; 3091e344ce4SChris Lattner simplifyBlock(knownValues, currentNode->node->getBlock(), 3109c61c76bSAndrew Young hasSSADominance); 311a250643eSChris Lattner } 312a250643eSChris Lattner 3137669a259SRiver Riddle // Otherwise, check to see if we need to process a child node. 314a250643eSChris Lattner if (currentNode->childIterator != currentNode->node->end()) { 3157669a259SRiver Riddle auto *childNode = *(currentNode->childIterator++); 3167669a259SRiver Riddle stack.emplace_back( 31779f53b0cSJacques Pienaar std::make_unique<CFGStackNode>(knownValues, childNode)); 3187669a259SRiver Riddle } else { 3197669a259SRiver Riddle // Finally, if the node and all of its children have been processed 3207669a259SRiver Riddle // then we delete the node. 3217669a259SRiver Riddle stack.pop_back(); 3227669a259SRiver Riddle } 3237669a259SRiver Riddle } 324cdbfd484SRiver Riddle } 325cdbfd484SRiver Riddle 326*039b969bSMichele Scuttari void CSE::runOnOperation() { 3272b61b797SRiver Riddle /// A scoped hash table of defining operations within a region. 328b67cab4cSRiver Riddle ScopedMapTy knownValues; 3292b61b797SRiver Riddle 3301e344ce4SChris Lattner domInfo = &getAnalysis<DominanceInfo>(); 3311e344ce4SChris Lattner Operation *rootOp = getOperation(); 3321e344ce4SChris Lattner 3336134231aSChris Lattner for (auto ®ion : rootOp->getRegions()) 3346134231aSChris Lattner simplifyRegion(knownValues, region); 335485746f5SRiver Riddle 336485746f5SRiver Riddle // If no operations were erased, then we mark all analyses as preserved. 337b67cab4cSRiver Riddle if (opsToErase.empty()) 338b67cab4cSRiver Riddle return markAllAnalysesPreserved(); 3397669a259SRiver Riddle 340a250643eSChris Lattner /// Erase any operations that were marked as dead during simplification. 341a250643eSChris Lattner for (auto *op : opsToErase) 342a250643eSChris Lattner op->erase(); 343a250643eSChris Lattner opsToErase.clear(); 3441d87b62aSRiver Riddle 3451d87b62aSRiver Riddle // We currently don't remove region operations, so mark dominance as 3461d87b62aSRiver Riddle // preserved. 3471d87b62aSRiver Riddle markAnalysesPreserved<DominanceInfo, PostDominanceInfo>(); 3481e344ce4SChris Lattner domInfo = nullptr; 3497669a259SRiver Riddle } 350*039b969bSMichele Scuttari 351*039b969bSMichele Scuttari std::unique_ptr<Pass> mlir::createCSEPass() { return std::make_unique<CSE>(); } 352