1 //===- CSE.cpp - Common Sub-expression Elimination ------------------------===// 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 transformation pass performs a simple common sub-expression elimination 10 // algorithm on operations within a region. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "PassDetail.h" 15 #include "mlir/IR/Dominance.h" 16 #include "mlir/Pass/Pass.h" 17 #include "mlir/Transforms/Passes.h" 18 #include "mlir/Transforms/Utils.h" 19 #include "llvm/ADT/DenseMapInfo.h" 20 #include "llvm/ADT/Hashing.h" 21 #include "llvm/ADT/ScopedHashTable.h" 22 #include "llvm/Support/Allocator.h" 23 #include "llvm/Support/RecyclingAllocator.h" 24 #include <deque> 25 26 using namespace mlir; 27 28 /// Return true if the specified region is known to follow SSA dominance 29 /// properties, i.e. it isn't a graph region. 30 static bool regionHasSSADominance(Operation &op, size_t regionNo, 31 RegionKindInterface regionKindItf) { 32 // If the op is unregistered, then we don't know if it has SSADominance or 33 // not, so assume not. 34 if (!op.isRegistered()) 35 return false; 36 37 // If the op is registered but has no RegionKindInterface, then it defaults to 38 // SSADominance. 39 if (!regionKindItf) 40 return true; 41 42 // Otherwise, ask the interface. 43 return regionKindItf.hasSSADominance(regionNo); 44 } 45 46 namespace { 47 struct SimpleOperationInfo : public llvm::DenseMapInfo<Operation *> { 48 static unsigned getHashValue(const Operation *opC) { 49 return OperationEquivalence::computeHash(const_cast<Operation *>(opC)); 50 } 51 static bool isEqual(const Operation *lhsC, const Operation *rhsC) { 52 auto *lhs = const_cast<Operation *>(lhsC); 53 auto *rhs = const_cast<Operation *>(rhsC); 54 if (lhs == rhs) 55 return true; 56 if (lhs == getTombstoneKey() || lhs == getEmptyKey() || 57 rhs == getTombstoneKey() || rhs == getEmptyKey()) 58 return false; 59 return OperationEquivalence::isEquivalentTo(const_cast<Operation *>(lhsC), 60 const_cast<Operation *>(rhsC)); 61 } 62 }; 63 } // end anonymous namespace 64 65 namespace { 66 /// Simple common sub-expression elimination. 67 struct CSE : public CSEBase<CSE> { 68 /// Shared implementation of operation elimination and scoped map definitions. 69 using AllocatorTy = llvm::RecyclingAllocator< 70 llvm::BumpPtrAllocator, 71 llvm::ScopedHashTableVal<Operation *, Operation *>>; 72 using ScopedMapTy = llvm::ScopedHashTable<Operation *, Operation *, 73 SimpleOperationInfo, AllocatorTy>; 74 75 /// Represents a single entry in the depth first traversal of a CFG. 76 struct CFGStackNode { 77 CFGStackNode(ScopedMapTy &knownValues, DominanceInfoNode *node) 78 : scope(knownValues), node(node), childIterator(node->begin()), 79 processed(false) {} 80 81 /// Scope for the known values. 82 ScopedMapTy::ScopeTy scope; 83 84 DominanceInfoNode *node; 85 DominanceInfoNode::const_iterator childIterator; 86 87 /// If this node has been fully processed yet or not. 88 bool processed; 89 }; 90 91 /// Attempt to eliminate a redundant operation. Returns success if the 92 /// operation was marked for removal, failure otherwise. 93 LogicalResult simplifyOperation(ScopedMapTy &knownValues, Operation *op, 94 bool hasSSADominance); 95 void simplifyBlock(ScopedMapTy &knownValues, Block *bb, bool hasSSADominance); 96 void simplifyRegion(ScopedMapTy &knownValues, Region ®ion, 97 bool hasSSADominance); 98 99 void runOnOperation() override; 100 101 private: 102 /// Operations marked as dead and to be erased. 103 std::vector<Operation *> opsToErase; 104 DominanceInfo *domInfo = nullptr; 105 }; 106 } // end anonymous namespace 107 108 /// Attempt to eliminate a redundant operation. 109 LogicalResult CSE::simplifyOperation(ScopedMapTy &knownValues, Operation *op, 110 bool hasSSADominance) { 111 // Don't simplify terminator operations. 112 if (op->hasTrait<OpTrait::IsTerminator>()) 113 return failure(); 114 115 // If the operation is already trivially dead just add it to the erase list. 116 if (isOpTriviallyDead(op)) { 117 opsToErase.push_back(op); 118 ++numDCE; 119 return success(); 120 } 121 122 // Don't simplify operations with nested blocks. We don't currently model 123 // equality comparisons correctly among other things. It is also unclear 124 // whether we would want to CSE such operations. 125 if (op->getNumRegions() != 0) 126 return failure(); 127 128 // TODO: We currently only eliminate non side-effecting 129 // operations. 130 if (!MemoryEffectOpInterface::hasNoEffect(op)) 131 return failure(); 132 133 // Look for an existing definition for the operation. 134 if (auto *existing = knownValues.lookup(op)) { 135 136 // If we find one then replace all uses of the current operation with the 137 // existing one and mark it for deletion. We can only replace an operand in 138 // an operation if it has not been visited yet. 139 if (hasSSADominance) { 140 // If the region has SSA dominance, then we are guaranteed to have not 141 // visited any use of the current operation. 142 op->replaceAllUsesWith(existing); 143 opsToErase.push_back(op); 144 } else { 145 // When the region does not have SSA dominance, we need to check if we 146 // have visited a use before replacing any use. 147 for (auto it : llvm::zip(op->getResults(), existing->getResults())) { 148 std::get<0>(it).replaceUsesWithIf( 149 std::get<1>(it), [&](OpOperand &operand) { 150 return !knownValues.count(operand.getOwner()); 151 }); 152 } 153 154 // There may be some remaining uses of the operation. 155 if (op->use_empty()) 156 opsToErase.push_back(op); 157 } 158 159 // If the existing operation has an unknown location and the current 160 // operation doesn't, then set the existing op's location to that of the 161 // current op. 162 if (existing->getLoc().isa<UnknownLoc>() && 163 !op->getLoc().isa<UnknownLoc>()) { 164 existing->setLoc(op->getLoc()); 165 } 166 167 ++numCSE; 168 return success(); 169 } 170 171 // Otherwise, we add this operation to the known values map. 172 knownValues.insert(op, op); 173 return failure(); 174 } 175 176 void CSE::simplifyBlock(ScopedMapTy &knownValues, Block *bb, 177 bool hasSSADominance) { 178 for (auto &op : *bb) { 179 // If the operation is simplified, we don't process any held regions. 180 if (succeeded(simplifyOperation(knownValues, &op, hasSSADominance))) 181 continue; 182 183 // Most operations don't have regions, so fast path that case. 184 if (op.getNumRegions() == 0) 185 continue; 186 187 auto regionKindItf = dyn_cast<RegionKindInterface>(op); 188 189 // If this operation is isolated above, we can't process nested regions with 190 // the given 'knownValues' map. This would cause the insertion of implicit 191 // captures in explicit capture only regions. 192 if (op.mightHaveTrait<OpTrait::IsIsolatedFromAbove>()) { 193 ScopedMapTy nestedKnownValues; 194 for (size_t i = 0, e = op.getNumRegions(); i != e; ++i) { 195 simplifyRegion(nestedKnownValues, op.getRegion(i), 196 regionHasSSADominance(op, i, regionKindItf)); 197 } 198 continue; 199 } 200 201 // Otherwise, process nested regions normally. 202 for (size_t i = 0, e = op.getNumRegions(); i != e; ++i) { 203 simplifyRegion(knownValues, op.getRegion(i), 204 regionHasSSADominance(op, i, regionKindItf)); 205 } 206 } 207 } 208 209 void CSE::simplifyRegion(ScopedMapTy &knownValues, Region ®ion, 210 bool hasSSADominance) { 211 // If the region is empty there is nothing to do. 212 if (region.empty()) 213 return; 214 215 // If the region only contains one block, then simplify it directly. 216 if (std::next(region.begin()) == region.end()) { 217 ScopedMapTy::ScopeTy scope(knownValues); 218 simplifyBlock(knownValues, ®ion.front(), hasSSADominance); 219 return; 220 } 221 222 // If the region does not have dominanceInfo, then skip it. 223 // TODO: Regions without SSA dominance should define a different 224 // traversal order which is appropriate and can be used here. 225 if (!hasSSADominance) 226 return; 227 228 // Note, deque is being used here because there was significant performance 229 // gains over vector when the container becomes very large due to the 230 // specific access patterns. If/when these performance issues are no 231 // longer a problem we can change this to vector. For more information see 232 // the llvm mailing list discussion on this: 233 // http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20120116/135228.html 234 std::deque<std::unique_ptr<CFGStackNode>> stack; 235 236 // Process the nodes of the dom tree for this region. 237 stack.emplace_back(std::make_unique<CFGStackNode>( 238 knownValues, domInfo->getRootNode(®ion))); 239 240 while (!stack.empty()) { 241 auto ¤tNode = stack.back(); 242 243 // Check to see if we need to process this node. 244 if (!currentNode->processed) { 245 currentNode->processed = true; 246 simplifyBlock(knownValues, currentNode->node->getBlock(), 247 hasSSADominance); 248 } 249 250 // Otherwise, check to see if we need to process a child node. 251 if (currentNode->childIterator != currentNode->node->end()) { 252 auto *childNode = *(currentNode->childIterator++); 253 stack.emplace_back( 254 std::make_unique<CFGStackNode>(knownValues, childNode)); 255 } else { 256 // Finally, if the node and all of its children have been processed 257 // then we delete the node. 258 stack.pop_back(); 259 } 260 } 261 } 262 263 void CSE::runOnOperation() { 264 /// A scoped hash table of defining operations within a region. 265 ScopedMapTy knownValues; 266 267 domInfo = &getAnalysis<DominanceInfo>(); 268 Operation *rootOp = getOperation(); 269 270 auto regionKindItf = dyn_cast<RegionKindInterface>(getOperation()); 271 for (size_t i = 0, e = rootOp->getNumRegions(); i != e; ++i) { 272 simplifyRegion(knownValues, rootOp->getRegion(i), 273 regionHasSSADominance(*rootOp, i, regionKindItf)); 274 } 275 276 // If no operations were erased, then we mark all analyses as preserved. 277 if (opsToErase.empty()) 278 return markAllAnalysesPreserved(); 279 280 /// Erase any operations that were marked as dead during simplification. 281 for (auto *op : opsToErase) 282 op->erase(); 283 opsToErase.clear(); 284 285 // We currently don't remove region operations, so mark dominance as 286 // preserved. 287 markAnalysesPreserved<DominanceInfo, PostDominanceInfo>(); 288 domInfo = nullptr; 289 } 290 291 std::unique_ptr<Pass> mlir::createCSEPass() { return std::make_unique<CSE>(); } 292