llvm.org GIT mirror llvm / ace061b
[WinEH] Make setjmp work correctly with EH 32-bit X86 EH on Windows utilizes a stack of registration nodes allocated and deallocated on entry/exit. A registration node contains a bunch of EH personality specific information like which try-state we are currently in. Because a setjmp target allows control flow from arbitrary program points, there is no way to ensure that the try-state we are in is correctly updated once we transfer control. MSVC compatible compilers, like MSVC and ICC, utilize runtime helpers to reinitialize the try-state when a longjmp occurs. This is implemented by adding additional arguments to _setjmp3: the desired try-state and a helper routine to update the try-state. Differential Revision: http://reviews.llvm.org/D17721 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@262241 91177308-0d34-0410-b5e6-96231b3b80d8 David Majnemer 4 years ago
2 changed file(s) with 211 addition(s) and 20 deletion(s). Raw diff Collapse all Expand all
7272
7373 Function *generateLSDAInEAXThunk(Function *ParentFunc);
7474
75 bool isStateStoreNeeded(EHPersonality Personality, CallSite CS);
76 void rewriteSetJmpCallSite(IRBuilder<> &Builder, Function &F, CallSite CS,
77 Value *State);
78 int getBaseStateForBB(DenseMap &BlockColors,
79 WinEHFuncInfo &FuncInfo, BasicBlock *BB);
80 int getStateForCallSite(DenseMap &BlockColors,
81 WinEHFuncInfo &FuncInfo, CallSite CS);
82
7583 // Module-level type getters.
7684 Type *getEHLinkRegistrationType();
7785 Type *getSEHRegistrationType();
8290 StructType *EHLinkRegistrationTy = nullptr;
8391 StructType *CXXEHRegistrationTy = nullptr;
8492 StructType *SEHRegistrationTy = nullptr;
85 Function *FrameRecover = nullptr;
86 Function *FrameAddress = nullptr;
87 Function *FrameEscape = nullptr;
93 Constant *SetJmp3 = nullptr;
94 Constant *CxxLongjmpUnwind = nullptr;
8895
8996 // Per-function state
9097 EHPersonality Personality = EHPersonality::Unknown;
9198 Function *PersonalityFn = nullptr;
9299 bool UseStackGuard = false;
93100 int ParentBaseState;
101 Constant *SehLongjmpUnwind = nullptr;
102 Constant *Cookie = nullptr;
94103
95104 /// The stack allocation containing all EH data, including the link in the
96105 /// fs:00 chain and the current state.
122131 EHLinkRegistrationTy = nullptr;
123132 CXXEHRegistrationTy = nullptr;
124133 SEHRegistrationTy = nullptr;
125 FrameEscape = nullptr;
126 FrameRecover = nullptr;
127 FrameAddress = nullptr;
134 SetJmp3 = nullptr;
135 CxxLongjmpUnwind = nullptr;
136 SehLongjmpUnwind = nullptr;
137 Cookie = nullptr;
128138 return false;
129139 }
130140
158168 if (!HasPads)
159169 return false;
160170
161 FrameEscape = Intrinsic::getDeclaration(TheModule, Intrinsic::localescape);
162 FrameRecover = Intrinsic::getDeclaration(TheModule, Intrinsic::localrecover);
163 FrameAddress = Intrinsic::getDeclaration(TheModule, Intrinsic::frameaddress);
171 Type *Int8PtrType = Type::getInt8PtrTy(TheModule->getContext());
172 SetJmp3 = TheModule->getOrInsertFunction(
173 "_setjmp3", FunctionType::get(
174 Type::getInt32Ty(TheModule->getContext()),
175 {Int8PtrType, Type::getInt32Ty(TheModule->getContext())},
176 /*isVarArg=*/true));
164177
165178 // Disable frame pointer elimination in this function.
166179 // FIXME: Do the nested handlers need to keep the parent ebp in ebp, or can we
275288 Function *Trampoline = generateLSDAInEAXThunk(F);
276289 Link = Builder.CreateStructGEP(RegNodeTy, RegNode, 1);
277290 linkExceptionRegistration(Builder, Trampoline);
291
292 CxxLongjmpUnwind = TheModule->getOrInsertFunction(
293 "__CxxLongjmpUnwind",
294 FunctionType::get(Type::getVoidTy(TheModule->getContext()), Int8PtrType,
295 /*isVarArg=*/false));
296 cast(CxxLongjmpUnwind->stripPointerCasts())
297 ->setCallingConv(CallingConv::X86_StdCall);
278298 } else if (Personality == EHPersonality::MSVC_X86SEH) {
279299 // If _except_handler4 is in use, some additional guard checks and prologue
280300 // stuff is required.
291311 ParentBaseState = UseStackGuard ? -2 : -1;
292312 insertStateNumberStore(&*Builder.GetInsertPoint(), ParentBaseState);
293313 // ScopeTable = llvm.x86.seh.lsda(F)
294 Value *FI8 = Builder.CreateBitCast(F, Int8PtrType);
295 Value *LSDA = Builder.CreateCall(
296 Intrinsic::getDeclaration(TheModule, Intrinsic::x86_seh_lsda), FI8);
314 Value *LSDA = emitEHLSDA(Builder, F);
297315 Type *Int32Ty = Type::getInt32Ty(TheModule->getContext());
298316 LSDA = Builder.CreatePtrToInt(LSDA, Int32Ty);
299317 // If using _except_handler4, xor the address of the table with
300318 // __security_cookie.
301319 if (UseStackGuard) {
302 Value *Cookie =
303 TheModule->getOrInsertGlobal("__security_cookie", Int32Ty);
320 Cookie = TheModule->getOrInsertGlobal("__security_cookie", Int32Ty);
304321 Value *Val = Builder.CreateLoad(Int32Ty, Cookie);
305322 LSDA = Builder.CreateXor(LSDA, Val);
306323 }
307324 Builder.CreateStore(LSDA, Builder.CreateStructGEP(RegNodeTy, RegNode, 3));
308325 Link = Builder.CreateStructGEP(RegNodeTy, RegNode, 2);
309326 linkExceptionRegistration(Builder, PersonalityFn);
327
328 SehLongjmpUnwind = TheModule->getOrInsertFunction(
329 UseStackGuard ? "_seh_longjmp_unwind4" : "_seh_longjmp_unwind",
330 FunctionType::get(Type::getVoidTy(TheModule->getContext()), Int8PtrType,
331 /*isVarArg=*/false));
332 cast(SehLongjmpUnwind->stripPointerCasts())
333 ->setCallingConv(CallingConv::X86_StdCall);
310334 } else {
311335 llvm_unreachable("unexpected personality function");
312336 }
401425 Builder.CreateStore(Next, FSZero);
402426 }
403427
428 // Calls to setjmp(p) are lowered to _setjmp3(p, 0) by the frontend.
429 // The idea behind _setjmp3 is that it takes an optional number of personality
430 // specific parameters to indicate how to restore the personality-specific frame
431 // state when longjmp is initiated. Typically, the current TryLevel is saved.
432 void WinEHStatePass::rewriteSetJmpCallSite(IRBuilder<> &Builder, Function &F,
433 CallSite CS, Value *State) {
434 // Don't rewrite calls with a weird number of arguments.
435 if (CS.getNumArgOperands() != 2)
436 return;
437
438 Instruction *Inst = CS.getInstruction();
439
440 SmallVector OpBundles;
441 CS.getOperandBundlesAsDefs(OpBundles);
442
443 SmallVector OptionalArgs;
444 if (Personality == EHPersonality::MSVC_CXX) {
445 OptionalArgs.push_back(CxxLongjmpUnwind);
446 OptionalArgs.push_back(State);
447 OptionalArgs.push_back(emitEHLSDA(Builder, &F));
448 } else if (Personality == EHPersonality::MSVC_X86SEH) {
449 OptionalArgs.push_back(SehLongjmpUnwind);
450 OptionalArgs.push_back(State);
451 if (UseStackGuard)
452 OptionalArgs.push_back(Cookie);
453 } else {
454 llvm_unreachable("unhandled personality!");
455 }
456
457 SmallVector Args;
458 Args.push_back(
459 Builder.CreateBitCast(CS.getArgOperand(0), Builder.getInt8PtrTy()));
460 Args.push_back(Builder.getInt32(OptionalArgs.size()));
461 Args.append(OptionalArgs.begin(), OptionalArgs.end());
462
463 CallSite NewCS;
464 if (CS.isCall()) {
465 auto *CI = cast(Inst);
466 CallInst *NewCI = Builder.CreateCall(SetJmp3, Args, OpBundles);
467 NewCI->setTailCallKind(CI->getTailCallKind());
468 NewCS = NewCI;
469 } else {
470 auto *II = cast(Inst);
471 NewCS = Builder.CreateInvoke(
472 SetJmp3, II->getNormalDest(), II->getUnwindDest(), Args, OpBundles);
473 }
474 NewCS.setCallingConv(CS.getCallingConv());
475 NewCS.setAttributes(CS.getAttributes());
476 NewCS->setDebugLoc(CS->getDebugLoc());
477
478 Instruction *NewInst = NewCS.getInstruction();
479 NewInst->takeName(Inst);
480 Inst->replaceAllUsesWith(NewInst);
481 Inst->eraseFromParent();
482 }
483
404484 // Figure out what state we should assign calls in this block.
405 static int getBaseStateForBB(DenseMap &BlockColors,
406 WinEHFuncInfo &FuncInfo, BasicBlock *BB) {
407 int BaseState = -1;
485 int WinEHStatePass::getBaseStateForBB(
486 DenseMap &BlockColors, WinEHFuncInfo &FuncInfo,
487 BasicBlock *BB) {
488 int BaseState = ParentBaseState;
408489 auto &BBColors = BlockColors[BB];
409490
410491 assert(BBColors.size() == 1 && "multi-color BB not removed by preparation");
420501 }
421502
422503 // Calculate the state a call-site is in.
423 static int getStateForCallSite(DenseMap &BlockColors,
424 WinEHFuncInfo &FuncInfo, CallSite CS) {
504 int WinEHStatePass::getStateForCallSite(
505 DenseMap &BlockColors, WinEHFuncInfo &FuncInfo,
506 CallSite CS) {
425507 if (auto *II = dyn_cast(CS.getInstruction())) {
426508 // Look up the state number of the EH pad this unwinds to.
427509 assert(FuncInfo.InvokeStateMap.count(II) && "invoke has no state!");
509591 return CommonState;
510592 }
511593
512 static bool isStateStoreNeeded(EHPersonality Personality, CallSite CS) {
594 bool WinEHStatePass::isStateStoreNeeded(EHPersonality Personality,
595 CallSite CS) {
513596 if (!CS)
514597 return false;
515598
599 // If the function touches memory, it needs a state store.
516600 if (isAsynchronousEHPersonality(Personality))
517601 return !CS.doesNotAccessMemory();
518602
603 // If the function throws, it needs a state store.
519604 return !CS.doesNotThrow();
520605 }
521606
635720 if (EndState->second != PrevState)
636721 insertStateNumberStore(BB->getTerminator(), EndState->second);
637722 }
723
724 SmallVector SetJmp3CallSites;
725 for (BasicBlock *BB : RPOT) {
726 for (Instruction &I : *BB) {
727 CallSite CS(&I);
728 if (!CS)
729 continue;
730 if (CS.getCalledValue()->stripPointerCasts() !=
731 SetJmp3->stripPointerCasts())
732 continue;
733
734 SetJmp3CallSites.push_back(CS);
735 }
736 }
737
738 for (CallSite CS : SetJmp3CallSites) {
739 auto &BBColors = BlockColors[CS->getParent()];
740 BasicBlock *FuncletEntryBB = BBColors.front();
741 bool InCleanup = isa(FuncletEntryBB->getFirstNonPHI());
742
743 IRBuilder<> Builder(CS.getInstruction());
744 Value *State;
745 if (InCleanup) {
746 Value *StateField =
747 Builder.CreateStructGEP(nullptr, RegNode, StateFieldIndex);
748 State = Builder.CreateLoad(StateField);
749 } else {
750 State = Builder.getInt32(getStateForCallSite(BlockColors, FuncInfo, CS));
751 }
752 rewriteSetJmpCallSite(Builder, F, CS, State);
753 }
638754 }
639755
640756 void WinEHStatePass::insertStateNumberStore(Instruction *IP, int State) {
0 ; RUN: opt -mtriple=i686-pc-windows-msvc -S -x86-winehstate < %s | FileCheck %s
1 target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
2 target triple = "i686-pc-windows-msvc"
3
4 @jb = external global i8
5
6 define i32 @test1() personality i32 (...)* @__CxxFrameHandler3 {
7 entry:
8 ; CHECK-LABEL: define i32 @test1(
9 ; CHECK: %[[eh_reg:.*]] = alloca
10 ; CHECK: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}} %[[eh_reg]], i32 0, i32 2
11 ; CHECK: store i32 -1, i32* %[[gep]]
12 ; CHECK: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}} %[[eh_reg]], i32 0, i32 2
13 ; CHECK: store i32 0, i32* %[[gep]]
14 ; CHECK: %[[lsda:.*]] = call i8* @llvm.x86.seh.lsda(i8* bitcast (i32 ()* @test1 to i8*))
15 ; CHECK: invoke i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 3, void (i8*)* @__CxxLongjmpUnwind, i32 0, i8* %[[lsda]])
16 %inv = invoke i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 0) #2
17 to label %invoke.cont unwind label %ehcleanup
18
19 invoke.cont:
20 ; CHECK: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}} %[[eh_reg]], i32 0, i32 2
21 ; CHECK: store i32 -1, i32* %[[gep]]
22 ; CHECK: %[[lsda:.*]] = call i8* @llvm.x86.seh.lsda(i8* bitcast (i32 ()* @test1 to i8*))
23 ; CHECK: call i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 3, void (i8*)* @__CxxLongjmpUnwind, i32 -1, i8* %[[lsda]])
24 call i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 0)
25 call void @cleanup()
26 ret i32 %inv
27
28 ehcleanup:
29 %cp = cleanuppad within none []
30 ; CHECK: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}} %[[eh_reg]], i32 0, i32 2
31 ; CHECK: %[[load:.*]] = load i32, i32* %[[gep]]
32 ; CHECK: %[[lsda:.*]] = call i8* @llvm.x86.seh.lsda(i8* bitcast (i32 ()* @test1 to i8*))
33 ; CHECK: call i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 3, void (i8*)* @__CxxLongjmpUnwind, i32 %[[load]], i8* %[[lsda]]) [ "funclet"(token %cp) ]
34 %cal = call i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 0) [ "funclet"(token %cp) ]
35 call void @cleanup() [ "funclet"(token %cp) ]
36 cleanupret from %cp unwind to caller
37 }
38
39 define i32 @test2() personality i32 (...)* @_except_handler3 {
40 entry:
41 ; CHECK-LABEL: define i32 @test2(
42 ; CHECK: %[[eh_reg:.*]] = alloca
43 ; CHECK: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}} %[[eh_reg]], i32 0, i32 4
44 ; CHECK: store i32 -1, i32* %[[gep]]
45 ; CHECK: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}} %[[eh_reg]], i32 0, i32 4
46 ; CHECK: store i32 0, i32* %[[gep]]
47 ; CHECK: invoke i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 2, void (i8*)* @_seh_longjmp_unwind, i32 0)
48 %inv = invoke i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 0) #2
49 to label %invoke.cont unwind label %ehcleanup
50
51 invoke.cont:
52 ; CHECK: %[[gep:.*]] = getelementptr inbounds {{.*}}, {{.*}} %[[eh_reg]], i32 0, i32 4
53 ; CHECK: store i32 -1, i32* %[[gep]]
54 ; CHECK: call i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 2, void (i8*)* @_seh_longjmp_unwind, i32 -1)
55 call i32 (i8*, i32, ...) @_setjmp3(i8* @jb, i32 0)
56 call void @cleanup()
57 ret i32 %inv
58
59 ehcleanup:
60 %cp = cleanuppad within none []
61 call void @cleanup() [ "funclet"(token %cp) ]
62 cleanupret from %cp unwind to caller
63 }
64
65 ; Function Attrs: returns_twice
66 declare i32 @_setjmp3(i8*, i32, ...) #2
67
68 declare i32 @__CxxFrameHandler3(...)
69
70 declare i32 @_except_handler3(...)
71
72 declare void @cleanup()
73
74 attributes #2 = { returns_twice }