Index: lib/CodeGen/WinEHPrepare.cpp =================================================================== --- lib/CodeGen/WinEHPrepare.cpp +++ lib/CodeGen/WinEHPrepare.cpp @@ -194,20 +194,22 @@ } // Given BB which ends in an unwind edge, return the EHPad that this BB belongs -// to. If the unwind edge came from an invoke, return null. +// to. If the unwind edge came from an invoke, return null. If OuterScope is +// passed, EHPads that do not have it as their OuterScope will also be ignored +// (returning null). static const BasicBlock *getEHPadFromPredecessor(const BasicBlock *BB, Value *OuterScope) { const TerminatorInst *TI = BB->getTerminator(); if (isa(TI)) return nullptr; if (auto *CatchSwitch = dyn_cast(TI)) { - if (CatchSwitch->getOuterScope() != OuterScope) + if (OuterScope && CatchSwitch->getOuterScope() != OuterScope) return nullptr; return BB; } assert(!TI->isEHPad() && "unexpected EHPad!"); auto *CleanupPad = cast(TI)->getCleanupPad(); - if (CleanupPad->getOuterScope() != OuterScope) + if (OuterScope && CleanupPad->getOuterScope() != OuterScope) return nullptr; return CleanupPad->getParent(); } @@ -443,8 +445,18 @@ if (BB.isLandingPad()) report_fatal_error("CoreCLR EH cannot use landingpads"); const Instruction *FirstNonPHI = BB.getFirstNonPHI(); - if (!isTopLevelPad(FirstNonPHI)) + if (isa(FirstNonPHI)) + // This will be visited with its catchswitch continue; + if (auto *CPI = dyn_cast(FirstNonPHI)) { + if (CPI->getCleanupRetUnwindDest()) + // This will be visited from its unwind dest + continue; + } else if (auto *CSI = dyn_cast(FirstNonPHI)) { + if (CSI->getUnwindDest()) + // This will be visited from its unwind dest + continue; + } // queue this with sentinel parent state -1 to mean unwind to caller. Worklist.emplace_back(FirstNonPHI, -1); } @@ -454,8 +466,7 @@ int ParentState; std::tie(Pad, ParentState) = Worklist.pop_back_val(); - Value *OuterScope; - int PredState; + int SelfState; if (const CleanupPadInst *Cleanup = dyn_cast(Pad)) { // A cleanup can have multiple exits; don't re-process after the first. if (FuncInfo.EHPadStateMap.count(Cleanup)) @@ -463,50 +474,39 @@ // CoreCLR personality uses arity to distinguish faults from finallies. const BasicBlock *PadBlock = Cleanup->getParent(); ClrHandlerType HandlerType = - (Cleanup->getNumOperands() ? ClrHandlerType::Fault - : ClrHandlerType::Finally); - int NewState = + (Cleanup->getNumArgOperands() ? ClrHandlerType::Fault + : ClrHandlerType::Finally); + SelfState = addClrEHHandler(FuncInfo, ParentState, HandlerType, 0, PadBlock); - FuncInfo.EHPadStateMap[Cleanup] = NewState; - // Propagate the new state to all preds of the cleanup - OuterScope = Cleanup->getOuterScope(); - PredState = NewState; } else if (const auto *CatchSwitch = dyn_cast(Pad)) { - SmallVector Handlers; - for (const Use &U : CatchSwitch->handlers()) { - const auto *Catch = - cast(cast(U)->getFirstNonPHI()); - Handlers.push_back(Catch); - } - FuncInfo.EHPadStateMap[CatchSwitch] = ParentState; + // Walk the catchpads in reverse order since the early ones are reported + // like descendants of the later ones in the EH tables. int NewState = ParentState; - for (auto HandlerI = Handlers.rbegin(), HandlerE = Handlers.rend(); - HandlerI != HandlerE; ++HandlerI) { - const CatchPadInst *Catch = *HandlerI; - const BasicBlock *PadBlock = Catch->getParent(); + SmallVector CatchBlocks(CatchSwitch->handlers()); + for (auto CBI = CatchBlocks.rbegin(), CBE = CatchBlocks.rend(); + CBI != CBE; ++CBI) { + const BasicBlock *CatchBlock = cast(*CBI); + const auto *Catch = cast(CatchBlock->getFirstNonPHI()); uint32_t TypeToken = static_cast( cast(Catch->getArgOperand(0))->getZExtValue()); NewState = addClrEHHandler(FuncInfo, NewState, ClrHandlerType::Catch, - TypeToken, PadBlock); + TypeToken, CatchBlock); FuncInfo.EHPadStateMap[Catch] = NewState; } - for (const auto *CatchPad : Handlers) { - for (const User *U : CatchPad->users()) { - const auto *UserI = cast(U); - if (UserI->isEHPad()) - Worklist.emplace_back(UserI, ParentState); - } - } - PredState = NewState; - OuterScope = CatchSwitch->getOuterScope(); + // The catchswitch uses the same state number as the first catch (or, if + // we ever see an empty catchswitch, the state number of its successor). + SelfState = NewState; } else { llvm_unreachable("Unexpected EH pad"); } + // Record this pad's state. + FuncInfo.EHPadStateMap[Pad] = SelfState; + // Queue all predecessors with the given state for (const BasicBlock *Pred : predecessors(Pad->getParent())) { - if ((Pred = getEHPadFromPredecessor(Pred, OuterScope))) - Worklist.emplace_back(Pred->getFirstNonPHI(), PredState); + if ((Pred = getEHPadFromPredecessor(Pred, nullptr))) + Worklist.emplace_back(Pred->getFirstNonPHI(), SelfState); } } Index: test/CodeGen/X86/wineh-coreclr.ll =================================================================== --- test/CodeGen/X86/wineh-coreclr.ll +++ test/CodeGen/X86/wineh-coreclr.ll @@ -40,7 +40,7 @@ ; CHECK-NEXT: callq f ; CHECK-NEXT: [[L_after_f1:.+]]: invoke void @f(i32 1) - to label %inner_try unwind label %finally.pad + to label %inner_try unwind label %finally inner_try: ; CHECK: # %inner_try ; CHECK: [[L_before_f2:.+]]: @@ -48,11 +48,11 @@ ; CHECK-NEXT: callq f ; CHECK-NEXT: [[L_after_f2:.+]]: invoke void @f(i32 2) - to label %finally.clone unwind label %catch1.pad -catch1.pad: - %cs1 = catchswitch within none [label %catch1.body, label %catch2.body] unwind label %finally.pad -catch1.body: - %catch1 = catchpad within %cs1 [i32 1] + to label %finally.clone unwind label %exn.dispatch +exn.dispatch: + %catchswitch = catchswitch within none [label %catch1, label %catch2] unwind label %finally +catch1: + %catch.pad1 = catchpad within %catchswitch [i32 1] ; CHECK: .seh_proc [[L_catch1:[^ ]+]] ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] ; ^ all funclets use the same frame size @@ -64,18 +64,18 @@ ; CHECK: movq %rdx, %rcx ; ^ exception pointer passed in rdx ; CHECK-NEXT: callq g - %exn1 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch1) + %exn1 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad1) call void @g(i8 addrspace(1)* %exn1) ; CHECK: [[L_before_f3:.+]]: ; CHECK-NEXT: movl $3, %ecx ; CHECK-NEXT: callq f ; CHECK-NEXT: [[L_after_f3:.+]]: invoke void @f(i32 3) - to label %catch1.ret unwind label %finally.pad + to label %catch1.ret unwind label %finally catch1.ret: - catchret from %catch1 to label %finally.clone -catch2.body: - %catch2 = catchpad within %cs1 [i32 2] + catchret from %catch.pad1 to label %finally.clone +catch2: + %catch.pad2 = catchpad within %catchswitch [i32 2] ; CHECK: .seh_proc [[L_catch2:[^ ]+]] ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] ; ^ all funclets use the same frame size @@ -87,14 +87,14 @@ ; CHECK: movq %rdx, %rcx ; ^ exception pointer passed in rdx ; CHECK-NEXT: callq g - %exn2 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch2) + %exn2 = call i8 addrspace(1)* @llvm.eh.exceptionpointer.p1i8(token %catch.pad2) call void @g(i8 addrspace(1)* %exn2) ; CHECK: [[L_before_f4:.+]]: ; CHECK-NEXT: movl $4, %ecx ; CHECK-NEXT: callq f ; CHECK-NEXT: [[L_after_f4:.+]]: invoke void @f(i32 4) - to label %try_in_catch unwind label %finally.pad + to label %try_in_catch unwind label %finally try_in_catch: ; CHECK: # %try_in_catch ; CHECK: [[L_before_f5:.+]]: @@ -102,10 +102,10 @@ ; CHECK-NEXT: callq f ; CHECK-NEXT: [[L_after_f5:.+]]: invoke void @f(i32 5) - to label %catch2.ret unwind label %fault.pad -fault.pad: + to label %catch2.ret unwind label %fault +fault: ; CHECK: .seh_proc [[L_fault:[^ ]+]] - %fault = cleanuppad within none [i32 undef] + %fault.pad = cleanuppad within %catch.pad2 [i32 undef] ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] ; ^ all funclets use the same frame size ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx @@ -118,17 +118,17 @@ ; CHECK-NEXT: callq f ; CHECK-NEXT: [[L_after_f6:.+]]: invoke void @f(i32 6) - to label %fault.ret unwind label %finally.pad + to label %fault.ret unwind label %finally fault.ret: - cleanupret from %fault unwind label %finally.pad + cleanupret from %fault.pad unwind label %finally catch2.ret: - catchret from %catch2 to label %finally.clone + catchret from %catch.pad2 to label %finally.clone finally.clone: call void @f(i32 7) br label %tail -finally.pad: +finally: ; CHECK: .seh_proc [[L_finally:[^ ]+]] - %finally = cleanuppad within none [] + %finally.pad = cleanuppad within none [] ; CHECK: .seh_stackalloc [[FuncletFrameSize:[0-9]+]] ; ^ all funclets use the same frame size ; CHECK: movq [[PSPSymOffset]](%rcx), %rcx @@ -139,129 +139,127 @@ ; CHECK-NEXT: movl $7, %ecx ; CHECK-NEXT: callq f call void @f(i32 7) - cleanupret from %finally unwind to caller + cleanupret from %finally.pad unwind to caller tail: call void @f(i32 8) ret void ; CHECK: [[L_end:.*func_end.*]]: } -; FIXME: Verify that the new clauses are correct and re-enable these checks. - ; Now check for EH table in xdata (following standard xdata) -; CHECKX-LABEL: .section .xdata +; CHECK-LABEL: .section .xdata ; standard xdata comes here -; CHECKX: .long 4{{$}} +; CHECK: .long 4{{$}} ; ^ number of funclets -; CHECKX-NEXT: .long [[L_catch1]]-[[L_begin]] +; CHECK-NEXT: .long [[L_catch1]]-[[L_begin]] ; ^ offset from L_begin to start of 1st funclet -; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]] +; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]] ; ^ offset from L_begin to start of 2nd funclet -; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]] +; CHECK-NEXT: .long [[L_fault]]-[[L_begin]] ; ^ offset from L_begin to start of 3rd funclet -; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] +; CHECK-NEXT: .long [[L_finally]]-[[L_begin]] ; ^ offset from L_begin to start of 4th funclet -; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] +; CHECK-NEXT: .long [[L_end]]-[[L_begin]] ; ^ offset from L_begin to end of last funclet -; CHECKX-NEXT: .long 7 +; CHECK-NEXT: .long 7 ; ^ number of EH clauses ; Clause 1: call f(2) is guarded by catch1 -; CHECKX-NEXT: .long 0 +; CHECK-NEXT: .long 0 ; ^ flags (0 => catch handler) -; CHECKX-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1 ; ^ offset of start of clause -; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 ; ^ offset of end of clause -; CHECKX-NEXT: .long [[L_catch1]]-[[L_begin]] +; CHECK-NEXT: .long [[L_catch1]]-[[L_begin]] ; ^ offset of start of handler -; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]] +; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]] ; ^ offset of end of handler -; CHECKX-NEXT: .long 1 +; CHECK-NEXT: .long 1 ; ^ type token of catch (from catchpad) ; Clause 2: call f(2) is also guarded by catch2 -; CHECKX-NEXT: .long 0 +; CHECK-NEXT: .long 0 ; ^ flags (0 => catch handler) -; CHECKX-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_before_f2]]-[[L_begin]])+1 ; ^ offset of start of clause -; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 ; ^ offset of end of clause -; CHECKX-NEXT: .long [[L_catch2]]-[[L_begin]] +; CHECK-NEXT: .long [[L_catch2]]-[[L_begin]] ; ^ offset of start of handler -; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]] +; CHECK-NEXT: .long [[L_fault]]-[[L_begin]] ; ^ offset of end of handler -; CHECKX-NEXT: .long 2 +; CHECK-NEXT: .long 2 ; ^ type token of catch (from catchpad) ; Clause 3: calls f(1) and f(2) are guarded by finally -; CHECKX-NEXT: .long 2 +; CHECK-NEXT: .long 2 ; ^ flags (2 => finally handler) -; CHECKX-NEXT: .long ([[L_before_f1]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_before_f1]]-[[L_begin]])+1 ; ^ offset of start of clause -; CHECKX-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_after_f2]]-[[L_begin]])+1 ; ^ offset of end of clause -; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] +; CHECK-NEXT: .long [[L_finally]]-[[L_begin]] ; ^ offset of start of handler -; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] +; CHECK-NEXT: .long [[L_end]]-[[L_begin]] ; ^ offset of end of handler -; CHECKX-NEXT: .long 0 +; CHECK-NEXT: .long 0 ; ^ type token slot (null for finally) ; Clause 4: call f(3) is guarded by finally ; This is a "duplicate" because the protected range (f(3)) ; is in funclet catch1 but the finally's immediate parent ; is the main function, not that funclet. -; CHECKX-NEXT: .long 10 +; CHECK-NEXT: .long 10 ; ^ flags (2 => finally handler | 8 => duplicate) -; CHECKX-NEXT: .long ([[L_before_f3]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_before_f3]]-[[L_begin]])+1 ; ^ offset of start of clause -; CHECKX-NEXT: .long ([[L_after_f3]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_after_f3]]-[[L_begin]])+1 ; ^ offset of end of clause -; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] +; CHECK-NEXT: .long [[L_finally]]-[[L_begin]] ; ^ offset of start of handler -; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] +; CHECK-NEXT: .long [[L_end]]-[[L_begin]] ; ^ offset of end of handler -; CHECKX-NEXT: .long 0 +; CHECK-NEXT: .long 0 ; ^ type token slot (null for finally) ; Clause 5: call f(5) is guarded by fault -; CHECKX-NEXT: .long 4 +; CHECK-NEXT: .long 4 ; ^ flags (4 => fault handler) -; CHECKX-NEXT: .long ([[L_before_f5]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_before_f5]]-[[L_begin]])+1 ; ^ offset of start of clause -; CHECKX-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1 ; ^ offset of end of clause -; CHECKX-NEXT: .long [[L_fault]]-[[L_begin]] +; CHECK-NEXT: .long [[L_fault]]-[[L_begin]] ; ^ offset of start of handler -; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] +; CHECK-NEXT: .long [[L_finally]]-[[L_begin]] ; ^ offset of end of handler -; CHECKX-NEXT: .long 0 +; CHECK-NEXT: .long 0 ; ^ type token slot (null for fault) ; Clause 6: calls f(4) and f(5) are guarded by finally ; This is a "duplicate" because the protected range (f(4)-f(5)) ; is in funclet catch2 but the finally's immediate parent ; is the main function, not that funclet. -; CHECKX-NEXT: .long 10 +; CHECK-NEXT: .long 10 ; ^ flags (2 => finally handler | 8 => duplicate) -; CHECKX-NEXT: .long ([[L_before_f4]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_before_f4]]-[[L_begin]])+1 ; ^ offset of start of clause -; CHECKX-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_after_f5]]-[[L_begin]])+1 ; ^ offset of end of clause -; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] +; CHECK-NEXT: .long [[L_finally]]-[[L_begin]] ; ^ offset of start of handler -; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] +; CHECK-NEXT: .long [[L_end]]-[[L_begin]] ; ^ offset of end of handler -; CHECKX-NEXT: .long 0 +; CHECK-NEXT: .long 0 ; ^ type token slot (null for finally) ; Clause 7: call f(6) is guarded by finally ; This is a "duplicate" because the protected range (f(3)) ; is in funclet catch1 but the finally's immediate parent ; is the main function, not that funclet. -; CHECKX-NEXT: .long 10 +; CHECK-NEXT: .long 10 ; ^ flags (2 => finally handler | 8 => duplicate) -; CHECKX-NEXT: .long ([[L_before_f6]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_before_f6]]-[[L_begin]])+1 ; ^ offset of start of clause -; CHECKX-NEXT: .long ([[L_after_f6]]-[[L_begin]])+1 +; CHECK-NEXT: .long ([[L_after_f6]]-[[L_begin]])+1 ; ^ offset of end of clause -; CHECKX-NEXT: .long [[L_finally]]-[[L_begin]] +; CHECK-NEXT: .long [[L_finally]]-[[L_begin]] ; ^ offset of start of handler -; CHECKX-NEXT: .long [[L_end]]-[[L_begin]] +; CHECK-NEXT: .long [[L_end]]-[[L_begin]] ; ^ offset of end of handler -; CHECKX-NEXT: .long 0 +; CHECK-NEXT: .long 0 ; ^ type token slot (null for finally)