Index: lib/CodeGen/BranchFolding.cpp =================================================================== --- lib/CodeGen/BranchFolding.cpp +++ lib/CodeGen/BranchFolding.cpp @@ -1561,9 +1561,31 @@ if (FallThrough != MF.end() && !TII->AnalyzeBranch(PrevBB, PrevTBB, PrevFBB, PrevCond, true) && PrevBB.isSuccessor(&*FallThrough)) { - MBB->moveAfter(&MF.back()); - MadeChange = true; - return MadeChange; + MachineFunction::iterator InsertAfter = MF.back().getIterator(); + // Single-block funclets are likely to meet the conditions to move to + // the end, but if multiple funclets meet these criteria we need to + // take care to avoid endlessly shuffling them past each other. + if (MBB->isEHFuncletEntry()) { + // If this funclet has successors (either it is a catch that returns + // to a block in another funclet or it is a cleanup that unwinds + // to another funclet), we would like to place it before other + // funclets that do not have a successor. So here, if the block we + // are placing has no successors, we skip past any other funclet entry + // blocks that have no successors but stop if we find a funclet entry + // block with successors, but if the block we are placing does have + // successors, we skip over all other funclet entry blocks that are + // already at the end. + while (MBB != InsertAfter && InsertAfter->isEHFuncletEntry() && + (InsertAfter->succ_empty() || !MBB->succ_empty())) + --InsertAfter; + } + // The logic above may have determined that the block is already where + // we want it. + if (MBB != InsertAfter) { + MBB->moveAfter(&*InsertAfter); + MadeChange = true; + return MadeChange; + } } } } Index: test/Transforms/BranchFolding/single-block-funclets.ll =================================================================== --- test/Transforms/BranchFolding/single-block-funclets.ll +++ test/Transforms/BranchFolding/single-block-funclets.ll @@ -0,0 +1,120 @@ +; RUN: llc -mtriple=x86_64-pc-windows-msvc -O2 < %s | FileCheck %s + +declare i32 @__CxxFrameHandler3(...) + +declare void @throw() +declare i16 @f() + +define i16 @test1(i16 %a, i8* %b) personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %cmp = icmp eq i16 %a, 10 + br i1 %cmp, label %if.then, label %if.else + +if.then: + %call1 = invoke i16 @f() + to label %cleanup unwind label %catch.dispatch + +if.else: + %call2 = invoke i16 @f() + to label %cleanup unwind label %catch.dispatch + +catch.dispatch: + catchpad [i8* null, i32 8, i8* null] + to label %catch unwind label %catch.dispatch.2 + +catch: + invoke void @throw() noreturn + to label %unreachable unwind label %catchendblock + +catch.dispatch.2: + catchpad [i8* null, i32 64, i8* null] + to label %catch.2 unwind label %catchendblock + +catch.2: + store i8 1, i8* %b + invoke void @throw() noreturn + to label %unreachable unwind label %catchendblock + +catchendblock: + catchendpad unwind to caller + +cleanup: + %retval = phi i16 [ %call1, %if.then ], [ %call2, %if.else ] + ret i16 %retval + +unreachable: + unreachable +} + +; This test verifies the case where two funclet blocks meet the criteria +; to be placed at the end and niether has a successor. +; +; CHECK-LABEL: .def test1; +; CHECK: "?catch$4@?0?test1@4HA" +; CHECK: .LBB0_4: # %catch.dispatch +; CHECK: "?catch$5@?0?test1@4HA": +; CHECK: .LBB0_5: # %catch.dispatch.2 + +define i16 @test2(i16 %a, i8* %b) personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %cmp = icmp eq i16 %a, 10 + br i1 %cmp, label %if.then, label %if.else + +if.then: + %call1 = invoke i16 @f() + to label %cleanup unwind label %catch.dispatch + +if.else: + %call2 = invoke i16 @f() + to label %cleanup unwind label %catch.dispatch + +catch.dispatch: + catchpad [i8* null, i32 8, i8* null] + to label %catch unwind label %catch.dispatch.2 + +catch: + invoke void @throw() noreturn + to label %unreachable unwind label %catchendblock + +catch.dispatch.2: + %c2 = catchpad [i8* null, i32 32, i8* null] + to label %catch.2 unwind label %catch.dispatch.3 + +catch.2: + store i8 1, i8* %b + catchret %c2 to label %cleanup + +catch.dispatch.3: + %c3 = catchpad [i8* null, i32 64, i8* null] + to label %catch.3 unwind label %catchendblock + +catch.3: + store i8 2, i8* %b + catchret %c3 to label %cleanup + +catchendblock: + catchendpad unwind to caller + +cleanup: + %retval = phi i16 [ %call1, %if.then ], [ %call2, %if.else ], [ -1, %catch.2 ], [ -1, %catch.3 ] + ret i16 %retval + +unreachable: + unreachable +} + +; This test verifies the case where three funclet blocks all meet the criteria +; to be placed at the end but two have successors (CATCHRET targets) and one +; does not. The selection criteria is designed to place funclets with no +; successors after funclets that have successors. +; +; CHECK-LABEL: .def test2; +; CHECK: "?catch$4@?0?test2@4HA" +; CHECK: .LBB1_4: # %catch.dispatch.2 +; CHECK: retq # CATCHRET +; CHECK: "?catch$5@?0?test2@4HA": +; CHECK: .LBB1_5: # %catch.dispatch.3 +; CHECK: retq # CATCHRET +; CHECK: "?catch$6@?0?test2@4HA": +; CHECK: .LBB1_6: # %catch.dispatch +; CHECK: callq throw