diff --git a/include/llvm/CodeGen/Passes.h b/include/llvm/CodeGen/Passes.h index bff58e12fe5..7e5e9008320 100644 --- a/include/llvm/CodeGen/Passes.h +++ b/include/llvm/CodeGen/Passes.h @@ -573,6 +573,10 @@ namespace llvm { /// adapted to code generation. Required if using dwarf exception handling. FunctionPass *createDwarfEHPass(const TargetMachine *TM); + /// createWinEHPass - Prepares personality functions used by MSVC on Windows, + /// in addition to the Itanium LSDA based personalities. + FunctionPass *createWinEHPass(const TargetMachine *TM); + /// createSjLjEHPreparePass - This pass adapts exception handling code to use /// the GCC-style builtin setjmp/longjmp (sjlj) to handling EH control flow. /// diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h index 91884097504..6a6d48cc42f 100644 --- a/include/llvm/InitializePasses.h +++ b/include/llvm/InitializePasses.h @@ -289,6 +289,7 @@ void initializeStackMapLivenessPass(PassRegistry&); void initializeMachineCombinerPass(PassRegistry &); void initializeLoadCombinePass(PassRegistry&); void initializeRewriteSymbolsPass(PassRegistry&); +void initializeWinEHPreparePass(PassRegistry&); } #endif diff --git a/lib/CodeGen/CMakeLists.txt b/lib/CodeGen/CMakeLists.txt index 8df2042d68b..417da69f543 100644 --- a/lib/CodeGen/CMakeLists.txt +++ b/lib/CodeGen/CMakeLists.txt @@ -118,6 +118,7 @@ add_llvm_library(LLVMCodeGen TwoAddressInstructionPass.cpp UnreachableBlockElim.cpp VirtRegMap.cpp + WinEHPrepare.cpp ) add_dependencies(LLVMCodeGen intrinsics_gen) diff --git a/lib/CodeGen/Passes.cpp b/lib/CodeGen/Passes.cpp index c66d2131f8f..b9dd0f78c81 100644 --- a/lib/CodeGen/Passes.cpp +++ b/lib/CodeGen/Passes.cpp @@ -450,9 +450,11 @@ void TargetPassConfig::addPassesToHandleExceptions() { // FALLTHROUGH case ExceptionHandling::DwarfCFI: case ExceptionHandling::ARM: - case ExceptionHandling::WinEH: addPass(createDwarfEHPass(TM)); break; + case ExceptionHandling::WinEH: + addPass(createWinEHPass(TM)); + break; case ExceptionHandling::None: addPass(createLowerInvokePass()); diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp new file mode 100644 index 00000000000..a01adcd43d0 --- /dev/null +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -0,0 +1,106 @@ +//===-- WinEHPrepare - Prepare exception handling for code generation ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass lowers LLVM IR exception handling into something closer to what the +// backend wants. It snifs the personality function to see which kind of +// preparation is necessary. If the personality function uses the Itanium LSDA, +// this pass delegates to the DWARF EH preparation pass. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/Passes.h" +#include "llvm/Analysis/LibCallSemantics.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Pass.h" +#include "llvm/Target/TargetLowering.h" +using namespace llvm; + +#define DEBUG_TYPE "winehprepare" + +namespace { +class WinEHPrepare : public FunctionPass { + const TargetMachine *TM; + FunctionPass *DwarfPrepare; + +public: + static char ID; // Pass identification, replacement for typeid. + WinEHPrepare(const TargetMachine *TM = nullptr) + : FunctionPass(ID), TM(TM), DwarfPrepare(createDwarfEHPass(TM)) { + initializeDominatorTreeWrapperPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &Fn) override; + + bool doFinalization(Module &M) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override; + + const char *getPassName() const override { + return "Windows exception handling preparation"; + } +}; +} // end anonymous namespace + +char WinEHPrepare::ID = 0; +INITIALIZE_TM_PASS(WinEHPrepare, "winehprepare", + "Prepare Windows exceptions", false, false) + +FunctionPass *llvm::createWinEHPass(const TargetMachine *TM) { + return new WinEHPrepare(TM); +} + +static bool isMSVCPersonality(EHPersonality Pers) { + return Pers == EHPersonality::MSVC_Win64SEH || + Pers == EHPersonality::MSVC_CXX; +} + +bool WinEHPrepare::runOnFunction(Function &Fn) { + SmallVector LPads; + SmallVector Resumes; + for (BasicBlock &BB : Fn) { + if (auto *LP = BB.getLandingPadInst()) + LPads.push_back(LP); + if (auto *Resume = dyn_cast(BB.getTerminator())) + Resumes.push_back(Resume); + } + + // No need to prepare functions that lack landing pads. + if (LPads.empty()) + return false; + + // Classify the personality to see what kind of preparation we need. + EHPersonality Pers = ClassifyEHPersonality(LPads.back()->getPersonalityFn()); + + // Delegate through to the DWARF pass if this is unrecognized. + if (!isMSVCPersonality(Pers)) + return DwarfPrepare->runOnFunction(Fn); + + // FIXME: Cleanups are unimplemented. Replace them with calls to @llvm.trap. + if (Resumes.empty()) + return false; + + Function *Trap = + Intrinsic::getDeclaration(Fn.getParent(), Intrinsic::trap, None); + for (ResumeInst *Resume : Resumes) { + IRBuilder<>(Resume).CreateUnreachable(); + Resume->eraseFromParent(); + } + + return true; +} + +bool WinEHPrepare::doFinalization(Module &M) { + return DwarfPrepare->doFinalization(M); +} + +void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const { + DwarfPrepare->getAnalysisUsage(AU); +} diff --git a/test/CodeGen/X86/seh-safe-div.ll b/test/CodeGen/X86/seh-safe-div.ll index e911df04ded..e294f24f28b 100644 --- a/test/CodeGen/X86/seh-safe-div.ll +++ b/test/CodeGen/X86/seh-safe-div.ll @@ -96,8 +96,9 @@ __try.cont: ; CHECK: movl $-2, [[rloc]] ; CHECK: jmp .LBB0_7 -; FIXME: EH preparation should not call _Unwind_Resume. -; CHECK: callq _Unwind_Resume +; FIXME: EH preparation should eliminate the 'resume' instr and we should not do +; the previous 'cmp;jeq'. +; CHECK-NOT: _Unwind_Resume ; CHECK: ud2 ; CHECK: .seh_handlerdata diff --git a/test/CodeGen/X86/win_eh_prepare.ll b/test/CodeGen/X86/win_eh_prepare.ll new file mode 100644 index 00000000000..f96fed5095f --- /dev/null +++ b/test/CodeGen/X86/win_eh_prepare.ll @@ -0,0 +1,80 @@ +; RUN: opt -S -winehprepare -mtriple x86_64-pc-windows-msvc < %s | FileCheck %s + +; FIXME: Add and test outlining here. + +declare void @maybe_throw() + +@_ZTIi = external constant i8* +@g = external global i32 + +declare i32 @__C_specific_handler(...) +declare i32 @__gxx_personality_seh0(...) +declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind + +define i32 @use_seh() { +entry: + invoke void @maybe_throw() + to label %cont unwind label %lpad + +cont: + ret i32 0 + +lpad: + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__C_specific_handler + cleanup + catch i8* bitcast (i32 (i8*, i8*)* @filt_g to i8*) + %ehsel = extractvalue { i8*, i32 } %ehvals, 1 + %filt_g_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @filt_g to i8*)) + %matches = icmp eq i32 %ehsel, %filt_g_sel + br i1 %matches, label %ret1, label %eh.resume + +ret1: + ret i32 1 + +eh.resume: + resume { i8*, i32 } %ehvals +} + +define internal i32 @filt_g(i8*, i8*) { + %g = load i32* @g + ret i32 %g +} + +; CHECK-LABEL: define i32 @use_seh() +; CHECK: invoke void @maybe_throw() +; CHECK-NEXT: to label %cont unwind label %lpad +; CHECK: eh.resume: +; CHECK-NEXT: unreachable + + +; A MinGW64-ish EH style. It could happen if a binary uses both MSVC CRT and +; mingw CRT and is linked with LTO. +define i32 @use_gcc() { +entry: + invoke void @maybe_throw() + to label %cont unwind label %lpad + +cont: + ret i32 0 + +lpad: + %ehvals = landingpad { i8*, i32 } personality i32 (...)* @__gxx_personality_seh0 + cleanup + catch i8* bitcast (i8** @_ZTIi to i8*) + %ehsel = extractvalue { i8*, i32 } %ehvals, 1 + %filt_g_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @filt_g to i8*)) + %matches = icmp eq i32 %ehsel, %filt_g_sel + br i1 %matches, label %ret1, label %eh.resume + +ret1: + ret i32 1 + +eh.resume: + resume { i8*, i32 } %ehvals +} + +; CHECK-LABEL: define i32 @use_gcc() +; CHECK: invoke void @maybe_throw() +; CHECK-NEXT: to label %cont unwind label %lpad +; CHECK: eh.resume: +; CHECK: call void @_Unwind_Resume(i8* %exn.obj) diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 3cd07c53e5d..6083e7a70a8 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -322,6 +322,7 @@ int main(int argc, char **argv) { initializeCodeGenPreparePass(Registry); initializeAtomicExpandPass(Registry); initializeRewriteSymbolsPass(Registry); + initializeWinEHPreparePass(Registry); #ifdef LINK_POLLY_INTO_TOOLS polly::initializePollyPasses(Registry);