|
| 1 | +//===-- ConstraintElimination.cpp - Eliminate conds using constraints. ----===// |
| 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 | +// Eliminate conditions based on constraints collected from dominating |
| 10 | +// conditions. |
| 11 | +// |
| 12 | +//===----------------------------------------------------------------------===// |
| 13 | + |
| 14 | +#include "llvm/ADT/SmallVector.h" |
| 15 | +#include "llvm/ADT/Statistic.h" |
| 16 | +#include "llvm/Analysis/ConstraintSystem.h" |
| 17 | +#include "llvm/Analysis/GlobalsModRef.h" |
| 18 | +#include "llvm/IR/DataLayout.h" |
| 19 | +#include "llvm/IR/Dominators.h" |
| 20 | +#include "llvm/IR/Function.h" |
| 21 | +#include "llvm/IR/Instructions.h" |
| 22 | +#include "llvm/IR/PatternMatch.h" |
| 23 | +#include "llvm/InitializePasses.h" |
| 24 | +#include "llvm/Pass.h" |
| 25 | +#include "llvm/Support/Debug.h" |
| 26 | +#include "llvm/Support/DebugCounter.h" |
| 27 | +#include "llvm/Transforms/Scalar.h" |
| 28 | + |
| 29 | +using namespace llvm; |
| 30 | +using namespace PatternMatch; |
| 31 | + |
| 32 | +#define DEBUG_TYPE "constraint-elimination" |
| 33 | + |
| 34 | +STATISTIC(NumCondsRemoved, "Number of instructions removed"); |
| 35 | +DEBUG_COUNTER(EliminatedCounter, "conds-eliminated", |
| 36 | + "Controls which conditions are eliminated"); |
| 37 | + |
| 38 | +static int64_t MaxConstraintValue = std::numeric_limits<int64_t>::max(); |
| 39 | + |
| 40 | +Optional<std::pair<int64_t, Value *>> decompose(Value *V) { |
| 41 | + if (auto *CI = dyn_cast<ConstantInt>(V)) { |
| 42 | + if (CI->isNegative() || CI->uge(MaxConstraintValue)) |
| 43 | + return {}; |
| 44 | + return {{CI->getSExtValue(), nullptr}}; |
| 45 | + } |
| 46 | + auto *GEP = dyn_cast<GetElementPtrInst>(V); |
| 47 | + if (GEP && GEP->getNumOperands() == 2 && |
| 48 | + isa<ConstantInt>(GEP->getOperand(GEP->getNumOperands() - 1))) { |
| 49 | + return {{cast<ConstantInt>(GEP->getOperand(GEP->getNumOperands() - 1)) |
| 50 | + ->getSExtValue(), |
| 51 | + GEP->getPointerOperand()}}; |
| 52 | + } |
| 53 | + return {{0, V}}; |
| 54 | +} |
| 55 | + |
| 56 | +/// Turn a condition \p CmpI into a constraint vector, using indices from \p |
| 57 | +/// Value2Index. If \p ShouldAdd is true, new indices are added for values not |
| 58 | +/// yet in \p Value2Index. |
| 59 | +static SmallVector<int64_t, 8> |
| 60 | +getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1, |
| 61 | + DenseMap<Value *, unsigned> &Value2Index, bool ShouldAdd) { |
| 62 | + Value *A, *B; |
| 63 | + |
| 64 | + int64_t Offset1 = 0; |
| 65 | + int64_t Offset2 = 0; |
| 66 | + |
| 67 | + auto TryToGetIndex = [ShouldAdd, |
| 68 | + &Value2Index](Value *V) -> Optional<unsigned> { |
| 69 | + if (ShouldAdd) { |
| 70 | + Value2Index.insert({V, Value2Index.size() + 1}); |
| 71 | + return Value2Index[V]; |
| 72 | + } |
| 73 | + auto I = Value2Index.find(V); |
| 74 | + if (I == Value2Index.end()) |
| 75 | + return None; |
| 76 | + return I->second; |
| 77 | + }; |
| 78 | + |
| 79 | + if (Pred == CmpInst::ICMP_UGT || Pred == CmpInst::ICMP_UGE) |
| 80 | + return getConstraint(CmpInst::getSwappedPredicate(Pred), Op1, Op0, |
| 81 | + Value2Index, ShouldAdd); |
| 82 | + |
| 83 | + if (Pred == CmpInst::ICMP_ULE || Pred == CmpInst::ICMP_ULT) { |
| 84 | + auto ADec = decompose(Op0); |
| 85 | + auto BDec = decompose(Op1); |
| 86 | + if (!ADec || !BDec) |
| 87 | + return {}; |
| 88 | + std::tie(Offset1, A) = *ADec; |
| 89 | + std::tie(Offset2, B) = *BDec; |
| 90 | + Offset1 *= -1; |
| 91 | + |
| 92 | + if (!A && !B) |
| 93 | + return {}; |
| 94 | + |
| 95 | + auto AIdx = A ? TryToGetIndex(A) : None; |
| 96 | + auto BIdx = B ? TryToGetIndex(B) : None; |
| 97 | + if ((A && !AIdx) || (B && !BIdx)) |
| 98 | + return {}; |
| 99 | + |
| 100 | + SmallVector<int64_t, 8> R(Value2Index.size() + 1, 0); |
| 101 | + if (AIdx) |
| 102 | + R[*AIdx] = 1; |
| 103 | + if (BIdx) |
| 104 | + R[*BIdx] = -1; |
| 105 | + R[0] = Offset1 + Offset2 + (Pred == CmpInst::ICMP_ULT ? -1 : 0); |
| 106 | + return R; |
| 107 | + } |
| 108 | + |
| 109 | + return {}; |
| 110 | +} |
| 111 | + |
| 112 | +static SmallVector<int64_t, 8> |
| 113 | +getConstraint(CmpInst *Cmp, DenseMap<Value *, unsigned> &Value2Index, |
| 114 | + bool ShouldAdd) { |
| 115 | + return getConstraint(Cmp->getPredicate(), Cmp->getOperand(0), |
| 116 | + Cmp->getOperand(1), Value2Index, ShouldAdd); |
| 117 | +} |
| 118 | + |
| 119 | +/// Represents either a condition that holds on entry to a block or a basic |
| 120 | +/// block, with their respective Dominator DFS in and out numbers. |
| 121 | +struct ConstraintOrBlock { |
| 122 | + unsigned NumIn; |
| 123 | + unsigned NumOut; |
| 124 | + bool IsBlock; |
| 125 | + bool Not; |
| 126 | + union { |
| 127 | + BasicBlock *BB; |
| 128 | + CmpInst *Condition; |
| 129 | + }; |
| 130 | + |
| 131 | + ConstraintOrBlock(DomTreeNode *DTN) |
| 132 | + : NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), IsBlock(true), |
| 133 | + BB(DTN->getBlock()) {} |
| 134 | + ConstraintOrBlock(DomTreeNode *DTN, CmpInst *Condition, bool Not) |
| 135 | + : NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), IsBlock(false), |
| 136 | + Not(Not), Condition(Condition) {} |
| 137 | +}; |
| 138 | + |
| 139 | +struct StackEntry { |
| 140 | + unsigned NumIn; |
| 141 | + unsigned NumOut; |
| 142 | + CmpInst *Condition; |
| 143 | + bool IsNot; |
| 144 | + |
| 145 | + StackEntry(unsigned NumIn, unsigned NumOut, CmpInst *Condition, bool IsNot) |
| 146 | + : NumIn(NumIn), NumOut(NumOut), Condition(Condition), IsNot(IsNot) {} |
| 147 | +}; |
| 148 | + |
| 149 | +static bool eliminateConstraints(Function &F, DominatorTree &DT) { |
| 150 | + bool Changed = false; |
| 151 | + DT.updateDFSNumbers(); |
| 152 | + ConstraintSystem CS; |
| 153 | + |
| 154 | + SmallVector<ConstraintOrBlock, 64> WorkList; |
| 155 | + |
| 156 | + // First, collect conditions implied by branches and blocks with their |
| 157 | + // Dominator DFS in and out numbers. |
| 158 | + for (BasicBlock &BB : F) { |
| 159 | + if (!DT.getNode(&BB)) |
| 160 | + continue; |
| 161 | + WorkList.emplace_back(DT.getNode(&BB)); |
| 162 | + |
| 163 | + auto *Br = dyn_cast<BranchInst>(BB.getTerminator()); |
| 164 | + if (!Br || !Br->isConditional()) |
| 165 | + continue; |
| 166 | + auto *CmpI = dyn_cast<CmpInst>(Br->getCondition()); |
| 167 | + if (!CmpI) |
| 168 | + continue; |
| 169 | + if (Br->getSuccessor(0)->getSinglePredecessor()) |
| 170 | + WorkList.emplace_back(DT.getNode(Br->getSuccessor(0)), CmpI, false); |
| 171 | + if (Br->getSuccessor(1)->getSinglePredecessor()) |
| 172 | + WorkList.emplace_back(DT.getNode(Br->getSuccessor(1)), CmpI, true); |
| 173 | + } |
| 174 | + |
| 175 | + // Next, sort worklist by dominance, so that dominating blocks and conditions |
| 176 | + // come before blocks and conditions dominated by them. If a block and a |
| 177 | + // condition have the same numbers, the condition comes before the block, as |
| 178 | + // it holds on entry to the block. |
| 179 | + sort(WorkList.begin(), WorkList.end(), |
| 180 | + [](const ConstraintOrBlock &A, const ConstraintOrBlock &B) { |
| 181 | + return std::tie(A.NumIn, A.IsBlock) < std::tie(B.NumIn, B.IsBlock); |
| 182 | + }); |
| 183 | + |
| 184 | + // Finally, process ordered worklist and eliminate implied conditions. |
| 185 | + SmallVector<StackEntry, 16> DFSInStack; |
| 186 | + DenseMap<Value *, unsigned> Value2Index; |
| 187 | + for (ConstraintOrBlock &CB : WorkList) { |
| 188 | + // First, pop entries from the stack that are out-of-scope for CB. Remove |
| 189 | + // the corresponding entry from the constraint system. |
| 190 | + while (!DFSInStack.empty()) { |
| 191 | + auto &E = DFSInStack.back(); |
| 192 | + LLVM_DEBUG(dbgs() << "Top of stack : " << E.NumIn << " " << E.NumOut |
| 193 | + << "\n"); |
| 194 | + LLVM_DEBUG(dbgs() << "CB: " << CB.NumIn << " " << CB.NumOut << "\n"); |
| 195 | + bool IsDom = CB.NumIn >= E.NumIn && CB.NumOut <= E.NumOut; |
| 196 | + if (IsDom) |
| 197 | + break; |
| 198 | + LLVM_DEBUG(dbgs() << "Removing " << *E.Condition << " " << E.IsNot |
| 199 | + << "\n"); |
| 200 | + DFSInStack.pop_back(); |
| 201 | + CS.popLastConstraint(); |
| 202 | + } |
| 203 | + |
| 204 | + LLVM_DEBUG({ |
| 205 | + dbgs() << "Processing "; |
| 206 | + if (CB.IsBlock) |
| 207 | + dbgs() << *CB.BB; |
| 208 | + else |
| 209 | + dbgs() << *CB.Condition; |
| 210 | + dbgs() << "\n"; |
| 211 | + }); |
| 212 | + |
| 213 | + // For a block, check if any CmpInsts become known based on the current set |
| 214 | + // of constraints. |
| 215 | + if (CB.IsBlock) { |
| 216 | + for (Instruction &I : *CB.BB) { |
| 217 | + auto *Cmp = dyn_cast<CmpInst>(&I); |
| 218 | + if (!Cmp) |
| 219 | + continue; |
| 220 | + auto R = getConstraint(Cmp, Value2Index, false); |
| 221 | + if (R.empty()) |
| 222 | + continue; |
| 223 | + if (CS.isConditionImplied(R)) { |
| 224 | + if (!DebugCounter::shouldExecute(EliminatedCounter)) |
| 225 | + continue; |
| 226 | + |
| 227 | + LLVM_DEBUG(dbgs() << "Condition " << *Cmp |
| 228 | + << " implied by dominating constraints\n"); |
| 229 | + LLVM_DEBUG({ |
| 230 | + for (auto &E : reverse(DFSInStack)) |
| 231 | + dbgs() << " C " << *E.Condition << " " << E.IsNot << "\n"; |
| 232 | + }); |
| 233 | + Cmp->replaceAllUsesWith( |
| 234 | + ConstantInt::getTrue(F.getParent()->getContext())); |
| 235 | + NumCondsRemoved++; |
| 236 | + Changed = true; |
| 237 | + } |
| 238 | + if (CS.isConditionImplied(ConstraintSystem::negate(R))) { |
| 239 | + if (!DebugCounter::shouldExecute(EliminatedCounter)) |
| 240 | + continue; |
| 241 | + |
| 242 | + LLVM_DEBUG(dbgs() << "Condition !" << *Cmp |
| 243 | + << " implied by dominating constraints\n"); |
| 244 | + LLVM_DEBUG({ |
| 245 | + for (auto &E : reverse(DFSInStack)) |
| 246 | + dbgs() << " C " << *E.Condition << " " << E.IsNot << "\n"; |
| 247 | + }); |
| 248 | + Cmp->replaceAllUsesWith( |
| 249 | + ConstantInt::getFalse(F.getParent()->getContext())); |
| 250 | + NumCondsRemoved++; |
| 251 | + Changed = true; |
| 252 | + } |
| 253 | + } |
| 254 | + continue; |
| 255 | + } |
| 256 | + |
| 257 | + // Otherwise, add the condition to the system and stack, if we can transform |
| 258 | + // it into a constraint. |
| 259 | + auto R = getConstraint(CB.Condition, Value2Index, true); |
| 260 | + if (R.empty()) |
| 261 | + continue; |
| 262 | + |
| 263 | + LLVM_DEBUG(dbgs() << "Adding " << *CB.Condition << " " << CB.Not << "\n"); |
| 264 | + if (CB.Not) |
| 265 | + R = ConstraintSystem::negate(R); |
| 266 | + |
| 267 | + CS.addVariableRowFill(R); |
| 268 | + DFSInStack.emplace_back(CB.NumIn, CB.NumOut, CB.Condition, CB.Not); |
| 269 | + } |
| 270 | + |
| 271 | + return Changed; |
| 272 | +} |
| 273 | + |
| 274 | +namespace { |
| 275 | + |
| 276 | +class ConstraintElimination : public FunctionPass { |
| 277 | +public: |
| 278 | + static char ID; |
| 279 | + |
| 280 | + ConstraintElimination() : FunctionPass(ID) { |
| 281 | + initializeConstraintEliminationPass(*PassRegistry::getPassRegistry()); |
| 282 | + } |
| 283 | + |
| 284 | + bool runOnFunction(Function &F) override { |
| 285 | + auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree(); |
| 286 | + return eliminateConstraints(F, DT); |
| 287 | + } |
| 288 | + |
| 289 | + void getAnalysisUsage(AnalysisUsage &AU) const override { |
| 290 | + AU.setPreservesCFG(); |
| 291 | + AU.addRequired<DominatorTreeWrapperPass>(); |
| 292 | + AU.addPreserved<GlobalsAAWrapperPass>(); |
| 293 | + AU.addPreserved<DominatorTreeWrapperPass>(); |
| 294 | + } |
| 295 | +}; |
| 296 | + |
| 297 | +} // end anonymous namespace |
| 298 | + |
| 299 | +char ConstraintElimination::ID = 0; |
| 300 | + |
| 301 | +INITIALIZE_PASS_BEGIN(ConstraintElimination, "constraint-elimination", |
| 302 | + "Constraint Elimination", false, false) |
| 303 | +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) |
| 304 | +INITIALIZE_PASS_DEPENDENCY(LazyValueInfoWrapperPass) |
| 305 | +INITIALIZE_PASS_END(ConstraintElimination, "constraint-elimination", |
| 306 | + "Constraint Elimination", false, false) |
| 307 | + |
| 308 | +FunctionPass *llvm::createConstraintEliminationPass() { |
| 309 | + return new ConstraintElimination(); |
| 310 | +} |
0 commit comments