| ; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -winehprepare < %s | FileCheck %s |
| |
| declare i32 @__CxxFrameHandler3(...) |
| |
| declare void @f() |
| |
| declare i32 @g() |
| |
| declare void @h(i32) |
| |
| declare i1 @i() |
| |
| declare void @llvm.bar() nounwind |
| |
| ; CHECK-LABEL: @test1( |
| define void @test1(i1 %B) personality i32 (...)* @__CxxFrameHandler3 { |
| entry: |
| ; Spill slot should be inserted here |
| ; CHECK: [[Slot:%[^ ]+]] = alloca |
| ; Can't store for %phi at these defs because the lifetimes overlap |
| ; CHECK-NOT: store |
| %x = call i32 @g() |
| %y = call i32 @g() |
| br i1 %B, label %left, label %right |
| left: |
| ; CHECK: left: |
| ; CHECK-NEXT: store i32 %x, i32* [[Slot]] |
| ; CHECK-NEXT: invoke void @f |
| invoke void @f() |
| to label %exit unwind label %merge |
| right: |
| ; CHECK: right: |
| ; CHECK-NEXT: store i32 %y, i32* [[Slot]] |
| ; CHECK-NEXT: invoke void @f |
| invoke void @f() |
| to label %exit unwind label %merge |
| merge: |
| ; CHECK: merge: |
| ; CHECK-NOT: = phi |
| %phi = phi i32 [ %x, %left ], [ %y, %right ] |
| %cs1 = catchswitch within none [label %catch] unwind to caller |
| |
| catch: |
| %cp = catchpad within %cs1 [] |
| ; CHECK: catch: |
| ; CHECK: [[Reload:%[^ ]+]] = load i32, i32* [[Slot]] |
| ; CHECK-NEXT: call void @h(i32 [[Reload]]) |
| call void @h(i32 %phi) [ "funclet"(token %cp) ] |
| catchret from %cp to label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; CHECK-LABEL: @test2( |
| define void @test2(i1 %B) personality i32 (...)* @__CxxFrameHandler3 { |
| entry: |
| br i1 %B, label %left, label %right |
| left: |
| ; Need two stores here because %x and %y interfere so they need 2 slots |
| ; CHECK: left: |
| ; CHECK: store i32 1, i32* [[Slot1:%[^ ]+]] |
| ; CHECK: store i32 1, i32* [[Slot2:%[^ ]+]] |
| ; CHECK-NEXT: invoke void @f |
| invoke void @f() |
| to label %exit unwind label %merge.inner |
| right: |
| ; Need two stores here because %x and %y interfere so they need 2 slots |
| ; CHECK: right: |
| ; CHECK-DAG: store i32 2, i32* [[Slot1]] |
| ; CHECK-DAG: store i32 2, i32* [[Slot2]] |
| ; CHECK: invoke void @f |
| invoke void @f() |
| to label %exit unwind label %merge.inner |
| merge.inner: |
| ; CHECK: merge.inner: |
| ; CHECK-NOT: = phi |
| ; CHECK: catchswitch within none |
| %x = phi i32 [ 1, %left ], [ 2, %right ] |
| %cs1 = catchswitch within none [label %catch.inner] unwind label %merge.outer |
| |
| catch.inner: |
| %cpinner = catchpad within %cs1 [] |
| ; Need just one store here because only %y is affected |
| ; CHECK: catch.inner: |
| %z = call i32 @g() [ "funclet"(token %cpinner) ] |
| ; CHECK: store i32 %z |
| ; CHECK-NEXT: invoke void @f |
| invoke void @f() [ "funclet"(token %cpinner) ] |
| to label %catchret.inner unwind label %merge.outer |
| |
| catchret.inner: |
| catchret from %cpinner to label %exit |
| |
| merge.outer: |
| %y = phi i32 [ %x, %merge.inner ], [ %z, %catch.inner ] |
| ; CHECK: merge.outer: |
| ; CHECK-NOT: = phi |
| ; CHECK: catchswitch within none |
| %cs2 = catchswitch within none [label %catch.outer] unwind to caller |
| |
| catch.outer: |
| %cpouter = catchpad within %cs2 [] |
| ; CHECK: catch.outer: |
| ; CHECK: [[CatchPad:%[^ ]+]] = catchpad within %cs2 [] |
| ; Need to load x and y from two different slots since they're both live |
| ; and can have different values (if we came from catch.inner) |
| ; CHECK-DAG: load i32, i32* [[Slot1]] |
| ; CHECK-DAG: load i32, i32* [[Slot2]] |
| ; CHECK: catchret from [[CatchPad]] to label |
| call void @h(i32 %x) [ "funclet"(token %cpouter) ] |
| call void @h(i32 %y) [ "funclet"(token %cpouter) ] |
| catchret from %cpouter to label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; test4: don't need stores for %phi.inner, as its only use is to feed %phi.outer |
| ; %phi.outer needs stores in %left, %right, and %join |
| ; CHECK-LABEL: @test4( |
| define void @test4(i1 %B) personality i32 (...)* @__CxxFrameHandler3 { |
| entry: |
| ; CHECK: entry: |
| ; CHECK: [[Slot:%[^ ]+]] = alloca |
| ; CHECK-NEXT: br |
| br i1 %B, label %left, label %right |
| left: |
| ; CHECK: left: |
| ; CHECK-NOT: store |
| ; CHECK: store i32 %l, i32* [[Slot]] |
| ; CHECK-NEXT: invoke void @f |
| %l = call i32 @g() |
| invoke void @f() |
| to label %join unwind label %catchpad.inner |
| right: |
| ; CHECK: right: |
| ; CHECK-NOT: store |
| ; CHECK: store i32 %r, i32* [[Slot]] |
| ; CHECK-NEXT: invoke void @f |
| %r = call i32 @g() |
| invoke void @f() |
| to label %join unwind label %catchpad.inner |
| catchpad.inner: |
| ; CHECK: catchpad.inner: |
| ; CHECK-NEXT: catchswitch within none |
| %phi.inner = phi i32 [ %l, %left ], [ %r, %right ] |
| %cs1 = catchswitch within none [label %catch.inner] unwind label %catchpad.outer |
| catch.inner: |
| %cp1 = catchpad within %cs1 [] |
| catchret from %cp1 to label %join |
| join: |
| ; CHECK: join: |
| ; CHECK-NOT: store |
| ; CHECK: store i32 %j, i32* [[Slot]] |
| ; CHECK-NEXT: invoke void @f |
| %j = call i32 @g() |
| invoke void @f() |
| to label %exit unwind label %catchpad.outer |
| |
| catchpad.outer: |
| ; CHECK: catchpad.outer: |
| ; CHECK-NEXT: catchswitch within none |
| %phi.outer = phi i32 [ %phi.inner, %catchpad.inner ], [ %j, %join ] |
| %cs2 = catchswitch within none [label %catch.outer] unwind to caller |
| catch.outer: |
| ; CHECK: catch.outer: |
| ; CHECK: [[Reload:%[^ ]+]] = load i32, i32* [[Slot]] |
| ; CHECK: call void @h(i32 [[Reload]]) |
| %cp2 = catchpad within %cs2 [] |
| call void @h(i32 %phi.outer) [ "funclet"(token %cp2) ] |
| catchret from %cp2 to label %exit |
| exit: |
| ret void |
| } |
| |
| ; CHECK-LABEL: @test5( |
| define void @test5() personality i32 (...)* @__CxxFrameHandler3 { |
| entry: |
| ; need store for %phi.cleanup |
| ; CHECK: entry: |
| ; CHECK: store i32 1, i32* [[CleanupSlot:%[^ ]+]] |
| ; CHECK-NEXT: invoke void @f |
| invoke void @f() |
| to label %invoke.cont unwind label %cleanup |
| |
| invoke.cont: |
| ; need store for %phi.cleanup |
| ; CHECK: invoke.cont: |
| ; CHECK-NEXT: store i32 2, i32* [[CleanupSlot]] |
| ; CHECK-NEXT: invoke void @f |
| invoke void @f() |
| to label %invoke.cont2 unwind label %cleanup |
| |
| cleanup: |
| ; cleanup phi can be loaded at cleanup entry |
| ; CHECK: cleanup: |
| ; CHECK-NEXT: cleanuppad within none [] |
| ; CHECK: [[CleanupReload:%[^ ]+]] = load i32, i32* [[CleanupSlot]] |
| %phi.cleanup = phi i32 [ 1, %entry ], [ 2, %invoke.cont ] |
| %cp = cleanuppad within none [] |
| %b = call i1 @i() [ "funclet"(token %cp) ] |
| br i1 %b, label %left, label %right |
| |
| left: |
| ; CHECK: left: |
| ; CHECK: call void @h(i32 [[CleanupReload]] |
| call void @h(i32 %phi.cleanup) [ "funclet"(token %cp) ] |
| br label %merge |
| |
| right: |
| ; CHECK: right: |
| ; CHECK: call void @h(i32 [[CleanupReload]] |
| call void @h(i32 %phi.cleanup) [ "funclet"(token %cp) ] |
| br label %merge |
| |
| merge: |
| ; need store for %phi.catch |
| ; CHECK: merge: |
| ; CHECK-NEXT: store i32 [[CleanupReload]], i32* [[CatchSlot:%[^ ]+]] |
| ; CHECK-NEXT: cleanupret |
| cleanupret from %cp unwind label %catchswitch |
| |
| invoke.cont2: |
| ; need store for %phi.catch |
| ; CHECK: invoke.cont2: |
| ; CHECK-NEXT: store i32 3, i32* [[CatchSlot]] |
| ; CHECK-NEXT: invoke void @f |
| invoke void @f() |
| to label %exit unwind label %catchswitch |
| |
| catchswitch: |
| ; CHECK: catchswitch: |
| ; CHECK-NEXT: catchswitch within none |
| %phi.catch = phi i32 [ %phi.cleanup, %merge ], [ 3, %invoke.cont2 ] |
| %cs1 = catchswitch within none [label %catch] unwind to caller |
| |
| catch: |
| ; CHECK: catch: |
| ; CHECK: catchpad within %cs1 |
| ; CHECK: [[CatchReload:%[^ ]+]] = load i32, i32* [[CatchSlot]] |
| ; CHECK: call void @h(i32 [[CatchReload]] |
| %cp2 = catchpad within %cs1 [] |
| call void @h(i32 %phi.catch) [ "funclet"(token %cp2) ] |
| catchret from %cp2 to label %exit |
| |
| exit: |
| ret void |
| } |
| |
| ; We used to demote %x, but we don't need to anymore. |
| ; CHECK-LABEL: @test6( |
| define void @test6() personality i32 (...)* @__CxxFrameHandler3 { |
| entry: |
| ; CHECK: entry: |
| ; CHECK: %x = invoke i32 @g() |
| ; CHECK-NEXT: to label %loop unwind label %to_caller |
| %x = invoke i32 @g() |
| to label %loop unwind label %to_caller |
| to_caller: |
| %cp1 = cleanuppad within none [] |
| cleanupret from %cp1 unwind to caller |
| loop: |
| invoke void @f() |
| to label %loop unwind label %cleanup |
| cleanup: |
| ; CHECK: cleanup: |
| ; CHECK: call void @h(i32 %x) |
| %cp2 = cleanuppad within none [] |
| call void @h(i32 %x) [ "funclet"(token %cp2) ] |
| cleanupret from %cp2 unwind to caller |
| } |
| |
| ; CHECK-LABEL: @test7( |
| define void @test7() personality i32 (...)* @__CxxFrameHandler3 { |
| entry: |
| ; %x is an EH pad phi, so gets stored in pred here |
| ; CHECK: entry: |
| ; CHECK: store i32 1, i32* [[SlotX:%[^ ]+]] |
| ; CHECK: invoke void @f() |
| invoke void @f() |
| to label %invoke.cont unwind label %catchpad |
| invoke.cont: |
| ; %x is an EH pad phi, so gets stored in pred here |
| ; CHECK: invoke.cont: |
| ; CHECK: store i32 2, i32* [[SlotX]] |
| ; CHECK: invoke void @f() |
| invoke void @f() |
| to label %exit unwind label %catchpad |
| catchpad: |
| ; %x phi should be eliminated |
| ; CHECK: catchpad: |
| ; CHECK-NEXT: catchswitch within none |
| %x = phi i32 [ 1, %entry ], [ 2, %invoke.cont ] |
| %cs1 = catchswitch within none [label %catch] unwind to caller |
| catch: |
| ; CHECK: catch: |
| ; CHECK-NEXT: %[[CatchPad:[^ ]+]] = catchpad within %cs1 [] |
| %cp = catchpad within %cs1 [] |
| %b = call i1 @i() [ "funclet"(token %cp) ] |
| br i1 %b, label %left, label %right |
| left: |
| ; Edge from %left to %join needs to be split so that |
| ; the load of %x can be inserted *after* the catchret |
| ; CHECK: left: |
| ; CHECK-NEXT: catchret from %[[CatchPad]] to label %[[SplitLeft:[^ ]+]] |
| catchret from %cp to label %join |
| ; CHECK: [[SplitLeft]]: |
| ; CHECK: [[LoadX:%[^ ]+]] = load i32, i32* [[SlotX]] |
| ; CHECK: br label %join |
| right: |
| ; Edge from %right to %join needs to be split so that |
| ; the load of %y can be inserted *after* the catchret |
| ; CHECK: right: |
| ; CHECK: %y = call i32 @g() |
| ; CHECK: catchret from %[[CatchPad]] to label %join |
| %y = call i32 @g() [ "funclet"(token %cp) ] |
| catchret from %cp to label %join |
| join: |
| ; CHECK: join: |
| ; CHECK: %phi = phi i32 [ [[LoadX]], %[[SplitLeft]] ], [ %y, %right ] |
| %phi = phi i32 [ %x, %left ], [ %y, %right ] |
| call void @h(i32 %phi) |
| br label %exit |
| exit: |
| ret void |
| } |
| |
| ; CHECK-LABEL: @test8( |
| define void @test8() personality i32 (...)* @__CxxFrameHandler3 { entry: |
| invoke void @f() |
| to label %done unwind label %cleanup1 |
| invoke void @f() |
| to label %done unwind label %cleanup2 |
| |
| done: |
| ret void |
| |
| cleanup1: |
| ; CHECK: [[CleanupPad1:%[^ ]+]] = cleanuppad within none [] |
| ; CHECK-NEXT: call void @llvm.bar() |
| ; CHECK-NEXT: cleanupret from [[CleanupPad1]] |
| %cp0 = cleanuppad within none [] |
| br label %cleanupexit |
| |
| cleanup2: |
| ; CHECK: cleanuppad within none [] |
| ; CHECK-NEXT: call void @llvm.bar() |
| ; CHECK-NEXT: unreachable |
| %cp1 = cleanuppad within none [] |
| br label %cleanupexit |
| |
| cleanupexit: |
| call void @llvm.bar() |
| cleanupret from %cp0 unwind label %cleanup2 |
| } |