llvm.org GIT mirror llvm / 7f410b1
[WinEH] Update CoreCLR EH state numbering Summary: Fix the CLR state numbering to generate correct tables, and update the lit test to verify them. The CLR numbering assigns one state number to each catchpad and cleanuppad. It also computes two tree-like relations over states: 1) Each state has a "HandlerParentState", which is the state of the next outer handler enclosing this state's handler (same as nearest ancestor per the ParentPad linkage on EH pads, but skipping over catchswitches). 2) Each state has a "TryParentState", which: a) for a catchpad that's not the last handler on its catchswitch, is the state of the next catchpad on that catchswitch. b) for all other pads, is the state of the pad whose try region is the next outer try region enclosing this state's try region. The "try regions are not present as such in the IR, but will be inferred based on the placement of invokes and pads which reach each other by exceptional exits. Catchswitches do not get their own states, but each gets mapped to the state of its first catchpad. Table generation requires each state's "unwind dest" state to have a lower state number than the given state. Since HandlerParentState can be computed as a function of a pad's ParentPad, and TryParentState can be computed as a function of its unwind dest and the TryParentStates of its children, the CLR state numbering algorithm first computes HandlerParentState in a top-down pass, then computes TryParentState in a bottom-up pass. Also reword some comments/names in the CLR EH table generation to make the distinction between the different kinds of "parent" clear. Reviewers: rnk, andrew.w.kaylor, majnemer Subscribers: AndyAyers, llvm-commits Differential Revision: http://reviews.llvm.org/D15325 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@256760 91177308-0d34-0410-b5e6-96231b3b80d8 Joseph Tremoulet 4 years ago
4 changed file(s) with 767 addition(s) and 219 deletion(s). Raw diff Collapse all Expand all
8282 struct ClrEHUnwindMapEntry {
8383 MBBOrBasicBlock Handler;
8484 uint32_t TypeToken;
85 int Parent;
85 int HandlerParentState; ///< Outer handler enclosing this entry's handler
86 int TryParentState; ///< Outer try region enclosing this entry's try region,
87 ///< treating later catches on same try as "outer"
8688 ClrHandlerType HandlerType;
8789 };
8890
975975 }
976976 }
977977
978 static int getRank(const WinEHFuncInfo &FuncInfo, int State) {
978 static int getTryRank(const WinEHFuncInfo &FuncInfo, int State) {
979979 int Rank = 0;
980980 while (State != -1) {
981981 ++Rank;
982 State = FuncInfo.ClrEHUnwindMap[State].Parent;
982 State = FuncInfo.ClrEHUnwindMap[State].TryParentState;
983983 }
984984 return Rank;
985985 }
986986
987 static int getAncestor(const WinEHFuncInfo &FuncInfo, int Left, int Right) {
988 int LeftRank = getRank(FuncInfo, Left);
989 int RightRank = getRank(FuncInfo, Right);
987 static int getTryAncestor(const WinEHFuncInfo &FuncInfo, int Left, int Right) {
988 int LeftRank = getTryRank(FuncInfo, Left);
989 int RightRank = getTryRank(FuncInfo, Right);
990990
991991 while (LeftRank < RightRank) {
992 Right = FuncInfo.ClrEHUnwindMap[Right].Parent;
992 Right = FuncInfo.ClrEHUnwindMap[Right].TryParentState;
993993 --RightRank;
994994 }
995995
996996 while (RightRank < LeftRank) {
997 Left = FuncInfo.ClrEHUnwindMap[Left].Parent;
997 Left = FuncInfo.ClrEHUnwindMap[Left].TryParentState;
998998 --LeftRank;
999999 }
10001000
10011001 while (Left != Right) {
1002 Left = FuncInfo.ClrEHUnwindMap[Left].Parent;
1003 Right = FuncInfo.ClrEHUnwindMap[Right].Parent;
1002 Left = FuncInfo.ClrEHUnwindMap[Left].TryParentState;
1003 Right = FuncInfo.ClrEHUnwindMap[Right].TryParentState;
10041004 }
10051005
10061006 return Left;
10341034 FuncInfo.ClrEHUnwindMap[State].Handler.get();
10351035 HandlerStates[HandlerBlock] = State;
10361036 // Use this loop through all handlers to verify our assumption (used in
1037 // the MinEnclosingState computation) that ancestors have lower state
1038 // numbers than their descendants.
1039 assert(FuncInfo.ClrEHUnwindMap[State].Parent < State &&
1037 // the MinEnclosingState computation) that enclosing funclets have lower
1038 // state numbers than their enclosed funclets.
1039 assert(FuncInfo.ClrEHUnwindMap[State].HandlerParentState < State &&
10401040 "ill-formed state numbering");
10411041 }
10421042 // Map the main function to the NullState.
10691069 SmallVector MinClauseMap((size_t)NumStates, NumStates);
10701070
10711071 // Visit the root function and each funclet.
1072
10731072 for (MachineFunction::const_iterator FuncletStart = MF->begin(),
10741073 FuncletEnd = MF->begin(),
10751074 End = MF->end();
10991098 for (const auto &StateChange :
11001099 InvokeStateChangeIterator::range(FuncInfo, FuncletStart, FuncletEnd)) {
11011100 // Close any try regions we're not still under
1102 int AncestorState =
1103 getAncestor(FuncInfo, CurrentState, StateChange.NewState);
1104 while (CurrentState != AncestorState) {
1105 assert(CurrentState != NullState && "Failed to find ancestor!");
1101 int StillPendingState =
1102 getTryAncestor(FuncInfo, CurrentState, StateChange.NewState);
1103 while (CurrentState != StillPendingState) {
1104 assert(CurrentState != NullState &&
1105 "Failed to find still-pending state!");
11061106 // Close the pending clause
11071107 Clauses.push_back({CurrentStartLabel, StateChange.PreviousEndLabel,
11081108 CurrentState, FuncletState});
1109 // Now the parent handler is current
1110 CurrentState = FuncInfo.ClrEHUnwindMap[CurrentState].Parent;
1109 // Now the next-outer try region is current
1110 CurrentState = FuncInfo.ClrEHUnwindMap[CurrentState].TryParentState;
11111111 // Pop the new start label from the handler stack if we've exited all
1112 // descendants of the corresponding handler.
1112 // inner try regions of the corresponding try region.
11131113 if (HandlerStack.back().second == CurrentState)
11141114 CurrentStartLabel = HandlerStack.pop_back_val().first;
11151115 }
11201120 // it.
11211121 for (int EnteredState = StateChange.NewState;
11221122 EnteredState != CurrentState;
1123 EnteredState = FuncInfo.ClrEHUnwindMap[EnteredState].Parent) {
1123 EnteredState =
1124 FuncInfo.ClrEHUnwindMap[EnteredState].TryParentState) {
11241125 int &MinEnclosingState = MinClauseMap[EnteredState];
11251126 if (FuncletState < MinEnclosingState)
11261127 MinEnclosingState = FuncletState;
1616 //===----------------------------------------------------------------------===//
1717
1818 #include "llvm/CodeGen/Passes.h"
19 #include "llvm/ADT/DenseMap.h"
1920 #include "llvm/ADT/MapVector.h"
21 #include "llvm/ADT/STLExtras.h"
2022 #include "llvm/Analysis/CFG.h"
2123 #include "llvm/Analysis/EHPersonalities.h"
2224 #include "llvm/CodeGen/MachineBasicBlock.h"
435437 calculateStateNumbersForInvokes(Fn, FuncInfo);
436438 }
437439
438 static int addClrEHHandler(WinEHFuncInfo &FuncInfo, int ParentState,
439 ClrHandlerType HandlerType, uint32_t TypeToken,
440 const BasicBlock *Handler) {
440 static int addClrEHHandler(WinEHFuncInfo &FuncInfo, int HandlerParentState,
441 int TryParentState, ClrHandlerType HandlerType,
442 uint32_t TypeToken, const BasicBlock *Handler) {
441443 ClrEHUnwindMapEntry Entry;
442 Entry.Parent = ParentState;
444 Entry.HandlerParentState = HandlerParentState;
445 Entry.TryParentState = TryParentState;
443446 Entry.Handler = Handler;
444447 Entry.HandlerType = HandlerType;
445448 Entry.TypeToken = TypeToken;
453456 if (!FuncInfo.EHPadStateMap.empty())
454457 return;
455458
459 // This numbering assigns one state number to each catchpad and cleanuppad.
460 // It also computes two tree-like relations over states:
461 // 1) Each state has a "HandlerParentState", which is the state of the next
462 // outer handler enclosing this state's handler (same as nearest ancestor
463 // per the ParentPad linkage on EH pads, but skipping over catchswitches).
464 // 2) Each state has a "TryParentState", which:
465 // a) for a catchpad that's not the last handler on its catchswitch, is
466 // the state of the next catchpad on that catchswitch
467 // b) for all other pads, is the state of the pad whose try region is the
468 // next outer try region enclosing this state's try region. The "try
469 // regions are not present as such in the IR, but will be inferred
470 // based on the placement of invokes and pads which reach each other
471 // by exceptional exits
472 // Catchswitches do not get their own states, but each gets mapped to the
473 // state of its first catchpad.
474
475 // Step one: walk down from outermost to innermost funclets, assigning each
476 // catchpad and cleanuppad a state number. Add an entry to the
477 // ClrEHUnwindMap for each state, recording its HandlerParentState and
478 // handler attributes. Record the TryParentState as well for each catchpad
479 // that's not the last on its catchswitch, but initialize all other entries'
480 // TryParentStates to a sentinel -1 value that the next pass will update.
481
482 // Seed a worklist with pads that have no parent.
456483 SmallVector, 8> Worklist;
457
458 // Each pad needs to be able to refer to its parent, so scan the function
459 // looking for top-level handlers and seed the worklist with them.
460484 for (const BasicBlock &BB : *Fn) {
461 if (!BB.isEHPad())
485 const Instruction *FirstNonPHI = BB.getFirstNonPHI();
486 const Value *ParentPad;
487 if (const auto *CPI = dyn_cast(FirstNonPHI))
488 ParentPad = CPI->getParentPad();
489 else if (const auto *CSI = dyn_cast(FirstNonPHI))
490 ParentPad = CSI->getParentPad();
491 else
462492 continue;
463 if (BB.isLandingPad())
464 report_fatal_error("CoreCLR EH cannot use landingpads");
465 const Instruction *FirstNonPHI = BB.getFirstNonPHI();
466 if (!isTopLevelPadForMSVC(FirstNonPHI))
467 continue;
468 // queue this with sentinel parent state -1 to mean unwind to caller.
469 Worklist.emplace_back(FirstNonPHI, -1);
470 }
471
493 if (isa(ParentPad))
494 Worklist.emplace_back(FirstNonPHI, -1);
495 }
496
497 // Use the worklist to visit all pads, from outer to inner. Record
498 // HandlerParentState for all pads. Record TryParentState only for catchpads
499 // that aren't the last on their catchswitch (setting all other entries'
500 // TryParentStates to an initial value of -1). This loop is also responsible
501 // for setting the EHPadStateMap entry for all catchpads, cleanuppads, and
502 // catchswitches.
472503 while (!Worklist.empty()) {
473504 const Instruction *Pad;
474 int ParentState;
475 std::tie(Pad, ParentState) = Worklist.pop_back_val();
476
477 Value *ParentPad;
478 int PredState;
479 if (const CleanupPadInst *Cleanup = dyn_cast(Pad)) {
480 // A cleanup can have multiple exits; don't re-process after the first.
481 if (FuncInfo.EHPadStateMap.count(Cleanup))
482 continue;
483 // CoreCLR personality uses arity to distinguish faults from finallies.
484 const BasicBlock *PadBlock = Cleanup->getParent();
505 int HandlerParentState;
506 std::tie(Pad, HandlerParentState) = Worklist.pop_back_val();
507
508 if (const auto *Cleanup = dyn_cast(Pad)) {
509 // Create the entry for this cleanup with the appropriate handler
510 // properties. Finaly and fault handlers are distinguished by arity.
485511 ClrHandlerType HandlerType =
486 (Cleanup->getNumOperands() ? ClrHandlerType::Fault
487 : ClrHandlerType::Finally);
488 int NewState =
489 addClrEHHandler(FuncInfo, ParentState, HandlerType, 0, PadBlock);
490 FuncInfo.EHPadStateMap[Cleanup] = NewState;
491 // Propagate the new state to all preds of the cleanup
492 ParentPad = Cleanup->getParentPad();
493 PredState = NewState;
494 } else if (const auto *CatchSwitch = dyn_cast(Pad)) {
495 SmallVector Handlers;
496 for (const BasicBlock *CatchPadBB : CatchSwitch->handlers()) {
497 const auto *Catch = cast(CatchPadBB->getFirstNonPHI());
498 Handlers.push_back(Catch);
499 }
500 FuncInfo.EHPadStateMap[CatchSwitch] = ParentState;
501 int NewState = ParentState;
502 for (auto HandlerI = Handlers.rbegin(), HandlerE = Handlers.rend();
503 HandlerI != HandlerE; ++HandlerI) {
504 const CatchPadInst *Catch = *HandlerI;
505 const BasicBlock *PadBlock = Catch->getParent();
512 (Cleanup->getNumArgOperands() ? ClrHandlerType::Fault
513 : ClrHandlerType::Finally);
514 int CleanupState = addClrEHHandler(FuncInfo, HandlerParentState, -1,
515 HandlerType, 0, Pad->getParent());
516 // Queue any child EH pads on the worklist.
517 for (const User *U : Cleanup->users())
518 if (const auto *I = dyn_cast(U))
519 if (I->isEHPad())
520 Worklist.emplace_back(I, CleanupState);
521 // Remember this pad's state.
522 FuncInfo.EHPadStateMap[Cleanup] = CleanupState;
523 } else {
524 // Walk the handlers of this catchswitch in reverse order since all but
525 // the last need to set the following one as its TryParentState.
526 const auto *CatchSwitch = cast(Pad);
527 int CatchState = -1, FollowerState = -1;
528 SmallVector CatchBlocks(CatchSwitch->handlers());
529 for (auto CBI = CatchBlocks.rbegin(), CBE = CatchBlocks.rend();
530 CBI != CBE; ++CBI, FollowerState = CatchState) {
531 const BasicBlock *CatchBlock = *CBI;
532 // Create the entry for this catch with the appropriate handler
533 // properties.
534 const auto *Catch = cast(CatchBlock->getFirstNonPHI());
506535 uint32_t TypeToken = static_cast(
507536 cast(Catch->getArgOperand(0))->getZExtValue());
508 NewState = addClrEHHandler(FuncInfo, NewState, ClrHandlerType::Catch,
509 TypeToken, PadBlock);
510 FuncInfo.EHPadStateMap[Catch] = NewState;
537 CatchState =
538 addClrEHHandler(FuncInfo, HandlerParentState, FollowerState,
539 ClrHandlerType::Catch, TypeToken, CatchBlock);
540 // Queue any child EH pads on the worklist.
541 for (const User *U : Catch->users())
542 if (const auto *I = dyn_cast(U))
543 if (I->isEHPad())
544 Worklist.emplace_back(I, CatchState);
545 // Remember this catch's state.
546 FuncInfo.EHPadStateMap[Catch] = CatchState;
511547 }
512 for (const auto *CatchPad : Handlers) {
513 for (const User *U : CatchPad->users()) {
514 const auto *UserI = cast(U);
515 if (UserI->isEHPad())
516 Worklist.emplace_back(UserI, ParentState);
548 // Associate the catchswitch with the state of its first catch.
549 assert(CatchSwitch->getNumHandlers());
550 FuncInfo.EHPadStateMap[CatchSwitch] = CatchState;
551 }
552 }
553
554 // Step two: record the TryParentState of each state. For cleanuppads that
555 // don't have cleanuprets, we may need to infer this from their child pads,
556 // so visit pads in descendant-most to ancestor-most order.
557 for (auto Entry = FuncInfo.ClrEHUnwindMap.rbegin(),
558 End = FuncInfo.ClrEHUnwindMap.rend();
559 Entry != End; ++Entry) {
560 const Instruction *Pad =
561 Entry->Handler.get()->getFirstNonPHI();
562 // For most pads, the TryParentState is the state associated with the
563 // unwind dest of exceptional exits from it.
564 const BasicBlock *UnwindDest;
565 if (const auto *Catch = dyn_cast(Pad)) {
566 // If a catch is not the last in its catchswitch, its TryParentState is
567 // the state associated with the next catch in the switch, even though
568 // that's not the unwind dest of exceptions escaping the catch. Those
569 // cases were already assigned a TryParentState in the first pass, so
570 // skip them.
571 if (Entry->TryParentState != -1)
572 continue;
573 // Otherwise, get the unwind dest from the catchswitch.
574 UnwindDest = Catch->getCatchSwitch()->getUnwindDest();
575 } else {
576 const auto *Cleanup = cast(Pad);
577 UnwindDest = nullptr;
578 for (const User *U : Cleanup->users()) {
579 if (auto *CleanupRet = dyn_cast(U)) {
580 // Common and unambiguous case -- cleanupret indicates cleanup's
581 // unwind dest.
582 UnwindDest = CleanupRet->getUnwindDest();
583 break;
517584 }
585
586 // Get an unwind dest for the user
587 const BasicBlock *UserUnwindDest = nullptr;
588 if (auto *Invoke = dyn_cast(U)) {
589 UserUnwindDest = Invoke->getUnwindDest();
590 } else if (auto *CatchSwitch = dyn_cast(U)) {
591 UserUnwindDest = CatchSwitch->getUnwindDest();
592 } else if (auto *ChildCleanup = dyn_cast(U)) {
593 int UserState = FuncInfo.EHPadStateMap[ChildCleanup];
594 int UserUnwindState =
595 FuncInfo.ClrEHUnwindMap[UserState].TryParentState;
596 if (UserUnwindState != -1)
597 UserUnwindDest = FuncInfo.ClrEHUnwindMap[UserUnwindState]
598 .Handler.get();
599 }
600
601 // Not having an unwind dest for this user might indicate that it
602 // doesn't unwind, so can't be taken as proof that the cleanup itself
603 // may unwind to caller (see e.g. SimplifyUnreachable and
604 // RemoveUnwindEdge).
605 if (!UserUnwindDest)
606 continue;
607
608 // Now we have an unwind dest for the user, but we need to see if it
609 // unwinds all the way out of the cleanup or if it stays within it.
610 const Instruction *UserUnwindPad = UserUnwindDest->getFirstNonPHI();
611 const Value *UserUnwindParent;
612 if (auto *CSI = dyn_cast(UserUnwindPad))
613 UserUnwindParent = CSI->getParentPad();
614 else
615 UserUnwindParent =
616 cast(UserUnwindPad)->getParentPad();
617
618 // The unwind stays within the cleanup iff it targets a child of the
619 // cleanup.
620 if (UserUnwindParent == Cleanup)
621 continue;
622
623 // This unwind exits the cleanup, so its dest is the cleanup's dest.
624 UnwindDest = UserUnwindDest;
625 break;
518626 }
519 PredState = NewState;
520 ParentPad = CatchSwitch->getParentPad();
627 }
628
629 // Record the state of the unwind dest as the TryParentState.
630 int UnwindDestState;
631
632 // If UnwindDest is null at this point, either the pad in question can
633 // be exited by unwind to caller, or it cannot be exited by unwind. In
634 // either case, reporting such cases as unwinding to caller is correct.
635 // This can lead to EH tables that "look strange" -- if this pad's is in
636 // a parent funclet which has other children that do unwind to an enclosing
637 // pad, the try region for this pad will be missing the "duplicate" EH
638 // clause entries that you'd expect to see covering the whole parent. That
639 // should be benign, since the unwind never actually happens. If it were
640 // an issue, we could add a subsequent pass that pushes unwind dests down
641 // from parents that have them to children that appear to unwind to caller.
642 if (!UnwindDest) {
643 UnwindDestState = -1;
521644 } else {
522 llvm_unreachable("Unexpected EH pad");
523 }
524
525 // Queue all predecessors with the given state
526 for (const BasicBlock *Pred : predecessors(Pad->getParent())) {
527 if ((Pred = getEHPadFromPredecessor(Pred, ParentPad)))
528 Worklist.emplace_back(Pred->getFirstNonPHI(), PredState);
529 }
530 }
531
645 UnwindDestState = FuncInfo.EHPadStateMap[UnwindDest->getFirstNonPHI()];
646 }
647
648 Entry->TryParentState = UnwindDestState;
649 }
650
651 // Step three: transfer information from pads to invokes.
532652 calculateStateNumbersForInvokes(Fn, FuncInfo);
533653 }
534654
2525 ; }
2626 ; f(8);
2727 ; }
28
28 ;
2929 ; CHECK-LABEL: test1: # @test1
30 ; CHECK-NEXT: [[L_begin:.*func_begin.*]]:
30 ; CHECK-NEXT: [[test1_begin:.*func_begin.*]]:
3131 define void @test1() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
3232 entry:
3333 ; CHECK: # %entry
3434 ; CHECK: leaq [[FPOffset:[0-9]+]](%rsp), %rbp
3535 ; CHECK: .seh_endprologue
3636 ; CHECK: movq %rsp, [[PSPSymOffset:[0-9]+]](%rsp)
37 ; CHECK: [[L_before_f1:.+]]:
37 ; CHECK: [[test1_before_f1:.+]]:
3838 ; CHECK-NEXT: movl $1, %ecx
3939 ; CHECK-NEXT: callq f
40 ; CHECK-NEXT: [[L_after_f1:.+]]:
40 ; CHECK-NEXT: [[test1_after_f1:.+]]:
4141 invoke void @f(i32 1)
42 to label %inner_try unwind label %finally.pad
42 to label %inner_try unwind label %finally
4343 inner_try:
4444 ; CHECK: # %inner_try
45 ; CHECK: [[L_before_f2:.+]]:
45 ; CHECK: [[test1_before_f2:.+]]:
4646 ; CHECK-NEXT: movl $2, %ecx
4747 ; CHECK-NEXT: callq f
48 ; CHECK-NEXT: [[L_after_f2:.+]]:
48 ; CHECK-NEXT: [[test1_after_f2:.+]]:
4949 invoke void @f(i32 2)
50 to label %finally.clone unwind label %catch1.pad
51 catch1.pad:
52 %cs1 = catchswitch within none [label %catch1.body, label %catch2.body] unwind label %finally.pad
53 catch1.body:
54 %catch1 = catchpad within %cs1 [i32 1]
55 ; CHECK: .seh_proc [[L_catch1:[^ ]+]]
50 to label %finally.clone unwind label %exn.dispatch
51 exn.dispatch:
52 %catchswitch = catchswitch within none [label %catch1, label %catch2] unwind label %finally
53 catch1:
54 %catch.pad1 = catchpad within %catchswitch [i32 1]
55 ; CHECK: .seh_proc [[test1_catch1:[^ ]+]]
5656 ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
5757 ; ^ all funclets use the same frame size
5858 ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
6363 ; CHECK: movq %rdx, %rcx
6464 ; ^ exception pointer passed in rdx
6565 ; CHECK-NEXT: callq g
66 %exn1 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch1)
67 call void @g(i8 addrspace(1)* %exn1) [ "funclet"(token %catch1) ]
68 ; CHECK: [[L_before_f3:.+]]:
66 %exn1 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad1)
67 call void @g(i8 addrspace(1)* %exn1) [ "funclet"(token %catch.pad1) ]
68 ; CHECK: [[test1_before_f3:.+]]:
6969 ; CHECK-NEXT: movl $3, %ecx
7070 ; CHECK-NEXT: callq f
71 ; CHECK-NEXT: [[L_after_f3:.+]]:
72 invoke void @f(i32 3) [ "funclet"(token %catch1) ]
73 to label %catch1.ret unwind label %finally.pad
71 ; CHECK-NEXT: [[test1_after_f3:.+]]:
72 invoke void @f(i32 3) [ "funclet"(token %catch.pad1) ]
73 to label %catch1.ret unwind label %finally
7474 catch1.ret:
75 catchret from %catch1 to label %finally.clone
76 catch2.body:
77 %catch2 = catchpad within %cs1 [i32 2]
78 ; CHECK: .seh_proc [[L_catch2:[^ ]+]]
75 catchret from %catch.pad1 to label %finally.clone
76 catch2:
77 %catch.pad2 = catchpad within %catchswitch [i32 2]
78 ; CHECK: .seh_proc [[test1_catch2:[^ ]+]]
7979 ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
8080 ; ^ all funclets use the same frame size
8181 ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
8686 ; CHECK: movq %rdx, %rcx
8787 ; ^ exception pointer passed in rdx
8888 ; CHECK-NEXT: callq g
89 %exn2 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch2)
90 call void @g(i8 addrspace(1)* %exn2) [ "funclet"(token %catch2) ]
91 ; CHECK: [[L_before_f4:.+]]:
89 %exn2 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad2)
90 call void @g(i8 addrspace(1)* %exn2) [ "funclet"(token %catch.pad2) ]
91 ; CHECK: [[test1_before_f4:.+]]:
9292 ; CHECK-NEXT: movl $4, %ecx
9393 ; CHECK-NEXT: callq f
94 ; CHECK-NEXT: [[L_after_f4:.+]]:
95 invoke void @f(i32 4) [ "funclet"(token %catch2) ]
96 to label %try_in_catch unwind label %finally.pad
94 ; CHECK-NEXT: [[test1_after_f4:.+]]:
95 invoke void @f(i32 4) [ "funclet"(token %catch.pad2) ]
96 to label %try_in_catch unwind label %finally
9797 try_in_catch:
9898 ; CHECK: # %try_in_catch
99 ; CHECK: [[L_before_f5:.+]]:
99 ; CHECK: [[test1_before_f5:.+]]:
100100 ; CHECK-NEXT: movl $5, %ecx
101101 ; CHECK-NEXT: callq f
102 ; CHECK-NEXT: [[L_after_f5:.+]]:
103 invoke void @f(i32 5) [ "funclet"(token %catch2) ]
104 to label %catch2.ret unwind label %fault.pad
105 fault.pad:
106 ; CHECK: .seh_proc [[L_fault:[^ ]+]]
107 %fault = cleanuppad within none [i32 undef]
102 ; CHECK-NEXT: [[test1_after_f5:.+]]:
103 invoke void @f(i32 5) [ "funclet"(token %catch.pad2) ]
104 to label %catch2.ret unwind label %fault
105 fault:
106 ; CHECK: .seh_proc [[test1_fault:[^ ]+]]
107 %fault.pad = cleanuppad within %catch.pad2 [i32 undef]
108108 ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
109109 ; ^ all funclets use the same frame size
110110 ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
112112 ; CHECK: movq %rcx, [[PSPSymOffset]](%rsp)
113113 ; CHECK: leaq [[FPOffset]](%rcx), %rbp
114114 ; CHECK: .seh_endprologue
115 ; CHECK: [[L_before_f6:.+]]:
115 ; CHECK: [[test1_before_f6:.+]]:
116116 ; CHECK-NEXT: movl $6, %ecx
117117 ; CHECK-NEXT: callq f
118 ; CHECK-NEXT: [[L_after_f6:.+]]:
119 invoke void @f(i32 6) [ "funclet"(token %fault) ]
120 to label %fault.ret unwind label %finally.pad
118 ; CHECK-NEXT: [[test1_after_f6:.+]]:
119 invoke void @f(i32 6) [ "funclet"(token %fault.pad) ]
120 to label %fault.ret unwind label %finally
121121 fault.ret:
122 cleanupret from %fault unwind label %finally.pad
122 cleanupret from %fault.pad unwind label %finally
123123 catch2.ret:
124 catchret from %catch2 to label %finally.clone
124 catchret from %catch.pad2 to label %finally.clone
125125 finally.clone:
126126 call void @f(i32 7)
127127 br label %tail
128 finally.pad:
129 ; CHECK: .seh_proc [[L_finally:[^ ]+]]
130 %finally = cleanuppad within none []
128 finally:
129 ; CHECK: .seh_proc [[test1_finally:[^ ]+]]
130 %finally.pad = cleanuppad within none []
131131 ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]]
132132 ; ^ all funclets use the same frame size
133133 ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx
137137 ; CHECK: .seh_endprologue
138138 ; CHECK-NEXT: movl $7, %ecx
139139 ; CHECK-NEXT: callq f
140 call void @f(i32 7) [ "funclet"(token %finally) ]
141 cleanupret from %finally unwind to caller
140 call void @f(i32 7) [ "funclet"(token %finally.pad) ]
141 cleanupret from %finally.pad unwind to caller
142142 tail:
143143 call void @f(i32 8)
144144 ret void
145 ; CHECK: [[L_end:.*func_end.*]]:
145 ; CHECK: [[test1_end:.*func_end.*]]:
146146 }
147147
148 ; FIXME: Verify that the new clauses are correct and re-enable these checks.
149
150148 ; Now check for EH table in xdata (following standard xdata)
151 ; CHECKX-LABEL: .section .xdata
149 ; CHECK-LABEL: .section .xdata
152150 ; standard xdata comes here
153 ; CHECKX: .long 4{{$}}
151 ; CHECK: .long 4{{$}}
154152 ; ^ number of funclets
155 ; CHECKX-NEXT: .long [[L_catch1]]-[[L_begin]]
153 ; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]
156154 ; ^ offset from L_begin to start of 1st funclet
157 ; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]]
155 ; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
158156 ; ^ offset from L_begin to start of 2nd funclet
159 ; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]]
157 ; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
160158 ; ^ offset from L_begin to start of 3rd funclet
161 ; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]]
159 ; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
162160 ; ^ offset from L_begin to start of 4th funclet
163 ; CHECKX-NEXT: .long [[L_end]]-[[L_begin]]
161 ; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
164162 ; ^ offset from L_begin to end of last funclet
165 ; CHECKX-NEXT: .long 7
163 ; CHECK-NEXT: .long 7
166164 ; ^ number of EH clauses
167165 ; Clause 1: call f(2) is guarded by catch1
168 ; CHECKX-NEXT: .long 0
166 ; CHECK-NEXT: .long 0
169167 ; ^ flags (0 => catch handler)
170 ; CHECKX-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1
171 ; ^ offset of start of clause
172 ; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
173 ; ^ offset of end of clause
174 ; CHECKX-NEXT: .long [[L_catch1]]-[[L_begin]]
175 ; ^ offset of start of handler
176 ; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]]
177 ; ^ offset of end of handler
178 ; CHECKX-NEXT: .long 1
168 ; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1
169 ; ^ offset of start of clause
170 ; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
171 ; ^ offset of end of clause
172 ; CHECK-NEXT: .long [[test1_catch1]]-[[test1_begin]]
173 ; ^ offset of start of handler
174 ; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
175 ; ^ offset of end of handler
176 ; CHECK-NEXT: .long 1
179177 ; ^ type token of catch (from catchpad)
180178 ; Clause 2: call f(2) is also guarded by catch2
181 ; CHECKX-NEXT: .long 0
179 ; CHECK-NEXT: .long 0
182180 ; ^ flags (0 => catch handler)
183 ; CHECKX-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1
184 ; ^ offset of start of clause
185 ; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
186 ; ^ offset of end of clause
187 ; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]]
188 ; ^ offset of start of handler
189 ; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]]
190 ; ^ offset of end of handler
191 ; CHECKX-NEXT: .long 2
181 ; CHECK-NEXT: .long ([[test1_before_f2]]-[[test1_begin]])+1
182 ; ^ offset of start of clause
183 ; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
184 ; ^ offset of end of clause
185 ; CHECK-NEXT: .long [[test1_catch2]]-[[test1_begin]]
186 ; ^ offset of start of handler
187 ; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
188 ; ^ offset of end of handler
189 ; CHECK-NEXT: .long 2
192190 ; ^ type token of catch (from catchpad)
193191 ; Clause 3: calls f(1) and f(2) are guarded by finally
194 ; CHECKX-NEXT: .long 2
192 ; CHECK-NEXT: .long 2
195193 ; ^ flags (2 => finally handler)
196 ; CHECKX-NEXT: .long ([[L_before_f1]]-[[L_begin]])+1
197 ; ^ offset of start of clause
198 ; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1
199 ; ^ offset of end of clause
200 ; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]]
201 ; ^ offset of start of handler
202 ; CHECKX-NEXT: .long [[L_end]]-[[L_begin]]
203 ; ^ offset of end of handler
204 ; CHECKX-NEXT: .long 0
194 ; CHECK-NEXT: .long ([[test1_before_f1]]-[[test1_begin]])+1
195 ; ^ offset of start of clause
196 ; CHECK-NEXT: .long ([[test1_after_f2]]-[[test1_begin]])+1
197 ; ^ offset of end of clause
198 ; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
199 ; ^ offset of start of handler
200 ; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
201 ; ^ offset of end of handler
202 ; CHECK-NEXT: .long 0
205203 ; ^ type token slot (null for finally)
206204 ; Clause 4: call f(3) is guarded by finally
207205 ; This is a "duplicate" because the protected range (f(3))
208206 ; is in funclet catch1 but the finally's immediate parent
209207 ; is the main function, not that funclet.
210 ; CHECKX-NEXT: .long 10
208 ; CHECK-NEXT: .long 10
211209 ; ^ flags (2 => finally handler | 8 => duplicate)
212 ; CHECKX-NEXT: .long ([[L_before_f3]]-[[L_begin]])+1
213 ; ^ offset of start of clause
214 ; CHECKX-NEXT: .long ([[L_after_f3]]-[[L_begin]])+1
215 ; ^ offset of end of clause
216 ; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]]
217 ; ^ offset of start of handler
218 ; CHECKX-NEXT: .long [[L_end]]-[[L_begin]]
219 ; ^ offset of end of handler
220 ; CHECKX-NEXT: .long 0
210 ; CHECK-NEXT: .long ([[test1_before_f3]]-[[test1_begin]])+1
211 ; ^ offset of start of clause
212 ; CHECK-NEXT: .long ([[test1_after_f3]]-[[test1_begin]])+1
213 ; ^ offset of end of clause
214 ; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
215 ; ^ offset of start of handler
216 ; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
217 ; ^ offset of end of handler
218 ; CHECK-NEXT: .long 0
221219 ; ^ type token slot (null for finally)
222220 ; Clause 5: call f(5) is guarded by fault
223 ; CHECKX-NEXT: .long 4
224 ; ^ flags (4 => fault handler)
225 ; CHECKX-NEXT: .long ([[L_before_f5]]-[[L_begin]])+1
226 ; ^ offset of start of clause
227 ; CHECKX-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
228 ; ^ offset of end of clause
229 ; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]]
230 ; ^ offset of start of handler
231 ; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]]
232 ; ^ offset of end of handler
233 ; CHECKX-NEXT: .long 0
221 ; CHECK-NEXT: .long 4
222 ; ^ flags (4 => fault handler)
223 ; CHECK-NEXT: .long ([[test1_before_f5]]-[[test1_begin]])+1
224 ; ^ offset of start of clause
225 ; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1
226 ; ^ offset of end of clause
227 ; CHECK-NEXT: .long [[test1_fault]]-[[test1_begin]]
228 ; ^ offset of start of handler
229 ; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
230 ; ^ offset of end of handler
231 ; CHECK-NEXT: .long 0
234232 ; ^ type token slot (null for fault)
235233 ; Clause 6: calls f(4) and f(5) are guarded by finally
236234 ; This is a "duplicate" because the protected range (f(4)-f(5))
237235 ; is in funclet catch2 but the finally's immediate parent
238236 ; is the main function, not that funclet.
239 ; CHECKX-NEXT: .long 10
237 ; CHECK-NEXT: .long 10
240238 ; ^ flags (2 => finally handler | 8 => duplicate)
241 ; CHECKX-NEXT: .long ([[L_before_f4]]-[[L_begin]])+1
242 ; ^ offset of start of clause
243 ; CHECKX-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1
244 ; ^ offset of end of clause
245 ; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]]
246 ; ^ offset of start of handler
247 ; CHECKX-NEXT: .long [[L_end]]-[[L_begin]]
248 ; ^ offset of end of handler
249 ; CHECKX-NEXT: .long 0
239 ; CHECK-NEXT: .long ([[test1_before_f4]]-[[test1_begin]])+1
240 ; ^ offset of start of clause
241 ; CHECK-NEXT: .long ([[test1_after_f5]]-[[test1_begin]])+1
242 ; ^ offset of end of clause
243 ; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
244 ; ^ offset of start of handler
245 ; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
246 ; ^ offset of end of handler
247 ; CHECK-NEXT: .long 0
250248 ; ^ type token slot (null for finally)
251249 ; Clause 7: call f(6) is guarded by finally
252250 ; This is a "duplicate" because the protected range (f(3))
253251 ; is in funclet catch1 but the finally's immediate parent
254252 ; is the main function, not that funclet.
255 ; CHECKX-NEXT: .long 10
253 ; CHECK-NEXT: .long 10
256254 ; ^ flags (2 => finally handler | 8 => duplicate)
257 ; CHECKX-NEXT: .long ([[L_before_f6]]-[[L_begin]])+1
258 ; ^ offset of start of clause
259 ; CHECKX-NEXT: .long ([[L_after_f6]]-[[L_begin]])+1
260 ; ^ offset of end of clause
261 ; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]]
262 ; ^ offset of start of handler
263 ; CHECKX-NEXT: .long [[L_end]]-[[L_begin]]
264 ; ^ offset of end of handler
265 ; CHECKX-NEXT: .long 0
255 ; CHECK-NEXT: .long ([[test1_before_f6]]-[[test1_begin]])+1
256 ; ^ offset of start of clause
257 ; CHECK-NEXT: .long ([[test1_after_f6]]-[[test1_begin]])+1
258 ; ^ offset of end of clause
259 ; CHECK-NEXT: .long [[test1_finally]]-[[test1_begin]]
260 ; ^ offset of start of handler
261 ; CHECK-NEXT: .long [[test1_end]]-[[test1_begin]]
262 ; ^ offset of end of handler
263 ; CHECK-NEXT: .long 0
266264 ; ^ type token slot (null for finally)
265
266 ; Test with a cleanup that has no cleanupret, and thus needs its unwind dest
267 ; inferred from an inner catchswitch
268 ;
269 ; corresponds to C# along the lines of:
270 ; void test2() {
271 ; try {
272 ; try {
273 ; f(1);
274 ; } fault {
275 ; try {
276 ; f(2);
277 ; } catch(type1) {
278 ; }
279 ; __unreachable();
280 ; }
281 ; } catch(type2) {
282 ; }
283 ; }
284 ;
285 ; CHECK-LABEL: test2: # @test2
286 ; CHECK-NEXT: [[test2_begin:.*func_begin.*]]:
287 define void @test2() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
288 entry:
289 ; CHECK: .seh_endprologue
290 ; CHECK: [[test2_before_f1:.+]]:
291 ; CHECK-NEXT: movl $1, %ecx
292 ; CHECK-NEXT: callq f
293 ; CHECK-NEXT: [[test2_after_f1:.+]]:
294 invoke void @f(i32 1)
295 to label %exit unwind label %fault
296 fault:
297 ; CHECK: .seh_proc [[test2_fault:[^ ]+]]
298 %fault.pad = cleanuppad within none [i32 undef]
299 ; CHECK: .seh_endprologue
300 ; CHECK: [[test2_before_f2:.+]]:
301 ; CHECK-NEXT: movl $2, %ecx
302 ; CHECK-NEXT: callq f
303 ; CHECK-NEXT: [[test2_after_f2:.+]]:
304 invoke void @f(i32 2) ["funclet"(token %fault.pad)]
305 to label %unreachable unwind label %exn.dispatch.inner
306 exn.dispatch.inner:
307 %catchswitch.inner = catchswitch within %fault.pad [label %catch1] unwind label %exn.dispatch.outer
308 catch1:
309 %catch.pad1 = catchpad within %catchswitch.inner [i32 1]
310 ; CHECK: .seh_proc [[test2_catch1:[^ ]+]]
311 catchret from %catch.pad1 to label %unreachable
312 exn.dispatch.outer:
313 %catchswitch.outer = catchswitch within none [label %catch2] unwind to caller
314 catch2:
315 %catch.pad2 = catchpad within %catchswitch.outer [i32 2]
316 ; CHECK: .seh_proc [[test2_catch2:[^ ]+]]
317 catchret from %catch.pad2 to label %exit
318 exit:
319 ret void
320 unreachable:
321 unreachable
322 ; CHECK: [[test2_end:.*func_end.*]]:
323 }
324
325 ; Now check for EH table in xdata (following standard xdata)
326 ; CHECK-LABEL: .section .xdata
327 ; standard xdata comes here
328 ; CHECK: .long 3{{$}}
329 ; ^ number of funclets
330 ; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
331 ; ^ offset from L_begin to start of 1st funclet
332 ; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
333 ; ^ offset from L_begin to start of 2nd funclet
334 ; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
335 ; ^ offset from L_begin to start of 3rd funclet
336 ; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
337 ; ^ offset from L_begin to end of last funclet
338 ; CHECK-NEXT: .long 4
339 ; ^ number of EH clauses
340 ; Clause 1: call f(1) is guarded by fault
341 ; CHECK-NEXT: .long 4
342 ; ^ flags (4 => fault handler)
343 ; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
344 ; ^ offset of start of clause
345 ; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
346 ; ^ offset of end of clause
347 ; CHECK-NEXT: .long [[test2_fault]]-[[test2_begin]]
348 ; ^ offset of start of handler
349 ; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
350 ; ^ offset of end of handler
351 ; CHECK-NEXT: .long 0
352 ; ^ type token slot (null for fault)
353 ; Clause 2: call f(1) is also guarded by catch2
354 ; CHECK-NEXT: .long 0
355 ; ^ flags (0 => catch handler)
356 ; CHECK-NEXT: .long ([[test2_before_f1]]-[[test2_begin]])+1
357 ; ^ offset of start of clause
358 ; CHECK-NEXT: .long ([[test2_after_f1]]-[[test2_begin]])+1
359 ; ^ offset of end of clause
360 ; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
361 ; ^ offset of start of handler
362 ; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
363 ; ^ offset of end of handler
364 ; CHECK-NEXT: .long 2
365 ; ^ type token of catch (from catchpad)
366 ; Clause 3: calls f(2) is guarded by catch1
367 ; CHECK-NEXT: .long 0
368 ; ^ flags (0 => catch handler)
369 ; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
370 ; ^ offset of start of clause
371 ; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
372 ; ^ offset of end of clause
373 ; CHECK-NEXT: .long [[test2_catch1]]-[[test2_begin]]
374 ; ^ offset of start of handler
375 ; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
376 ; ^ offset of end of handler
377 ; CHECK-NEXT: .long 1
378 ; ^ type token of catch (from catchpad)
379 ; Clause 4: call f(2) is also guarded by catch2
380 ; This is a "duplicate" because the protected range (f(2))
381 ; is in funclet fault but catch2's immediate parent
382 ; is the main function, not that funclet.
383 ; CHECK-NEXT: .long 8
384 ; ^ flags (0 => catch handler | 8 => duplicate)
385 ; CHECK-NEXT: .long ([[test2_before_f2]]-[[test2_begin]])+1
386 ; ^ offset of start of clause
387 ; CHECK-NEXT: .long ([[test2_after_f2]]-[[test2_begin]])+1
388 ; ^ offset of end of clause
389 ; CHECK-NEXT: .long [[test2_catch2]]-[[test2_begin]]
390 ; ^ offset of start of handler
391 ; CHECK-NEXT: .long [[test2_end]]-[[test2_begin]]
392 ; ^ offset of end of handler
393 ; CHECK-NEXT: .long 2
394 ; ^ type token of catch (from catchpad)
395
396 ; Test with several cleanups that need to infer their unwind dests from each
397 ; other, the inner one needing to make the inference from an invoke, ignoring
398 ; not-really-unwinding calls/unwind-to-caller catchswitches, as well as some
399 ; internal invokes/catchswitches
400 ;
401 ; Corresponds to something like:
402 ; void test3() {
403 ; try {
404 ; f(1);
405 ; } fault { // fault1
406 ; try {
407 ; try {
408 ; f(2);
409 ; __unreachable();
410 ; } fault { // fault2
411 ; try {
412 ; f(3);
413 ; } fault { // fault3
414 ; try {
415 ; f(4);
416 ; } fault { // fault4
417 ; f(5); // no unwind edge (e.g. front-end knew it wouldn't throw but
418 ; didn't bother to specify nounwind)
419 ; try {
420 ; try {
421 ; f(6);
422 ; } catch(type 1) {
423 ; goto __unreachable;
424 ; }
425 ; } catch (type 2) { // marked "unwinds to caller" because we allow
426 ; // that if the unwind won't be taken (see
427 ; // SimplifyUnreachable & RemoveUnwindEdge)
428 ; goto _unreachable;
429 ; }
430 ; f(7);
431 ; __unreachable();
432 ; }
433 ; }
434 ; }
435 ; } fault { // fault 5
436 ; }
437 ; }
438 ; }
439 ;
440 ; CHECK-LABEL: test3: # @test3
441 ; CHECK-NEXT: [[test3_begin:.*func_begin.*]]:
442 define void @test3() personality i8* bitcast (void ()* @ProcessCLRException to i8*) {
443 entry:
444 ; CHECK: .seh_endprologue
445 ; CHECK: [[test3_before_f1:.+]]:
446 ; CHECK-NEXT: movl $1, %ecx
447 ; CHECK-NEXT: callq f
448 ; CHECK-NEXT: [[test3_after_f1:.+]]:
449 invoke void @f(i32 1)
450 to label %exit unwind label %fault1
451 fault1:
452 ; check lines below since this gets reordered to end-of-func
453 %fault.pad1 = cleanuppad within none [i32 undef]
454 invoke void @f(i32 2) ["funclet"(token %fault.pad1)]
455 to label %unreachable unwind label %fault2
456 fault2:
457 ; check lines below since this gets reordered to end-of-func
458 %fault.pad2 = cleanuppad within %fault.pad1 [i32 undef]
459 invoke void @f(i32 3) ["funclet"(token %fault.pad2)]
460 to label %unreachable unwind label %fault3
461 fault3:
462 ; check lines below since this gets reordered to end-of-func
463 %fault.pad3 = cleanuppad within %fault.pad2 [i32 undef]
464 invoke void @f(i32 4) ["funclet"(token %fault.pad3)]
465 to label %unreachable unwind label %fault4
466 fault4:
467 ; CHECK: .seh_proc [[test3_fault4:[^ ]+]]
468 %fault.pad4 = cleanuppad within %fault.pad3 [i32 undef]
469 ; CHECK: .seh_endprologue
470 call void @f(i32 5) ["funclet"(token %fault.pad4)]
471 ; CHECK: [[test3_before_f6:.+]]:
472 ; CHECK-NEXT: movl $6, %ecx
473 ; CHECK-NEXT: callq f
474 ; CHECK-NEXT: [[test3_after_f6:.+]]:
475 invoke void @f(i32 6) ["funclet"(token %fault.pad4)]
476 to label %fault4.cont unwind label %exn.dispatch1
477 fault4.cont:
478 ; CHECK: # %fault4.cont
479 ; CHECK: [[test3_before_f7:.+]]:
480 ; CHECK-NEXT: movl $7, %ecx
481 ; CHECK-NEXT: callq f
482 ; CHECK-NEXT: [[test3_after_f7:.+]]:
483 invoke void @f(i32 7) ["funclet"(token %fault.pad4)]
484 to label %unreachable unwind label %fault5
485 exn.dispatch1:
486 %catchswitch1 = catchswitch within %fault.pad4 [label %catch1] unwind label %exn.dispatch2
487 catch1:
488 %catch.pad1 = catchpad within %catchswitch1 [i32 1]
489 ; CHECK: .seh_proc [[test3_catch1:[^ ]+]]
490 catchret from %catch.pad1 to label %unreachable
491 exn.dispatch2:
492 %catchswitch2 = catchswitch within %fault.pad4 [label %catch2] unwind to caller
493 catch2:
494 %catch.pad2 = catchpad within %catchswitch2 [i32 2]
495 ; CHECK: .seh_proc [[test3_catch2:[^ ]+]]
496 catchret from %catch.pad2 to label %unreachable
497 fault5:
498 ; CHECK: .seh_proc [[test3_fault5:[^ ]+]]
499 %fault.pad5 = cleanuppad within %fault.pad1 [i32 undef]
500 ; CHECK: .seh_endprologue
501 cleanupret from %fault.pad5 unwind to caller
502 exit:
503 ret void
504 unreachable:
505 unreachable
506 ; CHECK: .seh_proc [[test3_fault3:[^ ]+]]
507 ; CHECK: # %fault3
508 ; CHECK: .seh_endprologue
509 ; CHECK: [[test3_before_f4:.+]]:
510 ; CHECK-NEXT: movl $4, %ecx
511 ; CHECK-NEXT: callq f
512 ; CHECK-NEXT: [[test3_after_f4:.+]]:
513 ; CHECK: .seh_proc [[test3_fault2:[^ ]+]]
514 ; CHECK: # %fault2
515 ; CHECK: .seh_endprologue
516 ; CHECK: [[test3_before_f3:.+]]:
517 ; CHECK-NEXT: movl $3, %ecx
518 ; CHECK-NEXT: callq f
519 ; CHECK-NEXT: [[test3_after_f3:.+]]:
520 ; CHECK: .seh_proc [[test3_fault1:[^ ]+]]
521 ; CHECK: # %fault1
522 ; CHECK: .seh_endprologue
523 ; CHECK: [[test3_before_f2:.+]]:
524 ; CHECK-NEXT: movl $2, %ecx
525 ; CHECK-NEXT: callq f
526 ; CHECK-NEXT: [[test3_after_f2:.+]]:
527 ; CHECK: [[test3_end:.*func_end.*]]:
528 }
529
530 ; Now check for EH table in xdata (following standard xdata)
531 ; CHECK-LABEL: .section .xdata
532 ; standard xdata comes here
533 ; CHECK: .long 7{{$}}
534 ; ^ number of funclets
535 ; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
536 ; ^ offset from L_begin to start of 1st funclet
537 ; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
538 ; ^ offset from L_begin to start of 2nd funclet
539 ; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
540 ; ^ offset from L_begin to start of 3rd funclet
541 ; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
542 ; ^ offset from L_begin to start of 4th funclet
543 ; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
544 ; ^ offset from L_begin to start of 5th funclet
545 ; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
546 ; ^ offset from L_begin to start of 6th funclet
547 ; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
548 ; ^ offset from L_begin to start of 7th funclet
549 ; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
550 ; ^ offset from L_begin to end of last funclet
551 ; CHECK-NEXT: .long 10
552 ; ^ number of EH clauses
553 ; Clause 1: call f(1) is guarded by fault1
554 ; CHECK-NEXT: .long 4
555 ; ^ flags (4 => fault handler)
556 ; CHECK-NEXT: .long ([[test3_before_f1]]-[[test3_begin]])+1
557 ; ^ offset of start of clause
558 ; CHECK-NEXT: .long ([[test3_after_f1]]-[[test3_begin]])+1
559 ; ^ offset of end of clause
560 ; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
561 ; ^ offset of start of handler
562 ; CHECK-NEXT: .long [[test3_end]]-[[test3_begin]]
563 ; ^ offset of end of handler
564 ; CHECK-NEXT: .long 0
565 ; ^ type token slot (null for fault)
566 ; Clause 3: call f(6) is guarded by catch1
567 ; CHECK-NEXT: .long 0
568 ; ^ flags (0 => catch handler)
569 ; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
570 ; ^ offset of start of clause
571 ; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
572 ; ^ offset of end of clause
573 ; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
574 ; ^ offset of start of handler
575 ; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
576 ; ^ offset of end of handler
577 ; CHECK-NEXT: .long 1
578 ; ^ type token of catch (from catchpad)
579 ; Clause 3: call f(6) is also guarded by catch2
580 ; CHECK-NEXT: .long 0
581 ; ^ flags (0 => catch handler)
582 ; CHECK-NEXT: .long ([[test3_before_f6]]-[[test3_begin]])+1
583 ; ^ offset of start of clause
584 ; CHECK-NEXT: .long ([[test3_after_f6]]-[[test3_begin]])+1
585 ; ^ offset of end of clause
586 ; CHECK-NEXT: .long [[test3_catch2]]-[[test3_begin]]
587 ; ^ offset of start of handler
588 ; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
589 ; ^ offset of end of handler
590 ; CHECK-NEXT: .long 2
591 ; ^ type token of catch (from catchpad)
592 ; Clause 4: call f(7) is guarded by fault5
593 ; This is a "duplicate" because the protected range (f(6)-f(7))
594 ; is in funclet fault4 but fault5's immediate parent
595 ; is fault1, not that funclet.
596 ; CHECK-NEXT: .long 12
597 ; ^ flags (4 => fault handler | 8 => duplicate)
598 ; CHECK-NEXT: .long ([[test3_before_f7]]-[[test3_begin]])+1
599 ; ^ offset of start of clause
600 ; CHECK-NEXT: .long ([[test3_after_f7]]-[[test3_begin]])+1
601 ; ^ offset of end of clause
602 ; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
603 ; ^ offset of start of handler
604 ; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
605 ; ^ offset of end of handler
606 ; CHECK-NEXT: .long 0
607 ; ^ type token slot (null for fault)
608 ; Clause 5: call f(4) is guarded by fault4
609 ; CHECK-NEXT: .long 4
610 ; ^ flags (4 => fault handler)
611 ; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
612 ; ^ offset of start of clause
613 ; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
614 ; ^ offset of end of clause
615 ; CHECK-NEXT: .long [[test3_fault4]]-[[test3_begin]]
616 ; ^ offset of start of handler
617 ; CHECK-NEXT: .long [[test3_catch1]]-[[test3_begin]]
618 ; ^ offset of end of handler
619 ; CHECK-NEXT: .long 0
620 ; ^ type token slot (null for fault)
621 ; Clause 6: call f(4) is also guarded by fault5
622 ; This is a "duplicate" because the protected range (f(4))
623 ; is in funclet fault3 but fault5's immediate parent
624 ; is fault1, not that funclet.
625 ; CHECK-NEXT: .long 12
626 ; ^ flags (4 => fault handler)
627 ; CHECK-NEXT: .long ([[test3_before_f4]]-[[test3_begin]])+1
628 ; ^ offset of start of clause
629 ; CHECK-NEXT: .long ([[test3_after_f4]]-[[test3_begin]])+1
630 ; ^ offset of end of clause
631 ; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
632 ; ^ offset of start of handler
633 ; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
634 ; ^ offset of end of handler
635 ; CHECK-NEXT: .long 0
636 ; ^ type token slot (null for fault)
637 ; Clause 7: call f(3) is guarded by fault3
638 ; CHECK-NEXT: .long 4
639 ; ^ flags (4 => fault handler)
640 ; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
641 ; ^ offset of start of clause
642 ; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
643 ; ^ offset of end of clause
644 ; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
645 ; ^ offset of start of handler
646 ; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
647 ; ^ offset of end of handler
648 ; CHECK-NEXT: .long 0
649 ; ^ type token slot (null for fault)
650 ; Clause 8: call f(3) is guarded by fault5
651 ; This is a "duplicate" because the protected range (f(3))
652 ; is in funclet fault2 but fault5's immediate parent
653 ; is fault1, not that funclet.
654 ; CHECK-NEXT: .long 12
655 ; ^ flags (4 => fault handler | 8 => duplicate)
656 ; CHECK-NEXT: .long ([[test3_before_f3]]-[[test3_begin]])+1
657 ; ^ offset of start of clause
658 ; CHECK-NEXT: .long ([[test3_after_f3]]-[[test3_begin]])+1
659 ; ^ offset of end of clause
660 ; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
661 ; ^ offset of start of handler
662 ; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
663 ; ^ offset of end of handler
664 ; CHECK-NEXT: .long 0
665 ; ^ type token slot (null for fault)
666 ; Clause 9: call f(2) is guarded by fault2
667 ; CHECK-NEXT: .long 4
668 ; ^ flags (4 => fault handler)
669 ; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
670 ; ^ offset of start of clause
671 ; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
672 ; ^ offset of end of clause
673 ; CHECK-NEXT: .long [[test3_fault2]]-[[test3_begin]]
674 ; ^ offset of start of handler
675 ; CHECK-NEXT: .long [[test3_fault1]]-[[test3_begin]]
676 ; ^ offset of end of handler
677 ; CHECK-NEXT: .long 0
678 ; ^ type token slot (null for fault)
679 ; Clause 10: call f(2) is guarded by fault5
680 ; CHECK-NEXT: .long 4
681 ; ^ flags (4 => fault handler)
682 ; CHECK-NEXT: .long ([[test3_before_f2]]-[[test3_begin]])+1
683 ; ^ offset of start of clause
684 ; CHECK-NEXT: .long ([[test3_after_f2]]-[[test3_begin]])+1
685 ; ^ offset of end of clause
686 ; CHECK-NEXT: .long [[test3_fault5]]-[[test3_begin]]
687 ; ^ offset of start of handler
688 ; CHECK-NEXT: .long [[test3_fault3]]-[[test3_begin]]
689 ; ^ offset of end of handler
690 ; CHECK-NEXT: .long 0
691 ; ^ type token slot (null for fault)