[WinEH] Add an EH registration and state insertion pass for 32-bit x86

This pass is responsible for constructing the EH registration object
that gets linked into fs:00, which is all it does in this change. In the
future, it will also insert stores to update the EH state number.

I considered keeping this functionality in WinEHPrepare, but it's pretty
separable and X86 specific. It has conceptually very little to do with
the task of WinEHPrepare, which is currently outlining.  WinEHPrepare is
also in theory useful on ARM, but this logic is pretty x86 specific.

Reviewers: andrew.w.kaylor, majnemer

Differential Revision: http://reviews.llvm.org/D9422

llvm-svn: 236339
This commit is contained in:
Reid Kleckner 2015-05-01 20:04:54 +00:00
parent 88db89dfb9
commit 4856fc61b4
10 changed files with 428 additions and 12 deletions

View File

@ -37,6 +37,7 @@ enum class EncodingType {
ARM, /// Windows NT (Windows on ARM)
CE, /// Windows CE ARM, PowerPC, SH3, SH4
Itanium, /// Windows x64, Windows Itanium (IA-64)
X86, /// Windows x86, uses no CFI, just EH tables
MIPS = Alpha,
};
}
@ -506,12 +507,13 @@ public:
/// frame information to unwind.
bool usesCFIForEH() const {
return (ExceptionsType == ExceptionHandling::DwarfCFI ||
ExceptionsType == ExceptionHandling::ARM ||
ExceptionsType == ExceptionHandling::WinEH);
ExceptionsType == ExceptionHandling::ARM || usesWindowsCFI());
}
bool usesWindowsCFI() const {
return ExceptionsType == ExceptionHandling::WinEH;
return ExceptionsType == ExceptionHandling::WinEH &&
(WinEHEncodingType != WinEH::EncodingType::Invalid &&
WinEHEncodingType != WinEH::EncodingType::X86);
}
bool doesDwarfUseRelocationsAcrossSections() const {

View File

@ -266,6 +266,8 @@ bool AsmPrinter::doInitialization(Module &M) {
case ExceptionHandling::WinEH:
switch (MAI->getWinEHEncodingType()) {
default: llvm_unreachable("unsupported unwinding information encoding");
case WinEH::EncodingType::Invalid:
break;
case WinEH::EncodingType::Itanium:
ES = new Win64Exception(this);
break;

View File

@ -8,9 +8,11 @@
//===----------------------------------------------------------------------===//
//
// 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.
// backend wants for functions using a personality function from a runtime
// provided by MSVC. Functions with other personality functions are left alone
// and may be prepared by other passes. In particular, all supported MSVC
// personality functions require cleanup code to be outlined, and the C++
// personality requires catch handler code to be outlined.
//
//===----------------------------------------------------------------------===//
@ -31,7 +33,6 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
@ -922,7 +923,7 @@ bool WinEHPrepare::prepareExceptionHandlers(
if (SEHExceptionCodeSlot) {
if (SEHExceptionCodeSlot->hasNUses(0))
SEHExceptionCodeSlot->eraseFromParent();
else
else if (isAllocaPromotable(SEHExceptionCodeSlot))
PromoteMemToReg(SEHExceptionCodeSlot, *DT);
}

View File

@ -32,6 +32,7 @@ set(sources
X86TargetTransformInfo.cpp
X86VZeroUpper.cpp
X86FixupLEAs.cpp
X86WinEHState.cpp
)
if( CMAKE_CL_64 )

View File

@ -132,9 +132,10 @@ X86MCAsmInfoMicrosoft::X86MCAsmInfoMicrosoft(const Triple &Triple) {
PrivateLabelPrefix = ".L";
PointerSize = 8;
WinEHEncodingType = WinEH::EncodingType::Itanium;
ExceptionsType = ExceptionHandling::WinEH;
}
ExceptionsType = ExceptionHandling::WinEH;
AssemblerDialect = AsmWriterFlavor;
TextAlignFillValue = 0x90;

View File

@ -69,6 +69,12 @@ FunctionPass *createX86FixupLEAs();
/// esp-relative movs with pushes.
FunctionPass *createX86CallFrameOptimization();
/// createX86WinEHStatePass - Return an IR pass that inserts EH registration
/// stack objects and explicit EH state updates. This pass must run after EH
/// preparation, which does Windows-specific but architecture-neutral
/// preparation.
FunctionPass *createX86WinEHStatePass();
} // End llvm namespace
#endif

View File

@ -185,6 +185,7 @@ public:
void addIRPasses() override;
bool addInstSelector() override;
bool addILPOpts() override;
bool addPreISel() override;
void addPreRegAlloc() override;
void addPostRegAlloc() override;
void addPreEmitPass() override;
@ -220,6 +221,14 @@ bool X86PassConfig::addILPOpts() {
return true;
}
bool X86PassConfig::addPreISel() {
// Only add this pass for 32-bit x86.
Triple TT(TM->getTargetTriple());
if (TT.getArch() == Triple::x86)
addPass(createX86WinEHStatePass());
return true;
}
void X86PassConfig::addPreRegAlloc() {
addPass(createX86CallFrameOptimization());
}

View File

@ -0,0 +1,317 @@
//===-- X86WinEHState - Insert EH state updates for win32 exceptions ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// All functions using an MSVC EH personality use an explicitly updated state
// number stored in an exception registration stack object. The registration
// object is linked into a thread-local chain of registrations stored at fs:00.
// This pass adds the registration object and EH state updates.
//
//===----------------------------------------------------------------------===//
#include "X86.h"
#include "llvm/Analysis/LibCallSemantics.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/WinEHFuncInfo.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/Pass.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
using namespace llvm;
using namespace llvm::PatternMatch;
#define DEBUG_TYPE "winehstate"
namespace {
class WinEHStatePass : public FunctionPass {
public:
static char ID; // Pass identification, replacement for typeid.
WinEHStatePass() : FunctionPass(ID) {}
bool runOnFunction(Function &Fn) override;
bool doInitialization(Module &M) override;
bool doFinalization(Module &M) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
const char *getPassName() const override {
return "Windows 32-bit x86 EH state insertion";
}
private:
void emitExceptionRegistrationRecord(Function *F);
void linkExceptionRegistration(IRBuilder<> &Builder, Value *RegNode,
Value *Handler);
void unlinkExceptionRegistration(IRBuilder<> &Builder, Value *RegNode);
// Module-level type getters.
Type *getEHRegistrationType();
Type *getSEH3RegistrationType();
Type *getSEH4RegistrationType();
Type *getCXXEH3RegistrationType();
// Per-module data.
Module *TheModule = nullptr;
StructType *EHRegistrationTy = nullptr;
StructType *CXXEH3RegistrationTy = nullptr;
StructType *SEH3RegistrationTy = nullptr;
StructType *SEH4RegistrationTy = nullptr;
// Per-function state
EHPersonality Personality = EHPersonality::Unknown;
Function *PersonalityFn = nullptr;
};
}
FunctionPass *llvm::createX86WinEHStatePass() { return new WinEHStatePass(); }
char WinEHStatePass::ID = 0;
bool WinEHStatePass::doInitialization(Module &M) {
TheModule = &M;
return false;
}
bool WinEHStatePass::doFinalization(Module &M) {
assert(TheModule == &M);
TheModule = nullptr;
EHRegistrationTy = nullptr;
CXXEH3RegistrationTy = nullptr;
SEH3RegistrationTy = nullptr;
SEH4RegistrationTy = nullptr;
return false;
}
void WinEHStatePass::getAnalysisUsage(AnalysisUsage &AU) const {
// This pass should only insert a stack allocation, memory accesses, and
// framerecovers.
AU.setPreservesCFG();
}
bool WinEHStatePass::runOnFunction(Function &F) {
// Check the personality. Do nothing if this is not an MSVC personality.
LandingPadInst *LP = nullptr;
for (BasicBlock &BB : F) {
LP = BB.getLandingPadInst();
if (LP)
break;
}
if (!LP)
return false;
PersonalityFn =
dyn_cast<Function>(LP->getPersonalityFn()->stripPointerCasts());
if (!PersonalityFn)
return false;
Personality = classifyEHPersonality(PersonalityFn);
if (!isMSVCEHPersonality(Personality))
return false;
emitExceptionRegistrationRecord(&F);
// FIXME: State insertion.
// Reset per-function state.
PersonalityFn = nullptr;
Personality = EHPersonality::Unknown;
return true;
}
/// Get the common EH registration subobject:
/// struct EHRegistrationNode {
/// EHRegistrationNode *Next;
/// EXCEPTION_DISPOSITION (*Handler)(...);
/// };
Type *WinEHStatePass::getEHRegistrationType() {
if (EHRegistrationTy)
return EHRegistrationTy;
LLVMContext &Context = TheModule->getContext();
EHRegistrationTy = StructType::create(Context, "EHRegistrationNode");
Type *FieldTys[] = {
EHRegistrationTy->getPointerTo(0), // EHRegistrationNode *Next
Type::getInt8PtrTy(Context) // EXCEPTION_DISPOSITION (*Handler)(...)
};
EHRegistrationTy->setBody(FieldTys, false);
return EHRegistrationTy;
}
/// The __CxxFrameHandler3 registration node:
/// struct CXXExceptionRegistration {
/// void *SavedESP;
/// EHRegistrationNode SubRecord;
/// int32_t TryLevel;
/// };
Type *WinEHStatePass::getCXXEH3RegistrationType() {
if (CXXEH3RegistrationTy)
return CXXEH3RegistrationTy;
LLVMContext &Context = TheModule->getContext();
Type *FieldTys[] = {
Type::getInt8PtrTy(Context), // void *SavedESP
getEHRegistrationType(), // EHRegistrationNode SubRecord
Type::getInt32Ty(Context) // int32_t TryLevel
};
CXXEH3RegistrationTy =
StructType::create(FieldTys, "CXXExceptionRegistration");
return CXXEH3RegistrationTy;
}
/// The _except_handler3 registration node:
/// struct EH3ExceptionRegistration {
/// EHRegistrationNode SubRecord;
/// void *ScopeTable;
/// int32_t TryLevel;
/// };
Type *WinEHStatePass::getSEH3RegistrationType() {
if (SEH3RegistrationTy)
return SEH3RegistrationTy;
LLVMContext &Context = TheModule->getContext();
Type *FieldTys[] = {
getEHRegistrationType(), // EHRegistrationNode SubRecord
Type::getInt8PtrTy(Context), // void *ScopeTable
Type::getInt32Ty(Context) // int32_t TryLevel
};
SEH3RegistrationTy = StructType::create(FieldTys, "EH3ExceptionRegistration");
return SEH3RegistrationTy;
}
/// The _except_handler4 registration node:
/// struct EH4ExceptionRegistration {
/// void *SavedESP;
/// _EXCEPTION_POINTERS *ExceptionPointers;
/// EHRegistrationNode SubRecord;
/// int32_t EncodedScopeTable;
/// int32_t TryLevel;
/// };
Type *WinEHStatePass::getSEH4RegistrationType() {
if (SEH4RegistrationTy)
return SEH4RegistrationTy;
LLVMContext &Context = TheModule->getContext();
Type *FieldTys[] = {
Type::getInt8PtrTy(Context), // void *SavedESP
Type::getInt8PtrTy(Context), // void *ExceptionPointers
getEHRegistrationType(), // EHRegistrationNode SubRecord
Type::getInt32Ty(Context), // int32_t EncodedScopeTable
Type::getInt32Ty(Context) // int32_t TryLevel
};
SEH4RegistrationTy = StructType::create(FieldTys, "EH4ExceptionRegistration");
return SEH4RegistrationTy;
}
// Emit an exception registration record. These are stack allocations with the
// common subobject of two pointers: the previous registration record (the old
// fs:00) and the personality function for the current frame. The data before
// and after that is personality function specific.
void WinEHStatePass::emitExceptionRegistrationRecord(Function *F) {
assert(Personality == EHPersonality::MSVC_CXX ||
Personality == EHPersonality::MSVC_X86SEH);
StringRef PersonalityName = PersonalityFn->getName();
IRBuilder<> Builder(&F->getEntryBlock(), F->getEntryBlock().begin());
Type *Int8PtrType = Builder.getInt8PtrTy();
Value *SubRecord = nullptr;
if (PersonalityName == "__CxxFrameHandler3") {
Type *RegNodeTy = getCXXEH3RegistrationType();
Value *RegNode = Builder.CreateAlloca(RegNodeTy);
// FIXME: We can skip this in -GS- mode, when we figure that out.
// SavedESP = llvm.stacksave()
Value *SP = Builder.CreateCall(
Intrinsic::getDeclaration(TheModule, Intrinsic::stacksave));
Builder.CreateStore(SP, Builder.CreateStructGEP(RegNodeTy, RegNode, 0));
// TryLevel = -1
Builder.CreateStore(Builder.getInt32(-1),
Builder.CreateStructGEP(RegNodeTy, RegNode, 2));
// FIXME: 'Personality' is incorrect here. We need to generate a trampoline
// that effectively gets the LSDA.
SubRecord = Builder.CreateStructGEP(RegNodeTy, RegNode, 1);
linkExceptionRegistration(Builder, SubRecord, PersonalityFn);
} else if (PersonalityName == "_except_handler3") {
Type *RegNodeTy = getSEH3RegistrationType();
Value *RegNode = Builder.CreateAlloca(RegNodeTy);
// TryLevel = -1
Builder.CreateStore(Builder.getInt32(-1),
Builder.CreateStructGEP(RegNodeTy, RegNode, 2));
// FIXME: Generalize llvm.eh.sjljl.lsda for this.
// ScopeTable = nullptr
Builder.CreateStore(Constant::getNullValue(Int8PtrType),
Builder.CreateStructGEP(RegNodeTy, RegNode, 1));
SubRecord = Builder.CreateStructGEP(RegNodeTy, RegNode, 0);
linkExceptionRegistration(Builder, SubRecord, PersonalityFn);
} else if (PersonalityName == "_except_handler4") {
Type *RegNodeTy = getSEH4RegistrationType();
Value *RegNode = Builder.CreateAlloca(RegNodeTy);
// SavedESP = llvm.stacksave()
Value *SP = Builder.CreateCall(
Intrinsic::getDeclaration(TheModule, Intrinsic::stacksave));
Builder.CreateStore(SP, Builder.CreateStructGEP(RegNodeTy, RegNode, 0));
// TryLevel = -2
Builder.CreateStore(Builder.getInt32(-2),
Builder.CreateStructGEP(RegNodeTy, RegNode, 4));
// FIXME: Generalize llvm.eh.sjljl.lsda for this, and then do the stack
// cookie xor.
// ScopeTable = nullptr
Builder.CreateStore(Builder.getInt32(0),
Builder.CreateStructGEP(RegNodeTy, RegNode, 3));
SubRecord = Builder.CreateStructGEP(RegNodeTy, RegNode, 2);
linkExceptionRegistration(Builder, SubRecord, PersonalityFn);
} else {
llvm_unreachable("unexpected personality function");
}
// FIXME: Insert an unlink before all returns.
for (BasicBlock &BB : *F) {
TerminatorInst *T = BB.getTerminator();
if (!isa<ReturnInst>(T))
continue;
Builder.SetInsertPoint(T);
unlinkExceptionRegistration(Builder, SubRecord);
}
}
void WinEHStatePass::linkExceptionRegistration(IRBuilder<> &Builder,
Value *RegNode, Value *Handler) {
Type *RegNodeTy = getEHRegistrationType();
// Handler = Handler
Handler = Builder.CreateBitCast(Handler, Builder.getInt8PtrTy());
Builder.CreateStore(Handler, Builder.CreateStructGEP(RegNodeTy, RegNode, 1));
// Next = [fs:00]
Constant *FSZero =
Constant::getNullValue(RegNodeTy->getPointerTo()->getPointerTo(257));
Value *Next = Builder.CreateLoad(FSZero);
Builder.CreateStore(Next, Builder.CreateStructGEP(RegNodeTy, RegNode, 0));
// [fs:00] = RegNode
Builder.CreateStore(RegNode, FSZero);
}
void WinEHStatePass::unlinkExceptionRegistration(IRBuilder<> &Builder,
Value *RegNode) {
// Clone RegNode into the current BB for better address mode folding.
if (auto *GEP = dyn_cast<GetElementPtrInst>(RegNode)) {
GEP = cast<GetElementPtrInst>(GEP->clone());
Builder.Insert(GEP);
RegNode = GEP;
}
Type *RegNodeTy = getEHRegistrationType();
// [fs:00] = RegNode->Next
Value *Next =
Builder.CreateLoad(Builder.CreateStructGEP(RegNodeTy, RegNode, 0));
Constant *FSZero =
Constant::getNullValue(RegNodeTy->getPointerTo()->getPointerTo(257));
Builder.CreateStore(Next, FSZero);
}

View File

@ -4,6 +4,7 @@
%frame.reverse = type { %Iter, %Iter }
declare i32 @pers(...)
declare void @llvm.stackrestore(i8*)
declare i8* @llvm.stacksave()
declare void @begin(%Iter* sret)
@ -22,8 +23,7 @@ blah:
; CHECK: calll __chkstk
; CHECK: movl %esp, %[[beg:[^ ]*]]
; CHECK: movl %esp, %[[end:[^ ]*]]
; CHECK: addl $12, %[[end]]
; CHECK: leal 12(%[[beg]]), %[[end:[^ ]*]]
call void @begin(%Iter* sret %temp.lvalue)
; CHECK: calll _begin
@ -49,7 +49,7 @@ invoke.cont5: ; preds = %invoke.cont
ret i32 0
lpad: ; preds = %invoke.cont, %entry
%lp = landingpad { i8*, i32 } personality i8* null
%lp = landingpad { i8*, i32 } personality i32 (...)* @pers
cleanup
unreachable
}

View File

@ -0,0 +1,77 @@
; RUN: llc -mtriple=i686-pc-windows-msvc < %s | FileCheck %s
declare void @may_throw_or_crash()
declare i32 @_except_handler3(...)
declare i32 @_except_handler4(...)
declare i32 @__CxxFrameHandler3(...)
declare void @llvm.eh.begincatch(i8*, i8*)
declare void @llvm.eh.endcatch()
define void @use_except_handler3() {
invoke void @may_throw_or_crash()
to label %cont unwind label %catchall
cont:
ret void
catchall:
landingpad { i8*, i32 } personality i32 (...)* @_except_handler3
catch i8* null
br label %cont
}
; CHECK-LABEL: _use_except_handler3:
; CHECK: subl ${{[0-9]+}}, %esp
; CHECK: movl %fs:0, %[[next:[^ ,]*]]
; CHECK: movl %[[next]], (%esp)
; CHECK: leal (%esp), %[[node:[^ ,]*]]
; CHECK: movl %[[node]], %fs:0
; CHECK: calll _may_throw_or_crash
; CHECK: movl (%esp), %[[next:[^ ,]*]]
; CHECK: movl %[[next]], %fs:0
; CHECK: retl
define void @use_except_handler4() {
invoke void @may_throw_or_crash()
to label %cont unwind label %catchall
cont:
ret void
catchall:
landingpad { i8*, i32 } personality i32 (...)* @_except_handler4
catch i8* null
br label %cont
}
; CHECK-LABEL: _use_except_handler4:
; CHECK: subl ${{[0-9]+}}, %esp
; CHECK: leal 8(%esp), %[[node:[^ ,]*]]
; CHECK: movl %fs:0, %[[next:[^ ,]*]]
; CHECK: movl %[[next]], 8(%esp)
; CHECK: movl %[[node]], %fs:0
; CHECK: calll _may_throw_or_crash
; CHECK: movl 8(%esp), %[[next:[^ ,]*]]
; CHECK: movl %[[next]], %fs:0
; CHECK: retl
define void @use_CxxFrameHandler3() {
invoke void @may_throw_or_crash()
to label %cont unwind label %catchall
cont:
ret void
catchall:
%ehvals = landingpad { i8*, i32 } personality i32 (...)* @__CxxFrameHandler3
catch i8* null
%ehptr = extractvalue { i8*, i32 } %ehvals, 0
call void @llvm.eh.begincatch(i8* %ehptr, i8* null)
call void @llvm.eh.endcatch()
br label %cont
}
; CHECK-LABEL: _use_CxxFrameHandler3:
; CHECK: subl ${{[0-9]+}}, %esp
; CHECK: leal 4(%esp), %[[node:[^ ,]*]]
; CHECK: movl %fs:0, %[[next:[^ ,]*]]
; CHECK: movl %[[next]], 4(%esp)
; CHECK: movl %[[node]], %fs:0
; CHECK: calll _may_throw_or_crash
; CHECK: movl 4(%esp), %[[next:[^ ,]*]]
; CHECK: movl %[[next]], %fs:0
; CHECK: retl