[WinEH] Disallow cyclic unwinds
[oota-llvm.git] / test / Verifier / invalid-eh.ll
index 906b24a15c30a50722ce7045ecc369b8d8b3d07d..af3d987a39b55b28588f163dca7b51cd97578d71 100644 (file)
@@ -2,6 +2,23 @@
 ; RUN: sed -e s/.T2:// %s | not llvm-as -disable-output 2>&1 | FileCheck --check-prefix=CHECK2 %s
 ; RUN: sed -e s/.T3:// %s | not llvm-as -disable-output 2>&1 | FileCheck --check-prefix=CHECK3 %s
 ; RUN: sed -e s/.T4:// %s | not llvm-as -disable-output 2>&1 | FileCheck --check-prefix=CHECK4 %s
+; RUN: sed -e s/.T5:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK5 %s
+; RUN: sed -e s/.T6:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK6 %s
+; RUN: sed -e s/.T7:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK7 %s
+; RUN: sed -e s/.T8:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK8 %s
+; RUN: sed -e s/.T9:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK9 %s
+; RUN: sed -e s/.T10:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK10 %s
+; RUN: sed -e s/.T11:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK11 %s
+; RUN: sed -e s/.T12:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK12 %s
+; RUN: sed -e s/.T13:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK13 %s
+; RUN: sed -e s/.T14:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK14 %s
+; RUN: sed -e s/.T15:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK15 %s
+; RUN: sed -e s/.T16:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK16 %s
+; RUN: sed -e s/.T17:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK17 %s
+; RUN: sed -e s/.T18:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK18 %s
+; RUN: sed -e s/.T19:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK19 %s
+
+declare void @g()
 
 ;T1: define void @f() {
 ;T1:   entry:
 ;T4:     cleanupret from %x unwind to caller
 ;T4:     ; CHECK4: CleanupReturnInst needs to be provided a CleanupPad
 ;T4: }
+
+;T5: define void @f() personality void ()* @g {
+;T5:   entry:
+;T5:     ret void
+;T5:   switch:
+;T5:     %cs = catchswitch within none [label %catch] unwind to caller
+;T5:   catch:
+;T5:     catchpad within %cs []
+;T5:     unreachable
+;T5:   bogus:
+;T5:     cleanuppad within %cs []
+;T5:     ; CHECK5: CleanupPadInst has an invalid parent
+;T5:     unreachable
+;T5: }
+
+;T6: define void @f() personality void ()* @g {
+;T6:   entry:
+;T6:     ret void
+;T6:   switch1:
+;T6:     %cs1 = catchswitch within none [label %catch1] unwind label %catch2
+;T6:     ; CHECK6: Block containg CatchPadInst must be jumped to only by its catchswitch
+;T6:   catch1:
+;T6:     catchpad within %cs1 []
+;T6:     unreachable
+;T6:   switch2:
+;T6:     %cs2 = catchswitch within none [label %catch2] unwind to caller
+;T6:   catch2:
+;T6:     catchpad within %cs2 []
+;T6:     unreachable
+;T6: }
+
+;T7: define void @f() personality void ()* @g {
+;T7:   entry:
+;T7:     ret void
+;T7:   switch1:
+;T7:     %cs1 = catchswitch within none [label %catch1] unwind to caller
+;T7:   catch1:
+;T7:     catchpad within %cs1 []
+;T7:     unreachable
+;T7:   switch2:
+;T7:     %cs2 = catchswitch within %cs1 [label %catch2] unwind to caller
+;T7:     ; CHECK7: CatchSwitchInst has an invalid parent
+;T7:   catch2:
+;T7:     catchpad within %cs2 []
+;T7:     unreachable
+;T7: }
+
+;T8: define void @f() personality void ()* @g {
+;T8:   entry:
+;T8:     ret void
+;T8:   switch1:
+;T8:     %cs1 = catchswitch within none [ label %switch1 ] unwind to caller
+;T8:     ; CHECK8: CatchSwitchInst handlers must be catchpads
+;T8: }
+
+;T9: define void @f() personality void ()* @g {
+;T9:   entry:
+;T9:     ret void
+;T9:   cleanup:
+;T9:     %cp = cleanuppad within none []
+;T9:     invoke void @g() [ "funclet"(token %cp) ]
+;T9:       to label %exit unwind label %cleanup
+;T9:       ; CHECK9: EH pad cannot handle exceptions raised within it
+;T9:       ; CHECK9-NEXT: %cp = cleanuppad within none []
+;T9:       ; CHECK9-NEXT: invoke void @g() [ "funclet"(token %cp) ]
+;T9:   exit:
+;T9:     ret void
+;T9: }
+
+;T10: define void @f() personality void ()* @g {
+;T10:   entry:
+;T10:     ret void
+;T10:   cleanup1:
+;T10:     %cp1 = cleanuppad within none []
+;T10:     unreachable
+;T10:   switch:
+;T10:     %cs = catchswitch within %cp1 [label %catch] unwind to caller
+;T10:   catch:
+;T10:     %catchp1 = catchpad within %cs [i32 1]
+;T10:     unreachable
+;T10:   cleanup2:
+;T10:     %cp2 = cleanuppad within %catchp1 []
+;T10:     unreachable
+;T10:   cleanup3:
+;T10:     %cp3 = cleanuppad within %cp2 []
+;T10:     cleanupret from %cp3 unwind label %switch
+;T10:       ; CHECK10: EH pad cannot handle exceptions raised within it
+;T10:       ; CHECK10-NEXT: %cs = catchswitch within %cp1 [label %catch] unwind to caller
+;T10:       ; CHECK10-NEXT: cleanupret from %cp3 unwind label %switch
+;T10: }
+
+;T11: define void @f() personality void ()* @g {
+;T11:   entry:
+;T11:     ret void
+;T11:   cleanup1:
+;T11:     %cp1 = cleanuppad within none []
+;T11:     unreachable
+;T11:   cleanup2:
+;T11:     %cp2 = cleanuppad within %cp1 []
+;T11:     unreachable
+;T11:   switch:
+;T11:     %cs = catchswitch within none [label %catch] unwind label %cleanup2
+;T11:     ; CHECK11: A single unwind edge may only enter one EH pad
+;T11:     ; CHECK11-NEXT: %cs = catchswitch within none [label %catch] unwind label %cleanup2
+;T11:   catch:
+;T11:     catchpad within %cs [i32 1]
+;T11:     unreachable
+;T11: }
+
+;T12: define void @f() personality void ()* @g {
+;T12:   entry:
+;T12:     ret void
+;T12:   cleanup:
+;T12:     %cp = cleanuppad within none []
+;T12:     cleanupret from %cp unwind label %switch
+;T12:     ; CHECK12: A cleanupret must exit its cleanup
+;T12:     ; CHECK12-NEXT: cleanupret from %cp unwind label %switch
+;T12:   switch:
+;T12:     %cs = catchswitch within %cp [label %catch] unwind to caller
+;T12:   catch:
+;T12:     catchpad within %cs [i32 1]
+;T12:     unreachable
+;T12: }
+
+;T13: define void @f() personality void ()* @g {
+;T13:   entry:
+;T13:     ret void
+;T13:   switch:
+;T13:     %cs = catchswitch within none [label %catch] unwind label %switch
+;T13:     ; CHECK13: EH pad cannot handle exceptions raised within it
+;T13:     ; CHECK13-NEXT:  %cs = catchswitch within none [label %catch] unwind label %switch
+;T13:   catch:
+;T13:     catchpad within %cs [i32 0]
+;T13:     unreachable
+;T13: }
+
+;T14: define void @f() personality void ()* @g {
+;T14:   entry:
+;T14:     ret void
+;T14:   cleanup:
+;T14:     %cp = cleanuppad within none []
+;T14:     unreachable
+;T14:   left:
+;T14:     cleanupret from %cp unwind label %switch
+;T14:   right:
+;T14:     cleanupret from %cp unwind to caller
+;T14:     ; CHECK14: Unwind edges out of a funclet pad must have the same unwind dest
+;T14:     ; CHECK14-NEXT: %cp = cleanuppad within none []
+;T14:     ; CHECK14-NEXT: cleanupret from %cp unwind label %switch
+;T14:     ; CHECK14-NEXT: cleanupret from %cp unwind to caller
+;T14:   switch:
+;T14:     %cs = catchswitch within none [label %catch] unwind to caller
+;T14:   catch:
+;T14:     catchpad within %cs [i32 1]
+;T14:     unreachable
+;T14: }
+
+;T15: define void @f() personality void ()* @g {
+;T15:   entry:
+;T15:     ret void
+;T15:   switch:
+;T15:     %cs = catchswitch within none [label %catch] unwind to caller
+;T15:   catch:
+;T15:     %catch.pad = catchpad within %cs [i32 1]
+;T15:     invoke void @g() [ "funclet"(token %catch.pad) ]
+;T15:       to label %unreachable unwind label %target1
+;T15:   unreachable:
+;T15:     unreachable
+;T15:   target1:
+;T15:     cleanuppad within none []
+;T15:     unreachable
+;T15:   target2:
+;T15:     cleanuppad within none []
+;T15:     unreachable
+;T15:   nested.1:
+;T15:     %nested.pad.1 = cleanuppad within %catch.pad []
+;T15:     unreachable
+;T15:   nested.2:
+;T15:     %nested.pad.2 = cleanuppad within %nested.pad.1 []
+;T15:     cleanupret from %nested.pad.2 unwind label %target2
+;T15:     ; CHECK15: Unwind edges out of a funclet pad must have the same unwind dest
+;T15:     ; CHECK15-NEXT: %catch.pad = catchpad within %cs [i32 1]
+;T15:     ; CHECK15-NEXT: cleanupret from %nested.pad.2 unwind label %target2
+;T15:     ; CHECK15-NEXT: invoke void @g() [ "funclet"(token %catch.pad) ]
+;T15:     ; CHECK15-NEXT:   to label %unreachable unwind label %target1
+;T15: }
+
+;T16: define void @f() personality void ()* @g {
+;T16:   entry:
+;T16:     ret void
+;T16:   switch:
+;T16:     %cs = catchswitch within none [label %catch] unwind to caller
+;T16:   catch:
+;T16:     %catch.pad = catchpad within %cs [i32 1]
+;T16:     invoke void @g() [ "funclet"(token %catch.pad) ]
+;T16:       to label %unreachable unwind label %target1
+;T16:     ; CHECK16: Unwind edges out of a catch must have the same unwind dest as the parent catchswitch
+;T16:     ; CHECK16-NEXT:   %catch.pad = catchpad within %cs [i32 1]
+;T16:     ; CHECK16-NEXT:  invoke void @g() [ "funclet"(token %catch.pad) ]
+;T16:     ; CHECK16-NEXT:          to label %unreachable unwind label %target1
+;T16:     ; CHECK16-NEXT:  %cs = catchswitch within none [label %catch] unwind to caller
+;T16:   unreachable:
+;T16:     unreachable
+;T16:   target1:
+;T16:     cleanuppad within none []
+;T16:     unreachable
+;T16: }
+
+;T17: define void @f() personality void ()* @g {
+;T17:   entry:
+;T17:     ret void
+;T17:   switch:
+;T17:     %cs = catchswitch within none [label %catch] unwind label %target1
+;T17:   catch:
+;T17:     %catch.pad = catchpad within %cs [i32 1]
+;T17:     invoke void @g() [ "funclet"(token %catch.pad) ]
+;T17:       to label %unreachable unwind label %target2
+;T17:     ; CHECK17: Unwind edges out of a catch must have the same unwind dest as the parent catchswitch
+;T17:     ; CHECK17-NEXT:  %catch.pad = catchpad within %cs [i32 1]
+;T17:     ; CHECK17-NEXT:  invoke void @g() [ "funclet"(token %catch.pad) ]
+;T17:     ; CHECK17-NEXT:          to label %unreachable unwind label %target2
+;T17:     ; CHECK17-NEXT:  %cs = catchswitch within none [label %catch] unwind label %target1
+;T17:   unreachable:
+;T17:     unreachable
+;T17:   target1:
+;T17:     cleanuppad within none []
+;T17:     unreachable
+;T17:   target2:
+;T17:     cleanuppad within none []
+;T17:     unreachable
+;T17: }
+
+;T18: define void @f() personality void ()* @g {
+;T18:   entry:
+;T18:     invoke void @g()
+;T18:       to label %invoke.cont unwind label %left
+;T18:   invoke.cont:
+;T18:     invoke void @g()
+;T18:       to label %unreachable unwind label %right
+;T18:   left:
+;T18:     %cp.left = cleanuppad within none []
+;T18:     invoke void @g() [ "funclet"(token %cp.left) ]
+;T18:       to label %unreachable unwind label %right
+;T18:   right:
+;T18:     %cp.right = cleanuppad within none []
+;T18:     invoke void @g() [ "funclet"(token %cp.right) ]
+;T18:       to label %unreachable unwind label %left
+;T18:     ; CHECK18: EH pads can't handle each other's exceptions
+;T18:     ; CHECK18-NEXT: %cp.left = cleanuppad within none []
+;T18:     ; CHECK18-NEXT:  invoke void @g() [ "funclet"(token %cp.left) ]
+;T18:     ; CHECK18-NEXT:          to label %unreachable unwind label %right
+;T18:     ; CHECK18-NEXT:  %cp.right = cleanuppad within none []
+;T18:     ; CHECK18-NEXT:  invoke void @g() [ "funclet"(token %cp.right) ]
+;T18:     ; CHECK18-NEXT:          to label %unreachable unwind label %left
+;T18:   unreachable:
+;T18:     unreachable
+;T18: }
+
+;T19: define void @f() personality void ()* @g {
+;T19:   entry:
+;T19:     ret void
+;T19:   red:
+;T19:     %redpad = cleanuppad within none []
+;T19:     unreachable
+;T19:   red.inner:
+;T19:     %innerpad = cleanuppad within %redpad []
+;T19:     invoke void @g() [ "funclet"(token %innerpad) ]
+;T19:       to label %unreachable unwind label %green
+;T19:   green:
+;T19:     %greenswitch = catchswitch within none [label %catch] unwind label %blue
+;T19:   catch:
+;T19:     catchpad within %greenswitch [i32 42]
+;T19:     unreachable
+;T19:   blue:
+;T19:     %bluepad = cleanuppad within none []
+;T19:     cleanupret from %bluepad unwind label %red
+;T19:     ; CHECK19: EH pads can't handle each other's exceptions
+;T19:     ; CHECK19-NEXT: %redpad = cleanuppad within none []
+;T19:     ; CHECK19-NEXT: invoke void @g() [ "funclet"(token %innerpad) ]
+;T19:     ; CHECK19-NEXT:         to label %unreachable unwind label %green
+;T19:     ; CHECK19-NEXT: %greenswitch = catchswitch within none [label %catch] unwind label %blue
+;T19:     ; CHECK19-NEXT: %bluepad = cleanuppad within none []
+;T19:     ; CHECK19-NEXT: cleanupret from %bluepad unwind label %red
+;T19:   unreachable:
+;T19:     unreachable
+;T19: }