Index: include/llvm/CodeGen/Passes.h =================================================================== --- include/llvm/CodeGen/Passes.h +++ include/llvm/CodeGen/Passes.h @@ -571,6 +571,11 @@ /// FunctionPass *createSjLjEHPreparePass(const TargetMachine *TM); + /// createMSVCEHPreparePass - This pass adapts exception handling code to use + /// the MSVC-style (outlined) exception and unwind handlers. + /// + FunctionPass *createMSVCEHPreparePass(const TargetMachine *TM); + /// LocalStackSlotAllocation - This pass assigns local frame indices to stack /// slots relative to one another and allocates base registers to access them /// when it is estimated by the target to be out of range of normal frame Index: include/llvm/Transforms/Utils/Cloning.h =================================================================== --- include/llvm/Transforms/Utils/Cloning.h +++ include/llvm/Transforms/Utils/Cloning.h @@ -135,6 +135,28 @@ ValueMapTypeRemapper *TypeMapper = nullptr, ValueMaterializer *Materializer = nullptr); +// TODO: I don't really like this name. +class CloningStopHandler +{ +public: + CloningStopHandler() {} + virtual ~CloningStopHandler() {} + + virtual void createTerminateInst(BasicBlock *BB) = 0; + virtual bool shouldStopAt(const Instruction *Inst) = 0; +}; + +void CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc, + const Instruction *StartingInst, + ValueToValueMapTy &VMap, + bool ModuleLevelChanges, + SmallVectorImpl &Returns, + const char *NameSuffix = "", + ClonedCodeInfo *CodeInfo = nullptr, + const DataLayout *DL = nullptr, + CloningStopHandler *StopHandler = nullptr); + + /// CloneAndPruneFunctionInto - This works exactly like CloneFunctionInto, /// except that it does some simple constant prop and DCE on the fly. The /// effect of this is to copy significantly less code in cases where (for Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -71,6 +71,7 @@ MachineSink.cpp MachineTraceMetrics.cpp MachineVerifier.cpp + MSVCEHPrepare.cpp OcamlGC.cpp OptimizePHIs.cpp PHIElimination.cpp Index: lib/CodeGen/MSVCEHPrepare.cpp =================================================================== --- lib/CodeGen/MSVCEHPrepare.cpp +++ lib/CodeGen/MSVCEHPrepare.cpp @@ -0,0 +1,343 @@ +//===------ MSVCEHPrepare.cpp - Outline exception handling sequences ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This transformation is designed for use by code generators which use +// Windows exception handling targeted at the MSVC environment. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/ValueMap.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/Cloning.h" + +using namespace llvm; + +#define DEBUG_TYPE "msvcehprepare" + +namespace { +class MSVCEHPrepare : public FunctionPass { + const TargetMachine *TM; + +public: + static char ID; // Pass identification, replacement for typeid + explicit MSVCEHPrepare(const TargetMachine *TM) : FunctionPass(ID), TM(TM) {} + bool runOnFunction(Function &F) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override {} + const char *getPassName() const override { + return "MSVC Exception Handling preparation"; + } + +private: + bool prepareSEHHandlers(Function &F, BasicBlock *LandingPadBB); + + bool parseSEHLandingPad(BasicBlock::iterator &II, // in/out + LandingPadInst* &Lpad, // out + Value* &EHPtrs, // out + Value* &EHSel); // out + + bool parseSEHHandlerSelect(BasicBlock::iterator &II, // in/out + const Value* EHSel, // in + BasicBlock* &FilterHandler, // out + Function* &FilterFn); // out + + bool outlineSEHFilterHandler(Function *SrcFn, + Function *FilterFn, + BasicBlock *FilterHandler, + Value *EHPtrs); + + + bool prepareCPPEHHandlers(Function &F, BasicBlock *LandingPadBB); +}; + +class SEHFilterStopHandler : public CloningStopHandler +{ +public: + SEHFilterStopHandler() : FilterResult(nullptr) {} + virtual ~SEHFilterStopHandler() {} + + bool shouldStopAt(const Instruction *Inst) override; + void createTerminateInst(BasicBlock *BB) override; + +private: + Value *FilterResult; +}; + +} // end anonymous namespace + +char MSVCEHPrepare::ID = 0; + +// Public Interface To the MSVCEHPrepare pass. +FunctionPass *llvm::createMSVCEHPreparePass(const TargetMachine *TM) { + return new MSVCEHPrepare(TM); +} + +bool MSVCEHPrepare::runOnFunction(Function &F) { + bool MadeChange = false; + + for (Function::iterator BB : F) { + if (BB->isLandingPad()) { + BasicBlock::iterator II = BB->begin(); + LandingPadInst *lpad = BB->getLandingPadInst(); + + // The code below is not robust enough for actual use, + // but it is sufficient for getting things working. + Function *PersonalityFn = dyn_cast( + lpad->getPersonalityFn()->stripPointerCasts()); + if (!PersonalityFn) + continue; + + StringRef Personality = PersonalityFn->getName(); + if (Personality == "__C_specific_handler") { + MadeChange |= prepareSEHHandlers(F, BB); + } + else if (Personality == "__CxxFrameHandler3") { + MadeChange |= prepareCPPEHHandlers(F, BB); + } + else + { + // TODO: Add 32-bit handlers and other varieties as needed. + llvm_unreachable("unsupported MSVC EH personality function"); + } + } // if (BB->isLandingPad()) + } // for (BB : F) + + return MadeChange; +} + +bool MSVCEHPrepare::prepareSEHHandlers(Function &F, BasicBlock *LandingPadBB) +{ + BasicBlock::iterator II = LandingPadBB->begin(); + + // This parse function updates II to reference the next instruction + // beyond the standard SEH landing pad template. That instruction + // should be the beginning of the filter function dispatch for SEH. + LandingPadInst *Lpad; + Value *EHPtrs; + Value *EHSel; + if (!parseSEHLandingPad(II, Lpad, EHPtrs, EHSel)) { + DEBUG(dbgs() << "SEH initialization did not match!\n"); + return false; + } + + // The landing pad should be immediately followed by a + // sequence of blocks that correspond to the filter function + // for each catch clause. + // If parseEHSelect is successful, FilterHandler will point to the + // first basic block in the filter handler and II will be updated + // to point to either the first instruction of the next select + // sequence or (when the last filter is processed) the first + // instruction of the __finally handler. + BasicBlock *FilterHandler = nullptr; + Function *FilterFn = nullptr; + while (parseSEHHandlerSelect(II, EHSel, FilterHandler, FilterFn) && + FilterHandler) { + // The parse function matched a filter handler, so it should also + // have returned a pointer to the corresponding filter function. + assert(FilterFn && + "A filter handler was found but the filter function is null"); + // Move the body of the filter handler from the current function + // into the filter function. + if (!outlineSEHFilterHandler(&F, FilterFn, FilterHandler, EHPtrs)) { + DEBUG(dbgs() << "Error outling filter handler\n"); + return false; + } + } + + return true; +} + +bool MSVCEHPrepare::parseSEHLandingPad(BasicBlock::iterator &II, + LandingPadInst* &Lpad, + Value* &EHPtrs, + Value* &EHSel) +{ + Lpad = nullptr; + EHPtrs = nullptr; + EHSel = nullptr; + + // The pattern we're looking for is this: + // + // %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* + // @__C_specific_handler to i8*) + // cleanup + // catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*) + // catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*) + // %eh_ptrs = extractvalue { i8*, i32 } %vals, 0 + // %sel = extractvalue { i8*, i32 } %vals, 1 + Lpad = dyn_cast(II); + + // This check is redundant. The code that leads here already checked that the + // basic block we started in is a landing pad with the function named here. + // I'm leaving this for now because I'm starting with everything I could + // possibly check. I'll strip things back a bit once it all works. + if (!Lpad || + Lpad->getPersonalityFn()->stripPointerCasts()->getName() + != "__C_specific_handler") + return false; + ++II; + + // %eh_ptrs = extractvalue { i8*, i32 } %vals, 0 + // or + // %sel = extractvalue { i8*, i32 } %vals, 1 + ExtractValueInst *Extract = dyn_cast(II); + if (!Extract || + Extract->getAggregateOperand() != Lpad || + Extract->getNumIndices() != 1 || + !(*(Extract->idx_begin()) == 0 || + *(Extract->idx_begin()) == 1)) + return false; + ++II; + + if (*(Extract->idx_begin()) == 0) + EHPtrs = Extract; + else + EHSel = Extract; + + // %sel = extractvalue { i8*, i32 } %vals, 1 + // or + // %eh_ptrs = extractvalue { i8*, i32 } %vals, 0 + Extract = dyn_cast(II); + if (!Extract || + Extract->getAggregateOperand() != Lpad || + Extract->getNumIndices() != 1 || + !(*(Extract->idx_begin()) == 0 || + *(Extract->idx_begin()) == 1)) + return false; + ++II; + + if (*(Extract->idx_begin()) == 0) + EHPtrs = Extract; + else + EHSel = Extract; + + // We've matched two extract instructions that had an index of either 0 or 1. + // If we don't have two pointers here then both instructions must have been + // using the same index. + assert(EHPtrs && EHSel && + "Two extracts of the same value were found in an SEH landing pad"); + + return true; +} + +bool MSVCEHPrepare::parseSEHHandlerSelect(BasicBlock::iterator &II, + const Value *EHSel, + BasicBlock* &FilterHandler, + Function* &FilterFn) +{ + BasicBlock::iterator &InitialII = II; + FilterHandler = nullptr; + FilterFn = nullptr; + + // The pattern we're looking for is this: + // + // %0 = call i32 @llvm.eh.typeid.for() + // %1 = icmp eq i32 , %0 + // br i1 %1, label , label + // + IntrinsicInst *Call = dyn_cast(II); + if (!Call || Call->getIntrinsicID() != llvm::Intrinsic::eh_typeid_for) + return false; + ++II; + + // %1 = icmp eq i32 %sel, %0 + ICmpInst* Compare = dyn_cast(II); + if (!Compare || + Compare->getPredicate() != ICmpInst::ICMP_EQ || + !((Compare->getOperand(0) == EHSel && Compare->getOperand(1) == Call) || + (Compare->getOperand(1) == EHSel && Compare->getOperand(0) == Call))) { + II = InitialII; + return false; + } + ++II; + + // br i1 %1, label , label + BranchInst *Branch = dyn_cast(II); + if (!Branch || + !Branch->isConditional() || + Branch->getCondition() != Compare) { + II = InitialII; + return false; + } + + FilterFn = dyn_cast(Call->getArgOperand(0)->stripPointerCasts()); + FilterHandler = Branch->getSuccessor(0); + II = Branch->getSuccessor(1)->begin(); + + return true; +} + +bool MSVCEHPrepare::outlineSEHFilterHandler(Function *SrcFn, + Function *FilterFn, + BasicBlock *FilterHandler, + Value *EHPtrs) +{ + // TODO: Verify the signature of the filter function. + + // Initially the filter function is expected to contain a single + // "unreachable" instruction + assert(isa(FilterFn->front().getInstList().front()) && + "SEH filter stub function wasn't empty"); + + // Discard the contents of the filter function stub. + FilterFn->getBasicBlockList().clear(); + + // TODO: Insert a call to llvm.recoverframeallocation + + // Map the EHPtrs to the first argument to the filter function. + ValueToValueMapTy VMap; + VMap[EHPtrs] = FilterFn->arg_begin(); + + // TODO: Map other values referenced in the filter handler. + + SEHFilterStopHandler StopHandler; + + SmallVector Returns; + ClonedCodeInfo InlinedFunctionInfo; + + CloneAndPruneIntoFromInst(FilterFn, SrcFn, FilterHandler->begin(), + VMap, /*ModuleLevelChanges=*/false, Returns, ".seh", + &InlinedFunctionInfo, + SrcFn->getParent()->getDataLayout(), + &StopHandler); + return true; +} + +bool SEHFilterStopHandler::shouldStopAt(const Instruction *Inst) +{ + // TODO: Make this use an intrinsics ID. + const CallInst *Call = dyn_cast(Inst); + if (!Call || Call->getCalledFunction()->getName() != "llvm.eh.seh.filter") + return false; + + assert(!FilterResult && "An SEH filter handler contained multiple endpoints"); + + FilterResult = Call->getArgOperand(0); + + return true; +} + +void SEHFilterStopHandler::createTerminateInst(BasicBlock *BB) +{ + ReturnInst::Create(BB->getContext(), FilterResult, BB); +} + +bool MSVCEHPrepare::prepareCPPEHHandlers(Function &F, BasicBlock *LandingPadBB) +{ + // TODO: Implement C++ exception handler outlining. + llvm_unreachable("unsupported MSVC EH personality function"); + return false; +} Index: lib/CodeGen/Passes.cpp =================================================================== --- lib/CodeGen/Passes.cpp +++ lib/CodeGen/Passes.cpp @@ -432,6 +432,12 @@ case ExceptionHandling::ItaniumWinEH: addPass(createDwarfEHPass(TM)); break; +#if 0 // This depends on other in-flight patches. It's here for review. + case ExceptionHandling::MSVC + addPass(createMSVCEHPreparePass(TM)); + addPass(createDwarfEHPass(TM)); + break; +#endif case ExceptionHandling::None: addPass(createLowerInvokePass()); Index: lib/Transforms/Utils/CloneFunction.cpp =================================================================== --- lib/Transforms/Utils/CloneFunction.cpp +++ lib/Transforms/Utils/CloneFunction.cpp @@ -260,21 +260,26 @@ const char *NameSuffix; ClonedCodeInfo *CodeInfo; const DataLayout *DL; + CloningStopHandler *StopHandler; + public: PruningFunctionCloner(Function *newFunc, const Function *oldFunc, ValueToValueMapTy &valueMap, bool moduleLevelChanges, const char *nameSuffix, ClonedCodeInfo *codeInfo, - const DataLayout *DL) + const DataLayout *DL, + CloningStopHandler *stopHandler) : NewFunc(newFunc), OldFunc(oldFunc), VMap(valueMap), ModuleLevelChanges(moduleLevelChanges), - NameSuffix(nameSuffix), CodeInfo(codeInfo), DL(DL) { + NameSuffix(nameSuffix), CodeInfo(codeInfo), DL(DL), + StopHandler(stopHandler) { } /// CloneBlock - The specified block is found to be reachable, clone it and /// anything that it can reach. - void CloneBlock(const BasicBlock *BB, + void CloneBlock(const BasicBlock *BB, + BasicBlock::const_iterator StartingInst, std::vector &ToClone); }; } @@ -282,6 +287,7 @@ /// CloneBlock - The specified block is found to be reachable, clone it and /// anything that it can reach. void PruningFunctionCloner::CloneBlock(const BasicBlock *BB, + BasicBlock::const_iterator StartingInst, std::vector &ToClone){ WeakVH &BBEntry = VMap[BB]; @@ -307,14 +313,20 @@ const_cast(BB)); VMap[OldBBAddr] = BlockAddress::get(NewFunc, NewBB); } - bool hasCalls = false, hasDynamicAllocas = false, hasStaticAllocas = false; - + // Loop over all instructions, and copy them over, DCE'ing as we go. This // loop doesn't include the terminator. - for (BasicBlock::const_iterator II = BB->begin(), IE = --BB->end(); + for (BasicBlock::const_iterator II = StartingInst, IE = --BB->end(); II != IE; ++II) { + // If the stop handler says stop, we want to stop everything, not just + // break out of the loop (which would cause the terminator to be cloned). + if (StopHandler && StopHandler->shouldStopAt(II)) { + StopHandler->createTerminateInst(NewBB); + return; + } + Instruction *NewInst = II->clone(); // Eagerly remap operands to the newly cloned instruction, except for PHI @@ -409,39 +421,47 @@ } } -/// CloneAndPruneFunctionInto - This works exactly like CloneFunctionInto, -/// except that it does some simple constant prop and DCE on the fly. The -/// effect of this is to copy significantly less code in cases where (for -/// example) a function call with constant arguments is inlined, and those -/// constant arguments cause a significant amount of code in the callee to be -/// dead. Since this doesn't produce an exact copy of the input, it can't be -/// used for things like CloneFunction or CloneModule. -void llvm::CloneAndPruneFunctionInto(Function *NewFunc, const Function *OldFunc, +/// CloneAndPruneIntoFromInst - This works like CloneAndPruneFunctionInto, except +/// that it does not clone the entire function. Instead it starts at an +/// instruction provided by the caller and copies (and prunes) only the code +/// reachable from that instruction. +void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc, + const Instruction *StartingInst, ValueToValueMapTy &VMap, bool ModuleLevelChanges, SmallVectorImpl &Returns, const char *NameSuffix, ClonedCodeInfo *CodeInfo, const DataLayout *DL, - Instruction *TheCall) { + CloningStopHandler *StopHandler) { assert(NameSuffix && "NameSuffix cannot be null!"); - + #ifndef NDEBUG - for (Function::const_arg_iterator II = OldFunc->arg_begin(), - E = OldFunc->arg_end(); II != E; ++II) - assert(VMap.count(II) && "No mapping from source argument specified!"); + // If the cloning starts at the begining of the function, verify that + // the function arguments are mapped. + if (!StartingInst) + for (Function::const_arg_iterator II = OldFunc->arg_begin(), + E = OldFunc->arg_end(); II != E; ++II) + assert(VMap.count(II) && "No mapping from source argument specified!"); #endif PruningFunctionCloner PFC(NewFunc, OldFunc, VMap, ModuleLevelChanges, - NameSuffix, CodeInfo, DL); + NameSuffix, CodeInfo, DL, StopHandler); + const BasicBlock *StartingBB; + if (StartingInst) + StartingBB = StartingInst->getParent(); + else { + StartingBB = &OldFunc->getEntryBlock(); + StartingInst = StartingBB->begin(); + } // Clone the entry block, and anything recursively reachable from it. std::vector CloneWorklist; - CloneWorklist.push_back(&OldFunc->getEntryBlock()); + PFC.CloneBlock(StartingBB, StartingInst, CloneWorklist); while (!CloneWorklist.empty()) { const BasicBlock *BB = CloneWorklist.back(); CloneWorklist.pop_back(); - PFC.CloneBlock(BB, CloneWorklist); + PFC.CloneBlock(BB, BB->begin(), CloneWorklist); } // Loop over all of the basic blocks in the old function. If the block was @@ -569,7 +589,7 @@ // and zap unconditional fall-through branches. This happen all the time when // specializing code: code specialization turns conditional branches into // uncond branches, and this code folds them. - Function::iterator Begin = cast(VMap[&OldFunc->getEntryBlock()]); + Function::iterator Begin = cast(VMap[StartingBB]); Function::iterator I = Begin; while (I != NewFunc->end()) { // Check if this block has become dead during inlining or other @@ -620,9 +640,30 @@ // Make a final pass over the basic blocks from theh old function to gather // any return instructions which survived folding. We have to do this here // because we can iteratively remove and merge returns above. - for (Function::iterator I = cast(VMap[&OldFunc->getEntryBlock()]), + for (Function::iterator I = cast(VMap[StartingBB]), E = NewFunc->end(); I != E; ++I) if (ReturnInst *RI = dyn_cast(I->getTerminator())) Returns.push_back(RI); } + + +/// CloneAndPruneFunctionInto - This works exactly like CloneFunctionInto, +/// except that it does some simple constant prop and DCE on the fly. The +/// effect of this is to copy significantly less code in cases where (for +/// example) a function call with constant arguments is inlined, and those +/// constant arguments cause a significant amount of code in the callee to be +/// dead. Since this doesn't produce an exact copy of the input, it can't be +/// used for things like CloneFunction or CloneModule. +void llvm::CloneAndPruneFunctionInto(Function *NewFunc, const Function *OldFunc, + ValueToValueMapTy &VMap, + bool ModuleLevelChanges, + SmallVectorImpl &Returns, + const char *NameSuffix, + ClonedCodeInfo *CodeInfo, + const DataLayout *DL, + Instruction *TheCall) { + CloneAndPruneIntoFromInst(NewFunc, OldFunc, OldFunc->front().begin(), + VMap, ModuleLevelChanges, Returns, NameSuffix, + CodeInfo, DL, nullptr); +} \ No newline at end of file Index: test/CodeGen/X86/seh-outline.ll =================================================================== --- test/CodeGen/X86/seh-outline.ll +++ test/CodeGen/X86/seh-outline.ll @@ -0,0 +1,102 @@ +; RUN: llc -mtriple x86_64-pc-windows-msvc < %s + +; Review Comments: +; This test is only half baked. The IR is speculative and it won't compile +; without other patches that are in flight. My intention is that I'll +; use this to verify that the filter functions are properly outlined. +; So far I've just been dumping the outlined functions at runtime. + +define i32 @safe_div_filt0(i8* %eh_ptrs, i8* %rbp) { + unreachable ; Stub function, filled in by SEHPrepare +} +define i32 @safe_div_filt1(i8* %eh_ptrs, i8* %rbp) { + unreachable ; Stub function, filled in by SEHPrepare +} +declare void @cleanup() + +@str1=linkonce_odr unnamed_addr constant [3 x i8] c"One", align 1 +@str2=linkonce_odr unnamed_addr constant [3 x i8] c"Two", align 1 + +define i32 @safe_div(i32* %n, i32* %d) { +entry: + %r = alloca i32, align 4 + invoke void @try_body(i32* %r, i32* %n, i32* %d) + to label %__try.cont unwind label %lpad + +lpad: + %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*) + cleanup + catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*) + catch i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*) + %eh_ptrs = extractvalue { i8*, i32 } %vals, 0 + %sel = extractvalue { i8*, i32 } %vals, 1 + %filt0_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*)) + %is_filt0 = icmp eq i32 %sel, %filt0_val + br i1 %is_filt0, label %filter0, label %eh.dispatch1 + +eh.dispatch1: + %filt1_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*)) + %is_filt1 = icmp eq i32 %sel, %filt1_val + br i1 %is_filt1, label %filter1, label %__finally + +filter0: ; SEHPrepare traces out this basic block, after folding away preceding dispatch. + %eh_ptrs_c.0 = bitcast i8* %eh_ptrs to i32** + %eh_rec.0 = load i32** %eh_ptrs_c.0 + %eh_code.0 = load i32* %eh_rec.0 + ; EXCEPTION_ACCESS_VIOLATION = 0xC0000005 + %cmp.0 = icmp eq i32 %eh_code.0, 3221225477 + %filt0.res = zext i1 %cmp.0 to i32 + ; FILTER OUTLINING ENDS HERE + call void @llvm.eh.seh.filter(i32 %filt0.res) + br label %__finally + +filter1: + %eh_ptrs_c.1 = bitcast i8* %eh_ptrs to i32** + %eh_rec.1 = load i32** %eh_ptrs_c.1 + %eh_code.1 = load i32* %eh_rec.1 + ; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094 + %cmp.1 = icmp eq i32 %eh_code.1, 3221225620 + %filt1.res = zext i1 %cmp.1 to i32 + ; FILTER OUTLINING ENDS HERE + call void @llvm.eh.seh.filter(i32 %filt1.res) + br label %__finally + +__finally: + call void @cleanup() + ; Redo eh typeid dispatch. + %filt0_val.1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt0 to i8*)) + %is_filt0.1 = icmp eq i32 %sel, %filt0_val.1 + br i1 %is_filt0.1, label %handler0, label %eh.dispatch2 + +eh.dispatch2: + %filt1_val.1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @safe_div_filt1 to i8*)) + %is_filt1.1 = icmp eq i32 %sel, %filt1_val.1 + br i1 %is_filt1.1, label %handler1, label %eh.resume + + +handler0: + call void @puts(i8* getelementptr ([3 x i8]* @str1, i32 0, i32 0)) + store i32 -1, i32* %r, align 4 + br label %__try.cont + +handler1: + call void @puts(i8* getelementptr ([3 x i8]* @str2, i32 0, i32 0)) + store i32 -2, i32* %r, align 4 + br label %__try.cont + +eh.resume: + resume { i8*, i32 } %vals + +__try.cont: + %safe_ret = load i32* %r, align 4 + ret i32 %safe_ret +} + +declare i32 @__C_specific_handler(...) + +declare void @try_body(i32*, i32*, i32*) + +declare void @puts(i8*) + +declare void @llvm.eh.seh.filter(i32) +declare i32 @llvm.eh.typeid.for(i8*)