1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt < %s -gvn --basic-aa -S | FileCheck %s
3
4; load may be speculated, address is not null using context search.
5; There is a critical edge.
6define i32 @loadpre_critical_edge(i32* align 8 dereferenceable_or_null(48) %arg, i32 %N) nofree nosync {
7; CHECK-LABEL: @loadpre_critical_edge(
8; CHECK-NEXT:  entry:
9; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32* [[ARG:%.*]], null
10; CHECK-NEXT:    br i1 [[CMP]], label [[NULL_EXIT:%.*]], label [[ENTRY_HEADER_CRIT_EDGE:%.*]]
11; CHECK:       entry.header_crit_edge:
12; CHECK-NEXT:    [[V_PRE:%.*]] = load i32, i32* [[ARG]], align 4
13; CHECK-NEXT:    br label [[HEADER:%.*]]
14; CHECK:       header:
15; CHECK-NEXT:    [[V:%.*]] = phi i32 [ [[V_PRE]], [[ENTRY_HEADER_CRIT_EDGE]] ], [ [[SUM:%.*]], [[HEADER]] ]
16; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY_HEADER_CRIT_EDGE]] ], [ [[IV_NEXT:%.*]], [[HEADER]] ]
17; CHECK-NEXT:    [[NEW_V:%.*]] = call i32 @ro_foo(i32 [[IV]]) #[[ATTR0:[0-9]+]]
18; CHECK-NEXT:    [[SUM]] = add i32 [[NEW_V]], [[V]]
19; CHECK-NEXT:    store i32 [[SUM]], i32* [[ARG]], align 4
20; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
21; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[IV_NEXT]], [[N:%.*]]
22; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[HEADER]]
23; CHECK:       exit:
24; CHECK-NEXT:    ret i32 [[SUM]]
25; CHECK:       null_exit:
26; CHECK-NEXT:    ret i32 0
27;
28entry:
29  %cmp = icmp eq i32* %arg, null
30  br i1 %cmp, label %null_exit, label %header
31
32header:
33  %iv = phi i32 [0, %entry], [%iv.next, %header]
34; Call prevents to move load over due to it does not guarantee to return.
35  %new_v = call i32 @ro_foo(i32 %iv) readnone
36  %v = load i32, i32* %arg
37  %sum = add i32 %new_v, %v
38  store i32 %sum, i32* %arg
39  %iv.next = add i32 %iv, 1
40  %cond = icmp eq i32 %iv.next, %N
41  br i1 %cond, label %exit, label %header
42
43exit:
44  ret i32 %sum
45
46null_exit:
47  ret i32 0
48}
49
50; load may be speculated, address is not null using context search.
51define i32 @loadpre_basic(i32* align 8 dereferenceable_or_null(48) %arg, i32 %N) nofree nosync {
52; CHECK-LABEL: @loadpre_basic(
53; CHECK-NEXT:  entry:
54; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32* [[ARG:%.*]], null
55; CHECK-NEXT:    br i1 [[CMP]], label [[NULL_EXIT:%.*]], label [[PREHEADER:%.*]]
56; CHECK:       preheader:
57; CHECK-NEXT:    [[V_PRE:%.*]] = load i32, i32* [[ARG]], align 4
58; CHECK-NEXT:    br label [[HEADER:%.*]]
59; CHECK:       header:
60; CHECK-NEXT:    [[V:%.*]] = phi i32 [ [[V_PRE]], [[PREHEADER]] ], [ [[SUM:%.*]], [[HEADER]] ]
61; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IV_NEXT:%.*]], [[HEADER]] ]
62; CHECK-NEXT:    [[NEW_V:%.*]] = call i32 @ro_foo(i32 [[IV]]) #[[ATTR0]]
63; CHECK-NEXT:    [[SUM]] = add i32 [[NEW_V]], [[V]]
64; CHECK-NEXT:    store i32 [[SUM]], i32* [[ARG]], align 4
65; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
66; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[IV_NEXT]], [[N:%.*]]
67; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[HEADER]]
68; CHECK:       exit:
69; CHECK-NEXT:    ret i32 [[SUM]]
70; CHECK:       null_exit:
71; CHECK-NEXT:    ret i32 0
72;
73entry:
74  %cmp = icmp eq i32* %arg, null
75  br i1 %cmp, label %null_exit, label %preheader
76
77preheader:
78  br label %header
79
80header:
81  %iv = phi i32 [0, %preheader], [%iv.next, %header]
82; Call prevents to move load over due to it does not guarantee to return.
83  %new_v = call i32 @ro_foo(i32 %iv) readnone
84  %v = load i32, i32* %arg
85  %sum = add i32 %new_v, %v
86  store i32 %sum, i32* %arg
87  %iv.next = add i32 %iv, 1
88  %cond = icmp eq i32 %iv.next, %N
89  br i1 %cond, label %exit, label %header
90
91exit:
92  ret i32 %sum
93
94null_exit:
95  ret i32 0
96}
97
98; load cannot be speculated, check "address is not null" does not dominate the loop.
99define i32 @loadpre_maybe_null(i32* align 8 dereferenceable_or_null(48) %arg, i32 %N, i1 %c) nofree nosync {
100; CHECK-LABEL: @loadpre_maybe_null(
101; CHECK-NEXT:  entry:
102; CHECK-NEXT:    br i1 [[C:%.*]], label [[NULL_CHECK:%.*]], label [[PREHEADER:%.*]]
103; CHECK:       null_check:
104; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32* [[ARG:%.*]], null
105; CHECK-NEXT:    br i1 [[CMP]], label [[NULL_EXIT:%.*]], label [[PREHEADER]]
106; CHECK:       preheader:
107; CHECK-NEXT:    br label [[HEADER:%.*]]
108; CHECK:       header:
109; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IV_NEXT:%.*]], [[HEADER]] ]
110; CHECK-NEXT:    [[NEW_V:%.*]] = call i32 @ro_foo(i32 [[IV]]) #[[ATTR0]]
111; CHECK-NEXT:    [[V:%.*]] = load i32, i32* [[ARG]], align 4
112; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[NEW_V]], [[V]]
113; CHECK-NEXT:    store i32 [[SUM]], i32* [[ARG]], align 4
114; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
115; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[IV_NEXT]], [[N:%.*]]
116; CHECK-NEXT:    br i1 [[COND]], label [[EXIT:%.*]], label [[HEADER]]
117; CHECK:       exit:
118; CHECK-NEXT:    ret i32 [[SUM]]
119; CHECK:       null_exit:
120; CHECK-NEXT:    ret i32 0
121;
122entry:
123  br i1 %c, label %null_check, label %preheader
124
125null_check:
126  %cmp = icmp eq i32* %arg, null
127  br i1 %cmp, label %null_exit, label %preheader
128
129preheader:
130  br label %header
131
132header:
133  %iv = phi i32 [0, %preheader], [%iv.next, %header]
134; Call prevents to move load over due to it does not guarantee to return.
135  %new_v = call i32 @ro_foo(i32 %iv) readnone
136  %v = load i32, i32* %arg
137  %sum = add i32 %new_v, %v
138  store i32 %sum, i32* %arg
139  %iv.next = add i32 %iv, 1
140  %cond = icmp eq i32 %iv.next, %N
141  br i1 %cond, label %exit, label %header
142
143exit:
144  ret i32 %sum
145
146null_exit:
147  ret i32 0
148}
149
150; Does not guarantee that returns.
151declare i32 @ro_foo(i32) readnone
152