1; RUN: opt < %s -tailcallelim -S | FileCheck %s
2; PR4323
3
4; Several cases where tail call elimination should move the load above the call,
5; then eliminate the tail recursion.
6
7
8@global = external global i32		; <i32*> [#uses=1]
9@extern_weak_global = extern_weak global i32		; <i32*> [#uses=1]
10
11
12; This load can be moved above the call because the function won't write to it
13; and the call has no side effects.
14define fastcc i32 @raise_load_1(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) nounwind readonly {
15entry:
16	%tmp2 = icmp sge i32 %start_arg, %a_len_arg		; <i1> [#uses=1]
17	br i1 %tmp2, label %if, label %else
18
19if:		; preds = %entry
20	ret i32 0
21
22else:		; preds = %entry
23	%tmp7 = add i32 %start_arg, 1		; <i32> [#uses=1]
24; CHECK-NOT: call
25	%tmp8 = call fastcc i32 @raise_load_1(i32* %a_arg, i32 %a_len_arg, i32 %tmp7)		; <i32> [#uses=1]
26	%tmp9 = load i32* %a_arg		; <i32> [#uses=1]
27	%tmp10 = add i32 %tmp9, %tmp8		; <i32> [#uses=1]
28	ret i32 %tmp10
29}
30
31
32; This load can be moved above the call because the function won't write to it
33; and the load provably can't trap.
34define fastcc i32 @raise_load_2(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) readonly {
35entry:
36	%tmp2 = icmp sge i32 %start_arg, %a_len_arg		; <i1> [#uses=1]
37	br i1 %tmp2, label %if, label %else
38
39if:		; preds = %entry
40	ret i32 0
41
42else:		; preds = %entry
43	%nullcheck = icmp eq i32* %a_arg, null		; <i1> [#uses=1]
44	br i1 %nullcheck, label %unwind, label %recurse
45
46unwind:		; preds = %else
47	unreachable
48
49recurse:		; preds = %else
50	%tmp7 = add i32 %start_arg, 1		; <i32> [#uses=1]
51; CHECK-NOT: call
52	%tmp8 = call fastcc i32 @raise_load_2(i32* %a_arg, i32 %a_len_arg, i32 %tmp7)		; <i32> [#uses=1]
53	%tmp9 = load i32* @global		; <i32> [#uses=1]
54	%tmp10 = add i32 %tmp9, %tmp8		; <i32> [#uses=1]
55	ret i32 %tmp10
56}
57
58
59; This load can be safely moved above the call (even though it's from an
60; extern_weak global) because the call has no side effects.
61define fastcc i32 @raise_load_3(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) nounwind readonly {
62entry:
63	%tmp2 = icmp sge i32 %start_arg, %a_len_arg		; <i1> [#uses=1]
64	br i1 %tmp2, label %if, label %else
65
66if:		; preds = %entry
67	ret i32 0
68
69else:		; preds = %entry
70	%tmp7 = add i32 %start_arg, 1		; <i32> [#uses=1]
71; CHECK-NOT: call
72	%tmp8 = call fastcc i32 @raise_load_3(i32* %a_arg, i32 %a_len_arg, i32 %tmp7)		; <i32> [#uses=1]
73	%tmp9 = load i32* @extern_weak_global		; <i32> [#uses=1]
74	%tmp10 = add i32 %tmp9, %tmp8		; <i32> [#uses=1]
75	ret i32 %tmp10
76}
77
78
79; The second load can be safely moved above the call even though it's from an
80; unknown pointer (which normally means it might trap) because the first load
81; proves it doesn't trap.
82define fastcc i32 @raise_load_4(i32* %a_arg, i32 %a_len_arg, i32 %start_arg) readonly {
83entry:
84	%tmp2 = icmp sge i32 %start_arg, %a_len_arg		; <i1> [#uses=1]
85	br i1 %tmp2, label %if, label %else
86
87if:		; preds = %entry
88	ret i32 0
89
90else:		; preds = %entry
91	%nullcheck = icmp eq i32* %a_arg, null		; <i1> [#uses=1]
92	br i1 %nullcheck, label %unwind, label %recurse
93
94unwind:		; preds = %else
95	unreachable
96
97recurse:		; preds = %else
98	%tmp7 = add i32 %start_arg, 1		; <i32> [#uses=1]
99	%first = load i32* %a_arg		; <i32> [#uses=1]
100; CHECK-NOT: call
101	%tmp8 = call fastcc i32 @raise_load_4(i32* %a_arg, i32 %first, i32 %tmp7)		; <i32> [#uses=1]
102	%second = load i32* %a_arg		; <i32> [#uses=1]
103	%tmp10 = add i32 %second, %tmp8		; <i32> [#uses=1]
104	ret i32 %tmp10
105}
106