From: Reid Kleckner Date: Fri, 1 May 2015 20:04:54 +0000 (+0000) Subject: [WinEH] Add an EH registration and state insertion pass for 32-bit x86 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=018ed7b68b12e74a655a6ef00179455eda6e6e88;p=oota-llvm.git [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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@236339 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/MC/MCAsmInfo.h b/include/llvm/MC/MCAsmInfo.h index 05da1d1871f..60a415ec68d 100644 --- a/include/llvm/MC/MCAsmInfo.h +++ b/include/llvm/MC/MCAsmInfo.h @@ -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 { diff --git a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index d56a1808049..f385ac3c3d5 100644 --- a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -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; diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index c1067c5d560..db91c02ee46 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -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); } diff --git a/lib/Target/X86/CMakeLists.txt b/lib/Target/X86/CMakeLists.txt index be61b4701aa..be8e7f6b7c5 100644 --- a/lib/Target/X86/CMakeLists.txt +++ b/lib/Target/X86/CMakeLists.txt @@ -32,6 +32,7 @@ set(sources X86TargetTransformInfo.cpp X86VZeroUpper.cpp X86FixupLEAs.cpp + X86WinEHState.cpp ) if( CMAKE_CL_64 ) diff --git a/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp b/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp index e64b9635ba0..bda35f2b972 100644 --- a/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp +++ b/lib/Target/X86/MCTargetDesc/X86MCAsmInfo.cpp @@ -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; diff --git a/lib/Target/X86/X86.h b/lib/Target/X86/X86.h index 8b0a4cf477f..62c65bacef5 100644 --- a/lib/Target/X86/X86.h +++ b/lib/Target/X86/X86.h @@ -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 diff --git a/lib/Target/X86/X86TargetMachine.cpp b/lib/Target/X86/X86TargetMachine.cpp index 919072aa913..9484f678808 100644 --- a/lib/Target/X86/X86TargetMachine.cpp +++ b/lib/Target/X86/X86TargetMachine.cpp @@ -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()); } diff --git a/lib/Target/X86/X86WinEHState.cpp b/lib/Target/X86/X86WinEHState.cpp new file mode 100644 index 00000000000..e4bbffcc55a --- /dev/null +++ b/lib/Target/X86/X86WinEHState.cpp @@ -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(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(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(RegNode)) { + GEP = cast(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); +} diff --git a/test/CodeGen/X86/inalloca-invoke.ll b/test/CodeGen/X86/inalloca-invoke.ll index d6fc76ee50b..cf5cbe142ec 100644 --- a/test/CodeGen/X86/inalloca-invoke.ll +++ b/test/CodeGen/X86/inalloca-invoke.ll @@ -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 } diff --git a/test/CodeGen/X86/win32-eh.ll b/test/CodeGen/X86/win32-eh.ll new file mode 100644 index 00000000000..875949740ec --- /dev/null +++ b/test/CodeGen/X86/win32-eh.ll @@ -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