[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:
Johannes Doerfert 2019-11-01 21:59:32 -05:00
parent 3cbe3314b4
commit c7ab19dbb0
2 changed files with 66 additions and 8 deletions

View File

@ -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();
}
}

View File

@ -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.