llvm.org GIT mirror llvm / be7879e
[WinEH] Let cleanups post-dominated by unreachable get executed Cleanups in C++ are a little weird. They are only guaranteed to be reliably executed if, and only if, there is a viable catch handler which can handle the exception. This means that reachability of a cleanup is lexically determined by it being nested with a try-block which unwinds to a catch. It is *cannot* be reasoned about by examining the control flow edges leaving a cleanup. Usually this is not a problem. It becomes a problem when there are *no* edges out of a cleanup because we believed that code post-dominated by the cleanup is dead. In LLVM's case, this code is what informs the personality routine about the presence of a suitable catch handler. However, the lack of edges to that catch handler makes the handler become unreachable which causes us to remove it. By removing the handler, the cleanup becomes unreachable. Instead, inject a catch-all handler with every cleanup that has no unwind edges. This will allow us to properly unwind the stack. This fixes PR25997. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@258580 91177308-0d34-0410-b5e6-96231b3b80d8 David Majnemer 4 years ago
5 changed file(s) with 188 addition(s) and 4 deletion(s). Raw diff Collapse all Expand all
8080 void cloneCommonBlocks(Function &F);
8181 void removeImplausibleInstructions(Function &F);
8282 void cleanupPreparedFunclets(Function &F);
83 void fixupNoReturnCleanupPads(Function &F);
8384 void verifyPreparedFunclets(Function &F);
8485
8586 // All fields are reset by runOnFunction.
655656
656657 void WinEHPrepare::colorFunclets(Function &F) {
657658 BlockColors = colorEHFunclets(F);
659 FuncletBlocks.clear();
658660
659661 // Invert the map from BB to colors to color to BBs.
660662 for (BasicBlock &BB : F) {
10001002 removeUnreachableBlocks(F);
10011003 }
10021004
1005 // Cleanuppads which are postdominated by unreachable will not unwind to any
1006 // catchswitches, making them dead. This is problematic if the original source
1007 // had a catch clause which could legitimately catch the exception, causing the
1008 // cleanup to run.
1009 //
1010 // This is only a problem for C++ where down-stream catches cause cleanups to
1011 // run.
1012 void WinEHPrepare::fixupNoReturnCleanupPads(Function &F) {
1013 // We only need to do this for C++ personality routines,
1014 // skip this work for all others.
1015 if (Personality != EHPersonality::MSVC_CXX)
1016 return;
1017
1018 // Do a quick sanity check on the color map before we throw it away so as to
1019 // avoid hiding latent bugs.
1020 DEBUG(verifyPreparedFunclets(F));
1021 // Re-color the funclets because cleanupPreparedFunclets might have
1022 // invalidated FuncletBlocks.
1023 colorFunclets(F);
1024
1025 // We create a unique catchswitch for each parent it will be nested within.
1026 SmallDenseMap NewCatchSwitches;
1027 // Create a new catchswitch+catchpad where the catchpad is post-dominated by
1028 // unreachable.
1029 auto GetOrCreateCatchSwitch = [&](Value *ParentPad) {
1030 auto &CatchSwitch = NewCatchSwitches[ParentPad];
1031 if (CatchSwitch)
1032 return CatchSwitch;
1033
1034 auto *ParentBB = isa(ParentPad)
1035 ? &F.getEntryBlock()
1036 : cast(ParentPad)->getParent();
1037
1038 StringRef NameSuffix = ParentBB->getName();
1039
1040 BasicBlock *CatchSwitchBB = BasicBlock::Create(
1041 F.getContext(), Twine("catchswitchbb.for.", NameSuffix));
1042 CatchSwitchBB->insertInto(&F, ParentBB->getNextNode());
1043 CatchSwitch = CatchSwitchInst::Create(ParentPad, /*UnwindDest=*/nullptr,
1044 /*NumHandlers=*/1,
1045 Twine("catchswitch.for.", NameSuffix),
1046 CatchSwitchBB);
1047
1048 BasicBlock *CatchPadBB = BasicBlock::Create(
1049 F.getContext(), Twine("catchpadbb.for.", NameSuffix));
1050 CatchPadBB->insertInto(&F, CatchSwitchBB->getNextNode());
1051 Value *CatchPadArgs[] = {
1052 Constant::getNullValue(Type::getInt8PtrTy(F.getContext())),
1053 ConstantInt::get(Type::getInt32Ty(F.getContext()), 64),
1054 Constant::getNullValue(Type::getInt8PtrTy(F.getContext())),
1055 };
1056 CatchPadInst::Create(CatchSwitch, CatchPadArgs,
1057 Twine("catchpad.for.", NameSuffix), CatchPadBB);
1058 new UnreachableInst(F.getContext(), CatchPadBB);
1059
1060 CatchSwitch->addHandler(CatchPadBB);
1061
1062 return CatchSwitch;
1063 };
1064
1065 // Look for all basic blocks which are within cleanups which are postdominated
1066 // by unreachable.
1067 for (auto &Funclets : FuncletBlocks) {
1068 BasicBlock *FuncletPadBB = Funclets.first;
1069 auto *CleanupPad = dyn_cast(FuncletPadBB->getFirstNonPHI());
1070 // Skip over any non-cleanup funclets.
1071 if (!CleanupPad)
1072 continue;
1073 // Skip over any cleanups have unwind targets, they do not need this.
1074 if (getCleanupRetUnwindDest(CleanupPad) != nullptr)
1075 continue;
1076 // Walk the blocks within the cleanup which end in 'unreachable'.
1077 // We will replace the unreachable instruction with a cleanupret;
1078 // this cleanupret will unwind to a catchswitch with a lone catch-all
1079 // catchpad.
1080 std::vector &BlocksInFunclet = Funclets.second;
1081 for (BasicBlock *BB : BlocksInFunclet) {
1082 auto *UI = dyn_cast(BB->getTerminator());
1083 if (!UI)
1084 continue;
1085 // Remove the unreachable instruction.
1086 UI->eraseFromParent();
1087
1088 // Add our new cleanupret.
1089 auto *ParentPad = CleanupPad->getParentPad();
1090 CatchSwitchInst *CatchSwitch = GetOrCreateCatchSwitch(ParentPad);
1091 CleanupReturnInst::Create(CleanupPad, CatchSwitch->getParent(), BB);
1092 }
1093 }
1094
1095 // Update BlockColors and FuncletBlocks to maintain WinEHPrepare's
1096 // invariants.
1097 for (auto CatchSwitchKV : NewCatchSwitches) {
1098 CatchSwitchInst *CatchSwitch = CatchSwitchKV.second;
1099 BasicBlock *CatchSwitchBB = CatchSwitch->getParent();
1100
1101 assert(CatchSwitch->getNumSuccessors() == 1);
1102 BasicBlock *CatchPadBB = CatchSwitch->getSuccessor(0);
1103 assert(isa(CatchPadBB->getFirstNonPHI()));
1104
1105 BlockColors.insert({CatchSwitchBB, ColorVector(CatchSwitchBB)});
1106 FuncletBlocks[CatchSwitchBB] = {CatchSwitchBB};
1107
1108 BlockColors.insert({CatchPadBB, ColorVector(CatchPadBB)});
1109 FuncletBlocks[CatchPadBB] = {CatchPadBB};
1110 }
1111 }
1112
10031113 void WinEHPrepare::verifyPreparedFunclets(Function &F) {
10041114 for (BasicBlock &BB : F) {
10051115 size_t NumColors = BlockColors[&BB].size();
10341144 DEBUG(verifyFunction(F));
10351145 cleanupPreparedFunclets(F);
10361146 }
1147
1148 fixupNoReturnCleanupPads(F);
10371149
10381150 DEBUG(verifyPreparedFunclets(F));
10391151 // Recolor the CFG to verify that all is well.
0 ; RUN: opt -S -winehprepare < %s | FileCheck %s
1 target triple = "x86_64-pc-windows-msvc"
2
3 ; CHECK-LABEL: @test1(
4 define void @test1(i1 %b) personality i32 (...)* @__CxxFrameHandler3 {
5 entry:
6 invoke void @f()
7 to label %try.cont unwind label %cleanup.bb
8
9 ; CHECK: entry:
10
11 ; CHECK: [[catchswitch_entry:.*]]:
12 ; CHECK-NEXT: %[[cs0:.*]] = catchswitch within none [label %[[catchpad:.*]]] unwind to caller
13 ; CHECK: [[catchpad]]:
14 ; CHECK-NEXT: %[[cp0:.*]] = catchpad within %[[cs0]] [i8* null, i32 64, i8* null]
15 ; CHECK-NEXT: unreachable
16
17 try.cont:
18 invoke void @f()
19 to label %exit unwind label %catchswitch.bb
20
21 cleanup.bb:
22 %cleanup = cleanuppad within none []
23 br i1 %b, label %left, label %right
24
25 left:
26 call void @exit(i32 0) [ "funclet"(token %cleanup) ]
27 unreachable
28
29 right:
30 call void @exit(i32 1) [ "funclet"(token %cleanup) ]
31 unreachable
32
33 catchswitch.bb:
34 %cs = catchswitch within none [label %catchpad.bb] unwind to caller
35
36 ; CHECK: catchpad.bb:
37 ; CHECK-NEXT: %catch = catchpad within %cs [i8* null, i32 64, i8* null]
38
39 ; CHECK: [[catchswitch_catch:.*]]:
40 ; CHECK-NEXT: %[[cs1:.*]] = catchswitch within %catch [label %[[catchpad_catch:.*]]] unwind to caller
41 ; CHECK: [[catchpad_catch]]:
42 ; CHECK-NEXT: %[[cp1:.*]] = catchpad within %[[cs1]] [i8* null, i32 64, i8* null]
43 ; CHECK-NEXT: unreachable
44
45 ; CHECK: nested.cleanup.bb:
46 ; CHECK-NEXT: %nested.cleanup = cleanuppad within %catch []
47 ; CHECK-NEXT: call void @exit(i32 2) [ "funclet"(token %nested.cleanup) ]
48 ; CHECK-NEXT: cleanupret from %nested.cleanup unwind label %[[catchswitch_catch]]
49
50 catchpad.bb:
51 %catch = catchpad within %cs [i8* null, i32 64, i8* null]
52 invoke void @f() [ "funclet"(token %catch) ]
53 to label %unreachable unwind label %nested.cleanup.bb
54
55 nested.cleanup.bb:
56 %nested.cleanup = cleanuppad within %catch []
57 call void @exit(i32 2) [ "funclet"(token %nested.cleanup) ]
58 unreachable
59
60 unreachable:
61 unreachable
62
63 exit:
64 unreachable
65 }
66
67 declare void @f()
68 declare void @exit(i32) nounwind noreturn
69
70 declare i32 @__CxxFrameHandler3(...)
4343 ; CHECK: call void @llvm.foo(i32 %x)
4444
4545
46 define void @test2() personality i32 (...)* @__CxxFrameHandler3 {
46 define void @test2() personality i32 (...)* @__C_specific_handler {
4747 entry:
4848 invoke void @f()
4949 to label %exit unwind label %cleanup
7070 ; CHECK-NEXT: ret void
7171
7272
73 define void @test3() personality i32 (...)* @__CxxFrameHandler3 {
73 define void @test3() personality i32 (...)* @__C_specific_handler {
7474 entry:
7575 invoke void @f()
7676 to label %invoke.cont unwind label %catch.switch
0 ; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -winehprepare < %s | FileCheck %s
11
22 declare i32 @__CxxFrameHandler3(...)
3 declare i32 @__C_specific_handler(...)
34
45 declare void @f()
56
326327 }
327328
328329 ; CHECK-LABEL: @test8(
329 define void @test8() personality i32 (...)* @__CxxFrameHandler3 { entry:
330 define void @test8() personality i32 (...)* @__C_specific_handler { entry:
330331 invoke void @f()
331332 to label %done unwind label %cleanup1
332333 invoke void @f()
None ; RUN: opt -mtriple=x86_x64-pc-windows-msvc -S -winehprepare -disable-demotion -disable-cleanups < %s | FileCheck %s
0 ; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -winehprepare -disable-demotion -disable-cleanups < %s | FileCheck %s
11
22 declare i32 @__CxxFrameHandler3(...)
33