From f12c030f4879e2bf2caca21976f5de9ba8c48bdd Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 9 Jun 2015 21:42:19 +0000 Subject: [PATCH] [WinEH] Add 32-bit SEH state table emission prototype This gets all the handler info through to the asm printer and we can look at the .xdata tables now. I've convinced one small catch-all test case to work, but other than that, it would be a stretch to say this is functional. The state numbering algorithm avoids doing any scope reconstruction as we do for C++ to simplify the implementation. llvm-svn: 239433 --- llvm/include/llvm/IR/Intrinsics.td | 4 +- llvm/lib/CodeGen/AsmPrinter/WinException.cpp | 89 ++++++++++- llvm/lib/CodeGen/AsmPrinter/WinException.h | 7 + .../CodeGen/SelectionDAG/FunctionLoweringInfo.cpp | 17 ++- .../CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 1 + llvm/lib/CodeGen/WinEHPrepare.cpp | 22 ++- llvm/lib/Target/X86/X86ISelLowering.cpp | 17 +++ llvm/lib/Target/X86/X86WinEHState.cpp | 79 +++++++++- llvm/test/CodeGen/X86/seh-catch-all.ll | 55 +++++-- llvm/test/CodeGen/X86/seh-safe-div-win32.ll | 168 +++++++++++++++++++++ llvm/test/CodeGen/X86/win32-eh.ll | 16 ++ 11 files changed, 446 insertions(+), 29 deletions(-) create mode 100644 llvm/test/CodeGen/X86/seh-safe-div-win32.ll diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index beeffde..c4c5741 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -428,8 +428,8 @@ def int_eh_endcatch : Intrinsic<[], []>; // Represents the list of actions to take when an exception is thrown. def int_eh_actions : Intrinsic<[llvm_ptr_ty], [llvm_vararg_ty], []>; -def int_eh_exceptioncode : Intrinsic<[llvm_i32_ty], []>; -def int_eh_exceptioninfo : Intrinsic<[llvm_ptr_ty], []>; +def int_eh_exceptioncode : Intrinsic<[llvm_i32_ty], [], [IntrReadMem]>; +def int_eh_exceptioninfo : Intrinsic<[llvm_ptr_ty], [], [IntrReadMem]>; // __builtin_unwind_init is an undocumented GCC intrinsic that causes all // callee-saved registers to be saved and restored (regardless of whether they diff --git a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp index f166350..1143b04 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinException.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/WinException.cpp @@ -144,7 +144,7 @@ void WinException::endFunction(const MachineFunction *MF) { if (Per == EHPersonality::MSVC_Win64SEH) emitCSpecificHandlerTable(); else if (Per == EHPersonality::MSVC_X86SEH) - emitCSpecificHandlerTable(); // FIXME + emitExceptHandlerTable(MF); else if (Per == EHPersonality::MSVC_CXX) emitCXXFrameHandler3Table(MF); else @@ -541,3 +541,90 @@ void WinException::extendIP2StateTable(const MachineFunction *MF, } } } + +/// Emit the language-specific data that _except_handler3 and 4 expect. This is +/// functionally equivalent to the __C_specific_handler table, except it is +/// indexed by state number instead of IP. +void WinException::emitExceptHandlerTable(const MachineFunction *MF) { + auto &OS = *Asm->OutStreamer; + + // Emit the __ehtable label that we use for llvm.x86.seh.lsda. + const Function *F = MF->getFunction(); + StringRef FLinkageName = GlobalValue::getRealLinkageName(F->getName()); + MCSymbol *LSDALabel = Asm->OutContext.getOrCreateLSDASymbol(FLinkageName); + OS.EmitLabel(LSDALabel); + + const Function *Per = MMI->getPersonality(); + StringRef PerName = Per->getName(); + int BaseState = -1; + if (PerName == "_except_handler4") { + // The LSDA for _except_handler4 starts with this struct, followed by the + // scope table: + // + // struct EH4ScopeTable { + // int32_t GSCookieOffset; + // int32_t GSCookieXOROffset; + // int32_t EHCookieOffset; + // int32_t EHCookieXOROffset; + // ScopeTableEntry ScopeRecord[]; + // }; + // + // Only the EHCookieOffset field appears to vary, and it appears to be the + // offset from the final saved SP value to the retaddr. + OS.EmitIntValue(-2, 4); + OS.EmitIntValue(0, 4); + // FIXME: Calculate. + OS.EmitIntValue(9999, 4); + OS.EmitIntValue(0, 4); + BaseState = -2; + } + + // Build a list of pointers to LandingPadInfos and then sort by WinEHState. + const std::vector &PadInfos = MMI->getLandingPads(); + SmallVector LPads; + LPads.reserve((PadInfos.size())); + for (const LandingPadInfo &LPInfo : PadInfos) + LPads.push_back(&LPInfo); + std::sort(LPads.begin(), LPads.end(), + [](const LandingPadInfo *L, const LandingPadInfo *R) { + return L->WinEHState < R->WinEHState; + }); + + // For each action in each lpad, emit one of these: + // struct ScopeTableEntry { + // int32_t EnclosingLevel; + // int32_t (__cdecl *FilterOrFinally)(); + // void *HandlerLabel; + // }; + // + // The "outermost" action will use BaseState as its enclosing level. Each + // other action will refer to the previous state as its enclosing level. + int CurState = 0; + for (const LandingPadInfo *LPInfo : LPads) { + int EnclosingLevel = BaseState; + assert(CurState + LPInfo->SEHHandlers.size() - 1 == LPInfo->WinEHState && + "gaps in the SEH scope table"); + for (const SEHHandler &Handler : LPInfo->SEHHandlers) { + // Emit the filter or finally function pointer, if present. Otherwise, + // emit '1' to indicate a catch-all. + const MCExpr *FilterOrFinally; + if (const Function *F = Handler.FilterOrFinally) + FilterOrFinally = create32bitRef(Asm->getSymbol(F)); + else + FilterOrFinally = MCConstantExpr::create(1, Asm->OutContext); + + // Compute the recovery address, which is a block address or null. + const BlockAddress *BA = Handler.RecoverBA; + const MCExpr *RecoverBBOrNull = + create32bitRef(BA ? Asm->GetBlockAddressSymbol(BA) : nullptr); + + OS.EmitIntValue(EnclosingLevel, 4); + OS.EmitValue(FilterOrFinally, 4); + OS.EmitValue(RecoverBBOrNull, 4); + + // The next state unwinds to this state. + EnclosingLevel = CurState; + CurState++; + } + } +} diff --git a/llvm/lib/CodeGen/AsmPrinter/WinException.h b/llvm/lib/CodeGen/AsmPrinter/WinException.h index 478899b..4e276bc 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WinException.h +++ b/llvm/lib/CodeGen/AsmPrinter/WinException.h @@ -38,8 +38,15 @@ class WinException : public EHStreamer { void emitCSpecificHandlerTable(); + /// Emit the EH table data for 32-bit and 64-bit functions using + /// the __CxxFrameHandler3 personality. void emitCXXFrameHandler3Table(const MachineFunction *MF); + /// Emit the EH table data for _except_handler3 and _except_handler4 + /// personality functions. These are only used on 32-bit and do not use CFI + /// tables. + void emitExceptHandlerTable(const MachineFunction *MF); + void extendIP2StateTable(const MachineFunction *MF, const Function *ParentF, WinEHFuncInfo &FuncInfo); diff --git a/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp b/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp index f3d75cb..e611624 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp @@ -264,15 +264,22 @@ void FunctionLoweringInfo::set(const Function &fn, MachineFunction &mf, if (!isMSVCEHPersonality(Personality)) return; - if (Personality == EHPersonality::MSVC_Win64SEH) { + if (Personality == EHPersonality::MSVC_Win64SEH || + Personality == EHPersonality::MSVC_X86SEH) { addSEHHandlersForLPads(LPads); - } else if (Personality == EHPersonality::MSVC_CXX) { + } + + WinEHFuncInfo &EHInfo = MMI.getWinEHFuncInfo(&fn); + if (Personality == EHPersonality::MSVC_CXX) { const Function *WinEHParentFn = MMI.getWinEHParent(&fn); - WinEHFuncInfo &EHInfo = MMI.getWinEHFuncInfo(WinEHParentFn); calculateWinCXXEHStateNumbers(WinEHParentFn, EHInfo); + } - // Copy the state numbers to LandingPadInfo for the current function, which - // could be a handler or the parent. + // Copy the state numbers to LandingPadInfo for the current function, which + // could be a handler or the parent. This should happen for 32-bit SEH and + // C++ EH. + if (Personality == EHPersonality::MSVC_CXX || + Personality == EHPersonality::MSVC_X86SEH) { for (const LandingPadInst *LP : LPads) { MachineBasicBlock *LPadMBB = MBBMap[LP->getParent()]; MMI.addWinEHState(LPadMBB, EHInfo.LandingPadStateMap[LP]); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 8ba957d..4035820 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4983,6 +4983,7 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { assert(Reg && "cannot get exception code on this platform"); MVT PtrVT = TLI.getPointerTy(); const TargetRegisterClass *PtrRC = TLI.getRegClassFor(PtrVT); + assert(FuncInfo.MBB->isLandingPad() && "eh.exceptioncode in non-lpad"); unsigned VReg = FuncInfo.MBB->addLiveIn(Reg, PtrRC); SDValue N = DAG.getCopyFromReg(DAG.getEntryNode(), getCurSDLoc(), VReg, PtrVT); diff --git a/llvm/lib/CodeGen/WinEHPrepare.cpp b/llvm/lib/CodeGen/WinEHPrepare.cpp index c2b3d84..6bdc9c9 100644 --- a/llvm/lib/CodeGen/WinEHPrepare.cpp +++ b/llvm/lib/CodeGen/WinEHPrepare.cpp @@ -24,6 +24,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Analysis/LibCallSemantics.h" +#include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/CodeGen/WinEHFuncInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" @@ -124,6 +125,7 @@ private: // All fields are reset by runOnFunction. DominatorTree *DT = nullptr; + const TargetLibraryInfo *LibInfo = nullptr; EHPersonality Personality = EHPersonality::Unknown; CatchHandlerMapTy CatchHandlerMap; CleanupHandlerMapTy CleanupHandlerMap; @@ -384,6 +386,7 @@ bool WinEHPrepare::runOnFunction(Function &Fn) { return false; DT = &getAnalysis().getDomTree(); + LibInfo = &getAnalysis().getTLI(); // If there were any landing pads, prepareExceptionHandlers will make changes. prepareExceptionHandlers(Fn, LPads); @@ -394,6 +397,7 @@ bool WinEHPrepare::doFinalization(Module &M) { return false; } void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); + AU.addRequired(); } static bool isSelectorDispatch(BasicBlock *BB, BasicBlock *&CatchHandler, @@ -1016,10 +1020,17 @@ bool WinEHPrepare::prepareExceptionHandlers( Builder.CreateCall(FrameEscapeFn, AllocasToEscape); if (SEHExceptionCodeSlot) { - if (SEHExceptionCodeSlot->hasNUses(0)) - SEHExceptionCodeSlot->eraseFromParent(); - else if (isAllocaPromotable(SEHExceptionCodeSlot)) + if (isAllocaPromotable(SEHExceptionCodeSlot)) { + SmallPtrSet UserBlocks; + for (User *U : SEHExceptionCodeSlot->users()) { + if (auto *Inst = dyn_cast(U)) + UserBlocks.insert(Inst->getParent()); + } PromoteMemToReg(SEHExceptionCodeSlot, *DT); + // After the promotion, kill off dead instructions. + for (BasicBlock *BB : UserBlocks) + SimplifyInstructionsInBlock(BB, LibInfo); + } } // Clean up the handler action maps we created for this function @@ -1029,6 +1040,7 @@ bool WinEHPrepare::prepareExceptionHandlers( CleanupHandlerMap.clear(); HandlerToParentFP.clear(); DT = nullptr; + LibInfo = nullptr; SEHExceptionCodeSlot = nullptr; EHBlocks.clear(); NormalBlocks.clear(); @@ -1143,7 +1155,6 @@ void WinEHPrepare::completeNestedLandingPad(Function *ParentFn, ++II; // The instruction after the landing pad should now be a call to eh.actions. const Instruction *Recover = II; - assert(match(Recover, m_Intrinsic())); const IntrinsicInst *EHActions = cast(Recover); // Remap the return target in the nested handler. @@ -2454,6 +2465,8 @@ void WinEHPrepare::findCleanupHandlers(LandingPadActions &Actions, void llvm::parseEHActions( const IntrinsicInst *II, SmallVectorImpl> &Actions) { + assert(II->getIntrinsicID() == Intrinsic::eh_actions && + "attempted to parse non eh.actions intrinsic"); for (unsigned I = 0, E = II->getNumArgOperands(); I != E;) { uint64_t ActionKind = cast(II->getArgOperand(I))->getZExtValue(); @@ -2766,7 +2779,6 @@ void WinEHNumbering::calculateStateNumbers(const Function &F) { auto *ActionsCall = dyn_cast(LPI->getNextNode()); if (!ActionsCall) continue; - assert(ActionsCall->getIntrinsicID() == Intrinsic::eh_actions); parseEHActions(ActionsCall, ActionList); if (ActionList.empty()) continue; diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 88598a1..72f18cc 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -15506,6 +15506,23 @@ static SDValue LowerINTRINSIC_WO_CHAIN(SDValue Op, const X86Subtarget *Subtarget DAG.getTargetExternalSymbol(Name.data(), VT, X86II::MO_NOPREFIX); return DAG.getNode(X86ISD::Wrapper, dl, VT, Result); } + + case Intrinsic::eh_exceptioninfo: { + // Compute the symbol for the LSDA. We know it'll get emitted later. + MachineFunction &MF = DAG.getMachineFunction(); + SDValue Op1 = Op.getOperand(1); + auto *Fn = cast(cast(Op1)->getGlobal()); + MCSymbol *LSDASym = MF.getMMI().getContext().getOrCreateLSDASymbol( + GlobalValue::getRealLinkageName(Fn->getName())); + StringRef Name = LSDASym->getName(); + assert(Name.data()[Name.size()] == '\0' && "not null terminated"); + + // Generate a simple absolute symbol reference. This intrinsic is only + // supported on 32-bit Windows, which isn't PIC. + SDValue Result = + DAG.getTargetExternalSymbol(Name.data(), VT, X86II::MO_NOPREFIX); + return DAG.getNode(X86ISD::Wrapper, dl, VT, Result); + } } } diff --git a/llvm/lib/Target/X86/X86WinEHState.cpp b/llvm/lib/Target/X86/X86WinEHState.cpp index ce69ea7..0c4aaba 100644 --- a/llvm/lib/Target/X86/X86WinEHState.cpp +++ b/llvm/lib/Target/X86/X86WinEHState.cpp @@ -63,9 +63,12 @@ private: void linkExceptionRegistration(IRBuilder<> &Builder, Value *Handler); void unlinkExceptionRegistration(IRBuilder<> &Builder); void addCXXStateStores(Function &F, MachineModuleInfo &MMI); + void addSEHStateStores(Function &F, MachineModuleInfo &MMI); void addCXXStateStoresToFunclet(Value *ParentRegNode, WinEHFuncInfo &FuncInfo, Function &F, int BaseState); void insertStateNumberStore(Value *ParentRegNode, Instruction *IP, int State); + iplist::iterator + rewriteExceptionInfoIntrinsics(IntrinsicInst *Intrin); Value *emitEHLSDA(IRBuilder<> &Builder, Function *F); @@ -171,8 +174,10 @@ bool WinEHStatePass::runOnFunction(Function &F) { auto *MMIPtr = getAnalysisIfAvailable(); assert(MMIPtr && "MachineModuleInfo should always be available"); MachineModuleInfo &MMI = *MMIPtr; - if (Personality == EHPersonality::MSVC_CXX) { - addCXXStateStores(F, MMI); + switch (Personality) { + default: llvm_unreachable("unexpected personality function"); + case EHPersonality::MSVC_CXX: addCXXStateStores(F, MMI); break; + case EHPersonality::MSVC_X86SEH: addSEHStateStores(F, MMI); break; } // Reset per-function state. @@ -472,6 +477,76 @@ void WinEHStatePass::addCXXStateStoresToFunclet(Value *ParentRegNode, } } +/// Assign every distinct landingpad a unique state number for SEH. Unlike C++ +/// EH, we can use this very simple algorithm while C++ EH cannot because catch +/// handlers aren't outlined and the runtime doesn't have to figure out which +/// catch handler frame to unwind to. +/// FIXME: __finally blocks are outlined, so this approach may break down there. +void WinEHStatePass::addSEHStateStores(Function &F, MachineModuleInfo &MMI) { + WinEHFuncInfo &FuncInfo = MMI.getWinEHFuncInfo(&F); + + // Iterate all the instructions and emit state number stores. + int CurState = 0; + for (BasicBlock &BB : F) { + for (auto I = BB.begin(), E = BB.end(); I != E; ++I) { + if (auto *CI = dyn_cast(I)) { + auto *Intrin = dyn_cast(CI); + if (Intrin) { + I = rewriteExceptionInfoIntrinsics(Intrin); + // Calls that "don't throw" are considered to be able to throw asynch + // exceptions, but intrinsics cannot. + continue; + } + insertStateNumberStore(RegNode, CI, -1); + } else if (auto *II = dyn_cast(I)) { + // Look up the state number of the landingpad this unwinds to. + LandingPadInst *LPI = II->getUnwindDest()->getLandingPadInst(); + auto InsertionPair = + FuncInfo.LandingPadStateMap.insert(std::make_pair(LPI, 0)); + auto Iter = InsertionPair.first; + int &State = Iter->second; + bool Inserted = InsertionPair.second; + if (Inserted) { + // Each action consumes a state number. + auto *EHActions = cast(LPI->getNextNode()); + SmallVector, 4> ActionList; + parseEHActions(EHActions, ActionList); + assert(!ActionList.empty()); + CurState += ActionList.size(); + State += ActionList.size() - 1; + } + insertStateNumberStore(RegNode, II, State); + } + } + } +} + +/// Rewrite llvm.eh.exceptioncode and llvm.eh.exceptioninfo to memory loads in +/// IR. +iplist::iterator +WinEHStatePass::rewriteExceptionInfoIntrinsics(IntrinsicInst *Intrin) { + Intrinsic::ID ID = Intrin->getIntrinsicID(); + if (ID != Intrinsic::eh_exceptioncode && ID != Intrinsic::eh_exceptioninfo) + return Intrin; + + // RegNode->ExceptionPointers + IRBuilder<> Builder(Intrin); + Value *Ptrs = + Builder.CreateLoad(Builder.CreateStructGEP(RegNodeTy, RegNode, 1)); + Value *Res; + if (ID == Intrinsic::eh_exceptioncode) { + // Ptrs->ExceptionRecord->Code + Ptrs = Builder.CreateBitCast( + Ptrs, Builder.getInt32Ty()->getPointerTo()->getPointerTo()); + Value *Rec = Builder.CreateLoad(Ptrs); + Res = Builder.CreateLoad(Rec); + } else { + Res = Ptrs; + } + Intrin->replaceAllUsesWith(Res); + return Intrin->eraseFromParent(); +} + void WinEHStatePass::insertStateNumberStore(Value *ParentRegNode, Instruction *IP, int State) { IRBuilder<> Builder(IP); diff --git a/llvm/test/CodeGen/X86/seh-catch-all.ll b/llvm/test/CodeGen/X86/seh-catch-all.ll index 5184013..d02584a 100644 --- a/llvm/test/CodeGen/X86/seh-catch-all.ll +++ b/llvm/test/CodeGen/X86/seh-catch-all.ll @@ -1,4 +1,6 @@ -; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s --check-prefix=X64 +; RUN: sed -e 's/__C_specific_handler/_except_handler3/' %s | \ +; RUN: llc -mtriple=i686-windows-msvc | FileCheck %s --check-prefix=X86 @str = linkonce_odr unnamed_addr constant [27 x i8] c"GetExceptionCode(): 0x%lx\0A\00", align 1 @@ -29,16 +31,41 @@ eh.resume: ; Check that we can get the exception code from eax to the printf. -; CHECK-LABEL: main: -; CHECK: retq -; CHECK: # Block address taken -; CHECK: leaq str(%rip), %rcx -; CHECK: movl %eax, %edx -; CHECK: callq printf - -; CHECK: .seh_handlerdata -; CHECK-NEXT: .long 1 -; CHECK-NEXT: .Ltmp{{[0-9]+}}@IMGREL -; CHECK-NEXT: .Ltmp{{[0-9]+}}@IMGREL+1 -; CHECK-NEXT: 1 -; CHECK-NEXT: .Ltmp{{[0-9]+}}@IMGREL +; X64-LABEL: main: +; X64: callq crash +; X64: retq +; X64: # Block address taken +; X64: leaq str(%rip), %rcx +; X64: movl %eax, %edx +; X64: callq printf + +; X64: .seh_handlerdata +; X64-NEXT: .long 1 +; X64-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL +; X64-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL+1 +; X64-NEXT: .long 1 +; X64-NEXT: .long .Ltmp{{[0-9]+}}@IMGREL + +; X86-LABEL: _main: +; The EH code load should be this offset +4. +; X86: movl %esp, -24(%ebp) +; X86: movl $L__ehtable$main, +; EH state 0 +; X86: movl $0, -4(%ebp) +; X86: calll _crash +; X86: retl +; X86: # Block address taken +; X86: movl -20(%ebp), %[[ptrs:[^ ,]*]] +; X86: movl (%[[ptrs]]), %[[rec:[^ ,]*]] +; X86: movl (%[[rec]]), %[[code:[^ ,]*]] +; EH state -1 +; X86: movl $-1, -4(%ebp) +; X86-DAG: movl %[[code]], 4(%esp) +; X86-DAG: movl $_str, (%esp) +; X86: calll _printf + +; X86: .section .xdata,"dr" +; X86-NEXT: L__ehtable$main +; X86-NEXT: .long -1 +; X86-NEXT: .long 1 +; X86-NEXT: .long Ltmp{{[0-9]+}} diff --git a/llvm/test/CodeGen/X86/seh-safe-div-win32.ll b/llvm/test/CodeGen/X86/seh-safe-div-win32.ll new file mode 100644 index 0000000..6696779 --- /dev/null +++ b/llvm/test/CodeGen/X86/seh-safe-div-win32.ll @@ -0,0 +1,168 @@ +; RUN: llc -mtriple i686-pc-windows-msvc < %s | FileCheck %s + +; This test case is also intended to be run manually as a complete functional +; test. It should link, print something, and exit zero rather than crashing. +; It is the hypothetical lowering of a C source program that looks like: +; +; int safe_div(int *n, int *d) { +; int r; +; __try { +; __try { +; r = *n / *d; +; } __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) { +; puts("EXCEPTION_ACCESS_VIOLATION"); +; r = -1; +; } +; } __except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) { +; puts("EXCEPTION_INT_DIVIDE_BY_ZERO"); +; r = -2; +; } +; return r; +; } + +@str1 = internal constant [27 x i8] c"EXCEPTION_ACCESS_VIOLATION\00" +@str2 = internal constant [29 x i8] c"EXCEPTION_INT_DIVIDE_BY_ZERO\00" + +define i32 @safe_div(i32* %n, i32* %d) { +entry: + %r = alloca i32, align 4 + store i32 42, i32* %r + 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 (...)* @_except_handler3 to i8*) + catch i8* bitcast (i32 ()* @safe_div_filt0 to i8*) + catch i8* bitcast (i32 ()* @safe_div_filt1 to i8*) + %ehptr = extractvalue { i8*, i32 } %vals, 0 + %sel = extractvalue { i8*, i32 } %vals, 1 + %filt0_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @safe_div_filt0 to i8*)) + %is_filt0 = icmp eq i32 %sel, %filt0_val + br i1 %is_filt0, label %handler0, label %eh.dispatch1 + +eh.dispatch1: + %filt1_val = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 ()* @safe_div_filt1 to i8*)) + %is_filt1 = icmp eq i32 %sel, %filt1_val + br i1 %is_filt1, label %handler1, label %eh.resume + +handler0: + call void @puts(i8* getelementptr ([27 x i8], [27 x i8]* @str1, i32 0, i32 0)) + store i32 -1, i32* %r, align 4 + br label %__try.cont + +handler1: + call void @puts(i8* getelementptr ([29 x i8], [29 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, i32* %r, align 4 + ret i32 %safe_ret +} + +; Normal path code + +; CHECK: {{^}}_safe_div: +; CHECK: movl $42, [[rloc:.*\(%ebp\)]] +; CHECK: leal [[rloc]], +; CHECK: calll _try_body +; CHECK: [[cont_bb:LBB0_[0-9]+]]: +; CHECK: movl [[rloc]], %eax +; CHECK: retl + +; Landing pad code + +; CHECK: [[handler0:Ltmp[0-9]+]]: # Block address taken +; CHECK: # %handler0 +; CHECK: calll _puts +; CHECK: jmp [[cont_bb]] + +; CHECK: [[handler1:Ltmp[0-9]+]]: # Block address taken +; CHECK: # %handler1 +; CHECK: calll _puts +; CHECK: jmp [[cont_bb]] + +; CHECK: .section .xdata,"dr" +; CHECK-NEXT: L__ehtable$safe_div: +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long _safe_div_filt0 +; CHECK-NEXT: .long [[handler0]] +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long _safe_div_filt1 +; CHECK-NEXT: .long [[handler1]] + +define void @try_body(i32* %r, i32* %n, i32* %d) { +entry: + %0 = load i32, i32* %n, align 4 + %1 = load i32, i32* %d, align 4 + %div = sdiv i32 %0, %1 + store i32 %div, i32* %r, align 4 + ret void +} + +; The prototype of these filter functions is: +; int filter(EXCEPTION_POINTERS *eh_ptrs, void *rbp); + +; The definition of EXCEPTION_POINTERS is: +; typedef struct _EXCEPTION_POINTERS { +; EXCEPTION_RECORD *ExceptionRecord; +; CONTEXT *ContextRecord; +; } EXCEPTION_POINTERS; + +; The definition of EXCEPTION_RECORD is: +; typedef struct _EXCEPTION_RECORD { +; DWORD ExceptionCode; +; ... +; } EXCEPTION_RECORD; + +; FIXME: Use llvm.eh.exceptioninfo for this. +declare i32 @safe_div_filt0() +declare i32 @safe_div_filt1() +; define i32 @safe_div_filt0() { +; %eh_ptrs_c = bitcast i8* %eh_ptrs to i32** +; %eh_rec = load i32*, i32** %eh_ptrs_c +; %eh_code = load i32, i32* %eh_rec +; ; EXCEPTION_ACCESS_VIOLATION = 0xC0000005 +; %cmp = icmp eq i32 %eh_code, 3221225477 +; %filt.res = zext i1 %cmp to i32 +; ret i32 %filt.res +; } +; define i32 @safe_div_filt1() { +; %eh_ptrs_c = bitcast i8* %eh_ptrs to i32** +; %eh_rec = load i32*, i32** %eh_ptrs_c +; %eh_code = load i32, i32* %eh_rec +; ; EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094 +; %cmp = icmp eq i32 %eh_code, 3221225620 +; %filt.res = zext i1 %cmp to i32 +; ret i32 %filt.res +; } + +@str_result = internal constant [21 x i8] c"safe_div result: %d\0A\00" + +define i32 @main() { + %d.addr = alloca i32, align 4 + %n.addr = alloca i32, align 4 + + store i32 10, i32* %n.addr, align 4 + store i32 2, i32* %d.addr, align 4 + %r1 = call i32 @safe_div(i32* %n.addr, i32* %d.addr) + call void (i8*, ...) @printf(i8* getelementptr ([21 x i8], [21 x i8]* @str_result, i32 0, i32 0), i32 %r1) + + store i32 10, i32* %n.addr, align 4 + store i32 0, i32* %d.addr, align 4 + %r2 = call i32 @safe_div(i32* %n.addr, i32* %d.addr) + call void (i8*, ...) @printf(i8* getelementptr ([21 x i8], [21 x i8]* @str_result, i32 0, i32 0), i32 %r2) + + %r3 = call i32 @safe_div(i32* %n.addr, i32* null) + call void (i8*, ...) @printf(i8* getelementptr ([21 x i8], [21 x i8]* @str_result, i32 0, i32 0), i32 %r3) + ret i32 0 +} + +declare i32 @_except_handler3(...) +declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind +declare void @puts(i8*) +declare void @printf(i8*, ...) +declare void @abort() diff --git a/llvm/test/CodeGen/X86/win32-eh.ll b/llvm/test/CodeGen/X86/win32-eh.ll index 42c9d9e..ed8402a 100644 --- a/llvm/test/CodeGen/X86/win32-eh.ll +++ b/llvm/test/CodeGen/X86/win32-eh.ll @@ -34,6 +34,12 @@ catchall: ; CHECK: movl %[[next]], %fs:0 ; CHECK: retl +; CHECK: .section .xdata,"dr" +; CHECK-LABEL: L__ehtable$use_except_handler3: +; CHECK-NEXT: .long -1 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long Ltmp{{[0-9]+}} + define void @use_except_handler4() { invoke void @may_throw_or_crash() to label %cont unwind label %catchall @@ -64,6 +70,16 @@ catchall: ; CHECK: movl %[[next]], %fs:0 ; CHECK: retl +; CHECK: .section .xdata,"dr" +; CHECK-LABEL: L__ehtable$use_except_handler4: +; CHECK-NEXT: .long -2 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long 9999 +; CHECK-NEXT: .long 0 +; CHECK-NEXT: .long -2 +; CHECK-NEXT: .long 1 +; CHECK-NEXT: .long Ltmp{{[0-9]+}} + define void @use_CxxFrameHandler3() { invoke void @may_throw_or_crash() to label %cont unwind label %catchall -- 2.7.4