| //===-- X86WinEHState - Insert EH state updates for win32 exceptions ------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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/ADT/PostOrderIterator.h" |
| #include "llvm/Analysis/CFG.h" |
| #include "llvm/Analysis/EHPersonalities.h" |
| #include "llvm/CodeGen/MachineModuleInfo.h" |
| #include "llvm/CodeGen/WinEHFuncInfo.h" |
| #include "llvm/IR/CFG.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/IRBuilder.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/Intrinsics.h" |
| #include "llvm/IR/IntrinsicsX86.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/Debug.h" |
| #include <deque> |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "winehstate" |
| |
| namespace { |
| const int OverdefinedState = INT_MIN; |
| |
| 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; |
| |
| StringRef getPassName() const override { |
| return "Windows 32-bit x86 EH state insertion"; |
| } |
| |
| private: |
| void emitExceptionRegistrationRecord(Function *F); |
| |
| void linkExceptionRegistration(IRBuilder<> &Builder, Function *Handler); |
| void unlinkExceptionRegistration(IRBuilder<> &Builder); |
| void addStateStores(Function &F, WinEHFuncInfo &FuncInfo); |
| void insertStateNumberStore(Instruction *IP, int State); |
| |
| Value *emitEHLSDA(IRBuilder<> &Builder, Function *F); |
| |
| Function *generateLSDAInEAXThunk(Function *ParentFunc); |
| |
| bool isStateStoreNeeded(EHPersonality Personality, CallBase &Call); |
| void rewriteSetJmpCall(IRBuilder<> &Builder, Function &F, CallBase &Call, |
| Value *State); |
| int getBaseStateForBB(DenseMap<BasicBlock *, ColorVector> &BlockColors, |
| WinEHFuncInfo &FuncInfo, BasicBlock *BB); |
| int getStateForCall(DenseMap<BasicBlock *, ColorVector> &BlockColors, |
| WinEHFuncInfo &FuncInfo, CallBase &Call); |
| |
| // Module-level type getters. |
| Type *getEHLinkRegistrationType(); |
| Type *getSEHRegistrationType(); |
| Type *getCXXEHRegistrationType(); |
| |
| // Per-module data. |
| Module *TheModule = nullptr; |
| StructType *EHLinkRegistrationTy = nullptr; |
| StructType *CXXEHRegistrationTy = nullptr; |
| StructType *SEHRegistrationTy = nullptr; |
| FunctionCallee SetJmp3 = nullptr; |
| FunctionCallee CxxLongjmpUnwind = nullptr; |
| |
| // Per-function state |
| EHPersonality Personality = EHPersonality::Unknown; |
| Function *PersonalityFn = nullptr; |
| bool UseStackGuard = false; |
| int ParentBaseState = 0; |
| FunctionCallee SehLongjmpUnwind = nullptr; |
| Constant *Cookie = nullptr; |
| |
| /// The stack allocation containing all EH data, including the link in the |
| /// fs:00 chain and the current state. |
| AllocaInst *RegNode = nullptr; |
| |
| // The allocation containing the EH security guard. |
| AllocaInst *EHGuardNode = nullptr; |
| |
| /// The index of the state field of RegNode. |
| int StateFieldIndex = ~0U; |
| |
| /// The linked list node subobject inside of RegNode. |
| Value *Link = nullptr; |
| }; |
| } // namespace |
| |
| FunctionPass *llvm::createX86WinEHStatePass() { return new WinEHStatePass(); } |
| |
| char WinEHStatePass::ID = 0; |
| |
| INITIALIZE_PASS(WinEHStatePass, "x86-winehstate", |
| "Insert stores for EH state numbers", false, false) |
| |
| bool WinEHStatePass::doInitialization(Module &M) { |
| TheModule = &M; |
| return false; |
| } |
| |
| bool WinEHStatePass::doFinalization(Module &M) { |
| assert(TheModule == &M); |
| TheModule = nullptr; |
| EHLinkRegistrationTy = nullptr; |
| CXXEHRegistrationTy = nullptr; |
| SEHRegistrationTy = nullptr; |
| SetJmp3 = nullptr; |
| CxxLongjmpUnwind = nullptr; |
| SehLongjmpUnwind = nullptr; |
| Cookie = nullptr; |
| return false; |
| } |
| |
| void WinEHStatePass::getAnalysisUsage(AnalysisUsage &AU) const { |
| // This pass should only insert a stack allocation, memory accesses, and |
| // localrecovers. |
| AU.setPreservesCFG(); |
| } |
| |
| bool WinEHStatePass::runOnFunction(Function &F) { |
| // Don't insert state stores or exception handler thunks for |
| // available_externally functions. The handler needs to reference the LSDA, |
| // which will not be emitted in this case. |
| if (F.hasAvailableExternallyLinkage()) |
| return false; |
| |
| // Check the personality. Do nothing if this personality doesn't use funclets. |
| if (!F.hasPersonalityFn()) |
| return false; |
| PersonalityFn = |
| dyn_cast<Function>(F.getPersonalityFn()->stripPointerCasts()); |
| if (!PersonalityFn) |
| return false; |
| Personality = classifyEHPersonality(PersonalityFn); |
| if (!isFuncletEHPersonality(Personality)) |
| return false; |
| |
| // Skip this function if there are no EH pads and we aren't using IR-level |
| // outlining. |
| bool HasPads = false; |
| for (BasicBlock &BB : F) { |
| if (BB.isEHPad()) { |
| HasPads = true; |
| break; |
| } |
| } |
| if (!HasPads) |
| return false; |
| |
| Type *Int8PtrType = Type::getInt8PtrTy(TheModule->getContext()); |
| SetJmp3 = TheModule->getOrInsertFunction( |
| "_setjmp3", FunctionType::get( |
| Type::getInt32Ty(TheModule->getContext()), |
| {Int8PtrType, Type::getInt32Ty(TheModule->getContext())}, |
| /*isVarArg=*/true)); |
| |
| emitExceptionRegistrationRecord(&F); |
| |
| // The state numbers calculated here in IR must agree with what we calculate |
| // later on for the MachineFunction. In particular, if an IR pass deletes an |
| // unreachable EH pad after this point before machine CFG construction, we |
| // will be in trouble. If this assumption is ever broken, we should turn the |
| // numbers into an immutable analysis pass. |
| WinEHFuncInfo FuncInfo; |
| addStateStores(F, FuncInfo); |
| |
| // Reset per-function state. |
| PersonalityFn = nullptr; |
| Personality = EHPersonality::Unknown; |
| UseStackGuard = false; |
| RegNode = nullptr; |
| EHGuardNode = nullptr; |
| |
| return true; |
| } |
| |
| /// Get the common EH registration subobject: |
| /// typedef _EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE)( |
| /// _EXCEPTION_RECORD *, void *, _CONTEXT *, void *); |
| /// struct EHRegistrationNode { |
| /// EHRegistrationNode *Next; |
| /// PEXCEPTION_ROUTINE Handler; |
| /// }; |
| Type *WinEHStatePass::getEHLinkRegistrationType() { |
| if (EHLinkRegistrationTy) |
| return EHLinkRegistrationTy; |
| LLVMContext &Context = TheModule->getContext(); |
| EHLinkRegistrationTy = StructType::create(Context, "EHRegistrationNode"); |
| Type *FieldTys[] = { |
| EHLinkRegistrationTy->getPointerTo(0), // EHRegistrationNode *Next |
| Type::getInt8PtrTy(Context) // EXCEPTION_DISPOSITION (*Handler)(...) |
| }; |
| EHLinkRegistrationTy->setBody(FieldTys, false); |
| return EHLinkRegistrationTy; |
| } |
| |
| /// The __CxxFrameHandler3 registration node: |
| /// struct CXXExceptionRegistration { |
| /// void *SavedESP; |
| /// EHRegistrationNode SubRecord; |
| /// int32_t TryLevel; |
| /// }; |
| Type *WinEHStatePass::getCXXEHRegistrationType() { |
| if (CXXEHRegistrationTy) |
| return CXXEHRegistrationTy; |
| LLVMContext &Context = TheModule->getContext(); |
| Type *FieldTys[] = { |
| Type::getInt8PtrTy(Context), // void *SavedESP |
| getEHLinkRegistrationType(), // EHRegistrationNode SubRecord |
| Type::getInt32Ty(Context) // int32_t TryLevel |
| }; |
| CXXEHRegistrationTy = |
| StructType::create(FieldTys, "CXXExceptionRegistration"); |
| return CXXEHRegistrationTy; |
| } |
| |
| /// The _except_handler3/4 registration node: |
| /// struct EH4ExceptionRegistration { |
| /// void *SavedESP; |
| /// _EXCEPTION_POINTERS *ExceptionPointers; |
| /// EHRegistrationNode SubRecord; |
| /// int32_t EncodedScopeTable; |
| /// int32_t TryLevel; |
| /// }; |
| Type *WinEHStatePass::getSEHRegistrationType() { |
| if (SEHRegistrationTy) |
| return SEHRegistrationTy; |
| LLVMContext &Context = TheModule->getContext(); |
| Type *FieldTys[] = { |
| Type::getInt8PtrTy(Context), // void *SavedESP |
| Type::getInt8PtrTy(Context), // void *ExceptionPointers |
| getEHLinkRegistrationType(), // EHRegistrationNode SubRecord |
| Type::getInt32Ty(Context), // int32_t EncodedScopeTable |
| Type::getInt32Ty(Context) // int32_t TryLevel |
| }; |
| SEHRegistrationTy = StructType::create(FieldTys, "SEHExceptionRegistration"); |
| return SEHRegistrationTy; |
| } |
| |
| // 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); |
| |
| // Struct type of RegNode. Used for GEPing. |
| Type *RegNodeTy; |
| |
| IRBuilder<> Builder(&F->getEntryBlock(), F->getEntryBlock().begin()); |
| Type *Int8PtrType = Builder.getInt8PtrTy(); |
| Type *Int32Ty = Builder.getInt32Ty(); |
| Type *VoidTy = Builder.getVoidTy(); |
| |
| if (Personality == EHPersonality::MSVC_CXX) { |
| RegNodeTy = getCXXEHRegistrationType(); |
| 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 = -1 |
| StateFieldIndex = 2; |
| ParentBaseState = -1; |
| insertStateNumberStore(&*Builder.GetInsertPoint(), ParentBaseState); |
| // Handler = __ehhandler$F |
| Function *Trampoline = generateLSDAInEAXThunk(F); |
| Link = Builder.CreateStructGEP(RegNodeTy, RegNode, 1); |
| linkExceptionRegistration(Builder, Trampoline); |
| |
| CxxLongjmpUnwind = TheModule->getOrInsertFunction( |
| "__CxxLongjmpUnwind", |
| FunctionType::get(VoidTy, Int8PtrType, /*isVarArg=*/false)); |
| cast<Function>(CxxLongjmpUnwind.getCallee()->stripPointerCasts()) |
| ->setCallingConv(CallingConv::X86_StdCall); |
| } else if (Personality == EHPersonality::MSVC_X86SEH) { |
| // If _except_handler4 is in use, some additional guard checks and prologue |
| // stuff is required. |
| StringRef PersonalityName = PersonalityFn->getName(); |
| UseStackGuard = (PersonalityName == "_except_handler4"); |
| |
| // Allocate local structures. |
| RegNodeTy = getSEHRegistrationType(); |
| RegNode = Builder.CreateAlloca(RegNodeTy); |
| if (UseStackGuard) |
| EHGuardNode = Builder.CreateAlloca(Int32Ty); |
| |
| // SavedESP = llvm.stacksave() |
| Value *SP = Builder.CreateCall( |
| Intrinsic::getDeclaration(TheModule, Intrinsic::stacksave), {}); |
| Builder.CreateStore(SP, Builder.CreateStructGEP(RegNodeTy, RegNode, 0)); |
| // TryLevel = -2 / -1 |
| StateFieldIndex = 4; |
| ParentBaseState = UseStackGuard ? -2 : -1; |
| insertStateNumberStore(&*Builder.GetInsertPoint(), ParentBaseState); |
| // ScopeTable = llvm.x86.seh.lsda(F) |
| Value *LSDA = emitEHLSDA(Builder, F); |
| LSDA = Builder.CreatePtrToInt(LSDA, Int32Ty); |
| // If using _except_handler4, xor the address of the table with |
| // __security_cookie. |
| if (UseStackGuard) { |
| Cookie = TheModule->getOrInsertGlobal("__security_cookie", Int32Ty); |
| Value *Val = Builder.CreateLoad(Int32Ty, Cookie, "cookie"); |
| LSDA = Builder.CreateXor(LSDA, Val); |
| } |
| Builder.CreateStore(LSDA, Builder.CreateStructGEP(RegNodeTy, RegNode, 3)); |
| |
| // If using _except_handler4, the EHGuard contains: FramePtr xor Cookie. |
| if (UseStackGuard) { |
| Value *Val = Builder.CreateLoad(Int32Ty, Cookie); |
| Value *FrameAddr = Builder.CreateCall( |
| Intrinsic::getDeclaration( |
| TheModule, Intrinsic::frameaddress, |
| Builder.getInt8PtrTy( |
| TheModule->getDataLayout().getAllocaAddrSpace())), |
| Builder.getInt32(0), "frameaddr"); |
| Value *FrameAddrI32 = Builder.CreatePtrToInt(FrameAddr, Int32Ty); |
| FrameAddrI32 = Builder.CreateXor(FrameAddrI32, Val); |
| Builder.CreateStore(FrameAddrI32, EHGuardNode); |
| } |
| |
| // Register the exception handler. |
| Link = Builder.CreateStructGEP(RegNodeTy, RegNode, 2); |
| linkExceptionRegistration(Builder, PersonalityFn); |
| |
| SehLongjmpUnwind = TheModule->getOrInsertFunction( |
| UseStackGuard ? "_seh_longjmp_unwind4" : "_seh_longjmp_unwind", |
| FunctionType::get(Type::getVoidTy(TheModule->getContext()), Int8PtrType, |
| /*isVarArg=*/false)); |
| cast<Function>(SehLongjmpUnwind.getCallee()->stripPointerCasts()) |
| ->setCallingConv(CallingConv::X86_StdCall); |
| } else { |
| llvm_unreachable("unexpected personality function"); |
| } |
| |
| // Insert an unlink before all returns. |
| for (BasicBlock &BB : *F) { |
| Instruction *T = BB.getTerminator(); |
| if (!isa<ReturnInst>(T)) |
| continue; |
| Builder.SetInsertPoint(T); |
| unlinkExceptionRegistration(Builder); |
| } |
| } |
| |
| Value *WinEHStatePass::emitEHLSDA(IRBuilder<> &Builder, Function *F) { |
| Value *FI8 = Builder.CreateBitCast(F, Type::getInt8PtrTy(F->getContext())); |
| return Builder.CreateCall( |
| Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_lsda), FI8); |
| } |
| |
| /// Generate a thunk that puts the LSDA of ParentFunc in EAX and then calls |
| /// PersonalityFn, forwarding the parameters passed to PEXCEPTION_ROUTINE: |
| /// typedef _EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE)( |
| /// _EXCEPTION_RECORD *, void *, _CONTEXT *, void *); |
| /// We essentially want this code: |
| /// movl $lsda, %eax |
| /// jmpl ___CxxFrameHandler3 |
| Function *WinEHStatePass::generateLSDAInEAXThunk(Function *ParentFunc) { |
| LLVMContext &Context = ParentFunc->getContext(); |
| Type *Int32Ty = Type::getInt32Ty(Context); |
| Type *Int8PtrType = Type::getInt8PtrTy(Context); |
| Type *ArgTys[5] = {Int8PtrType, Int8PtrType, Int8PtrType, Int8PtrType, |
| Int8PtrType}; |
| FunctionType *TrampolineTy = |
| FunctionType::get(Int32Ty, makeArrayRef(&ArgTys[0], 4), |
| /*isVarArg=*/false); |
| FunctionType *TargetFuncTy = |
| FunctionType::get(Int32Ty, makeArrayRef(&ArgTys[0], 5), |
| /*isVarArg=*/false); |
| Function *Trampoline = |
| Function::Create(TrampolineTy, GlobalValue::InternalLinkage, |
| Twine("__ehhandler$") + GlobalValue::dropLLVMManglingEscape( |
| ParentFunc->getName()), |
| TheModule); |
| if (auto *C = ParentFunc->getComdat()) |
| Trampoline->setComdat(C); |
| BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", Trampoline); |
| IRBuilder<> Builder(EntryBB); |
| Value *LSDA = emitEHLSDA(Builder, ParentFunc); |
| Value *CastPersonality = |
| Builder.CreateBitCast(PersonalityFn, TargetFuncTy->getPointerTo()); |
| auto AI = Trampoline->arg_begin(); |
| Value *Args[5] = {LSDA, &*AI++, &*AI++, &*AI++, &*AI++}; |
| CallInst *Call = Builder.CreateCall(TargetFuncTy, CastPersonality, Args); |
| // Can't use musttail due to prototype mismatch, but we can use tail. |
| Call->setTailCall(true); |
| // Set inreg so we pass it in EAX. |
| Call->addParamAttr(0, Attribute::InReg); |
| Builder.CreateRet(Call); |
| return Trampoline; |
| } |
| |
| void WinEHStatePass::linkExceptionRegistration(IRBuilder<> &Builder, |
| Function *Handler) { |
| // Emit the .safeseh directive for this function. |
| Handler->addFnAttr("safeseh"); |
| |
| Type *LinkTy = getEHLinkRegistrationType(); |
| // Handler = Handler |
| Value *HandlerI8 = Builder.CreateBitCast(Handler, Builder.getInt8PtrTy()); |
| Builder.CreateStore(HandlerI8, Builder.CreateStructGEP(LinkTy, Link, 1)); |
| // Next = [fs:00] |
| Constant *FSZero = |
| Constant::getNullValue(LinkTy->getPointerTo()->getPointerTo(257)); |
| Value *Next = Builder.CreateLoad(LinkTy->getPointerTo(), FSZero); |
| Builder.CreateStore(Next, Builder.CreateStructGEP(LinkTy, Link, 0)); |
| // [fs:00] = Link |
| Builder.CreateStore(Link, FSZero); |
| } |
| |
| void WinEHStatePass::unlinkExceptionRegistration(IRBuilder<> &Builder) { |
| // Clone Link into the current BB for better address mode folding. |
| if (auto *GEP = dyn_cast<GetElementPtrInst>(Link)) { |
| GEP = cast<GetElementPtrInst>(GEP->clone()); |
| Builder.Insert(GEP); |
| Link = GEP; |
| } |
| Type *LinkTy = getEHLinkRegistrationType(); |
| // [fs:00] = Link->Next |
| Value *Next = Builder.CreateLoad(LinkTy->getPointerTo(), |
| Builder.CreateStructGEP(LinkTy, Link, 0)); |
| Constant *FSZero = |
| Constant::getNullValue(LinkTy->getPointerTo()->getPointerTo(257)); |
| Builder.CreateStore(Next, FSZero); |
| } |
| |
| // Calls to setjmp(p) are lowered to _setjmp3(p, 0) by the frontend. |
| // The idea behind _setjmp3 is that it takes an optional number of personality |
| // specific parameters to indicate how to restore the personality-specific frame |
| // state when longjmp is initiated. Typically, the current TryLevel is saved. |
| void WinEHStatePass::rewriteSetJmpCall(IRBuilder<> &Builder, Function &F, |
| CallBase &Call, Value *State) { |
| // Don't rewrite calls with a weird number of arguments. |
| if (Call.arg_size() != 2) |
| return; |
| |
| SmallVector<OperandBundleDef, 1> OpBundles; |
| Call.getOperandBundlesAsDefs(OpBundles); |
| |
| SmallVector<Value *, 3> OptionalArgs; |
| if (Personality == EHPersonality::MSVC_CXX) { |
| OptionalArgs.push_back(CxxLongjmpUnwind.getCallee()); |
| OptionalArgs.push_back(State); |
| OptionalArgs.push_back(emitEHLSDA(Builder, &F)); |
| } else if (Personality == EHPersonality::MSVC_X86SEH) { |
| OptionalArgs.push_back(SehLongjmpUnwind.getCallee()); |
| OptionalArgs.push_back(State); |
| if (UseStackGuard) |
| OptionalArgs.push_back(Cookie); |
| } else { |
| llvm_unreachable("unhandled personality!"); |
| } |
| |
| SmallVector<Value *, 5> Args; |
| Args.push_back( |
| Builder.CreateBitCast(Call.getArgOperand(0), Builder.getInt8PtrTy())); |
| Args.push_back(Builder.getInt32(OptionalArgs.size())); |
| Args.append(OptionalArgs.begin(), OptionalArgs.end()); |
| |
| CallBase *NewCall; |
| if (auto *CI = dyn_cast<CallInst>(&Call)) { |
| CallInst *NewCI = Builder.CreateCall(SetJmp3, Args, OpBundles); |
| NewCI->setTailCallKind(CI->getTailCallKind()); |
| NewCall = NewCI; |
| } else { |
| auto *II = cast<InvokeInst>(&Call); |
| NewCall = Builder.CreateInvoke( |
| SetJmp3, II->getNormalDest(), II->getUnwindDest(), Args, OpBundles); |
| } |
| NewCall->setCallingConv(Call.getCallingConv()); |
| NewCall->setAttributes(Call.getAttributes()); |
| NewCall->setDebugLoc(Call.getDebugLoc()); |
| |
| NewCall->takeName(&Call); |
| Call.replaceAllUsesWith(NewCall); |
| Call.eraseFromParent(); |
| } |
| |
| // Figure out what state we should assign calls in this block. |
| int WinEHStatePass::getBaseStateForBB( |
| DenseMap<BasicBlock *, ColorVector> &BlockColors, WinEHFuncInfo &FuncInfo, |
| BasicBlock *BB) { |
| int BaseState = ParentBaseState; |
| auto &BBColors = BlockColors[BB]; |
| |
| assert(BBColors.size() == 1 && "multi-color BB not removed by preparation"); |
| BasicBlock *FuncletEntryBB = BBColors.front(); |
| if (auto *FuncletPad = |
| dyn_cast<FuncletPadInst>(FuncletEntryBB->getFirstNonPHI())) { |
| auto BaseStateI = FuncInfo.FuncletBaseStateMap.find(FuncletPad); |
| if (BaseStateI != FuncInfo.FuncletBaseStateMap.end()) |
| BaseState = BaseStateI->second; |
| } |
| |
| return BaseState; |
| } |
| |
| // Calculate the state a call-site is in. |
| int WinEHStatePass::getStateForCall( |
| DenseMap<BasicBlock *, ColorVector> &BlockColors, WinEHFuncInfo &FuncInfo, |
| CallBase &Call) { |
| if (auto *II = dyn_cast<InvokeInst>(&Call)) { |
| // Look up the state number of the EH pad this unwinds to. |
| assert(FuncInfo.InvokeStateMap.count(II) && "invoke has no state!"); |
| return FuncInfo.InvokeStateMap[II]; |
| } |
| // Possibly throwing call instructions have no actions to take after |
| // an unwind. Ensure they are in the -1 state. |
| return getBaseStateForBB(BlockColors, FuncInfo, Call.getParent()); |
| } |
| |
| // Calculate the intersection of all the FinalStates for a BasicBlock's |
| // predecessors. |
| static int getPredState(DenseMap<BasicBlock *, int> &FinalStates, Function &F, |
| int ParentBaseState, BasicBlock *BB) { |
| // The entry block has no predecessors but we know that the prologue always |
| // sets us up with a fixed state. |
| if (&F.getEntryBlock() == BB) |
| return ParentBaseState; |
| |
| // This is an EH Pad, conservatively report this basic block as overdefined. |
| if (BB->isEHPad()) |
| return OverdefinedState; |
| |
| int CommonState = OverdefinedState; |
| for (BasicBlock *PredBB : predecessors(BB)) { |
| // We didn't manage to get a state for one of these predecessors, |
| // conservatively report this basic block as overdefined. |
| auto PredEndState = FinalStates.find(PredBB); |
| if (PredEndState == FinalStates.end()) |
| return OverdefinedState; |
| |
| // This code is reachable via exceptional control flow, |
| // conservatively report this basic block as overdefined. |
| if (isa<CatchReturnInst>(PredBB->getTerminator())) |
| return OverdefinedState; |
| |
| int PredState = PredEndState->second; |
| assert(PredState != OverdefinedState && |
| "overdefined BBs shouldn't be in FinalStates"); |
| if (CommonState == OverdefinedState) |
| CommonState = PredState; |
| |
| // At least two predecessors have different FinalStates, |
| // conservatively report this basic block as overdefined. |
| if (CommonState != PredState) |
| return OverdefinedState; |
| } |
| |
| return CommonState; |
| } |
| |
| // Calculate the intersection of all the InitialStates for a BasicBlock's |
| // successors. |
| static int getSuccState(DenseMap<BasicBlock *, int> &InitialStates, Function &F, |
| int ParentBaseState, BasicBlock *BB) { |
| // This block rejoins normal control flow, |
| // conservatively report this basic block as overdefined. |
| if (isa<CatchReturnInst>(BB->getTerminator())) |
| return OverdefinedState; |
| |
| int CommonState = OverdefinedState; |
| for (BasicBlock *SuccBB : successors(BB)) { |
| // We didn't manage to get a state for one of these predecessors, |
| // conservatively report this basic block as overdefined. |
| auto SuccStartState = InitialStates.find(SuccBB); |
| if (SuccStartState == InitialStates.end()) |
| return OverdefinedState; |
| |
| // This is an EH Pad, conservatively report this basic block as overdefined. |
| if (SuccBB->isEHPad()) |
| return OverdefinedState; |
| |
| int SuccState = SuccStartState->second; |
| assert(SuccState != OverdefinedState && |
| "overdefined BBs shouldn't be in FinalStates"); |
| if (CommonState == OverdefinedState) |
| CommonState = SuccState; |
| |
| // At least two successors have different InitialStates, |
| // conservatively report this basic block as overdefined. |
| if (CommonState != SuccState) |
| return OverdefinedState; |
| } |
| |
| return CommonState; |
| } |
| |
| bool WinEHStatePass::isStateStoreNeeded(EHPersonality Personality, |
| CallBase &Call) { |
| // If the function touches memory, it needs a state store. |
| if (isAsynchronousEHPersonality(Personality)) |
| return !Call.doesNotAccessMemory(); |
| |
| // If the function throws, it needs a state store. |
| return !Call.doesNotThrow(); |
| } |
| |
| void WinEHStatePass::addStateStores(Function &F, WinEHFuncInfo &FuncInfo) { |
| // Mark the registration node. The backend needs to know which alloca it is so |
| // that it can recover the original frame pointer. |
| IRBuilder<> Builder(RegNode->getNextNode()); |
| Value *RegNodeI8 = Builder.CreateBitCast(RegNode, Builder.getInt8PtrTy()); |
| Builder.CreateCall( |
| Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_ehregnode), |
| {RegNodeI8}); |
| |
| if (EHGuardNode) { |
| IRBuilder<> Builder(EHGuardNode->getNextNode()); |
| Value *EHGuardNodeI8 = |
| Builder.CreateBitCast(EHGuardNode, Builder.getInt8PtrTy()); |
| Builder.CreateCall( |
| Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_ehguard), |
| {EHGuardNodeI8}); |
| } |
| |
| // Calculate state numbers. |
| if (isAsynchronousEHPersonality(Personality)) |
| calculateSEHStateNumbers(&F, FuncInfo); |
| else |
| calculateWinCXXEHStateNumbers(&F, FuncInfo); |
| |
| // Iterate all the instructions and emit state number stores. |
| DenseMap<BasicBlock *, ColorVector> BlockColors = colorEHFunclets(F); |
| ReversePostOrderTraversal<Function *> RPOT(&F); |
| |
| // InitialStates yields the state of the first call-site for a BasicBlock. |
| DenseMap<BasicBlock *, int> InitialStates; |
| // FinalStates yields the state of the last call-site for a BasicBlock. |
| DenseMap<BasicBlock *, int> FinalStates; |
| // Worklist used to revisit BasicBlocks with indeterminate |
| // Initial/Final-States. |
| std::deque<BasicBlock *> Worklist; |
| // Fill in InitialStates and FinalStates for BasicBlocks with call-sites. |
| for (BasicBlock *BB : RPOT) { |
| int InitialState = OverdefinedState; |
| int FinalState; |
| if (&F.getEntryBlock() == BB) |
| InitialState = FinalState = ParentBaseState; |
| for (Instruction &I : *BB) { |
| auto *Call = dyn_cast<CallBase>(&I); |
| if (!Call || !isStateStoreNeeded(Personality, *Call)) |
| continue; |
| |
| int State = getStateForCall(BlockColors, FuncInfo, *Call); |
| if (InitialState == OverdefinedState) |
| InitialState = State; |
| FinalState = State; |
| } |
| // No call-sites in this basic block? That's OK, we will come back to these |
| // in a later pass. |
| if (InitialState == OverdefinedState) { |
| Worklist.push_back(BB); |
| continue; |
| } |
| LLVM_DEBUG(dbgs() << "X86WinEHState: " << BB->getName() |
| << " InitialState=" << InitialState << '\n'); |
| LLVM_DEBUG(dbgs() << "X86WinEHState: " << BB->getName() |
| << " FinalState=" << FinalState << '\n'); |
| InitialStates.insert({BB, InitialState}); |
| FinalStates.insert({BB, FinalState}); |
| } |
| |
| // Try to fill-in InitialStates and FinalStates which have no call-sites. |
| while (!Worklist.empty()) { |
| BasicBlock *BB = Worklist.front(); |
| Worklist.pop_front(); |
| // This BasicBlock has already been figured out, nothing more we can do. |
| if (InitialStates.count(BB) != 0) |
| continue; |
| |
| int PredState = getPredState(FinalStates, F, ParentBaseState, BB); |
| if (PredState == OverdefinedState) |
| continue; |
| |
| // We successfully inferred this BasicBlock's state via it's predecessors; |
| // enqueue it's successors to see if we can infer their states. |
| InitialStates.insert({BB, PredState}); |
| FinalStates.insert({BB, PredState}); |
| for (BasicBlock *SuccBB : successors(BB)) |
| Worklist.push_back(SuccBB); |
| } |
| |
| // Try to hoist stores from successors. |
| for (BasicBlock *BB : RPOT) { |
| int SuccState = getSuccState(InitialStates, F, ParentBaseState, BB); |
| if (SuccState == OverdefinedState) |
| continue; |
| |
| // Update our FinalState to reflect the common InitialState of our |
| // successors. |
| FinalStates.insert({BB, SuccState}); |
| } |
| |
| // Finally, insert state stores before call-sites which transition us to a new |
| // state. |
| for (BasicBlock *BB : RPOT) { |
| auto &BBColors = BlockColors[BB]; |
| BasicBlock *FuncletEntryBB = BBColors.front(); |
| if (isa<CleanupPadInst>(FuncletEntryBB->getFirstNonPHI())) |
| continue; |
| |
| int PrevState = getPredState(FinalStates, F, ParentBaseState, BB); |
| LLVM_DEBUG(dbgs() << "X86WinEHState: " << BB->getName() |
| << " PrevState=" << PrevState << '\n'); |
| |
| for (Instruction &I : *BB) { |
| auto *Call = dyn_cast<CallBase>(&I); |
| if (!Call || !isStateStoreNeeded(Personality, *Call)) |
| continue; |
| |
| int State = getStateForCall(BlockColors, FuncInfo, *Call); |
| if (State != PrevState) |
| insertStateNumberStore(&I, State); |
| PrevState = State; |
| } |
| |
| // We might have hoisted a state store into this block, emit it now. |
| auto EndState = FinalStates.find(BB); |
| if (EndState != FinalStates.end()) |
| if (EndState->second != PrevState) |
| insertStateNumberStore(BB->getTerminator(), EndState->second); |
| } |
| |
| SmallVector<CallBase *, 1> SetJmp3Calls; |
| for (BasicBlock *BB : RPOT) { |
| for (Instruction &I : *BB) { |
| auto *Call = dyn_cast<CallBase>(&I); |
| if (!Call) |
| continue; |
| if (Call->getCalledOperand()->stripPointerCasts() != |
| SetJmp3.getCallee()->stripPointerCasts()) |
| continue; |
| |
| SetJmp3Calls.push_back(Call); |
| } |
| } |
| |
| for (CallBase *Call : SetJmp3Calls) { |
| auto &BBColors = BlockColors[Call->getParent()]; |
| BasicBlock *FuncletEntryBB = BBColors.front(); |
| bool InCleanup = isa<CleanupPadInst>(FuncletEntryBB->getFirstNonPHI()); |
| |
| IRBuilder<> Builder(Call); |
| Value *State; |
| if (InCleanup) { |
| Value *StateField = Builder.CreateStructGEP(RegNode->getAllocatedType(), |
| RegNode, StateFieldIndex); |
| State = Builder.CreateLoad(Builder.getInt32Ty(), StateField); |
| } else { |
| State = Builder.getInt32(getStateForCall(BlockColors, FuncInfo, *Call)); |
| } |
| rewriteSetJmpCall(Builder, F, *Call, State); |
| } |
| } |
| |
| void WinEHStatePass::insertStateNumberStore(Instruction *IP, int State) { |
| IRBuilder<> Builder(IP); |
| Value *StateField = Builder.CreateStructGEP(RegNode->getAllocatedType(), |
| RegNode, StateFieldIndex); |
| Builder.CreateStore(Builder.getInt32(State), StateField); |
| } |