[llvm] c7ab19d - [Attributor][FIX] Transform invoke of nounwind to call + br %normal_dest
Johannes Doerfert via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 1 22:54:47 PDT 2019
Author: Johannes Doerfert
Date: 2019-11-02T00:54:00-05:00
New Revision: c7ab19dbb0f1f5c76ff70c7acab9f20c796cafb3
URL: https://github.com/llvm/llvm-project/commit/c7ab19dbb0f1f5c76ff70c7acab9f20c796cafb3
DIFF: https://github.com/llvm/llvm-project/commit/c7ab19dbb0f1f5c76ff70c7acab9f20c796cafb3.diff
LOG: [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.
Added:
Modified:
llvm/lib/Transforms/IPO/Attributor.cpp
llvm/test/Transforms/FunctionAttrs/liveness.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index fa0ea7e0ff14..448a2d4b6c1e 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -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();
}
}
diff --git a/llvm/test/Transforms/FunctionAttrs/liveness.ll b/llvm/test/Transforms/FunctionAttrs/liveness.ll
index e16677dc158f..eef674b53eca 100644
--- a/llvm/test/Transforms/FunctionAttrs/liveness.ll
+++ b/llvm/test/Transforms/FunctionAttrs/liveness.ll
@@ -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.
More information about the llvm-commits
mailing list