From 85b9ebb7e8115403754c053517a95ba23ff58c5b Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 29 Apr 2015 22:49:54 +0000 Subject: [PATCH] [WinEH] Start EH preparation for 32-bit x86, it uses no arguments 32-bit x86 MSVC-style exceptions are functionaly similar to 64-bit, but they take no arguments. Instead, they implicitly use the value of EBP passed in by the caller as a pointer to the parent's frame. In LLVM, we can represent this as llvm.frameaddress(1), and feed that into all of our calls to llvm.framerecover. The next steps are: - Add an alloca to the fs:00 linked list of handlers - Add something like llvm.sjlj.lsda or generalize it to store in the alloca - Move state number calculation to WinEHPrepare, arrange for FunctionLoweringInfo to call it - Use the state numbers to insert explicit loads and stores in the IR git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@236172 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/LibCallSemantics.cpp | 4 +- lib/CodeGen/WinEHPrepare.cpp | 135 ++++++++++++++------- test/CodeGen/WinEH/cppeh-demote-liveout.ll | 2 +- test/CodeGen/WinEH/cppeh-inalloca.ll | 4 +- test/CodeGen/WinEH/seh-simple.ll | 18 ++- 5 files changed, 108 insertions(+), 55 deletions(-) diff --git a/lib/Analysis/LibCallSemantics.cpp b/lib/Analysis/LibCallSemantics.cpp index 328b186b527..e98540ba7e9 100644 --- a/lib/Analysis/LibCallSemantics.cpp +++ b/lib/Analysis/LibCallSemantics.cpp @@ -73,8 +73,8 @@ EHPersonality llvm::classifyEHPersonality(const Value *Pers) { .Case("__gxx_personality_v0", EHPersonality::GNU_CXX) .Case("__gcc_personality_v0", EHPersonality::GNU_C) .Case("__objc_personality_v0", EHPersonality::GNU_ObjC) - .Case("__except_handler3", EHPersonality::MSVC_X86SEH) - .Case("__except_handler4", EHPersonality::MSVC_X86SEH) + .Case("_except_handler3", EHPersonality::MSVC_X86SEH) + .Case("_except_handler4", EHPersonality::MSVC_X86SEH) .Case("__C_specific_handler", EHPersonality::MSVC_Win64SEH) .Case("__CxxFrameHandler3", EHPersonality::MSVC_CXX) .Default(EHPersonality::Unknown); diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 0fa5a12e2da..0461d71507b 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/Triple.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Analysis/LibCallSemantics.h" #include "llvm/CodeGen/WinEHFuncInfo.h" @@ -71,7 +72,10 @@ class WinEHPrepare : public FunctionPass { public: static char ID; // Pass identification, replacement for typeid. WinEHPrepare(const TargetMachine *TM = nullptr) - : FunctionPass(ID), DT(nullptr), SEHExceptionCodeSlot(nullptr) {} + : FunctionPass(ID), DT(nullptr), SEHExceptionCodeSlot(nullptr) { + if (TM) + TheTriple = Triple(TM->getTargetTriple()); + } bool runOnFunction(Function &Fn) override; @@ -97,6 +101,8 @@ private: LandingPadInst *OutlinedLPad, const LandingPadInst *OriginalLPad, FrameVarInfoMap &VarInfo); + Function *createHandlerFunc(Type *RetTy, const Twine &Name, Module *M, + Value *&ParentFP); bool outlineHandler(ActionHandler *Action, Function *SrcFn, LandingPadInst *LPad, BasicBlock *StartBB, FrameVarInfoMap &VarInfo); @@ -110,6 +116,8 @@ private: void processSEHCatchHandler(CatchHandler *Handler, BasicBlock *StartBB); + Triple TheTriple; + // All fields are reset by runOnFunction. DominatorTree *DT; EHPersonality Personality; @@ -138,12 +146,16 @@ private: // outlined but before the outlined code is pruned from the parent function. DenseMap LPadTargetBlocks; + // Map from outlined handler to call to llvm.frameaddress(1). Only used for + // 32-bit EH. + DenseMap HandlerToParentFP; + AllocaInst *SEHExceptionCodeSlot; }; class WinEHFrameVariableMaterializer : public ValueMaterializer { public: - WinEHFrameVariableMaterializer(Function *OutlinedFn, + WinEHFrameVariableMaterializer(Function *OutlinedFn, Value *ParentFP, FrameVarInfoMap &FrameVarInfo); ~WinEHFrameVariableMaterializer() override {} @@ -179,16 +191,12 @@ private: class WinEHCloningDirectorBase : public CloningDirector { public: - WinEHCloningDirectorBase(Function *HandlerFn, FrameVarInfoMap &VarInfo, - LandingPadMap &LPadMap) - : Materializer(HandlerFn, VarInfo), + WinEHCloningDirectorBase(Function *HandlerFn, Value *ParentFP, + FrameVarInfoMap &VarInfo, LandingPadMap &LPadMap) + : Materializer(HandlerFn, ParentFP, VarInfo), SelectorIDType(Type::getInt32Ty(HandlerFn->getContext())), Int8PtrType(Type::getInt8PtrTy(HandlerFn->getContext())), - LPadMap(LPadMap) { - auto AI = HandlerFn->getArgumentList().begin(); - ++AI; - EstablisherFrame = AI; - } + LPadMap(LPadMap), ParentFP(ParentFP) {} CloningAction handleInstruction(ValueToValueMapTy &VMap, const Instruction *Inst, @@ -225,16 +233,16 @@ protected: LandingPadMap &LPadMap; /// The value representing the parent frame pointer. - Value *EstablisherFrame; + Value *ParentFP; }; class WinEHCatchDirector : public WinEHCloningDirectorBase { public: WinEHCatchDirector( - Function *CatchFn, Value *Selector, FrameVarInfoMap &VarInfo, - LandingPadMap &LPadMap, + Function *CatchFn, Value *ParentFP, Value *Selector, + FrameVarInfoMap &VarInfo, LandingPadMap &LPadMap, DenseMap &NestedLPads) - : WinEHCloningDirectorBase(CatchFn, VarInfo, LPadMap), + : WinEHCloningDirectorBase(CatchFn, ParentFP, VarInfo, LPadMap), CurrentSelector(Selector->stripPointerCasts()), ExceptionObjectVar(nullptr), NestedLPtoOriginalLP(NestedLPads) {} @@ -272,9 +280,10 @@ private: class WinEHCleanupDirector : public WinEHCloningDirectorBase { public: - WinEHCleanupDirector(Function *CleanupFn, FrameVarInfoMap &VarInfo, - LandingPadMap &LPadMap) - : WinEHCloningDirectorBase(CleanupFn, VarInfo, LPadMap) {} + WinEHCleanupDirector(Function *CleanupFn, Value *ParentFP, + FrameVarInfoMap &VarInfo, LandingPadMap &LPadMap) + : WinEHCloningDirectorBase(CleanupFn, ParentFP, VarInfo, + LPadMap) {} CloningAction handleBeginCatch(ValueToValueMapTy &VMap, const Instruction *Inst, @@ -880,19 +889,23 @@ bool WinEHPrepare::prepareExceptionHandlers( if (TempAlloca == getCatchObjectSentinel()) continue; // Skip catch parameter sentinels. Function *HandlerFn = TempAlloca->getParent()->getParent(); - // FIXME: Sink this GEP into the blocks where it is used. + llvm::Value *FP = HandlerToParentFP[HandlerFn]; + assert(FP); + + // FIXME: Sink this framerecover into the blocks where it is used. Builder.SetInsertPoint(TempAlloca); Builder.SetCurrentDebugLocation(TempAlloca->getDebugLoc()); Value *RecoverArgs[] = { - Builder.CreateBitCast(&F, Int8PtrType, ""), - &(HandlerFn->getArgumentList().back()), + Builder.CreateBitCast(&F, Int8PtrType, ""), FP, llvm::ConstantInt::get(Int32Type, AllocasToEscape.size() - 1)}; - Value *RecoveredAlloca = Builder.CreateCall(RecoverFrameFn, RecoverArgs); + Instruction *RecoveredAlloca = + Builder.CreateCall(RecoverFrameFn, RecoverArgs); + // Add a pointer bitcast if the alloca wasn't an i8. if (RecoveredAlloca->getType() != TempAlloca->getType()) { RecoveredAlloca->setName(Twine(TempAlloca->getName()) + ".i8"); - RecoveredAlloca = - Builder.CreateBitCast(RecoveredAlloca, TempAlloca->getType()); + RecoveredAlloca = cast( + Builder.CreateBitCast(RecoveredAlloca, TempAlloca->getType())); } TempAlloca->replaceAllUsesWith(RecoveredAlloca); TempAlloca->removeFromParent(); @@ -918,6 +931,8 @@ bool WinEHPrepare::prepareExceptionHandlers( CatchHandlerMap.clear(); DeleteContainerSeconds(CleanupHandlerMap); CleanupHandlerMap.clear(); + HandlerToParentFP.clear(); + DT = nullptr; return HandlersOutlined; } @@ -987,7 +1002,6 @@ void WinEHPrepare::completeNestedLandingPad(Function *ParentFn, IntrinsicInst *EHActions = cast(Recover->clone()); // Remap the exception variables into the outlined function. - WinEHFrameVariableMaterializer Materializer(OutlinedHandlerFn, FrameVarInfo); SmallVector ActionTargets; SmallVector ActionList; parseEHActions(EHActions, ActionList); @@ -1148,35 +1162,62 @@ void WinEHPrepare::addStubInvokeToHandlerIfNeeded(Function *Handler, InvokeInst::Create(F, NewRetBB, StubLandingPad, None, "", OldRetBB); } +// FIXME: Consider sinking this into lib/Target/X86 somehow. TargetLowering +// usually doesn't build LLVM IR, so that's probably the wrong place. +Function *WinEHPrepare::createHandlerFunc(Type *RetTy, const Twine &Name, + Module *M, Value *&ParentFP) { + // x64 uses a two-argument prototype where the parent FP is the second + // argument. x86 uses no arguments, just the incoming EBP value. + LLVMContext &Context = M->getContext(); + FunctionType *FnType; + if (TheTriple.getArch() == Triple::x86_64) { + Type *Int8PtrType = Type::getInt8PtrTy(Context); + Type *ArgTys[2] = {Int8PtrType, Int8PtrType}; + FnType = FunctionType::get(RetTy, ArgTys, false); + } else { + FnType = FunctionType::get(RetTy, None, false); + } + + Function *Handler = + Function::Create(FnType, GlobalVariable::InternalLinkage, Name, M); + BasicBlock *Entry = BasicBlock::Create(Context, "entry"); + Handler->getBasicBlockList().push_front(Entry); + if (TheTriple.getArch() == Triple::x86_64) { + ParentFP = &(Handler->getArgumentList().back()); + } else { + assert(M); + Function *FrameAddressFn = + Intrinsic::getDeclaration(M, Intrinsic::frameaddress); + Value *Args[1] = {ConstantInt::get(Type::getInt32Ty(Context), 1)}; + ParentFP = CallInst::Create(FrameAddressFn, Args, "parent_fp", + &Handler->getEntryBlock()); + } + return Handler; +} + bool WinEHPrepare::outlineHandler(ActionHandler *Action, Function *SrcFn, LandingPadInst *LPad, BasicBlock *StartBB, FrameVarInfoMap &VarInfo) { Module *M = SrcFn->getParent(); LLVMContext &Context = M->getContext(); + Type *Int8PtrType = Type::getInt8PtrTy(Context); // Create a new function to receive the handler contents. - Type *Int8PtrType = Type::getInt8PtrTy(Context); - std::vector ArgTys; - ArgTys.push_back(Int8PtrType); - ArgTys.push_back(Int8PtrType); + Value *ParentFP; Function *Handler; if (Action->getType() == Catch) { - FunctionType *FnType = FunctionType::get(Int8PtrType, ArgTys, false); - Handler = Function::Create(FnType, GlobalVariable::InternalLinkage, - SrcFn->getName() + ".catch", M); + Handler = createHandlerFunc(Int8PtrType, SrcFn->getName() + ".catch", M, + ParentFP); } else { - FunctionType *FnType = - FunctionType::get(Type::getVoidTy(Context), ArgTys, false); - Handler = Function::Create(FnType, GlobalVariable::InternalLinkage, - SrcFn->getName() + ".cleanup", M); + Handler = createHandlerFunc(Type::getVoidTy(Context), + SrcFn->getName() + ".cleanup", M, ParentFP); } - + HandlerToParentFP[Handler] = ParentFP; Handler->addFnAttr("wineh-parent", SrcFn->getName()); + BasicBlock *Entry = &Handler->getEntryBlock(); // Generate a standard prolog to setup the frame recovery structure. IRBuilder<> Builder(Context); - BasicBlock *Entry = BasicBlock::Create(Context, "entry"); - Handler->getBasicBlockList().push_front(Entry); Builder.SetInsertPoint(Entry); Builder.SetCurrentDebugLocation(LPad->getDebugLoc()); @@ -1189,12 +1230,14 @@ bool WinEHPrepare::outlineHandler(ActionHandler *Action, Function *SrcFn, LPadMap.mapLandingPad(LPad); if (auto *CatchAction = dyn_cast(Action)) { Constant *Sel = CatchAction->getSelector(); - Director.reset(new WinEHCatchDirector(Handler, Sel, VarInfo, LPadMap, + Director.reset(new WinEHCatchDirector(Handler, ParentFP, Sel, + VarInfo, LPadMap, NestedLPtoOriginalLP)); LPadMap.remapEHValues(VMap, UndefValue::get(Int8PtrType), ConstantInt::get(Type::getInt32Ty(Context), 1)); } else { - Director.reset(new WinEHCleanupDirector(Handler, VarInfo, LPadMap)); + Director.reset( + new WinEHCleanupDirector(Handler, ParentFP, VarInfo, LPadMap)); LPadMap.remapEHValues(VMap, UndefValue::get(Int8PtrType), UndefValue::get(Type::getInt32Ty(Context))); } @@ -1421,7 +1464,7 @@ CloningDirector::CloningAction WinEHCloningDirectorBase::handleInstruction( // When outlining llvm.frameaddress(i32 0), remap that to the second argument, // which is the FP of the parent. if (isFrameAddressCall(Inst)) { - VMap[Inst] = EstablisherFrame; + VMap[Inst] = ParentFP; return CloningDirector::SkipInstruction; } @@ -1660,10 +1703,16 @@ WinEHCleanupDirector::handleCompare(ValueToValueMapTy &VMap, } WinEHFrameVariableMaterializer::WinEHFrameVariableMaterializer( - Function *OutlinedFn, FrameVarInfoMap &FrameVarInfo) + Function *OutlinedFn, Value *ParentFP, FrameVarInfoMap &FrameVarInfo) : FrameVarInfo(FrameVarInfo), Builder(OutlinedFn->getContext()) { BasicBlock *EntryBB = &OutlinedFn->getEntryBlock(); - Builder.SetInsertPoint(EntryBB, EntryBB->getFirstInsertionPt()); + + // New allocas should be inserted in the entry block, but after the parent FP + // is established if it is an instruction. + Instruction *InsertPoint = EntryBB->getFirstInsertionPt(); + if (auto *FPInst = dyn_cast(ParentFP)) + InsertPoint = FPInst->getNextNode(); + Builder.SetInsertPoint(EntryBB, InsertPoint); } Value *WinEHFrameVariableMaterializer::materializeValueFor(Value *V) { diff --git a/test/CodeGen/WinEH/cppeh-demote-liveout.ll b/test/CodeGen/WinEH/cppeh-demote-liveout.ll index 2de2cc67aaa..48d9b39ca64 100644 --- a/test/CodeGen/WinEH/cppeh-demote-liveout.ll +++ b/test/CodeGen/WinEH/cppeh-demote-liveout.ll @@ -1,4 +1,4 @@ -; RUN: opt -winehprepare -S < %s | FileCheck %s +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S < %s | FileCheck %s ; Notionally based on this C++ source: ; int liveout_catch(int p) { diff --git a/test/CodeGen/WinEH/cppeh-inalloca.ll b/test/CodeGen/WinEH/cppeh-inalloca.ll index 927aeed3b6f..13471b8661a 100644 --- a/test/CodeGen/WinEH/cppeh-inalloca.ll +++ b/test/CodeGen/WinEH/cppeh-inalloca.ll @@ -1,4 +1,4 @@ -; RUN: opt -mtriple=i386-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s ; This test is built from the following code: ; struct A { @@ -24,8 +24,6 @@ ; the inalloca instruction was manually sunk into the landingpad. ; ModuleID = 'cppeh-inalloca.cpp' -target datalayout = "e-m:w-p:32:32-i64:64-f80:32-n8:16:32-S32" -target triple = "i386-pc-windows-msvc" %rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } %struct.A = type { i32 } diff --git a/test/CodeGen/WinEH/seh-simple.ll b/test/CodeGen/WinEH/seh-simple.ll index aa9a6ca03b1..ea453df80a2 100644 --- a/test/CodeGen/WinEH/seh-simple.ll +++ b/test/CodeGen/WinEH/seh-simple.ll @@ -1,7 +1,10 @@ -; RUN: opt -S -winehprepare -mtriple=x86_64-windows-msvc < %s | FileCheck %s +; RUN: opt -S -winehprepare -mtriple=x86_64-windows-msvc < %s \ +; RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X64 -target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-windows-msvc" +; This test should also pass in 32-bit using _except_handler3. +; RUN: sed %s -e 's/__C_specific_handler/_except_handler3/' \ +; RUN: | opt -S -winehprepare -mtriple=i686-windows-msvc \ +; RUN: | FileCheck %s --check-prefix=CHECK --check-prefix=X86 declare void @cleanup() declare i32 @filt() @@ -139,7 +142,7 @@ eh.resume: ; CHECK: landingpad { i8*, i32 } ; CHECK-NEXT: cleanup ; CHECK-NEXT: catch i32 ()* @filt -; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void (i8*, i8*)* @lpad_phi.cleanup, i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@lpad_phi, %lpad.return_crit_edge)) +; CHECK-NEXT: call i8* (...) @llvm.eh.actions(i32 0, void ({{.*}})* @lpad_phi.cleanup, i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@lpad_phi, %lpad.return_crit_edge)) ; CHECK-NEXT: indirectbr {{.*}} [label %lpad.return_crit_edge] ; ; CHECK: lpad.return_crit_edge: @@ -178,7 +181,7 @@ eh.resume: ; CHECK-NEXT: cleanup ; CHECK-NEXT: catch i32 ()* @filt ; CHECK-NEXT: call i8* (...) @llvm.eh.actions( -; CHECK: i32 0, void (i8*, i8*)* @cleanup_and_except.cleanup, +; CHECK: i32 0, void ({{.*}})* @cleanup_and_except.cleanup, ; CHECK: i32 1, i8* bitcast (i32 ()* @filt to i8*), i32 -1, i8* blockaddress(@cleanup_and_except, %lpad.return_crit_edge)) ; CHECK-NEXT: indirectbr {{.*}} [label %lpad.return_crit_edge] ; @@ -190,6 +193,9 @@ eh.resume: ; CHECK-NEXT: ret i32 %r ; FIXME: This cleanup is an artifact of bad demotion. -; CHECK-LABEL: define internal void @lpad_phi.cleanup(i8*, i8*) +; X64-LABEL: define internal void @lpad_phi.cleanup(i8*, i8*) +; X86-LABEL: define internal void @lpad_phi.cleanup() +; X86: call i8* @llvm.frameaddress(i32 1) +; CHECK: call i8* @llvm.framerecover({{.*}}) ; CHECK: load i32 ; CHECK: store i32 %{{.*}}, i32* -- 2.34.1