18fb3d57eSArtur Pilipenko //===-- LoopPredication.cpp - Guard based loop predication pass -----------===// 28fb3d57eSArtur Pilipenko // 38fb3d57eSArtur Pilipenko // The LLVM Compiler Infrastructure 48fb3d57eSArtur Pilipenko // 58fb3d57eSArtur Pilipenko // This file is distributed under the University of Illinois Open Source 68fb3d57eSArtur Pilipenko // License. See LICENSE.TXT for details. 78fb3d57eSArtur Pilipenko // 88fb3d57eSArtur Pilipenko //===----------------------------------------------------------------------===// 98fb3d57eSArtur Pilipenko // 108fb3d57eSArtur Pilipenko // The LoopPredication pass tries to convert loop variant range checks to loop 118fb3d57eSArtur Pilipenko // invariant by widening checks across loop iterations. For example, it will 128fb3d57eSArtur Pilipenko // convert 138fb3d57eSArtur Pilipenko // 148fb3d57eSArtur Pilipenko // for (i = 0; i < n; i++) { 158fb3d57eSArtur Pilipenko // guard(i < len); 168fb3d57eSArtur Pilipenko // ... 178fb3d57eSArtur Pilipenko // } 188fb3d57eSArtur Pilipenko // 198fb3d57eSArtur Pilipenko // to 208fb3d57eSArtur Pilipenko // 218fb3d57eSArtur Pilipenko // for (i = 0; i < n; i++) { 228fb3d57eSArtur Pilipenko // guard(n - 1 < len); 238fb3d57eSArtur Pilipenko // ... 248fb3d57eSArtur Pilipenko // } 258fb3d57eSArtur Pilipenko // 268fb3d57eSArtur Pilipenko // After this transformation the condition of the guard is loop invariant, so 278fb3d57eSArtur Pilipenko // loop-unswitch can later unswitch the loop by this condition which basically 288fb3d57eSArtur Pilipenko // predicates the loop by the widened condition: 298fb3d57eSArtur Pilipenko // 308fb3d57eSArtur Pilipenko // if (n - 1 < len) 318fb3d57eSArtur Pilipenko // for (i = 0; i < n; i++) { 328fb3d57eSArtur Pilipenko // ... 338fb3d57eSArtur Pilipenko // } 348fb3d57eSArtur Pilipenko // else 358fb3d57eSArtur Pilipenko // deoptimize 368fb3d57eSArtur Pilipenko // 378fb3d57eSArtur Pilipenko //===----------------------------------------------------------------------===// 388fb3d57eSArtur Pilipenko 398fb3d57eSArtur Pilipenko #include "llvm/Transforms/Scalar/LoopPredication.h" 408fb3d57eSArtur Pilipenko #include "llvm/Pass.h" 418fb3d57eSArtur Pilipenko #include "llvm/Analysis/LoopInfo.h" 428fb3d57eSArtur Pilipenko #include "llvm/Analysis/LoopPass.h" 438fb3d57eSArtur Pilipenko #include "llvm/Analysis/ScalarEvolution.h" 448fb3d57eSArtur Pilipenko #include "llvm/Analysis/ScalarEvolutionExpander.h" 458fb3d57eSArtur Pilipenko #include "llvm/Analysis/ScalarEvolutionExpressions.h" 468fb3d57eSArtur Pilipenko #include "llvm/IR/Function.h" 478fb3d57eSArtur Pilipenko #include "llvm/IR/GlobalValue.h" 488fb3d57eSArtur Pilipenko #include "llvm/IR/IntrinsicInst.h" 498fb3d57eSArtur Pilipenko #include "llvm/IR/Module.h" 508fb3d57eSArtur Pilipenko #include "llvm/IR/PatternMatch.h" 518fb3d57eSArtur Pilipenko #include "llvm/Support/Debug.h" 528fb3d57eSArtur Pilipenko #include "llvm/Transforms/Scalar.h" 538fb3d57eSArtur Pilipenko #include "llvm/Transforms/Utils/LoopUtils.h" 548fb3d57eSArtur Pilipenko 558fb3d57eSArtur Pilipenko #define DEBUG_TYPE "loop-predication" 568fb3d57eSArtur Pilipenko 578fb3d57eSArtur Pilipenko using namespace llvm; 588fb3d57eSArtur Pilipenko 598fb3d57eSArtur Pilipenko namespace { 608fb3d57eSArtur Pilipenko class LoopPredication { 61a6c27804SArtur Pilipenko /// Represents an induction variable check: 62a6c27804SArtur Pilipenko /// icmp Pred, <induction variable>, <loop invariant limit> 63a6c27804SArtur Pilipenko struct LoopICmp { 64a6c27804SArtur Pilipenko ICmpInst::Predicate Pred; 65a6c27804SArtur Pilipenko const SCEVAddRecExpr *IV; 66a6c27804SArtur Pilipenko const SCEV *Limit; 67*c488dfabSArtur Pilipenko LoopICmp(ICmpInst::Predicate Pred, const SCEVAddRecExpr *IV, 68*c488dfabSArtur Pilipenko const SCEV *Limit) 69a6c27804SArtur Pilipenko : Pred(Pred), IV(IV), Limit(Limit) {} 70a6c27804SArtur Pilipenko LoopICmp() {} 71a6c27804SArtur Pilipenko }; 72*c488dfabSArtur Pilipenko 73*c488dfabSArtur Pilipenko ScalarEvolution *SE; 74*c488dfabSArtur Pilipenko 75*c488dfabSArtur Pilipenko Loop *L; 76*c488dfabSArtur Pilipenko const DataLayout *DL; 77*c488dfabSArtur Pilipenko BasicBlock *Preheader; 78*c488dfabSArtur Pilipenko 79a6c27804SArtur Pilipenko Optional<LoopICmp> parseLoopICmp(ICmpInst *ICI); 80a6c27804SArtur Pilipenko 816780ba65SArtur Pilipenko Value *expandCheck(SCEVExpander &Expander, IRBuilder<> &Builder, 826780ba65SArtur Pilipenko ICmpInst::Predicate Pred, const SCEV *LHS, const SCEV *RHS, 836780ba65SArtur Pilipenko Instruction *InsertAt); 846780ba65SArtur Pilipenko 858fb3d57eSArtur Pilipenko Optional<Value *> widenICmpRangeCheck(ICmpInst *ICI, SCEVExpander &Expander, 868fb3d57eSArtur Pilipenko IRBuilder<> &Builder); 878fb3d57eSArtur Pilipenko bool widenGuardConditions(IntrinsicInst *II, SCEVExpander &Expander); 888fb3d57eSArtur Pilipenko 898fb3d57eSArtur Pilipenko public: 908fb3d57eSArtur Pilipenko LoopPredication(ScalarEvolution *SE) : SE(SE){}; 918fb3d57eSArtur Pilipenko bool runOnLoop(Loop *L); 928fb3d57eSArtur Pilipenko }; 938fb3d57eSArtur Pilipenko 948fb3d57eSArtur Pilipenko class LoopPredicationLegacyPass : public LoopPass { 958fb3d57eSArtur Pilipenko public: 968fb3d57eSArtur Pilipenko static char ID; 978fb3d57eSArtur Pilipenko LoopPredicationLegacyPass() : LoopPass(ID) { 988fb3d57eSArtur Pilipenko initializeLoopPredicationLegacyPassPass(*PassRegistry::getPassRegistry()); 998fb3d57eSArtur Pilipenko } 1008fb3d57eSArtur Pilipenko 1018fb3d57eSArtur Pilipenko void getAnalysisUsage(AnalysisUsage &AU) const override { 1028fb3d57eSArtur Pilipenko getLoopAnalysisUsage(AU); 1038fb3d57eSArtur Pilipenko } 1048fb3d57eSArtur Pilipenko 1058fb3d57eSArtur Pilipenko bool runOnLoop(Loop *L, LPPassManager &LPM) override { 1068fb3d57eSArtur Pilipenko if (skipLoop(L)) 1078fb3d57eSArtur Pilipenko return false; 1088fb3d57eSArtur Pilipenko auto *SE = &getAnalysis<ScalarEvolutionWrapperPass>().getSE(); 1098fb3d57eSArtur Pilipenko LoopPredication LP(SE); 1108fb3d57eSArtur Pilipenko return LP.runOnLoop(L); 1118fb3d57eSArtur Pilipenko } 1128fb3d57eSArtur Pilipenko }; 1138fb3d57eSArtur Pilipenko 1148fb3d57eSArtur Pilipenko char LoopPredicationLegacyPass::ID = 0; 1158fb3d57eSArtur Pilipenko } // end namespace llvm 1168fb3d57eSArtur Pilipenko 1178fb3d57eSArtur Pilipenko INITIALIZE_PASS_BEGIN(LoopPredicationLegacyPass, "loop-predication", 1188fb3d57eSArtur Pilipenko "Loop predication", false, false) 1198fb3d57eSArtur Pilipenko INITIALIZE_PASS_DEPENDENCY(LoopPass) 1208fb3d57eSArtur Pilipenko INITIALIZE_PASS_END(LoopPredicationLegacyPass, "loop-predication", 1218fb3d57eSArtur Pilipenko "Loop predication", false, false) 1228fb3d57eSArtur Pilipenko 1238fb3d57eSArtur Pilipenko Pass *llvm::createLoopPredicationPass() { 1248fb3d57eSArtur Pilipenko return new LoopPredicationLegacyPass(); 1258fb3d57eSArtur Pilipenko } 1268fb3d57eSArtur Pilipenko 1278fb3d57eSArtur Pilipenko PreservedAnalyses LoopPredicationPass::run(Loop &L, LoopAnalysisManager &AM, 1288fb3d57eSArtur Pilipenko LoopStandardAnalysisResults &AR, 1298fb3d57eSArtur Pilipenko LPMUpdater &U) { 1308fb3d57eSArtur Pilipenko LoopPredication LP(&AR.SE); 1318fb3d57eSArtur Pilipenko if (!LP.runOnLoop(&L)) 1328fb3d57eSArtur Pilipenko return PreservedAnalyses::all(); 1338fb3d57eSArtur Pilipenko 1348fb3d57eSArtur Pilipenko return getLoopPassPreservedAnalyses(); 1358fb3d57eSArtur Pilipenko } 1368fb3d57eSArtur Pilipenko 137a6c27804SArtur Pilipenko Optional<LoopPredication::LoopICmp> 138a6c27804SArtur Pilipenko LoopPredication::parseLoopICmp(ICmpInst *ICI) { 139a6c27804SArtur Pilipenko ICmpInst::Predicate Pred = ICI->getPredicate(); 140a6c27804SArtur Pilipenko 141a6c27804SArtur Pilipenko Value *LHS = ICI->getOperand(0); 142a6c27804SArtur Pilipenko Value *RHS = ICI->getOperand(1); 143a6c27804SArtur Pilipenko const SCEV *LHSS = SE->getSCEV(LHS); 144a6c27804SArtur Pilipenko if (isa<SCEVCouldNotCompute>(LHSS)) 145a6c27804SArtur Pilipenko return None; 146a6c27804SArtur Pilipenko const SCEV *RHSS = SE->getSCEV(RHS); 147a6c27804SArtur Pilipenko if (isa<SCEVCouldNotCompute>(RHSS)) 148a6c27804SArtur Pilipenko return None; 149a6c27804SArtur Pilipenko 150a6c27804SArtur Pilipenko // Canonicalize RHS to be loop invariant bound, LHS - a loop computable IV 151a6c27804SArtur Pilipenko if (SE->isLoopInvariant(LHSS, L)) { 152a6c27804SArtur Pilipenko std::swap(LHS, RHS); 153a6c27804SArtur Pilipenko std::swap(LHSS, RHSS); 154a6c27804SArtur Pilipenko Pred = ICmpInst::getSwappedPredicate(Pred); 155a6c27804SArtur Pilipenko } 156a6c27804SArtur Pilipenko 157a6c27804SArtur Pilipenko const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(LHSS); 158a6c27804SArtur Pilipenko if (!AR || AR->getLoop() != L) 159a6c27804SArtur Pilipenko return None; 160a6c27804SArtur Pilipenko 161a6c27804SArtur Pilipenko return LoopICmp(Pred, AR, RHSS); 162a6c27804SArtur Pilipenko } 163a6c27804SArtur Pilipenko 1646780ba65SArtur Pilipenko Value *LoopPredication::expandCheck(SCEVExpander &Expander, 1656780ba65SArtur Pilipenko IRBuilder<> &Builder, 1666780ba65SArtur Pilipenko ICmpInst::Predicate Pred, const SCEV *LHS, 1676780ba65SArtur Pilipenko const SCEV *RHS, Instruction *InsertAt) { 1686780ba65SArtur Pilipenko Type *Ty = LHS->getType(); 1696780ba65SArtur Pilipenko assert(Ty == RHS->getType() && "expandCheck operands have different types?"); 1706780ba65SArtur Pilipenko Value *LHSV = Expander.expandCodeFor(LHS, Ty, InsertAt); 1716780ba65SArtur Pilipenko Value *RHSV = Expander.expandCodeFor(RHS, Ty, InsertAt); 1726780ba65SArtur Pilipenko return Builder.CreateICmp(Pred, LHSV, RHSV); 1736780ba65SArtur Pilipenko } 1746780ba65SArtur Pilipenko 1758fb3d57eSArtur Pilipenko /// If ICI can be widened to a loop invariant condition emits the loop 1768fb3d57eSArtur Pilipenko /// invariant condition in the loop preheader and return it, otherwise 1778fb3d57eSArtur Pilipenko /// returns None. 1788fb3d57eSArtur Pilipenko Optional<Value *> LoopPredication::widenICmpRangeCheck(ICmpInst *ICI, 1798fb3d57eSArtur Pilipenko SCEVExpander &Expander, 1808fb3d57eSArtur Pilipenko IRBuilder<> &Builder) { 1818fb3d57eSArtur Pilipenko DEBUG(dbgs() << "Analyzing ICmpInst condition:\n"); 1828fb3d57eSArtur Pilipenko DEBUG(ICI->dump()); 1838fb3d57eSArtur Pilipenko 184a6c27804SArtur Pilipenko auto RangeCheck = parseLoopICmp(ICI); 185a6c27804SArtur Pilipenko if (!RangeCheck) 1868fb3d57eSArtur Pilipenko return None; 1878fb3d57eSArtur Pilipenko 188a6c27804SArtur Pilipenko ICmpInst::Predicate Pred = RangeCheck->Pred; 189a6c27804SArtur Pilipenko const SCEVAddRecExpr *IndexAR = RangeCheck->IV; 190a6c27804SArtur Pilipenko const SCEV *RHSS = RangeCheck->Limit; 191aab28666SArtur Pilipenko 192aab28666SArtur Pilipenko auto CanExpand = [this](const SCEV *S) { 193aab28666SArtur Pilipenko return SE->isLoopInvariant(S, L) && isSafeToExpand(S, *SE); 194aab28666SArtur Pilipenko }; 195aab28666SArtur Pilipenko if (!CanExpand(RHSS)) 1968fb3d57eSArtur Pilipenko return None; 1978fb3d57eSArtur Pilipenko 1988fb3d57eSArtur Pilipenko DEBUG(dbgs() << "IndexAR: "); 1998fb3d57eSArtur Pilipenko DEBUG(IndexAR->dump()); 2008fb3d57eSArtur Pilipenko 2018fb3d57eSArtur Pilipenko bool IsIncreasing = false; 2028fb3d57eSArtur Pilipenko if (!SE->isMonotonicPredicate(IndexAR, Pred, IsIncreasing)) 2038fb3d57eSArtur Pilipenko return None; 2048fb3d57eSArtur Pilipenko 2058fb3d57eSArtur Pilipenko // If the predicate is increasing the condition can change from false to true 2068fb3d57eSArtur Pilipenko // as the loop progresses, in this case take the value on the first iteration 2078fb3d57eSArtur Pilipenko // for the widened check. Otherwise the condition can change from true to 2088fb3d57eSArtur Pilipenko // false as the loop progresses, so take the value on the last iteration. 2098fb3d57eSArtur Pilipenko const SCEV *NewLHSS = IsIncreasing 2108fb3d57eSArtur Pilipenko ? IndexAR->getStart() 2118fb3d57eSArtur Pilipenko : SE->getSCEVAtScope(IndexAR, L->getParentLoop()); 2128fb3d57eSArtur Pilipenko if (NewLHSS == IndexAR) { 2132cbaded5SArtur Pilipenko DEBUG(dbgs() << "Can't compute NewLHSS!\n"); 2148fb3d57eSArtur Pilipenko return None; 2158fb3d57eSArtur Pilipenko } 2168fb3d57eSArtur Pilipenko 2178fb3d57eSArtur Pilipenko DEBUG(dbgs() << "NewLHSS: "); 2188fb3d57eSArtur Pilipenko DEBUG(NewLHSS->dump()); 2198fb3d57eSArtur Pilipenko 220aab28666SArtur Pilipenko if (!CanExpand(NewLHSS)) 2218fb3d57eSArtur Pilipenko return None; 2228fb3d57eSArtur Pilipenko 2238fb3d57eSArtur Pilipenko DEBUG(dbgs() << "NewLHSS is loop invariant and safe to expand. Expand!\n"); 2248fb3d57eSArtur Pilipenko 2250860bfc6SArtur Pilipenko Instruction *InsertAt = Preheader->getTerminator(); 2266780ba65SArtur Pilipenko return expandCheck(Expander, Builder, Pred, NewLHSS, RHSS, InsertAt); 2278fb3d57eSArtur Pilipenko } 2288fb3d57eSArtur Pilipenko 2298fb3d57eSArtur Pilipenko bool LoopPredication::widenGuardConditions(IntrinsicInst *Guard, 2308fb3d57eSArtur Pilipenko SCEVExpander &Expander) { 2318fb3d57eSArtur Pilipenko DEBUG(dbgs() << "Processing guard:\n"); 2328fb3d57eSArtur Pilipenko DEBUG(Guard->dump()); 2338fb3d57eSArtur Pilipenko 2348fb3d57eSArtur Pilipenko IRBuilder<> Builder(cast<Instruction>(Preheader->getTerminator())); 2358fb3d57eSArtur Pilipenko 2368fb3d57eSArtur Pilipenko // The guard condition is expected to be in form of: 2378fb3d57eSArtur Pilipenko // cond1 && cond2 && cond3 ... 2388fb3d57eSArtur Pilipenko // Iterate over subconditions looking for for icmp conditions which can be 2398fb3d57eSArtur Pilipenko // widened across loop iterations. Widening these conditions remember the 2408fb3d57eSArtur Pilipenko // resulting list of subconditions in Checks vector. 2418fb3d57eSArtur Pilipenko SmallVector<Value *, 4> Worklist(1, Guard->getOperand(0)); 2428fb3d57eSArtur Pilipenko SmallPtrSet<Value *, 4> Visited; 2438fb3d57eSArtur Pilipenko 2448fb3d57eSArtur Pilipenko SmallVector<Value *, 4> Checks; 2458fb3d57eSArtur Pilipenko 2468fb3d57eSArtur Pilipenko unsigned NumWidened = 0; 2478fb3d57eSArtur Pilipenko do { 2488fb3d57eSArtur Pilipenko Value *Condition = Worklist.pop_back_val(); 2498fb3d57eSArtur Pilipenko if (!Visited.insert(Condition).second) 2508fb3d57eSArtur Pilipenko continue; 2518fb3d57eSArtur Pilipenko 2528fb3d57eSArtur Pilipenko Value *LHS, *RHS; 2538fb3d57eSArtur Pilipenko using namespace llvm::PatternMatch; 2548fb3d57eSArtur Pilipenko if (match(Condition, m_And(m_Value(LHS), m_Value(RHS)))) { 2558fb3d57eSArtur Pilipenko Worklist.push_back(LHS); 2568fb3d57eSArtur Pilipenko Worklist.push_back(RHS); 2578fb3d57eSArtur Pilipenko continue; 2588fb3d57eSArtur Pilipenko } 2598fb3d57eSArtur Pilipenko 2608fb3d57eSArtur Pilipenko if (ICmpInst *ICI = dyn_cast<ICmpInst>(Condition)) { 2618fb3d57eSArtur Pilipenko if (auto NewRangeCheck = widenICmpRangeCheck(ICI, Expander, Builder)) { 2628fb3d57eSArtur Pilipenko Checks.push_back(NewRangeCheck.getValue()); 2638fb3d57eSArtur Pilipenko NumWidened++; 2648fb3d57eSArtur Pilipenko continue; 2658fb3d57eSArtur Pilipenko } 2668fb3d57eSArtur Pilipenko } 2678fb3d57eSArtur Pilipenko 2688fb3d57eSArtur Pilipenko // Save the condition as is if we can't widen it 2698fb3d57eSArtur Pilipenko Checks.push_back(Condition); 2708fb3d57eSArtur Pilipenko } while (Worklist.size() != 0); 2718fb3d57eSArtur Pilipenko 2728fb3d57eSArtur Pilipenko if (NumWidened == 0) 2738fb3d57eSArtur Pilipenko return false; 2748fb3d57eSArtur Pilipenko 2758fb3d57eSArtur Pilipenko // Emit the new guard condition 2768fb3d57eSArtur Pilipenko Builder.SetInsertPoint(Guard); 2778fb3d57eSArtur Pilipenko Value *LastCheck = nullptr; 2788fb3d57eSArtur Pilipenko for (auto *Check : Checks) 2798fb3d57eSArtur Pilipenko if (!LastCheck) 2808fb3d57eSArtur Pilipenko LastCheck = Check; 2818fb3d57eSArtur Pilipenko else 2828fb3d57eSArtur Pilipenko LastCheck = Builder.CreateAnd(LastCheck, Check); 2838fb3d57eSArtur Pilipenko Guard->setOperand(0, LastCheck); 2848fb3d57eSArtur Pilipenko 2858fb3d57eSArtur Pilipenko DEBUG(dbgs() << "Widened checks = " << NumWidened << "\n"); 2868fb3d57eSArtur Pilipenko return true; 2878fb3d57eSArtur Pilipenko } 2888fb3d57eSArtur Pilipenko 2898fb3d57eSArtur Pilipenko bool LoopPredication::runOnLoop(Loop *Loop) { 2908fb3d57eSArtur Pilipenko L = Loop; 2918fb3d57eSArtur Pilipenko 2928fb3d57eSArtur Pilipenko DEBUG(dbgs() << "Analyzing "); 2938fb3d57eSArtur Pilipenko DEBUG(L->dump()); 2948fb3d57eSArtur Pilipenko 2958fb3d57eSArtur Pilipenko Module *M = L->getHeader()->getModule(); 2968fb3d57eSArtur Pilipenko 2978fb3d57eSArtur Pilipenko // There is nothing to do if the module doesn't use guards 2988fb3d57eSArtur Pilipenko auto *GuardDecl = 2998fb3d57eSArtur Pilipenko M->getFunction(Intrinsic::getName(Intrinsic::experimental_guard)); 3008fb3d57eSArtur Pilipenko if (!GuardDecl || GuardDecl->use_empty()) 3018fb3d57eSArtur Pilipenko return false; 3028fb3d57eSArtur Pilipenko 3038fb3d57eSArtur Pilipenko DL = &M->getDataLayout(); 3048fb3d57eSArtur Pilipenko 3058fb3d57eSArtur Pilipenko Preheader = L->getLoopPreheader(); 3068fb3d57eSArtur Pilipenko if (!Preheader) 3078fb3d57eSArtur Pilipenko return false; 3088fb3d57eSArtur Pilipenko 3098fb3d57eSArtur Pilipenko // Collect all the guards into a vector and process later, so as not 3108fb3d57eSArtur Pilipenko // to invalidate the instruction iterator. 3118fb3d57eSArtur Pilipenko SmallVector<IntrinsicInst *, 4> Guards; 3128fb3d57eSArtur Pilipenko for (const auto BB : L->blocks()) 3138fb3d57eSArtur Pilipenko for (auto &I : *BB) 3148fb3d57eSArtur Pilipenko if (auto *II = dyn_cast<IntrinsicInst>(&I)) 3158fb3d57eSArtur Pilipenko if (II->getIntrinsicID() == Intrinsic::experimental_guard) 3168fb3d57eSArtur Pilipenko Guards.push_back(II); 3178fb3d57eSArtur Pilipenko 31846c4e0a4SArtur Pilipenko if (Guards.empty()) 31946c4e0a4SArtur Pilipenko return false; 32046c4e0a4SArtur Pilipenko 3218fb3d57eSArtur Pilipenko SCEVExpander Expander(*SE, *DL, "loop-predication"); 3228fb3d57eSArtur Pilipenko 3238fb3d57eSArtur Pilipenko bool Changed = false; 3248fb3d57eSArtur Pilipenko for (auto *Guard : Guards) 3258fb3d57eSArtur Pilipenko Changed |= widenGuardConditions(Guard, Expander); 3268fb3d57eSArtur Pilipenko 3278fb3d57eSArtur Pilipenko return Changed; 3288fb3d57eSArtur Pilipenko } 329