//===------------------------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Scalar/DropUnnecessaryAssumes.h" #include "llvm/ADT/SetVector.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Transforms/Utils/Local.h" using namespace llvm; using namespace llvm::PatternMatch; static bool affectedValuesAreEphemeral(ArrayRef Affected) { // Check whether all the uses are ephemeral, i.e. recursively only used // by assumes. In that case, the assume does not provide useful information. // Note that additional users may appear as a result of inlining and CSE, // so we should only make this assumption late in the optimization pipeline. SmallSetVector Worklist; auto AddUsers = [&](Value *V) { for (User *U : V->users()) { // Bail out if we need to inspect too many users. if (Worklist.size() >= 32) return false; Worklist.insert(cast(U)); } return true; }; for (Value *V : Affected) { // Do not handle assumes on globals for now. The use list for them may // contain uses in other functions. if (!isa(V)) return false; if (!AddUsers(V)) return false; } for (unsigned Idx = 0; Idx < Worklist.size(); ++Idx) { Instruction *I = Worklist[Idx]; // Use in assume is ephemeral. if (isa(I)) continue; // Use in side-effecting instruction is non-ephemeral. if (I->mayHaveSideEffects() || I->isTerminator()) return false; // Otherwise, recursively look at the users. if (!AddUsers(I)) return false; } return true; } PreservedAnalyses DropUnnecessaryAssumesPass::run(Function &F, FunctionAnalysisManager &FAM) { AssumptionCache &AC = FAM.getResult(F); bool Changed = false; for (const WeakVH &Elem : AC.assumptions()) { auto *Assume = cast_or_null(Elem); if (!Assume) continue; if (Assume->hasOperandBundles()) { // Handle operand bundle assumptions. SmallVector DeadBundleArgs; SmallVector KeptBundles; unsigned NumBundles = Assume->getNumOperandBundles(); for (unsigned I = 0; I != NumBundles; ++I) { auto IsDead = [](OperandBundleUse Bundle) { // "ignore" operand bundles are always dead. if (Bundle.getTagName() == "ignore") return true; // Bundles without arguments do not affect any specific values. // Always keep them for now. if (Bundle.Inputs.empty()) return false; SmallVector Affected; AssumptionCache::findValuesAffectedByOperandBundle( Bundle, [&](Value *A) { Affected.push_back(A); }); return affectedValuesAreEphemeral(Affected); }; OperandBundleUse Bundle = Assume->getOperandBundleAt(I); if (IsDead(Bundle)) append_range(DeadBundleArgs, Bundle.Inputs); else KeptBundles.emplace_back(Bundle); } if (KeptBundles.size() != NumBundles) { if (KeptBundles.empty()) { // All operand bundles are dead, remove the whole assume. Assume->eraseFromParent(); } else { // Otherwise only drop the dead operand bundles. CallBase *NewAssume = CallBase::Create(Assume, KeptBundles, Assume->getIterator()); AC.registerAssumption(cast(NewAssume)); Assume->eraseFromParent(); } RecursivelyDeleteTriviallyDeadInstructionsPermissive(DeadBundleArgs); Changed = true; } continue; } Value *Cond = Assume->getArgOperand(0); // Don't drop type tests, which have special semantics. if (match(Cond, m_Intrinsic())) continue; SmallVector Affected; findValuesAffectedByCondition(Cond, /*IsAssume=*/true, [&](Value *A) { Affected.push_back(A); }); if (!affectedValuesAreEphemeral(Affected)) continue; Assume->eraseFromParent(); RecursivelyDeleteTriviallyDeadInstructions(Cond); Changed = true; } if (Changed) { PreservedAnalyses PA; PA.preserveSet(); return PA; } return PreservedAnalyses::all(); }