summaryrefslogtreecommitdiff
path: root/test/Transforms
diff options
context:
space:
mode:
Diffstat (limited to 'test/Transforms')
-rw-r--r--test/Transforms/CodeGenPrepare/ARM/bitreverse-recognize.ll37
-rw-r--r--test/Transforms/CodeGenPrepare/ARM/lit.local.cfg3
-rw-r--r--test/Transforms/CodeGenPrepare/bitreverse-hang.ll53
-rw-r--r--test/Transforms/Inline/inline-funclets.ll455
-rw-r--r--test/Transforms/InstCombine/bitreverse-hang.ll53
-rw-r--r--test/Transforms/InstCombine/bitreverse-recognize.ll114
-rw-r--r--test/Transforms/InstCombine/cos-2.ll16
-rw-r--r--test/Transforms/InstCombine/double-float-shrink-1.ll20
8 files changed, 634 insertions, 117 deletions
diff --git a/test/Transforms/CodeGenPrepare/ARM/bitreverse-recognize.ll b/test/Transforms/CodeGenPrepare/ARM/bitreverse-recognize.ll
new file mode 100644
index 000000000000..36440da21626
--- /dev/null
+++ b/test/Transforms/CodeGenPrepare/ARM/bitreverse-recognize.ll
@@ -0,0 +1,37 @@
+; RUN: opt -S -loop-unroll -codegenprepare < %s | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "armv7--linux-gnueabihf"
+
+; CHECK-LABEL: @f
+define i32 @f(i32 %a) #0 {
+; CHECK: call i32 @llvm.bitreverse.i32
+entry:
+ br label %for.body
+
+for.cond.cleanup: ; preds = %for.body
+ ret i32 %or
+
+for.body: ; preds = %for.body, %entry
+ %i.08 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+ %b.07 = phi i32 [ 0, %entry ], [ %or, %for.body ]
+ %shr = lshr i32 %a, %i.08
+ %and = and i32 %shr, 1
+ %sub = sub nuw nsw i32 31, %i.08
+ %shl = shl i32 %and, %sub
+ %or = or i32 %shl, %b.07
+ %inc = add nuw nsw i32 %i.08, 1
+ %exitcond = icmp eq i32 %inc, 32
+ br i1 %exitcond, label %for.cond.cleanup, label %for.body, !llvm.loop !3
+}
+
+attributes #0 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-a8" "target-features"="+dsp,+neon,+vfp3" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, !"min_enum_size", i32 4}
+!2 = !{!"clang version 3.8.0 (http://llvm.org/git/clang.git b7441a0f42c43a8eea9e3e706be187252db747fa)"}
+!3 = distinct !{!3, !4}
+!4 = !{!"llvm.loop.unroll.full"}
diff --git a/test/Transforms/CodeGenPrepare/ARM/lit.local.cfg b/test/Transforms/CodeGenPrepare/ARM/lit.local.cfg
new file mode 100644
index 000000000000..98c6700c209d
--- /dev/null
+++ b/test/Transforms/CodeGenPrepare/ARM/lit.local.cfg
@@ -0,0 +1,3 @@
+if not 'ARM' in config.root.targets:
+ config.unsupported = True
+
diff --git a/test/Transforms/CodeGenPrepare/bitreverse-hang.ll b/test/Transforms/CodeGenPrepare/bitreverse-hang.ll
new file mode 100644
index 000000000000..c81dcc15cae9
--- /dev/null
+++ b/test/Transforms/CodeGenPrepare/bitreverse-hang.ll
@@ -0,0 +1,53 @@
+; RUN: opt < %s -loop-unroll -codegenprepare -S | FileCheck %s
+
+; This test is a worst-case scenario for bitreversal/byteswap detection.
+; After loop unrolling (the unrolled loop is unreadably large so it has been kept
+; rolled here), we have a binary tree of OR operands (as bitreversal detection
+; looks straight through shifts):
+;
+; OR
+; | \
+; | LSHR
+; | /
+; OR
+; | \
+; | LSHR
+; | /
+; OR
+;
+; This results in exponential runtime. The loop here is 32 iterations which will
+; totally hang if we don't deal with this case cleverly.
+
+@b = common global i32 0, align 4
+
+; CHECK: define i32 @fn1
+define i32 @fn1() #0 {
+entry:
+ %b.promoted = load i32, i32* @b, align 4, !tbaa !2
+ br label %for.body
+
+for.body: ; preds = %for.body, %entry
+ %or4 = phi i32 [ %b.promoted, %entry ], [ %or, %for.body ]
+ %i.03 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+ %shr = lshr i32 %or4, 1
+ %or = or i32 %shr, %or4
+ %inc = add nuw nsw i32 %i.03, 1
+ %exitcond = icmp eq i32 %inc, 32
+ br i1 %exitcond, label %for.end, label %for.body
+
+for.end: ; preds = %for.body
+ store i32 %or, i32* @b, align 4, !tbaa !2
+ ret i32 undef
+}
+
+attributes #0 = { norecurse nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"PIC Level", i32 2}
+!1 = !{!"clang version 3.8.0 (http://llvm.org/git/clang.git eb70f4e9cc9a4dc3dd57b032fb858d56b4b64a0e)"}
+!2 = !{!3, !3, i64 0}
+!3 = !{!"int", !4, i64 0}
+!4 = !{!"omnipotent char", !5, i64 0}
+!5 = !{!"Simple C/C++ TBAA"}
diff --git a/test/Transforms/Inline/inline-funclets.ll b/test/Transforms/Inline/inline-funclets.ll
new file mode 100644
index 000000000000..362e03d36a32
--- /dev/null
+++ b/test/Transforms/Inline/inline-funclets.ll
@@ -0,0 +1,455 @@
+; RUN: opt -inline -S %s | FileCheck %s
+
+declare void @g()
+
+
+;;; Test with a call in a funclet that needs to remain a call
+;;; when inlined because the funclet doesn't unwind to caller.
+;;; CHECK-LABEL: define void @test1(
+define void @test1() personality void ()* @g {
+entry:
+; CHECK-NEXT: entry:
+ invoke void @test1_inlinee()
+ to label %exit unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test1_inlinee() alwaysinline personality void ()* @g {
+entry:
+ invoke void @g()
+ to label %exit unwind label %cleanup.inner
+; CHECK-NEXT: invoke void @g()
+; CHECK-NEXT: unwind label %[[cleanup_inner:.+]]
+
+cleanup.inner:
+ %pad.inner = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad.inner) ]
+ cleanupret from %pad.inner unwind label %cleanup.outer
+; CHECK: [[cleanup_inner]]:
+; The call here needs to remain a call becuase pad.inner has a cleanupret
+; that stays within the inlinee.
+; CHECK-NEXT: %[[pad_inner:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: call void @g() [ "funclet"(token %[[pad_inner]]) ]
+; CHECK-NEXT: cleanupret from %[[pad_inner]] unwind label %[[cleanup_outer:.+]]
+
+cleanup.outer:
+ %pad.outer = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad.outer) ]
+ cleanupret from %pad.outer unwind to caller
+; CHECK: [[cleanup_outer]]:
+; The call and cleanupret here need to be redirected to caller cleanup
+; CHECK-NEXT: %[[pad_outer:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[pad_outer]]) ]
+; CHECK-NEXT: unwind label %cleanup
+; CHECK: cleanupret from %[[pad_outer]] unwind label %cleanup{{$}}
+
+exit:
+ ret void
+}
+
+
+
+;;; Test with an "unwind to caller" catchswitch in a parent funclet
+;;; that needs to remain "unwind to caller" because the parent
+;;; doesn't unwind to caller.
+;;; CHECK-LABEL: define void @test2(
+define void @test2() personality void ()* @g {
+entry:
+; CHECK-NEXT: entry:
+ invoke void @test2_inlinee()
+ to label %exit unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test2_inlinee() alwaysinline personality void ()* @g {
+entry:
+ invoke void @g()
+ to label %exit unwind label %cleanup1
+; CHECK-NEXT: invoke void @g()
+; CHECK-NEXT: unwind label %[[cleanup1:.+]]
+
+cleanup1:
+ %outer = cleanuppad within none []
+ invoke void @g() [ "funclet"(token %outer) ]
+ to label %ret1 unwind label %catchswitch
+; CHECK: [[cleanup1]]:
+; CHECK-NEXT: %[[outer:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[outer]]) ]
+; CHECK-NEXT: unwind label %[[catchswitch:.+]]
+
+catchswitch:
+ %cs = catchswitch within %outer [label %catch] unwind to caller
+; CHECK: [[catchswitch]]:
+; The catchswitch here needs to remain "unwind to caller" since %outer
+; has a cleanupret that remains within the inlinee.
+; CHECK-NEXT: %[[cs:[^ ]+]] = catchswitch within %[[outer]] [label %[[catch:.+]]] unwind to caller
+
+catch:
+ %inner = catchpad within %cs []
+ call void @g() [ "funclet"(token %inner) ]
+ catchret from %inner to label %ret1
+; CHECK: [[catch]]:
+; The call here needs to remain a call since it too is within %outer
+; CHECK: %[[inner:[^ ]+]] = catchpad within %[[cs]]
+; CHECK-NEXT: call void @g() [ "funclet"(token %[[inner]]) ]
+
+ret1:
+ cleanupret from %outer unwind label %cleanup2
+; CHECK: cleanupret from %[[outer]] unwind label %[[cleanup2:.+]]
+
+cleanup2:
+ %later = cleanuppad within none []
+ cleanupret from %later unwind to caller
+; CHECK: [[cleanup2]]:
+; The cleanupret here needs to get redirected to the caller cleanup
+; CHECK-NEXT: %[[later:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: cleanupret from %[[later]] unwind label %cleanup{{$}}
+
+exit:
+ ret void
+}
+
+
+;;; Test with a call in a cleanup that has no definitive unwind
+;;; destination, that must be rewritten to an invoke.
+;;; CHECK-LABEL: define void @test3(
+define void @test3() personality void ()* @g {
+entry:
+; CHECK-NEXT: entry:
+ invoke void @test3_inlinee()
+ to label %exit unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test3_inlinee() alwaysinline personality void ()* @g {
+entry:
+ invoke void @g()
+ to label %exit unwind label %cleanup
+; CHECK-NEXT: invoke void @g()
+; CHECK-NEXT: unwind label %[[cleanup:.+]]
+
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ unreachable
+; CHECK: [[cleanup]]:
+; The call must be rewritten to an invoke targeting the caller cleanup
+; because it may well unwind to there.
+; CHECK-NEXT: %[[pad:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[pad]]) ]
+; CHECK-NEXT: unwind label %cleanup{{$}}
+
+exit:
+ ret void
+}
+
+
+;;; Test with a catchswitch in a cleanup that has no definitive
+;;; unwind destination, that must be rewritten to unwind to the
+;;; inlined invoke's unwind dest
+;;; CHECK-LABEL: define void @test4(
+define void @test4() personality void ()* @g {
+entry:
+; CHECK-NEXT: entry:
+ invoke void @test4_inlinee()
+ to label %exit unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test4_inlinee() alwaysinline personality void ()* @g {
+entry:
+ invoke void @g()
+ to label %exit unwind label %cleanup
+; CHECK-NEXT: invoke void @g()
+; CHECK-NEXT: unwind label %[[cleanup:.+]]
+
+cleanup:
+ %clean = cleanuppad within none []
+ invoke void @g() [ "funclet"(token %clean) ]
+ to label %unreachable unwind label %dispatch
+; CHECK: [[cleanup]]:
+; CHECK-NEXT: %[[clean:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[clean]]) ]
+; CHECK-NEXT: unwind label %[[dispatch:.+]]
+
+dispatch:
+ %cs = catchswitch within %clean [label %catch] unwind to caller
+; CHECK: [[dispatch]]:
+; The catchswitch must be rewritten to unwind to %cleanup in the caller
+; because it may well unwind to there.
+; CHECK-NEXT: %[[cs:[^ ]+]] = catchswitch within %[[clean]] [label %[[catch:.+]]] unwind label %cleanup{{$}}
+
+catch:
+ catchpad within %cs []
+ br label %unreachable
+unreachable:
+ unreachable
+exit:
+ ret void
+}
+
+
+;;; Test with multiple levels of nesting, and unwind dests
+;;; that need to be inferred from ancestors, descendants,
+;;; and cousins.
+;;; CHECK-LABEL: define void @test5(
+define void @test5() personality void ()* @g {
+entry:
+; CHECK-NEXT: entry:
+ invoke void @test5_inlinee()
+ to label %exit unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test5_inlinee() alwaysinline personality void ()* @g {
+entry:
+ invoke void @g()
+ to label %cont unwind label %noinfo.root
+; CHECK-NEXT: invoke void @g()
+; CHECK-NEXT: to label %[[cont:[^ ]+]] unwind label %[[noinfo_root:.+]]
+
+noinfo.root:
+ %noinfo.root.pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %noinfo.root.pad) ]
+ invoke void @g() [ "funclet"(token %noinfo.root.pad) ]
+ to label %noinfo.root.cont unwind label %noinfo.left
+; CHECK: [[noinfo_root]]:
+; Nothing under "noinfo.root" has a definitive unwind destination, so
+; we must assume all of it may actually unwind, and redirect unwinds
+; to the cleanup in the caller.
+; CHECK-NEXT: %[[noinfo_root_pad:[^ ]+]] = cleanuppad within none []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
+; CHECK-NEXT: to label %[[next:[^ ]+]] unwind label %cleanup{{$}}
+; CHECK: [[next]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
+; CHECK-NEXT: to label %[[noinfo_root_cont:[^ ]+]] unwind label %[[noinfo_left:.+]]
+
+noinfo.left:
+ %noinfo.left.pad = cleanuppad within %noinfo.root.pad []
+ invoke void @g() [ "funclet"(token %noinfo.left.pad) ]
+ to label %unreachable unwind label %noinfo.left.child
+; CHECK: [[noinfo_left]]:
+; CHECK-NEXT: %[[noinfo_left_pad:[^ ]+]] = cleanuppad within %[[noinfo_root_pad]]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_left_pad]]) ]
+; CHECK-NEXT: unwind label %[[noinfo_left_child:.+]]
+
+noinfo.left.child:
+ %noinfo.left.child.cs = catchswitch within %noinfo.left.pad [label %noinfo.left.child.catch] unwind to caller
+; CHECK: [[noinfo_left_child]]:
+; CHECK-NEXT: %[[noinfo_left_child_cs:[^ ]+]] = catchswitch within %[[noinfo_left_pad]] [label %[[noinfo_left_child_catch:[^ ]+]]] unwind label %cleanup{{$}}
+
+noinfo.left.child.catch:
+ %noinfo.left.child.pad = catchpad within %noinfo.left.child.cs []
+ call void @g() [ "funclet"(token %noinfo.left.child.pad) ]
+ br label %unreachable
+; CHECK: [[noinfo_left_child_catch]]:
+; CHECK-NEXT: %[[noinfo_left_child_pad:[^ ]+]] = catchpad within %[[noinfo_left_child_cs]] []
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_left_child_pad]]) ]
+; CHECK-NEXT: unwind label %cleanup{{$}}
+
+noinfo.root.cont:
+ invoke void @g() [ "funclet"(token %noinfo.root.pad) ]
+ to label %unreachable unwind label %noinfo.right
+; CHECK: [[noinfo_root_cont]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
+; CHECK-NEXT: unwind label %[[noinfo_right:.+]]
+
+noinfo.right:
+ %noinfo.right.cs = catchswitch within %noinfo.root.pad [label %noinfo.right.catch] unwind to caller
+; CHECK: [[noinfo_right]]:
+; CHECK-NEXT: %[[noinfo_right_cs:[^ ]+]] = catchswitch within %[[noinfo_root_pad]] [label %[[noinfo_right_catch:[^ ]+]]] unwind label %cleanup{{$}}
+
+noinfo.right.catch:
+ %noinfo.right.pad = catchpad within %noinfo.right.cs []
+ invoke void @g() [ "funclet"(token %noinfo.right.pad) ]
+ to label %unreachable unwind label %noinfo.right.child
+; CHECK: [[noinfo_right_catch]]:
+; CHECK-NEXT: %[[noinfo_right_pad:[^ ]+]] = catchpad within %[[noinfo_right_cs]]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_right_pad]]) ]
+; CHECK-NEXT: unwind label %[[noinfo_right_child:.+]]
+
+noinfo.right.child:
+ %noinfo.right.child.pad = cleanuppad within %noinfo.right.pad []
+ call void @g() [ "funclet"(token %noinfo.right.child.pad) ]
+ br label %unreachable
+; CHECK: [[noinfo_right_child]]:
+; CHECK-NEXT: %[[noinfo_right_child_pad:[^ ]+]] = cleanuppad within %[[noinfo_right_pad]]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_right_child_pad]]) ]
+; CHECK-NEXT: unwind label %cleanup{{$}}
+
+cont:
+ invoke void @g()
+ to label %exit unwind label %implicit.root
+; CHECK: [[cont]]:
+; CHECK-NEXT: invoke void @g()
+; CHECK-NEXT: unwind label %[[implicit_root:.+]]
+
+implicit.root:
+ %implicit.root.pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %implicit.root.pad) ]
+ invoke void @g() [ "funclet"(token %implicit.root.pad) ]
+ to label %implicit.root.cont unwind label %implicit.left
+; CHECK: [[implicit_root]]:
+; There's an unwind edge to %internal in implicit.right, and we need to propagate that
+; fact down to implicit.right.grandchild, up to implicit.root, and down to
+; implicit.left.child.catch, leaving all calls and "unwind to caller" catchswitches
+; alone to so they don't conflict with the unwind edge in implicit.right
+; CHECK-NEXT: %[[implicit_root_pad:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
+; CHECK-NEXT: to label %[[implicit_root_cont:[^ ]+]] unwind label %[[implicit_left:.+]]
+
+implicit.left:
+ %implicit.left.pad = cleanuppad within %implicit.root.pad []
+ invoke void @g() [ "funclet"(token %implicit.left.pad) ]
+ to label %unreachable unwind label %implicit.left.child
+; CHECK: [[implicit_left]]:
+; CHECK-NEXT: %[[implicit_left_pad:[^ ]+]] = cleanuppad within %[[implicit_root_pad:[^ ]+]]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_left_pad]]) ]
+; CHECK-NEXT: unwind label %[[implicit_left_child:.+]]
+
+implicit.left.child:
+ %implicit.left.child.cs = catchswitch within %implicit.left.pad [label %implicit.left.child.catch] unwind to caller
+; CHECK: [[implicit_left_child]]:
+; CHECK-NEXT: %[[implicit_left_child_cs:[^ ]+]] = catchswitch within %[[implicit_left_pad]] [label %[[implicit_left_child_catch:[^ ]+]]] unwind to caller
+
+implicit.left.child.catch:
+ %implicit.left.child.pad = catchpad within %implicit.left.child.cs []
+ call void @g() [ "funclet"(token %implicit.left.child.pad) ]
+ br label %unreachable
+; CHECK: [[implicit_left_child_catch]]:
+; CHECK-NEXT: %[[implicit_left_child_pad:[^ ]+]] = catchpad within %[[implicit_left_child_cs]]
+; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_left_child_pad]]) ]
+
+implicit.root.cont:
+ invoke void @g() [ "funclet"(token %implicit.root.pad) ]
+ to label %unreachable unwind label %implicit.right
+; CHECK: [[implicit_root_cont]]:
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
+; CHECK-NEXT: unwind label %[[implicit_right:.+]]
+
+implicit.right:
+ %implicit.right.cs = catchswitch within %implicit.root.pad [label %implicit.right.catch] unwind label %internal
+; CHECK: [[implicit_right]]:
+; This is the unwind edge (to %internal) whose existence needs to get propagated around the "implicit" tree
+; CHECK-NEXT: %[[implicit_right_cs:[^ ]+]] = catchswitch within %[[implicit_root_pad]] [label %[[implicit_right_catch:[^ ]+]]] unwind label %[[internal:.+]]
+
+implicit.right.catch:
+ %implicit.right.pad = catchpad within %implicit.right.cs []
+ invoke void @g() [ "funclet"(token %implicit.right.pad) ]
+ to label %unreachable unwind label %implicit.right.child
+; CHECK: [[implicit_right_catch]]:
+; CHECK-NEXT: %[[implicit_right_pad:[^ ]+]] = catchpad within %[[implicit_right_cs]]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_right_pad]]) ]
+; CHECK-NEXT: unwind label %[[implicit_right_child:.+]]
+
+implicit.right.child:
+ %implicit.right.child.pad = cleanuppad within %implicit.right.pad []
+ invoke void @g() [ "funclet"(token %implicit.right.child.pad) ]
+ to label %unreachable unwind label %implicit.right.grandchild
+; CHECK: [[implicit_right_child]]:
+; CHECK-NEXT: %[[implicit_right_child_pad:[^ ]+]] = cleanuppad within %[[implicit_right_pad]]
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_right_child_pad]]) ]
+; CHECK-NEXT: unwind label %[[implicit_right_grandchild:.+]]
+
+implicit.right.grandchild:
+ %implicit.right.grandchild.cs = catchswitch within %implicit.right.child.pad [label %implicit.right.grandchild.catch] unwind to caller
+; CHECK: [[implicit_right_grandchild]]:
+; CHECK-NEXT: %[[implicit_right_grandchild_cs:[^ ]+]] = catchswitch within %[[implicit_right_child_pad]] [label %[[implicit_right_grandchild_catch:[^ ]+]]] unwind to caller
+
+implicit.right.grandchild.catch:
+ %implicit.right.grandhcild.pad = catchpad within %implicit.right.grandchild.cs []
+ call void @g() [ "funclet"(token %implicit.right.grandhcild.pad) ]
+ br label %unreachable
+; CHECK: [[implicit_right_grandchild_catch]]:
+; CHECK-NEXT: %[[implicit_right_grandhcild_pad:[^ ]+]] = catchpad within %[[implicit_right_grandchild_cs]]
+; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_right_grandhcild_pad]]) ]
+
+internal:
+ %internal.pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %internal.pad) ]
+ cleanupret from %internal.pad unwind to caller
+; CHECK: [[internal]]:
+; internal is a cleanup with a "return to caller" cleanuppad; that needs to get redirected
+; to %cleanup in the caller, and the call needs to get similarly rewritten to an invoke.
+; CHECK-NEXT: %[[internal_pad:[^ ]+]] = cleanuppad within none
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %internal.pad.i) ]
+; CHECK-NEXT: to label %[[next:[^ ]+]] unwind label %cleanup{{$}}
+; CHECK: [[next]]:
+; CHECK-NEXT: cleanupret from %[[internal_pad]] unwind label %cleanup{{$}}
+
+unreachable:
+ unreachable
+exit:
+ ret void
+}
+
+
+declare void @ProcessCLRException()
+
+; Make sure the logic doesn't get tripped up when the inlined invoke is
+; itself within a funclet in the caller.
+; CHECK-LABEL: define void @test6(
+define void @test6() personality void ()* @ProcessCLRException {
+entry:
+ invoke void @g()
+ to label %exit unwind label %callsite_parent
+callsite_parent:
+ %callsite_parent.pad = cleanuppad within none []
+; CHECK: %callsite_parent.pad = cleanuppad within none
+ invoke void @test6_inlinee() [ "funclet"(token %callsite_parent.pad) ]
+ to label %ret unwind label %cleanup
+ret:
+ cleanupret from %callsite_parent.pad unwind label %cleanup
+cleanup:
+ %pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %pad) ]
+ cleanupret from %pad unwind to caller
+exit:
+ ret void
+}
+
+define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
+entry:
+ invoke void @g()
+ to label %exit unwind label %inlinee_cleanup
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %callsite_parent.pad) ]
+; CHECK-NEXT: unwind label %[[inlinee_cleanup:.+]]
+
+inlinee_cleanup:
+ %inlinee.pad = cleanuppad within none []
+ call void @g() [ "funclet"(token %inlinee.pad) ]
+ unreachable
+; CHECK: [[inlinee_cleanup]]:
+; CHECK-NEXT: %[[inlinee_pad:[^ ]+]] = cleanuppad within %callsite_parent.pad
+; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[inlinee_pad]]) ]
+; CHECK-NEXT: unwind label %cleanup{{$}}
+
+exit:
+ ret void
+}
diff --git a/test/Transforms/InstCombine/bitreverse-hang.ll b/test/Transforms/InstCombine/bitreverse-hang.ll
new file mode 100644
index 000000000000..6823bd0ed653
--- /dev/null
+++ b/test/Transforms/InstCombine/bitreverse-hang.ll
@@ -0,0 +1,53 @@
+; RUN: opt < %s -loop-unroll -instcombine -S | FileCheck %s
+
+; This test is a worst-case scenario for bitreversal/byteswap detection.
+; After loop unrolling (the unrolled loop is unreadably large so it has been kept
+; rolled here), we have a binary tree of OR operands (as bitreversal detection
+; looks straight through shifts):
+;
+; OR
+; | \
+; | LSHR
+; | /
+; OR
+; | \
+; | LSHR
+; | /
+; OR
+;
+; This results in exponential runtime. The loop here is 32 iterations which will
+; totally hang if we don't deal with this case cleverly.
+
+@b = common global i32 0, align 4
+
+; CHECK: define i32 @fn1
+define i32 @fn1() #0 {
+entry:
+ %b.promoted = load i32, i32* @b, align 4, !tbaa !2
+ br label %for.body
+
+for.body: ; preds = %for.body, %entry
+ %or4 = phi i32 [ %b.promoted, %entry ], [ %or, %for.body ]
+ %i.03 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
+ %shr = lshr i32 %or4, 1
+ %or = or i32 %shr, %or4
+ %inc = add nuw nsw i32 %i.03, 1
+ %exitcond = icmp eq i32 %inc, 32
+ br i1 %exitcond, label %for.end, label %for.body
+
+for.end: ; preds = %for.body
+ store i32 %or, i32* @b, align 4, !tbaa !2
+ ret i32 undef
+}
+
+attributes #0 = { norecurse nounwind ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="core2" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"PIC Level", i32 2}
+!1 = !{!"clang version 3.8.0 (http://llvm.org/git/clang.git eb70f4e9cc9a4dc3dd57b032fb858d56b4b64a0e)"}
+!2 = !{!3, !3, i64 0}
+!3 = !{!"int", !4, i64 0}
+!4 = !{!"omnipotent char", !5, i64 0}
+!5 = !{!"Simple C/C++ TBAA"}
diff --git a/test/Transforms/InstCombine/bitreverse-recognize.ll b/test/Transforms/InstCombine/bitreverse-recognize.ll
deleted file mode 100644
index fbd5cb6d139c..000000000000
--- a/test/Transforms/InstCombine/bitreverse-recognize.ll
+++ /dev/null
@@ -1,114 +0,0 @@
-; RUN: opt < %s -instcombine -S | FileCheck %s
-
-target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-apple-macosx10.10.0"
-
-define zeroext i8 @f_u8(i8 zeroext %a) {
-; CHECK-LABEL: @f_u8
-; CHECK-NEXT: %[[A:.*]] = call i8 @llvm.bitreverse.i8(i8 %a)
-; CHECK-NEXT: ret i8 %[[A]]
- %1 = shl i8 %a, 7
- %2 = shl i8 %a, 5
- %3 = and i8 %2, 64
- %4 = shl i8 %a, 3
- %5 = and i8 %4, 32
- %6 = shl i8 %a, 1
- %7 = and i8 %6, 16
- %8 = lshr i8 %a, 1
- %9 = and i8 %8, 8
- %10 = lshr i8 %a, 3
- %11 = and i8 %10, 4
- %12 = lshr i8 %a, 5
- %13 = and i8 %12, 2
- %14 = lshr i8 %a, 7
- %15 = or i8 %14, %1
- %16 = or i8 %15, %3
- %17 = or i8 %16, %5
- %18 = or i8 %17, %7
- %19 = or i8 %18, %9
- %20 = or i8 %19, %11
- %21 = or i8 %20, %13
- ret i8 %21
-}
-
-; The ANDs with 32 and 64 have been swapped here, so the sequence does not
-; completely match a bitreverse.
-define zeroext i8 @f_u8_fail(i8 zeroext %a) {
-; CHECK-LABEL: @f_u8_fail
-; CHECK-NOT: call
-; CHECK: ret i8
- %1 = shl i8 %a, 7
- %2 = shl i8 %a, 5
- %3 = and i8 %2, 32
- %4 = shl i8 %a, 3
- %5 = and i8 %4, 64
- %6 = shl i8 %a, 1
- %7 = and i8 %6, 16
- %8 = lshr i8 %a, 1
- %9 = and i8 %8, 8
- %10 = lshr i8 %a, 3
- %11 = and i8 %10, 4
- %12 = lshr i8 %a, 5
- %13 = and i8 %12, 2
- %14 = lshr i8 %a, 7
- %15 = or i8 %14, %1
- %16 = or i8 %15, %3
- %17 = or i8 %16, %5
- %18 = or i8 %17, %7
- %19 = or i8 %18, %9
- %20 = or i8 %19, %11
- %21 = or i8 %20, %13
- ret i8 %21
-}
-
-define zeroext i16 @f_u16(i16 zeroext %a) {
-; CHECK-LABEL: @f_u16
-; CHECK-NEXT: %[[A:.*]] = call i16 @llvm.bitreverse.i16(i16 %a)
-; CHECK-NEXT: ret i16 %[[A]]
- %1 = shl i16 %a, 15
- %2 = shl i16 %a, 13
- %3 = and i16 %2, 16384
- %4 = shl i16 %a, 11
- %5 = and i16 %4, 8192
- %6 = shl i16 %a, 9
- %7 = and i16 %6, 4096
- %8 = shl i16 %a, 7
- %9 = and i16 %8, 2048
- %10 = shl i16 %a, 5
- %11 = and i16 %10, 1024
- %12 = shl i16 %a, 3
- %13 = and i16 %12, 512
- %14 = shl i16 %a, 1
- %15 = and i16 %14, 256
- %16 = lshr i16 %a, 1
- %17 = and i16 %16, 128
- %18 = lshr i16 %a, 3
- %19 = and i16 %18, 64
- %20 = lshr i16 %a, 5
- %21 = and i16 %20, 32
- %22 = lshr i16 %a, 7
- %23 = and i16 %22, 16
- %24 = lshr i16 %a, 9
- %25 = and i16 %24, 8
- %26 = lshr i16 %a, 11
- %27 = and i16 %26, 4
- %28 = lshr i16 %a, 13
- %29 = and i16 %28, 2
- %30 = lshr i16 %a, 15
- %31 = or i16 %30, %1
- %32 = or i16 %31, %3
- %33 = or i16 %32, %5
- %34 = or i16 %33, %7
- %35 = or i16 %34, %9
- %36 = or i16 %35, %11
- %37 = or i16 %36, %13
- %38 = or i16 %37, %15
- %39 = or i16 %38, %17
- %40 = or i16 %39, %19
- %41 = or i16 %40, %21
- %42 = or i16 %41, %23
- %43 = or i16 %42, %25
- %44 = or i16 %43, %27
- %45 = or i16 %44, %29
- ret i16 %45
-} \ No newline at end of file
diff --git a/test/Transforms/InstCombine/cos-2.ll b/test/Transforms/InstCombine/cos-2.ll
index c9a9c7c07712..a85cc8fa6bde 100644
--- a/test/Transforms/InstCombine/cos-2.ll
+++ b/test/Transforms/InstCombine/cos-2.ll
@@ -1,12 +1,11 @@
-; Test that the cos library call simplifier works correctly.
-;
; RUN: opt < %s -instcombine -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
declare float @cos(double)
+declare signext i8 @sqrt(...)
-; Check that cos functions with the wrong prototype aren't simplified.
+; Check that functions with the wrong prototype aren't simplified.
define float @test_no_simplify1(double %d) {
; CHECK-LABEL: @test_no_simplify1(
@@ -15,3 +14,14 @@ define float @test_no_simplify1(double %d) {
; CHECK: call float @cos(double %neg)
ret float %cos
}
+
+
+define i8 @bogus_sqrt() {
+ %fake_sqrt = call signext i8 (...) @sqrt()
+ ret i8 %fake_sqrt
+
+; CHECK-LABEL: bogus_sqrt(
+; CHECK-NEXT: %fake_sqrt = call signext i8 (...) @sqrt()
+; CHECK-NEXT: ret i8 %fake_sqrt
+}
+
diff --git a/test/Transforms/InstCombine/double-float-shrink-1.ll b/test/Transforms/InstCombine/double-float-shrink-1.ll
index 319ea3259830..74f3ebbf5230 100644
--- a/test/Transforms/InstCombine/double-float-shrink-1.ll
+++ b/test/Transforms/InstCombine/double-float-shrink-1.ll
@@ -364,6 +364,26 @@ define float @max1(float %a, float %b) {
; CHECK-NEXT: ret
}
+; A function can have a name that matches a common libcall,
+; but with the wrong type(s). Let it be.
+
+define float @fake_fmin(float %a, float %b) {
+ %c = fpext float %a to fp128
+ %d = fpext float %b to fp128
+ %e = call fp128 @fmin(fp128 %c, fp128 %d)
+ %f = fptrunc fp128 %e to float
+ ret float %f
+
+; CHECK-LABEL: fake_fmin(
+; CHECK-NEXT: %c = fpext float %a to fp128
+; CHECK-NEXT: %d = fpext float %b to fp128
+; CHECK-NEXT: %e = call fp128 @fmin(fp128 %c, fp128 %d)
+; CHECK-NEXT: %f = fptrunc fp128 %e to float
+; CHECK-NEXT: ret float %f
+}
+
+declare fp128 @fmin(fp128, fp128) ; This is not the 'fmin' you're looking for.
+
declare double @fmax(double, double)
declare double @tanh(double) #1