forked from OSchip/llvm-project
[Attributor][FIX] Transform invoke of nounwind to call + br %normal_dest
Even if the invoked function may-return, we can replace it with a call and branch if it is nounwind. We had almost everything in place to do this but did not which actually caused a crash when we removed the landingpad from the actually dead unwind block. Exposed by the IPConstantProp tests.
This commit is contained in:
parent
3cbe3314b4
commit
c7ab19dbb0
|
@ -2338,7 +2338,8 @@ struct AAIsDeadFunction : public AAIsDead {
|
|||
continue;
|
||||
const auto &NoReturnAA =
|
||||
A.getAAFor<AANoReturn>(*this, IRPosition::callsite_function(*CB));
|
||||
if (!NoReturnAA.isAssumedNoReturn())
|
||||
bool MayReturn = !NoReturnAA.isAssumedNoReturn();
|
||||
if (MayReturn && (!Invoke2CallAllowed || !isa<InvokeInst>(CB)))
|
||||
continue;
|
||||
Instruction *I = const_cast<Instruction *>(DeadEndI);
|
||||
BasicBlock *BB = I->getParent();
|
||||
|
@ -2361,6 +2362,26 @@ struct AAIsDeadFunction : public AAIsDead {
|
|||
if (AANoUnw.isAssumedNoUnwind()) {
|
||||
LLVM_DEBUG(dbgs()
|
||||
<< "[AAIsDead] Replace invoke with call inst\n");
|
||||
CallInst *CI = createCallMatchingInvoke(II);
|
||||
CI->insertBefore(II);
|
||||
CI->takeName(II);
|
||||
II->replaceAllUsesWith(CI);
|
||||
|
||||
// If this is a nounwind + mayreturn invoke we only remove the unwind edge.
|
||||
// This is done by moving the invoke into a new and dead block and connecting
|
||||
// the normal destination of the invoke with a branch that follows the call
|
||||
// replacement we created above.
|
||||
if (MayReturn) {
|
||||
BasicBlock *NewDeadBB = SplitBlock(BB, II, nullptr, nullptr, nullptr, ".i2c");
|
||||
assert(isa<BranchInst>(BB->getTerminator()) &&
|
||||
BB->getTerminator()->getNumSuccessors() == 1 &&
|
||||
BB->getTerminator()->getSuccessor(0) == NewDeadBB);
|
||||
new UnreachableInst(I->getContext(), NewDeadBB);
|
||||
BB->getTerminator()->setOperand(0, NormalDestBB);
|
||||
A.deleteAfterManifest(*II);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We do not need an invoke (II) but instead want a call followed
|
||||
// by an unreachable. However, we do not remove II as other
|
||||
// abstract attributes might have it cached as part of their
|
||||
|
@ -2370,10 +2391,6 @@ struct AAIsDeadFunction : public AAIsDead {
|
|||
// only reached from the current block of II and then not reached
|
||||
// at all when we insert the unreachable.
|
||||
SplitBlockPredecessors(NormalDestBB, {BB}, ".i2c");
|
||||
CallInst *CI = createCallMatchingInvoke(II);
|
||||
CI->insertBefore(II);
|
||||
CI->takeName(II);
|
||||
II->replaceAllUsesWith(CI);
|
||||
SplitPos = CI->getNextNode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s
|
||||
; UTC_ARGS: --turn off
|
||||
|
||||
|
@ -8,6 +8,8 @@ declare void @normal_call() readnone
|
|||
|
||||
declare i32 @foo()
|
||||
|
||||
declare i32 @foo_nounwind() nounwind
|
||||
|
||||
declare i32 @foo_noreturn_nounwind() noreturn nounwind
|
||||
|
||||
declare i32 @foo_noreturn() noreturn
|
||||
|
@ -162,7 +164,7 @@ cond.end: ; preds = %cond.false, %cond.t
|
|||
ret i32 %cond
|
||||
}
|
||||
|
||||
; TEST 5 noreturn invoke instruction with a unreachable normal successor block.
|
||||
; TEST 5.1 noreturn invoke instruction with a unreachable normal successor block.
|
||||
|
||||
; CHECK: define i32 @invoke_noreturn(i32 %a)
|
||||
define i32 @invoke_noreturn(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
|
@ -197,7 +199,7 @@ cleanup:
|
|||
ret i32 0
|
||||
}
|
||||
|
||||
; TEST 4.1 noreturn invoke instruction replaced by a call and an unreachable instruction
|
||||
; TEST 5.2 noreturn invoke instruction replaced by a call and an unreachable instruction
|
||||
; put after it.
|
||||
|
||||
; CHECK: define i32 @invoke_noreturn_nounwind(i32 %a)
|
||||
|
@ -234,6 +236,45 @@ cleanup:
|
|||
ret i32 0
|
||||
}
|
||||
|
||||
; TEST 5.3 unounwind invoke instruction replaced by a call and a branch instruction put after it.
|
||||
define i32 @invoke_nounwind(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@invoke_nounwind
|
||||
; CHECK: cond.true:
|
||||
; CHECK-NEXT: call void @normal_call()
|
||||
; CHECK-NEXT: [[CALL:%.*]] = call i32 @foo_nounwind()
|
||||
; CHECK-NEXT: br label [[CONTINUE:%.*]]
|
||||
; CHECK: continue:
|
||||
; CHECK-NEXT: br label [[COND_END:%.*]]
|
||||
;
|
||||
entry:
|
||||
%cmp = icmp eq i32 %a, 0
|
||||
br i1 %cmp, label %cond.true, label %cond.false
|
||||
|
||||
cond.true: ; preds = %entry
|
||||
call void @normal_call()
|
||||
%call = invoke i32 @foo_nounwind() to label %continue
|
||||
unwind label %cleanup
|
||||
|
||||
cond.false: ; preds = %entry
|
||||
call void @normal_call()
|
||||
%call1 = call i32 @bar()
|
||||
br label %cond.end
|
||||
|
||||
cond.end: ; preds = %cond.false, %continue
|
||||
%cond = phi i32 [ %call, %continue ], [ %call1, %cond.false ]
|
||||
ret i32 %cond
|
||||
|
||||
continue:
|
||||
br label %cond.end
|
||||
|
||||
cleanup:
|
||||
%res = landingpad { i8*, i32 }
|
||||
catch i8* null
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
; UTC_ARGS: --turn off
|
||||
|
||||
; TEST 6: Undefined behvior, taken from LangRef.
|
||||
; FIXME: Should be able to detect undefined behavior.
|
||||
|
||||
|
|
Loading…
Reference in New Issue