///
/// \param BB Block whose terminator will be replaced. Its terminator must
/// have an unwind successor.
-void removeUnwindEdge(BasicBlock *BB, DomTreeUpdater *DTU = nullptr);
+/// \param WouldUnwindBeUB What is the behaviour should the call actually
+/// unwind? For example, if the original landing pad
+/// unconditionally ended with `unreachable`,
+/// then the unwind would be UB.
+void removeUnwindEdge(BasicBlock *BB, bool WouldUnwindBeUB = false,
+ DomTreeUpdater *DTU = nullptr);
/// Remove all blocks that can not be reached from the function's entry.
///
return Changed;
}
-void llvm::removeUnwindEdge(BasicBlock *BB, DomTreeUpdater *DTU) {
+void llvm::removeUnwindEdge(BasicBlock *BB, bool WouldUnwindBeUB,
+ DomTreeUpdater *DTU) {
Instruction *TI = BB->getTerminator();
if (auto *II = dyn_cast<InvokeInst>(TI)) {
- changeToCall(II, DTU);
+ CallInst *CI = changeToCall(II, DTU);
+ if (WouldUnwindBeUB && !CI->doesNotThrow())
+ CI->setDoesNotThrow();
return;
}
DTU->applyUpdates(Updates);
Updates.clear();
}
- removeUnwindEdge(TI->getParent(), DTU);
+ removeUnwindEdge(TI->getParent(), /*WouldUnwindBeUB=*/true, DTU);
Changed = true;
}
} else if (auto *CSI = dyn_cast<CatchSwitchInst>(TI)) {
define i32 @foo() personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: @foo(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @bar()
+; CHECK-NEXT: call void @bar() #[[ATTR0:[0-9]+]]
; CHECK-NEXT: ret i32 0
;
entry:
}
declare i32 @__gxx_personality_v0(i32, i64, ptr, ptr)
+;.
+; CHECK: attributes #[[ATTR0]] = { nounwind }
+;.
define void @test2() personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: @test2(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @test2()
+; CHECK-NEXT: call void @test2() #[[ATTR3:[0-9]+]]
; CHECK-NEXT: ret void
;
entry:
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }
; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
; CHECK: attributes #[[ATTR2:[0-9]+]] = { null_pointer_is_valid }
+; CHECK: attributes #[[ATTR3]] = { nounwind }
;.
define void @foo() personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: @foo(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @bar(i32 undef)
+; CHECK-NEXT: call void @bar(i32 undef) #[[ATTR0:[0-9]+]]
; CHECK-NEXT: ret void
;
entry:
}
declare i32 @__gxx_personality_v0(...)
+;.
+; CHECK: attributes #[[ATTR0]] = { nounwind }
+;.
define void @f(i1 %B) personality i1 undef {
; CHECK-LABEL: @f(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @g()
+; CHECK-NEXT: call void @g() #[[ATTR0:[0-9]+]]
; CHECK-NEXT: br label [[CONTINUE:%.*]]
; CHECK: continue:
-; CHECK-NEXT: call void @g()
+; CHECK-NEXT: call void @g() #[[ATTR0]]
; CHECK-NEXT: br label [[CONTINUE]]
;
entry:
}
declare void @g()
+;.
+; CHECK: attributes #[[ATTR0]] = { nounwind }
+;.
define void @f1() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f1(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @g()
+; CHECK-NEXT: call void @g() #[[ATTR1:[0-9]+]]
; CHECK-NEXT: ret void
;
entry:
define void @f4() personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f4(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @g()
+; CHECK-NEXT: call void @g() #[[ATTR1]]
; CHECK-NEXT: invoke void @g()
; CHECK-NEXT: to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK: catch.dispatch:
define void @f10(i32 %V) personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @f10(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @g()
+; CHECK-NEXT: call void @g() #[[ATTR1]]
; CHECK-NEXT: unreachable
;
entry:
declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+; CHECK: attributes #[[ATTR1]] = { nounwind }
;.
define void @test2() personality ptr @ProcessCLRException {
; CHECK-LABEL: @test2(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @f()
+; CHECK-NEXT: call void @f() #[[ATTR0:[0-9]+]]
; CHECK-NEXT: invoke void @f()
; CHECK-NEXT: to label [[VIA_CATCHSWITCH:%.*]] unwind label [[CLEANUP_INNER:%.*]]
; CHECK: cleanup.inner:
ret void
}
;.
-; CHECK: attributes #[[ATTR0:[0-9]+]] = { nounwind }
+; CHECK: attributes #[[ATTR0]] = { nounwind }
;.
; instructions to call instructions if the handler just rethrows the exception.
define i32 @test1() personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: @test1(
-; CHECK-NEXT: call void @bar(), !prof [[PROF0:![0-9]+]]
+; CHECK-NEXT: call void @bar() #[[ATTR0:[0-9]+]], !prof [[PROF0:![0-9]+]]
; CHECK-NEXT: ret i32 0
;
invoke void @bar( )
define i32 @test2() personality ptr @__gxx_personality_v0 {
; CHECK-LABEL: @test2(
-; CHECK-NEXT: call void @bar() [ "foo"(i32 100) ]
+; CHECK-NEXT: call void @bar() #[[ATTR0]] [ "foo"(i32 100) ]
; CHECK-NEXT: ret i32 0
;
invoke void @bar( ) [ "foo"(i32 100) ]
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[COND:%.*]], label [[BR1:%.*]], label [[BR2:%.*]]
; CHECK: br1:
-; CHECK-NEXT: [[CALL1:%.*]] = call i64 @dummy1()
+; CHECK-NEXT: [[CALL1:%.*]] = call i64 @dummy1() #[[ATTR0]]
; CHECK-NEXT: br label [[INVOKE_CONT:%.*]]
; CHECK: br2:
-; CHECK-NEXT: [[CALL2:%.*]] = call i64 @dummy2()
+; CHECK-NEXT: [[CALL2:%.*]] = call i64 @dummy2() #[[ATTR0]]
; CHECK-NEXT: br label [[INVOKE_CONT]]
; CHECK: invoke.cont:
; CHECK-NEXT: [[C:%.*]] = phi i64 [ [[CALL1]], [[BR1]] ], [ [[CALL2]], [[BR2]] ]
declare i32 @__gxx_personality_v0(...)
;.
+; CHECK: attributes #[[ATTR0]] = { nounwind }
+;.
; CHECK: [[PROF0]] = !{!"branch_weights", i32 371}
;.
; CHECK-NEXT: call void @escape(ptr [[I6]])
; CHECK-NEXT: br i1 [[C:%.*]], label [[V0:%.*]], label [[V1:%.*]]
; CHECK: v0:
-; CHECK-NEXT: call void @throwing_callee_foo()
+; CHECK-NEXT: call void @throwing_callee_foo() #[[ATTR1:[0-9]+]]
; CHECK-NEXT: unreachable
; CHECK: v1:
-; CHECK-NEXT: call void @throwing_callee_bar()
+; CHECK-NEXT: call void @throwing_callee_bar() #[[ATTR1]]
; CHECK-NEXT: unreachable
;
entry:
}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+; CHECK: attributes #[[ATTR1]] = { nounwind }
;.
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 1, ptr nonnull [[A]]) #[[ATTR1:[0-9]+]]
-; CHECK-NEXT: call void @bar()
+; CHECK-NEXT: call void @bar() #[[ATTR1]]
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 1, ptr nonnull [[A]]) #[[ATTR1]]
; CHECK-NEXT: ret void
;
define void @test1() personality ptr @Personality {
; CHECK-LABEL: @test1(
; CHECK-NEXT: entry:
-; CHECK-NEXT: call void @f()
+; CHECK-NEXT: call void @f() #[[ATTR0:[0-9]+]]
; CHECK-NEXT: ret void
;
entry:
; CHECK-NEXT: to label [[EXIT:%.*]] unwind label [[CLEANUP_PAD:%.*]]
; CHECK: cleanup.pad:
; CHECK-NEXT: [[CLEANUP:%.*]] = cleanuppad within none []
-; CHECK-NEXT: call void @f()
+; CHECK-NEXT: call void @f() #[[ATTR0]]
; CHECK-NEXT: unreachable
; CHECK: exit:
; CHECK-NEXT: ret void
exit:
ret void
}
+;.
+; CHECK: attributes #[[ATTR0]] = { nounwind }
+;.