forked from OSchip/llvm-project
[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
This commit is contained in:
parent
d131713307
commit
924d2138fc
|
@ -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<Instruction *>(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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<const Instruction *, 8> 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<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());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue