diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h --- a/llvm/include/llvm/Transforms/Utils/Local.h +++ b/llvm/include/llvm/Transforms/Utils/Local.h @@ -87,8 +87,16 @@ /// Return true if the result produced by the instruction would have no side /// effects if it was not used. This is equivalent to checking whether /// isInstructionTriviallyDead would be true if the use count was 0. -bool wouldInstructionBeTriviallyDead(Instruction *I, - const TargetLibraryInfo *TLI = nullptr); +bool wouldInstructionBeTriviallyDeadIfUsesDead( + Instruction *I, const TargetLibraryInfo *TLI = nullptr); + +/// Return true if the result produced by the instruction has no side effects on +/// any paths other than where it is used. This is less conservative than +/// wouldInstructionBeTriviallyDeadIfUsesDead which is based on the assumption +/// that the use count will be 0. An example usage of this API is for +/// identifying instructions that can be sunk down to use(s). +bool wouldInstructionBeTriviallyDeadOnPathsWithoutUse( + Instruction *I, const TargetLibraryInfo *TLI = nullptr); /// If the specified value is a trivially dead instruction, delete it. /// If that makes any of its operands trivially dead, delete them too, diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -3344,7 +3344,7 @@ /// Determine if \p I is assumed to be side-effect free. bool isAssumedSideEffectFree(Attributor &A, Instruction *I) { - if (!I || wouldInstructionBeTriviallyDead(I)) + if (!I || wouldInstructionBeTriviallyDeadIfUsesDead(I)) return true; auto *CB = dyn_cast(I); diff --git a/llvm/lib/Transforms/Scalar/BDCE.cpp b/llvm/lib/Transforms/Scalar/BDCE.cpp --- a/llvm/lib/Transforms/Scalar/BDCE.cpp +++ b/llvm/lib/Transforms/Scalar/BDCE.cpp @@ -104,7 +104,7 @@ // during analysis or have no demanded bits. if (DB.isInstructionDead(&I) || (I.getType()->isIntOrIntVectorTy() && DB.getDemandedBits(&I).isZero() && - wouldInstructionBeTriviallyDead(&I))) { + wouldInstructionBeTriviallyDeadIfUsesDead(&I))) { Worklist.push_back(&I); Changed = true; continue; diff --git a/llvm/lib/Transforms/Scalar/NewGVN.cpp b/llvm/lib/Transforms/Scalar/NewGVN.cpp --- a/llvm/lib/Transforms/Scalar/NewGVN.cpp +++ b/llvm/lib/Transforms/Scalar/NewGVN.cpp @@ -4068,7 +4068,7 @@ // At this point, anything still in the ProbablyDead set is actually dead if // would be trivially dead. for (auto *I : ProbablyDead) - if (wouldInstructionBeTriviallyDead(I)) + if (wouldInstructionBeTriviallyDeadIfUsesDead(I)) markInstructionForDeletion(I); // Cleanup the congruence class. diff --git a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp --- a/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp +++ b/llvm/lib/Transforms/Utils/AssumeBundleBuilder.cpp @@ -159,7 +159,7 @@ return true; } if (auto *Inst = dyn_cast(RK.WasOn)) - if (wouldInstructionBeTriviallyDead(Inst)) { + if (wouldInstructionBeTriviallyDeadIfUsesDead(Inst)) { if (RK.WasOn->use_empty()) return false; Use *SingleUse = RK.WasOn->getSingleUndroppableUse(); diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -399,11 +399,11 @@ const TargetLibraryInfo *TLI) { if (!I->use_empty()) return false; - return wouldInstructionBeTriviallyDead(I, TLI); + return wouldInstructionBeTriviallyDeadIfUsesDead(I, TLI); } -bool llvm::wouldInstructionBeTriviallyDead(Instruction *I, - const TargetLibraryInfo *TLI) { +bool llvm::wouldInstructionBeTriviallyDeadOnPathsWithoutUse( + Instruction *I, const TargetLibraryInfo *TLI) { if (I->isTerminator()) return false; @@ -436,6 +436,55 @@ if (!I->mayHaveSideEffects()) return true; + if (isAllocLikeFn(I, TLI)) + return true; + + if (CallInst *CI = isFreeCall(I, TLI)) + if (Constant *C = dyn_cast(CI->getArgOperand(0))) + return C->isNullValue() || isa(C); + + if (auto *Call = dyn_cast(I)) + if (isMathLibCallNoop(Call, TLI)) + return true; + + // To express possible interaction with floating point environment constrained + // intrinsics are described as if they access memory. So they look like having + // side effect but actually do not have it unless they raise floating point + // exception. If FP exceptions are ignored, the intrinsic may be deleted. + if (auto *CI = dyn_cast(I)) { + Optional EB = CI->getExceptionBehavior(); + if (!EB || *EB == fp::ExceptionBehavior::ebIgnore) + return true; + } + + // Intrinsics that do not return a value and are "no-op" + // as well, imply they are trivially dead on all paths. + if (IntrinsicInst *II = dyn_cast(I)) { + // Assumptions are dead if their condition is trivially true. Guards on + // true are operationally no-ops. In the future we can consider more + // sophisticated tradeoffs for guards considering potential for check + // widening, but for now we keep things simple. + if ((II->getIntrinsicID() == Intrinsic::assume && + isAssumeWithEmptyBundle(cast(*II))) || + II->getIntrinsicID() == Intrinsic::experimental_guard) { + if (ConstantInt *Cond = dyn_cast(II->getArgOperand(0))) + return !Cond->isZero(); + + return false; + } + } + + return false; +} + +bool llvm::wouldInstructionBeTriviallyDeadIfUsesDead( + Instruction *I, const TargetLibraryInfo *TLI) { + + // I would automatically be dead on all paths since there are no + // uses. + if (wouldInstructionBeTriviallyDeadOnPathsWithoutUse(I, TLI)) + return true; + // Special case intrinsics that "may have side effects" but can be deleted // when dead. if (IntrinsicInst *II = dyn_cast(I)) { @@ -461,46 +510,12 @@ return false; } - // Assumptions are dead if their condition is trivially true. Guards on - // true are operationally no-ops. In the future we can consider more - // sophisticated tradeoffs for guards considering potential for check - // widening, but for now we keep things simple. - if ((II->getIntrinsicID() == Intrinsic::assume && - isAssumeWithEmptyBundle(cast(*II))) || - II->getIntrinsicID() == Intrinsic::experimental_guard) { - if (ConstantInt *Cond = dyn_cast(II->getArgOperand(0))) - return !Cond->isZero(); - - return false; - } - if (auto *FPI = dyn_cast(I)) { Optional ExBehavior = FPI->getExceptionBehavior(); return ExBehavior.getValue() != fp::ebStrict; } } - if (isAllocLikeFn(I, TLI)) - return true; - - if (CallInst *CI = isFreeCall(I, TLI)) - if (Constant *C = dyn_cast(CI->getArgOperand(0))) - return C->isNullValue() || isa(C); - - if (auto *Call = dyn_cast(I)) - if (isMathLibCallNoop(Call, TLI)) - return true; - - // To express possible interaction with floating point environment constrained - // intrinsics are described as if they access memory. So they look like having - // side effect but actually do not have it unless they raise floating point - // exception. If FP exceptions are ignored, the intrinsic may be deleted. - if (auto *CI = dyn_cast(I)) { - Optional EB = CI->getExceptionBehavior(); - if (!EB || *EB == fp::ExceptionBehavior::ebIgnore) - return true; - } - return false; }