Index: lib/Transforms/Utils/SimplifyCFG.cpp =================================================================== --- lib/Transforms/Utils/SimplifyCFG.cpp +++ lib/Transforms/Utils/SimplifyCFG.cpp @@ -124,6 +124,7 @@ bool SimplifyReturn(ReturnInst *RI, IRBuilder<> &Builder); bool SimplifyResume(ResumeInst *RI, IRBuilder<> &Builder); + bool SimplifyCleanupReturn(CleanupReturnInst *RI); bool SimplifyUnreachable(UnreachableInst *UI); bool SimplifySwitch(SwitchInst *SI, IRBuilder<> &Builder); bool SimplifyIndirectBr(IndirectBrInst *IBI); @@ -2899,6 +2900,31 @@ return true; } +// FIXME: This seems like a pretty common thing to want to do. Consider +// whether there is a more accessible place to put this. +static void convertInvokeToCall(InvokeInst *II) { + SmallVector Args(II->op_begin(), II->op_end() - 3); + // Insert a call instruction before the invoke. + CallInst *Call = CallInst::Create(II->getCalledValue(), Args, "", II); + Call->takeName(II); + Call->setCallingConv(II->getCallingConv()); + Call->setAttributes(II->getAttributes()); + Call->setDebugLoc(II->getDebugLoc()); + + // Anything that used the value produced by the invoke instruction now uses + // the value produced by the call instruction. Note that we do this even + // for void functions and calls with no uses so that the callgraph edge is + // updated. + II->replaceAllUsesWith(Call); + II->getUnwindDest()->removePredecessor(II->getParent()); + + // Insert a branch to the normal destination right before the invoke. + BranchInst::Create(II->getNormalDest(), II); + + // Finally, delete the invoke instruction! + II->eraseFromParent(); +} + bool SimplifyCFGOpt::SimplifyResume(ResumeInst *RI, IRBuilder<> &Builder) { // If this is a trivial landing pad that just continues unwinding the caught // exception then zap the landing pad, turning its invokes into calls. @@ -2918,29 +2944,104 @@ // Turn all invokes that unwind here into calls and delete the basic block. for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE;) { InvokeInst *II = cast((*PI++)->getTerminator()); - SmallVector Args(II->op_begin(), II->op_end() - 3); - // Insert a call instruction before the invoke. - CallInst *Call = CallInst::Create(II->getCalledValue(), Args, "", II); - Call->takeName(II); - Call->setCallingConv(II->getCallingConv()); - Call->setAttributes(II->getAttributes()); - Call->setDebugLoc(II->getDebugLoc()); + convertInvokeToCall(II); + } - // Anything that used the value produced by the invoke instruction now uses - // the value produced by the call instruction. Note that we do this even - // for void functions and calls with no uses so that the callgraph edge is - // updated. - II->replaceAllUsesWith(Call); - BB->removePredecessor(II->getParent()); + // The landingpad is now unreachable. Zap it. + BB->eraseFromParent(); + return true; +} - // Insert a branch to the normal destination right before the invoke. - BranchInst::Create(II->getNormalDest(), II); +bool SimplifyCFGOpt::SimplifyCleanupReturn(CleanupReturnInst *RI) { + // If this is a trivial cleanup pad that executes no instructions, it can be + // eliminated. If the cleanup pad continues to the caller, any predecessor + // that is an EH pad will be updated to continue to the caller and any + // predecessor that terminates with an invoke instruction will have its invoke + // instruction converted to a call instruction. If the cleanup pad being + // simplified does not continue to the caller, each predecessor will be + // updated to continue to the unwind destination of the cleanup pad being + // simplified. + BasicBlock *BB = RI->getParent(); + Instruction *CPInst = cast(BB->getFirstNonPHI()); - // Finally, delete the invoke instruction! - II->eraseFromParent(); + // Check that there are no other instructions except for debug intrinsics. + BasicBlock::iterator I = CPInst, E = RI; + while (++I != E) + if (!isa(I)) + return false; + + // If the cleanup return we are simplifying unwinds to the caller, this + // will set UnwindDest to nullptr. + BasicBlock *UnwindDest = RI->getUnwindDest(); + + for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB); PI != PE;) { + // The iterator must be updated here because we are removing this pred. + BasicBlock *PredBB = *PI++; + TerminatorInst *TI = PredBB->getTerminator(); + if (UnwindDest == nullptr) { + if (auto *II = dyn_cast(TI)) { + // The cleanup return being simplified continues to the caller and this + // predecessor terminated with an invoke instruction. Convert the + // invoke to a call. + // This call updates the predecessor/successor chain. + convertInvokeToCall(II); + } else { + // In the remaining cases the predecessor's terminator unwinds to the + // block we are removing. We need to create a new instruction that + // unwinds to the caller. Simply setting the unwind destination to + // nullptr would leave the objects internal data in an inconsistent + // state. + // FIXME: Consider whether it is better to update setUnwindDest to + // keep things consistent. + if (auto *CRI = dyn_cast(TI)) { + auto *NewCRI = CleanupReturnInst::Create(CRI->getCleanupPad(), + nullptr, CRI); + NewCRI->takeName(CRI); + NewCRI->setDebugLoc(CRI->getDebugLoc()); + CRI->eraseFromParent(); + } else if (auto *CEP = dyn_cast(TI)) { + auto *NewCEP = CatchEndPadInst::Create(CEP->getContext(), nullptr, + CEP); + NewCEP->takeName(CEP); + NewCEP->setDebugLoc(CEP->getDebugLoc()); + CEP->eraseFromParent(); + } else if (auto *TPI = dyn_cast(TI)) { + SmallVector TerminatePadArgs; + for (Value *Operand : TPI->arg_operands()) + TerminatePadArgs.push_back(Operand); + auto *NewTPI = TerminatePadInst::Create(TPI->getContext(), nullptr, + TerminatePadArgs, TPI); + NewTPI->takeName(TPI); + NewTPI->setDebugLoc(TPI->getDebugLoc()); + TPI->eraseFromParent(); + } else if (auto *CPI = dyn_cast(TI)) { + llvm_unreachable("A catchpad may not unwind to a cleanuppad."); + } else { + llvm_unreachable("Unexpected predecessor to cleanup pad."); + } + } + } else { + // If the predecessor did not terminate with an invoke instruction, it + // must be some variety of EH pad. + UnwindDest->removePredecessor(BB); + TerminatorInst *TI = PredBB->getTerminator(); + // FIXME: Introducing an EH terminator base class would simplify this. + if (auto *II = dyn_cast(TI)) + II->setUnwindDest(UnwindDest); + else if (auto *CRI = dyn_cast(TI)) + CRI->setUnwindDest(UnwindDest); + else if (auto *CEP = dyn_cast(TI)) + CEP->setUnwindDest(UnwindDest); + else if (auto *TPI = dyn_cast(TI)) + TPI->setUnwindDest(UnwindDest); + else if (auto *CPI = dyn_cast(TI)) + llvm_unreachable("A catchpad may not unwind to a cleanuppad."); + else + llvm_unreachable("Unexpected predecessor to cleanup pad."); + } } - // The landingpad is now unreachable. Zap it. + // The cleanup pad is now unreachable. Zap it. BB->eraseFromParent(); return true; } @@ -4662,6 +4763,9 @@ if (SimplifyReturn(RI, Builder)) return true; } else if (ResumeInst *RI = dyn_cast(BB->getTerminator())) { if (SimplifyResume(RI, Builder)) return true; + } else if (CleanupReturnInst *RI = + dyn_cast(BB->getTerminator())) { + if (SimplifyCleanupReturn(RI)) return true; } else if (SwitchInst *SI = dyn_cast(BB->getTerminator())) { if (SimplifySwitch(SI, Builder)) return true; } else if (UnreachableInst *UI = Index: test/Transforms/SimplifyCFG/empty-cleanuppad.ll =================================================================== --- test/Transforms/SimplifyCFG/empty-cleanuppad.ll +++ test/Transforms/SimplifyCFG/empty-cleanuppad.ll @@ -0,0 +1,293 @@ +; RUN: opt < %s -simplifycfg -S | filecheck %s + +; ModuleID = 'cppeh-simplify.cpp' +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc18.0.0" + + +; This case arises when two objects with empty destructors are cleaned up. +; +; void f1() { +; S a; +; S b; +; g(); +; } +; +; In this case, both cleanup pads can be eliminated and the invoke can be +; converted to a call. +; +; CHECK: define void @f1() +; CHECK: entry: +; CHECK: call void @g() +; CHECK: ret void +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f1() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + ret void + +ehcleanup: ; preds = %entry + %0 = cleanuppad [] + cleanupret %0 unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %ehcleanup + %1 = cleanuppad [] + cleanupret %1 unwind to caller +} + + +; This case arises when an object with an empty destructor must be cleaned up +; outside of a try-block and an object with a non-empty destructor must be +; cleaned up within the try-block. +; +; void f2() { +; S a; +; try { +; S2 b; +; g(); +; } catch (...) {} +; } +; +; In this case, the outermost cleanup pad can be eliminated and the catch block +; should unwind to the caller (that is, exception handling continues with the +; parent frame of the caller). +; +; CHECK: define void @f2() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: ehcleanup: +; CHECK: cleanuppad +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) +; CHECK: cleanupret %0 unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchpad +; CHECK: catch: +; CHECK: catchret +; CHECK-NOT: cleanuppad +; CHECK: catchendblock: ; preds = %catch.dispatch +; CHECK: catchendpad unwind to caller +; CHECK: } +; +define void @f2() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %b = alloca %struct.S2, align 1 + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + br label %try.cont + +ehcleanup: ; preds = %entry + %0 = cleanuppad [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %b) + cleanupret %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %1 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %1 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %try.cont + +try.cont: ; preds = %catchret.dest, %invoke.cont + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %catchendblock + %2 = cleanuppad [] + cleanupret %2 unwind to caller +} + + +; This case arises when an object with a non-empty destructor must be cleaned up +; outside of a try-block and an object with an empty destructor must be cleaned +; within the try-block. +; +; void f3() { +; S2 a; +; try { +; S b; +; g(); +; } catch (...) {} +; } +; +; In this case the inner cleanup pad should be eliminated and the invoke of g() +; should unwind directly to the catchpad. +; +; CHECK: define void @f3() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock +; CHECK: catch: +; CHECK: catchret +; CHECK: catchendblock: +; CHECK: catchendpad unwind label %ehcleanup.1 +; CHECK: ehcleanup.1: +; CHECK: cleanuppad +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) +; CHECK: cleanupret %1 unwind to caller +; CHECK: } +; +define void @f3() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %a = alloca %struct.S2, align 1 + invoke void @g() to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + br label %try.cont + +ehcleanup: ; preds = %entry + %0 = cleanuppad [] + cleanupret %0 unwind label %catch.dispatch + +catch.dispatch: ; preds = %ehcleanup + %1 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %1 to label %catchret.dest + +catchret.dest: ; preds = %catch + br label %try.cont + +try.cont: ; preds = %catchret.dest, %invoke.cont + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %catchendblock + %2 = cleanuppad [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) + cleanupret %2 unwind to caller +} + + +; This case arises when an object with an empty destructor may require cleanup +; from either inside or outside of a try-block. +; +; void f4() { +; S a; +; g(); +; try { +; g(); +; } catch (...) {} +; } +; +; In this case, the cleanuppad should be eliminated, the invoke outside of the +; call block should be converted to a call and the catchendpad should unwind +; to the caller (that is, that is, exception handling continues with the parent +; frame of the caller).) +; +; CHECK: define void @f4() +; CHECK: entry: +; CHECK: call void @g +; Note: The cleanuppad simplification will insert an unconditional branch here +; but it will be eliminated, placing the following invoke in the entry BB. +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %catch.dispatch +; CHECK: catch.dispatch: +; CHECK: catchpad +; CHECK: catch: +; CHECK: catchret +; CHECK: catchendblock: +; CHECK: catchendpad unwind to caller +; CHECK-NOT: cleanuppad +; CHECK: } +; +define void @f4() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + invoke void @g() + to label %invoke.cont unwind label %ehcleanup + +invoke.cont: ; preds = %entry + invoke void @g() + to label %try.cont unwind label %catch.dispatch + +catch.dispatch: ; preds = %invoke.cont + %0 = catchpad [i8* null, i8* null] to label %catch unwind label %catchendblock + +catch: ; preds = %catch.dispatch + catchret %0 to label %try.cont + +try.cont: ; preds = %catch, %invoke.cont + ret void + +catchendblock: ; preds = %catch.dispatch + catchendpad unwind label %ehcleanup + +ehcleanup: ; preds = %catchendblock, %entry + %1 = cleanuppad [] + cleanupret %1 unwind to caller +} + +; This tests the case where a terminatepad unwinds to a cleanuppad. +; I'm not sure how this case would arise, but it seems to be syntactically +; legal so I'm testing it. +; +; CHECK: define void @f5() +; CHECK: entry: +; CHECK: invoke void @g() +; CHECK: to label %try.cont unwind label %terminate +; CHECK: terminate: +; CHECK: terminatepad [i7 4] unwind to caller +; CHECK-NOT: cleanuppad +; CHECK: try.cont: +; CHECK: invoke void @g() +; CHECK: to label %try.cont.1 unwind label %terminate.1 +; CHECK: terminate.1: +; CHECK: terminatepad [i7 4] unwind label %ehcleanup.2 +; CHECK-NOT: ehcleanup.1: +; CHECK: ehcleanup.2: +; CHECK: [[TMP:\%.+]] = cleanuppad +; CHECK: call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) +; CHECK: cleanupret [[TMP]] unwind to caller +; CHECK: } +define void @f5() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { +entry: + %a = alloca %struct.S2, align 1 + invoke void @g() + to label %try.cont unwind label %terminate + +terminate: ; preds = %entry + terminatepad [i7 4] unwind label %ehcleanup + +ehcleanup: ; preds = %terminate + %0 = cleanuppad [] + cleanupret %0 unwind to caller + +try.cont: ; preds = %entry + invoke void @g() + to label %try.cont.1 unwind label %terminate.1 + +terminate.1: ; preds = %try.cont + terminatepad [i7 4] unwind label %ehcleanup.1 + +ehcleanup.1: ; preds = %terminate.1 + %1 = cleanuppad [] + cleanupret %1 unwind label %ehcleanup.2 + +ehcleanup.2: ; preds = %ehcleanup.1 + %2 = cleanuppad [] + call void @"\01??1S2@@QEAA@XZ"(%struct.S2* %a) + cleanupret %2 unwind to caller + +try.cont.1: ; preds = %try.cont + ret void +} + +%struct.S = type { i8 } +%struct.S2 = type { i8 } +declare void @"\01??1S2@@QEAA@XZ"(%struct.S2*) +declare void @g() + +declare i32 @__CxxFrameHandler3(...) +