1 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2 ; RUN: opt < %s -unify-loop-exits -enable-new-pm=0 -S | FileCheck %s
3 ; RUN: opt < %s -passes='lowerswitch,unify-loop-exits' -S | FileCheck %s
5 ; Loop consists of A and B:
7 ; - A and B are exiting blocks
8 ; - C and return are exit blocks.
9 ; Pattern: Value (%mytmp42) defined in exiting block (A) and used in
10 ; exit block (return).
11 ; The relevant code uses DT::dominates(Value,
12 ; BasicBlock). This is misnamed because it actually checks
13 ; strict dominance, causing the pattern to be miscompiled
14 ; (the use receives an undef value).
15 define i32 @exiting-used-in-exit(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 {
16 ; CHECK-LABEL: @exiting-used-in-exit(
18 ; CHECK-NEXT: br label [[A:%.*]]
20 ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4
21 ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
22 ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]]
24 ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4
25 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP41]], 0
26 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
28 ; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1
29 ; CHECK-NEXT: br label [[RETURN:%.*]]
31 ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[INC]], [[C:%.*]] ], [ [[PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD]] ]
32 ; CHECK-NEXT: ret i32 [[PHI]]
33 ; CHECK: loop.exit.guard:
34 ; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A]] ], [ false, [[B]] ]
35 ; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP42]], [[A]] ], [ undef, [[B]] ]
36 ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[B]] ]
37 ; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[C]]
43 %mytmp42 = load i32, i32* %arg1, align 4
44 %cmp1 = icmp slt i32 %mytmp42, 0
45 br i1 %cmp1, label %B, label %return
48 %mytmp41 = load i32, i32* %arg2, align 4
49 %cmp = icmp slt i32 %mytmp41, 0
50 br i1 %cmp, label %A, label %C
53 %inc = add i32 %mytmp41, 1
57 %phi = phi i32 [ %inc, %C ], [ %mytmp42, %A ]
61 ; Loop consists of A, B and C:
63 ; - A and C are exiting blocks
64 ; - B is an "internal" block that dominates exiting block C
65 ; - D and return are exit blocks.
66 ; Pattern: Value (%mytmp41) defined in internal block (B) and used in an
68 define i32 @internal-used-in-exit(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 {
69 ; CHECK-LABEL: @internal-used-in-exit(
71 ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4
72 ; CHECK-NEXT: br label [[A:%.*]]
74 ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
75 ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]]
77 ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4
78 ; CHECK-NEXT: br label [[C:%.*]]
80 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
81 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
83 ; CHECK-NEXT: [[INC:%.*]] = add i32 [[MYTMP41_MOVED:%.*]], 1
84 ; CHECK-NEXT: br label [[RETURN:%.*]]
86 ; CHECK-NEXT: ret i32 0
87 ; CHECK: loop.exit.guard:
88 ; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ]
89 ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[C]] ]
90 ; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D:%.*]]
93 %mytmp42 = load i32, i32* %arg1, align 4
97 %cmp1 = icmp slt i32 %mytmp42, 0
98 br i1 %cmp1, label %B, label %return
101 %mytmp41 = load i32, i32* %arg2, align 4
105 %cmp = icmp slt i32 %mytmp42, 0
106 br i1 %cmp, label %A, label %D
109 %inc = add i32 %mytmp41, 1
116 ; Loop consists of A, B and C:
118 ; - A and C are exiting blocks
119 ; - B is an "internal" block that dominates exiting block C
120 ; - D and return are exit blocks.
121 ; Pattern: %return contains a phi node that receives values from
122 ; %entry, %A and %D. This mixes all the special cases in a single phi.
123 define i32 @mixed-use-in-exit(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 {
124 ; CHECK-LABEL: @mixed-use-in-exit(
126 ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4
127 ; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[MYTMP42]], 0
128 ; CHECK-NEXT: br i1 [[CMP2]], label [[A:%.*]], label [[RETURN:%.*]]
130 ; CHECK-NEXT: [[MYTMP43:%.*]] = add i32 [[MYTMP42]], 1
131 ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
132 ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]]
134 ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4
135 ; CHECK-NEXT: br label [[C:%.*]]
137 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
138 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
140 ; CHECK-NEXT: br label [[RETURN]]
142 ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[MYTMP41_MOVED:%.*]], [[D:%.*]] ], [ [[MYTMP42]], [[ENTRY:%.*]] ], [ [[PHI_MOVED:%.*]], [[LOOP_EXIT_GUARD]] ]
143 ; CHECK-NEXT: ret i32 [[PHI]]
144 ; CHECK: loop.exit.guard:
145 ; CHECK-NEXT: [[GUARD_RETURN:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ]
146 ; CHECK-NEXT: [[PHI_MOVED]] = phi i32 [ [[MYTMP43]], [[A]] ], [ undef, [[C]] ]
147 ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[C]] ]
148 ; CHECK-NEXT: br i1 [[GUARD_RETURN]], label [[RETURN]], label [[D]]
151 %mytmp42 = load i32, i32* %arg1, align 4
152 %cmp2 = icmp slt i32 %mytmp42, 0
153 br i1 %cmp2, label %A, label %return
156 %mytmp43 = add i32 %mytmp42, 1
157 %cmp1 = icmp slt i32 %mytmp42, 0
158 br i1 %cmp1, label %B, label %return
161 %mytmp41 = load i32, i32* %arg2, align 4
165 %cmp = icmp slt i32 %mytmp42, 0
166 br i1 %cmp, label %A, label %D
172 %phi = phi i32 [ %mytmp41, %D ], [ %mytmp43, %A ], [%mytmp42, %entry]
176 ; Loop consists of A, B and C:
178 ; - A and C are exiting blocks
179 ; - B is an "internal" block that dominates exiting block C
180 ; - D and E are exit blocks.
181 ; Pattern: Value (%mytmp41) defined in internal block (B) and used in a
182 ; downstream block not related to the loop (return). The use
183 ; is a phi where the incoming block for %mytmp41 is not related
185 ; This pattern does not involve either the exiting blocks or
186 ; the exit blocks, which catches any such assumptions built
187 ; into the SSA reconstruction phase.
188 define i32 @phi-via-external-block(i32* %arg1, i32* %arg2) local_unnamed_addr align 2 {
189 ; CHECK-LABEL: @phi-via-external-block(
191 ; CHECK-NEXT: [[MYTMP42:%.*]] = load i32, i32* [[ARG1:%.*]], align 4
192 ; CHECK-NEXT: br label [[A:%.*]]
194 ; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[MYTMP42]], 0
195 ; CHECK-NEXT: br i1 [[CMP1]], label [[B:%.*]], label [[LOOP_EXIT_GUARD:%.*]]
197 ; CHECK-NEXT: [[MYTMP41:%.*]] = load i32, i32* [[ARG2:%.*]], align 4
198 ; CHECK-NEXT: br label [[C:%.*]]
200 ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[MYTMP42]], 0
201 ; CHECK-NEXT: br i1 [[CMP]], label [[A]], label [[LOOP_EXIT_GUARD]]
203 ; CHECK-NEXT: br label [[RETURN:%.*]]
205 ; CHECK-NEXT: br label [[RETURN]]
207 ; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ [[MYTMP41_MOVED:%.*]], [[D:%.*]] ], [ [[MYTMP42]], [[E:%.*]] ]
208 ; CHECK-NEXT: ret i32 [[PHI]]
209 ; CHECK: loop.exit.guard:
210 ; CHECK-NEXT: [[GUARD_E:%.*]] = phi i1 [ true, [[A]] ], [ false, [[C]] ]
211 ; CHECK-NEXT: [[MYTMP41_MOVED]] = phi i32 [ undef, [[A]] ], [ [[MYTMP41]], [[C]] ]
212 ; CHECK-NEXT: br i1 [[GUARD_E]], label [[E]], label [[D]]
215 %mytmp42 = load i32, i32* %arg1, align 4
219 %cmp1 = icmp slt i32 %mytmp42, 0
220 br i1 %cmp1, label %B, label %E
223 %mytmp41 = load i32, i32* %arg2, align 4
227 %cmp = icmp slt i32 %mytmp42, 0
228 br i1 %cmp, label %A, label %D
237 %phi = phi i32 [ %mytmp41, %D ], [ %mytmp42, %E ]