diff test/CodeGen/WinEH/wineh-cloning.ll @ 95:afa8332a0e37

LLVM 3.8
author Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp>
date Tue, 13 Oct 2015 17:48:58 +0900
parents
children 7d135dc70f03
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/CodeGen/WinEH/wineh-cloning.ll	Tue Oct 13 17:48:58 2015 +0900
@@ -0,0 +1,506 @@
+; RUN: opt -mtriple=x86_x64-pc-windows-msvc -S -winehprepare  < %s | FileCheck %s
+
+declare i32 @__CxxFrameHandler3(...)
+
+declare void @f()
+declare i32 @g()
+declare void @h(i32)
+declare i1 @b()
+
+
+define void @test1() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ; %x def colors: {entry} subset of use colors; must spill
+  %x = call i32 @g()
+  invoke void @f()
+    to label %noreturn unwind label %catch
+catch:
+  catchpad []
+    to label %noreturn unwind label %endcatch
+noreturn:
+  ; %x use colors: {entry, cleanup}
+  call void @h(i32 %x)
+  unreachable
+endcatch:
+  catchendpad unwind to caller
+}
+; Need two copies of the call to @h, one under entry and one under catch.
+; Currently we generate a load for each, though we shouldn't need one
+; for the use in entry's copy.
+; CHECK-LABEL: define void @test1(
+; CHECK: entry:
+; CHECK:   store i32 %x, i32* [[Slot:%[^ ]+]]
+; CHECK:   invoke void @f()
+; CHECK:     to label %[[EntryCopy:[^ ]+]] unwind label %catch
+; CHECK: catch:
+; CHECK:   catchpad []
+; CHECK-NEXT: to label %[[CatchCopy:[^ ]+]] unwind
+; CHECK: [[CatchCopy]]:
+; CHECK:   [[LoadX2:%[^ ]+]] = load i32, i32* [[Slot]]
+; CHECK:   call void @h(i32 [[LoadX2]]
+; CHECK: [[EntryCopy]]:
+; CHECK:   [[LoadX1:%[^ ]+]] = load i32, i32* [[Slot]]
+; CHECK:   call void @h(i32 [[LoadX1]]
+
+
+define void @test2() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %exit unwind label %cleanup
+cleanup:
+  cleanuppad []
+  br label %exit
+exit:
+  call void @f()
+  ret void
+}
+; Need two copies of %exit's call to @f -- the subsequent ret is only
+; valid when coming from %entry, but on the path from %cleanup, this
+; might be a valid call to @f which might dynamically not return.
+; CHECK-LABEL: define void @test2(
+; CHECK: entry:
+; CHECK:   invoke void @f()
+; CHECK:     to label %[[exit:[^ ]+]] unwind label %cleanup
+; CHECK: cleanup:
+; CHECK:   cleanuppad []
+; CHECK:   call void @f()
+; CHECK-NEXT: unreachable
+; CHECK: [[exit]]:
+; CHECK:   call void @f()
+; CHECK-NEXT: ret void
+
+
+define void @test3() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %invoke.cont unwind label %catch
+invoke.cont:
+  invoke void @f()
+    to label %exit unwind label %cleanup
+catch:
+  catchpad [] to label %shared unwind label %endcatch
+endcatch:
+  catchendpad unwind to caller
+cleanup:
+  cleanuppad []
+  br label %shared
+shared:
+  call void @f()
+  br label %exit
+exit:
+  ret void
+}
+; Need two copies of %shared's call to @f (similar to @test2 but
+; the two regions here are siblings, not parent-child).
+; CHECK-LABEL: define void @test3(
+; CHECK:   invoke void @f()
+; CHECK:   invoke void @f()
+; CHECK:     to label %[[exit:[^ ]+]] unwind
+; CHECK: catch:
+; CHECK:   catchpad []
+; CHECK-NEXT: to label %[[shared:[^ ]+]] unwind
+; CHECK: cleanup:
+; CHECK:   cleanuppad []
+; CHECK:   call void @f()
+; CHECK-NEXT: unreachable
+; CHECK: [[shared]]:
+; CHECK:   call void @f()
+; CHECK-NEXT: unreachable
+; CHECK: [[exit]]:
+; CHECK:   ret void
+
+
+define void @test4() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %shared unwind label %catch
+catch:
+  catchpad []
+    to label %shared unwind label %endcatch
+endcatch:
+  catchendpad unwind to caller
+shared:
+  %x = call i32 @g()
+  %i = call i32 @g()
+  %zero.trip = icmp eq i32 %i, 0
+  br i1 %zero.trip, label %exit, label %loop
+loop:
+  %i.loop = phi i32 [ %i, %shared ], [ %i.dec, %loop.tail ]
+  %b = call i1 @b()
+  br i1 %b, label %left, label %right
+left:
+  %y = call i32 @g()
+  br label %loop.tail
+right:
+  call void @h(i32 %x)
+  br label %loop.tail
+loop.tail:
+  %i.dec = sub i32 %i.loop, 1
+  %done = icmp eq i32 %i.dec, 0
+  br i1 %done, label %exit, label %loop
+exit:
+  call void @h(i32 %x)
+  unreachable
+}
+; Make sure we can clone regions that have internal control
+; flow and SSA values.  Here we need two copies of everything
+; from %shared to %exit.
+; CHECK-LABEL: define void @test4(
+; CHECK:  entry:
+; CHECK:    to label %[[shared_E:[^ ]+]] unwind label %catch
+; CHECK:  catch:
+; CHECK:    to label %[[shared_C:[^ ]+]] unwind label %endcatch
+; CHECK:  [[shared_C]]:
+; CHECK:    [[x_C:%[^ ]+]] = call i32 @g()
+; CHECK:    [[i_C:%[^ ]+]] = call i32 @g()
+; CHECK:    [[zt_C:%[^ ]+]] = icmp eq i32 [[i_C]], 0
+; CHECK:    br i1 [[zt_C]], label %[[exit_C:[^ ]+]], label %[[loop_C:[^ ]+]]
+; CHECK:  [[shared_E]]:
+; CHECK:    [[x_E:%[^ ]+]] = call i32 @g()
+; CHECK:    [[i_E:%[^ ]+]] = call i32 @g()
+; CHECK:    [[zt_E:%[^ ]+]] = icmp eq i32 [[i_E]], 0
+; CHECK:    br i1 [[zt_E]], label %[[exit_E:[^ ]+]], label %[[loop_E:[^ ]+]]
+; CHECK:  [[loop_C]]:
+; CHECK:    [[iloop_C:%[^ ]+]] = phi i32 [ [[i_C]], %[[shared_C]] ], [ [[idec_C:%[^ ]+]], %[[looptail_C:[^ ]+]] ]
+; CHECK:    [[b_C:%[^ ]+]] = call i1 @b()
+; CHECK:    br i1 [[b_C]], label %[[left_C:[^ ]+]], label %[[right_C:[^ ]+]]
+; CHECK:  [[loop_E]]:
+; CHECK:    [[iloop_E:%[^ ]+]] = phi i32 [ [[i_E]], %[[shared_E]] ], [ [[idec_E:%[^ ]+]], %[[looptail_E:[^ ]+]] ]
+; CHECK:    [[b_E:%[^ ]+]] = call i1 @b()
+; CHECK:    br i1 [[b_E]], label %[[left_E:[^ ]+]], label %[[right_E:[^ ]+]]
+; CHECK:  [[left_C]]:
+; CHECK:    [[y_C:%[^ ]+]] = call i32 @g()
+; CHECK:    br label %[[looptail_C]]
+; CHECK:  [[left_E]]:
+; CHECK:    [[y_E:%[^ ]+]] = call i32 @g()
+; CHECK:    br label %[[looptail_E]]
+; CHECK:  [[right_C]]:
+; CHECK:    call void @h(i32 [[x_C]])
+; CHECK:    br label %[[looptail_C]]
+; CHECK:  [[right_E]]:
+; CHECK:    call void @h(i32 [[x_E]])
+; CHECK:    br label %[[looptail_E]]
+; CHECK:  [[looptail_C]]:
+; CHECK:    [[idec_C]] = sub i32 [[iloop_C]], 1
+; CHECK:    [[done_C:%[^ ]+]] = icmp eq i32 [[idec_C]], 0
+; CHECK:    br i1 [[done_C]], label %[[exit_C]], label %[[loop_C]]
+; CHECK:  [[looptail_E]]:
+; CHECK:    [[idec_E]] = sub i32 [[iloop_E]], 1
+; CHECK:    [[done_E:%[^ ]+]] = icmp eq i32 [[idec_E]], 0
+; CHECK:    br i1 [[done_E]], label %[[exit_E]], label %[[loop_E]]
+; CHECK:  [[exit_C]]:
+; CHECK:    call void @h(i32 [[x_C]])
+; CHECK:    unreachable
+; CHECK:  [[exit_E]]:
+; CHECK:    call void @h(i32 [[x_E]])
+; CHECK:    unreachable
+
+
+define void @test5() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %exit unwind label %outer
+outer:
+  %o = cleanuppad []
+  %x = call i32 @g()
+  invoke void @f()
+    to label %outer.ret unwind label %inner
+inner:
+  %i = catchpad []
+    to label %inner.catch unwind label %inner.endcatch
+inner.catch:
+  catchret %i to label %outer.post-inner
+inner.endcatch:
+  catchendpad unwind to caller
+outer.post-inner:
+  call void @h(i32 %x)
+  br label %outer.ret
+outer.ret:
+  cleanupret %o unwind to caller
+exit:
+  ret void
+}
+; Simple nested case (catch-inside-cleanup).  Nothing needs
+; to be cloned.  The def and use of %x are both in %outer
+; and so don't need to be spilled.
+; CHECK-LABEL: define void @test5(
+; CHECK:      outer:
+; CHECK:        %x = call i32 @g()
+; CHECK-NEXT:   invoke void @f()
+; CHECK-NEXT:     to label %outer.ret unwind label %inner
+; CHECK:      inner:
+; CHECK:          to label %inner.catch unwind label %inner.endcatch
+; CHECK:      inner.catch:
+; CHECK-NEXT:   catchret %i to label %outer.post-inner
+; CHECK:      outer.post-inner:
+; CHECK-NEXT:   call void @h(i32 %x)
+; CHECK-NEXT:   br label %outer.ret
+
+
+define void @test6() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %invoke.cont unwind label %left
+invoke.cont:
+  invoke void @f()
+    to label %exit unwind label %right
+left:
+  cleanuppad []
+  br label %shared
+right:
+  catchpad []
+    to label %right.catch unwind label %right.end
+right.catch:
+  br label %shared
+right.end:
+  catchendpad unwind to caller
+shared:
+  %x = call i32 @g()
+  invoke void @f()
+    to label %shared.cont unwind label %inner
+shared.cont:
+  unreachable
+inner:
+  %i = cleanuppad []
+  call void @h(i32 %x)
+  cleanupret %i unwind label %right.end
+exit:
+  ret void
+}
+; %inner is a cleanup which appears both as a child of
+; %left and as a child of %right.  Since statically we
+; need each funclet to have a single parent, we need to
+; clone the entire %inner funclet so we can have one
+; copy under each parent.  The cleanupret in %inner
+; unwinds to the catchendpad for %right, so the copy
+; of %inner under %right should include it; the copy
+; of %inner under %left should instead have an
+; `unreachable` inserted there, but the copy under
+; %left still needs to be created because it's possible
+; the dynamic path enters %left, then enters %inner,
+; then calls @h, and that the call to @h doesn't return.
+; CHECK-LABEL: define void @test6(
+; TODO: CHECKs
+
+
+define void @test7() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %invoke.cont unwind label %left
+invoke.cont:
+  invoke void @f()
+    to label %unreachable unwind label %right
+left:
+  cleanuppad []
+  invoke void @f() to label %unreachable unwind label %inner
+right:
+  catchpad []
+    to label %right.catch unwind label %right.end
+right.catch:
+  invoke void @f() to label %unreachable unwind label %inner
+right.end:
+  catchendpad unwind to caller
+inner:
+  %i = cleanuppad []
+  %x = call i32 @g()
+  call void @h(i32 %x)
+  cleanupret %i unwind label %right.end
+unreachable:
+  unreachable
+}
+; Another case of a two-parent child (like @test6), this time
+; with the join at the entry itself instead of following a
+; non-pad join.
+; CHECK-LABEL: define void @test7(
+; TODO: CHECKs
+
+
+define void @test8() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %invoke.cont unwind label %left
+invoke.cont:
+  invoke void @f()
+    to label %unreachable unwind label %right
+left:
+  cleanuppad []
+  br label %shared
+right:
+  catchpad []
+    to label %right.catch unwind label %right.end
+right.catch:
+  br label %shared
+right.end:
+  catchendpad unwind to caller
+shared:
+  invoke void @f()
+    to label %unreachable unwind label %inner
+inner:
+  cleanuppad []
+  invoke void @f()
+    to label %unreachable unwind label %inner.child
+inner.child:
+  cleanuppad []
+  %x = call i32 @g()
+  call void @h(i32 %x)
+  unreachable
+unreachable:
+  unreachable
+}
+; %inner is a two-parent child which itself has a child; need
+; to make two copies of both the %inner and %inner.child.
+; CHECK-LABEL: define void @test8(
+; TODO: CHECKs
+
+
+define void @test9() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %invoke.cont unwind label %left
+invoke.cont:
+  invoke void @f()
+    to label %unreachable unwind label %right
+left:
+  cleanuppad []
+  call void @h(i32 1)
+  invoke void @f()
+    to label %unreachable unwind label %right
+right:
+  cleanuppad []
+  call void @h(i32 2)
+  invoke void @f()
+    to label %unreachable unwind label %left
+unreachable:
+  unreachable
+}
+; This is an irreducible loop with two funclets that enter each other;
+; need to make two copies of each funclet (one a child of root, the
+; other a child of the opposite funclet), but also make sure not to
+; clone self-descendants (if we tried to do that we'd need to make an
+; infinite number of them).  Presumably if optimizations ever generated
+; such a thing it would mean that one of the two cleanups was originally
+; the parent of the other, but that we'd somehow lost track in the CFG
+; of which was which along the way; generating each possibility lets
+; whichever case was correct execute correctly.
+; CHECK-LABEL: define void @test9(
+; TODO: CHECKs
+
+define void @test10() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %unreachable unwind label %inner
+inner:
+  %cleanup = cleanuppad []
+  ; make sure we don't overlook this cleanupret and try to process
+  ; successor %outer as a child of inner.
+  cleanupret %cleanup unwind label %outer
+outer:
+  %catch = catchpad [] to label %catch.body unwind label %endpad
+catch.body:
+  catchret %catch to label %exit
+endpad:
+  catchendpad unwind to caller
+exit:
+  ret void
+unreachable:
+  unreachable
+}
+; CHECK-LABEL: define void @test10(
+; CHECK-NEXT: entry:
+; CHECK-NEXT:   invoke
+; CHECK-NEXT:     to label %unreachable unwind label %inner
+; CHECK:      inner:
+; CHECK-NEXT:   %cleanup = cleanuppad
+; CHECK-NEXT:   cleanupret %cleanup unwind label %outer
+; CHECK:      outer:
+; CHECK-NEXT:   %catch = catchpad []
+; CHECK-NEXT:	      to label %catch.body unwind label %endpad
+; CHECK:      catch.body:
+; CHECK-NEXT:   catchret %catch to label %exit
+; CHECK:      endpad:
+; CHECK-NEXT:   catchendpad unwind to caller
+; CHECK:      exit:
+; CHECK-NEXT:   ret void
+
+define void @test11() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %exit unwind label %cleanup.outer
+cleanup.outer:
+  %outer = cleanuppad []
+  invoke void @f()
+    to label %outer.cont unwind label %cleanup.inner
+outer.cont:
+  br label %merge
+cleanup.inner:
+  %inner = cleanuppad []
+  br label %merge
+merge:
+  invoke void @f()
+    to label %unreachable unwind label %merge.end
+unreachable:
+  unreachable
+merge.end:
+  cleanupendpad %outer unwind to caller
+exit:
+  ret void
+}
+; merge.end will get cloned for outer and inner, but is implausible
+; from inner, so the invoke @f() in inner's copy of merge should be
+; rewritten to call @f()
+; CHECK-LABEL: define void @test11()
+; CHECK:      %inner = cleanuppad []
+; CHECK-NEXT: call void @f()
+; CHECK-NEXT: unreachable
+
+define void @test12() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  invoke void @f()
+    to label %cont unwind label %left, !dbg !8
+cont:
+  invoke void @f()
+    to label %exit unwind label %right
+left:
+  cleanuppad []
+  br label %join
+right:
+  cleanuppad []
+  br label %join
+join:
+  ; This call will get cloned; make sure we can handle cloning
+  ; instructions with debug metadata attached.
+  call void @f(), !dbg !9
+  unreachable
+exit:
+  ret void
+}
+
+; CHECK-LABEL: define void @test13()
+; CHECK: ret void
+define void @test13() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ret void
+
+unreachable:
+  cleanuppad []
+  unreachable
+}
+
+; Make sure the DISubprogram doesn't get cloned
+; CHECK-LABEL: !llvm.module.flags
+; CHECK-NOT: !DISubprogram
+; CHECK: !{{[0-9]+}} = distinct !DISubprogram(name: "test12"
+; CHECK-NOT: !DISubprogram
+!llvm.module.flags = !{!0}
+!llvm.dbg.cu = !{!1}
+
+!0 = !{i32 2, !"Debug Info Version", i32 3}
+!1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, producer: "compiler", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !3, subprograms: !4)
+!2 = !DIFile(filename: "test.cpp", directory: ".")
+!3 = !{}
+!4 = !{!5}
+!5 = distinct !DISubprogram(name: "test12", scope: !2, file: !2, type: !6, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, function: void ()* @test12, variables: !3)
+!6 = !DISubroutineType(types: !7)
+!7 = !{null}
+!8 = !DILocation(line: 1, scope: !5)
+!9 = !DILocation(line: 2, scope: !5)