#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/Analysis/GlobalsModRef.h"
#include "llvm/Analysis/Loads.h"
#include "llvm/Analysis/ValueTracking.h"
"Attempted to manifest an invalid state!");
ChangeStatus HasChanged = ChangeStatus::UNCHANGED;
+ const Function &F = getAnchorScope();
+
+ // Flag to determine if we can change an invoke to a call assuming the callee
+ // is nounwind. This is not possible if the personality of the function allows
+ // to catch asynchronous exceptions.
+ bool Invoke2CallAllowed = !mayCatchAsynchronousExceptions(F);
for (const Instruction *NRC : NoReturnCalls) {
Instruction *I = const_cast<Instruction *>(NRC);
/// Invoke is replaced with a call and unreachable is placed after it if
/// the callee is nounwind and noreturn. Otherwise, we keep the invoke
/// and only place an unreachable in the normal successor.
- if (Function *Callee = II->getCalledFunction()) {
- auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Callee);
- if (Callee->hasFnAttribute(Attribute::NoUnwind) ||
- (AANoUnw && AANoUnw->isAssumedNoUnwind())) {
- LLVM_DEBUG(dbgs() << "[AAIsDead] Replace invoke with call inst\n");
- changeToCall(II);
- changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
- continue;
+ if (Invoke2CallAllowed) {
+ if (Function *Callee = II->getCalledFunction()) {
+ auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Callee);
+ if (Callee->hasFnAttribute(Attribute::NoUnwind) ||
+ (AANoUnw && AANoUnw->isAssumedNoUnwind())) {
+ LLVM_DEBUG(dbgs()
+ << "[AAIsDead] Replace invoke with call inst\n");
+ changeToCall(II);
+ changeToUnreachable(BB->getTerminator(), /* UseLLVMTrap */ false);
+ continue;
+ }
}
}
/// Check if instruction is after noreturn call, in other words, assumed dead.
bool isAfterNoReturn(const Instruction *I) const;
+ /// Determine if \p F might catch asynchronous exceptions.
+ static bool mayCatchAsynchronousExceptions(const Function &F) {
+ return F.hasPersonalityFn() && !canSimplifyInvokeNoUnwind(&F);
+ }
+
/// Collection of to be explored paths.
SmallSetVector<const Instruction *, 8> ToBeExploredPaths;
const Instruction *AAIsDeadFunction::findNextNoReturn(Attributor &A,
const Instruction *I) {
const BasicBlock *BB = I->getParent();
+ const Function &F = *BB->getParent();
+
+ // Flag to determine if we can change an invoke to a call assuming the callee
+ // is nounwind. This is not possible if the personality of the function allows
+ // to catch asynchronous exceptions.
+ bool Invoke2CallAllowed = !mayCatchAsynchronousExceptions(F);
// TODO: We should have a function that determines if an "edge" is dead.
// Edges could be from an instruction to the next or from a terminator
if (auto *Invoke = dyn_cast<InvokeInst>(I)) {
// Use nounwind to justify the unwind block is dead as well.
auto *AANoUnw = A.getAAFor<AANoUnwind>(*this, *Invoke);
- if (!AANoUnw || !AANoUnw->isAssumedNoUnwind()) {
+ if (!Invoke2CallAllowed ||
+ (!AANoUnw || !AANoUnw->isAssumedNoUnwind())) {
AssumedLiveBlocks.insert(Invoke->getUnwindDest());
ToBeExploredPaths.insert(&Invoke->getUnwindDest()->front());
}