From 924d2138fce43e9bcca98d06dccad28927d74c26 Mon Sep 17 00:00:00 2001 From: Johannes Doerfert Date: Mon, 5 Aug 2019 21:34:45 +0000 Subject: [PATCH] [Attributor][Fix] Keep invokes if handlers catch asynchronous exceptions Similar to other places where we transform invokes to calls we need to be careful if the handler (=personality) can catch asynchronous exceptions as they are not modeled as part of nounwind. This is tested with D59978. llvm-svn: 367931 --- llvm/lib/Transforms/IPO/Attributor.cpp | 40 ++++++++++++++++++++------ 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index cea8cffa5c69..1ae8ac77de09 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -22,6 +22,7 @@ #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" @@ -1563,6 +1564,12 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { "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(NRC); @@ -1573,14 +1580,17 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { /// 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(*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(*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; + } } } @@ -1639,6 +1649,11 @@ struct AAIsDeadFunction : AAIsDead, BooleanState { /// 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 ToBeExploredPaths; @@ -1662,6 +1677,12 @@ bool AAIsDeadFunction::isAfterNoReturn(const Instruction *I) const { 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 @@ -1678,7 +1699,8 @@ const Instruction *AAIsDeadFunction::findNextNoReturn(Attributor &A, if (auto *Invoke = dyn_cast(I)) { // Use nounwind to justify the unwind block is dead as well. auto *AANoUnw = A.getAAFor(*this, *Invoke); - if (!AANoUnw || !AANoUnw->isAssumedNoUnwind()) { + if (!Invoke2CallAllowed || + (!AANoUnw || !AANoUnw->isAssumedNoUnwind())) { AssumedLiveBlocks.insert(Invoke->getUnwindDest()); ToBeExploredPaths.insert(&Invoke->getUnwindDest()->front()); }