xref: /llvm-project/llvm/test/CodeGen/WinEH/wineh-demotion.ll (revision d1a83ff3e0274f26746e874d480c866bec3818d6)
1; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -win-eh-prepare  < %s | FileCheck %s
2; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -passes=win-eh-prepare  < %s | FileCheck %s
3
4declare i32 @__CxxFrameHandler3(...)
5
6declare void @f()
7
8declare i32 @g()
9
10declare void @h(i32)
11
12declare i1 @i()
13
14declare void @llvm.bar() nounwind
15
16; CHECK-LABEL: @test1(
17define void @test1(i1 %B) personality ptr @__CxxFrameHandler3 {
18entry:
19  ; Spill slot should be inserted here
20  ; CHECK: [[Slot:%[^ ]+]] = alloca
21  ; Can't store for %phi at these defs because the lifetimes overlap
22  ; CHECK-NOT: store
23  %x = call i32 @g()
24  %y = call i32 @g()
25  br i1 %B, label %left, label %right
26left:
27  ; CHECK: left:
28  ; CHECK-NEXT: store i32 %x, ptr [[Slot]]
29  ; CHECK-NEXT: invoke void @f
30  invoke void @f()
31          to label %exit unwind label %merge
32right:
33  ; CHECK: right:
34  ; CHECK-NEXT: store i32 %y, ptr [[Slot]]
35  ; CHECK-NEXT: invoke void @f
36  invoke void @f()
37          to label %exit unwind label %merge
38merge:
39  ; CHECK: merge:
40  ; CHECK-NOT: = phi
41  %phi = phi i32 [ %x, %left ], [ %y, %right ]
42  %cs1 = catchswitch within none [label %catch] unwind to caller
43
44catch:
45  %cp = catchpad within %cs1 []
46  ; CHECK: catch:
47  ; CHECK: [[Reload:%[^ ]+]] = load i32, ptr [[Slot]]
48  ; CHECK-NEXT: call void @h(i32 [[Reload]])
49  call void @h(i32 %phi) [ "funclet"(token %cp) ]
50  catchret from %cp to label %exit
51
52exit:
53  ret void
54}
55
56; CHECK-LABEL: @test2(
57define void @test2(i1 %B) personality ptr @__CxxFrameHandler3 {
58entry:
59  br i1 %B, label %left, label %right
60left:
61  ; Need two stores here because %x and %y interfere so they need 2 slots
62  ; CHECK: left:
63  ; CHECK:   store i32 1, ptr [[Slot1:%[^ ]+]]
64  ; CHECK:   store i32 1, ptr [[Slot2:%[^ ]+]]
65  ; CHECK-NEXT: invoke void @f
66  invoke void @f()
67          to label %exit unwind label %merge.inner
68right:
69  ; Need two stores here because %x and %y interfere so they need 2 slots
70  ; CHECK: right:
71  ; CHECK-DAG:   store i32 2, ptr [[Slot1]]
72  ; CHECK-DAG:   store i32 2, ptr [[Slot2]]
73  ; CHECK: invoke void @f
74  invoke void @f()
75          to label %exit unwind label %merge.inner
76merge.inner:
77  ; CHECK: merge.inner:
78  ; CHECK-NOT: = phi
79  ; CHECK: catchswitch within none
80  %x = phi i32 [ 1, %left ], [ 2, %right ]
81  %cs1 = catchswitch within none [label %catch.inner] unwind label %merge.outer
82
83catch.inner:
84  %cpinner = catchpad within %cs1 []
85  ; Need just one store here because only %y is affected
86  ; CHECK: catch.inner:
87  %z = call i32 @g() [ "funclet"(token %cpinner) ]
88  ; CHECK:   store i32 %z
89  ; CHECK-NEXT: invoke void @f
90  invoke void @f() [ "funclet"(token %cpinner) ]
91          to label %catchret.inner unwind label %merge.outer
92
93catchret.inner:
94  catchret from %cpinner to label %exit
95
96merge.outer:
97  %y = phi i32 [ %x, %merge.inner ], [ %z, %catch.inner ]
98  ; CHECK: merge.outer:
99  ; CHECK-NOT: = phi
100  ; CHECK: catchswitch within none
101  %cs2 = catchswitch within none [label %catch.outer] unwind to caller
102
103catch.outer:
104  %cpouter = catchpad within %cs2 []
105  ; CHECK: catch.outer:
106  ; CHECK: [[CatchPad:%[^ ]+]] = catchpad within %cs2 []
107  ; Need to load x and y from two different slots since they're both live
108  ; and can have different values (if we came from catch.inner)
109  ; CHECK-DAG: load i32, ptr [[Slot1]]
110  ; CHECK-DAG: load i32, ptr [[Slot2]]
111  ; CHECK: catchret from [[CatchPad]] to label
112  call void @h(i32 %x) [ "funclet"(token %cpouter) ]
113  call void @h(i32 %y) [ "funclet"(token %cpouter) ]
114  catchret from %cpouter to label %exit
115
116exit:
117  ret void
118}
119
120; test4: don't need stores for %phi.inner, as its only use is to feed %phi.outer
121;        %phi.outer needs stores in %left, %right, and %join
122; CHECK-LABEL: @test4(
123define void @test4(i1 %B) personality ptr @__CxxFrameHandler3 {
124entry:
125  ; CHECK:      entry:
126  ; CHECK:        [[Slot:%[^ ]+]] = alloca
127  ; CHECK-NEXT:   br
128  br i1 %B, label %left, label %right
129left:
130  ; CHECK: left:
131  ; CHECK-NOT: store
132  ; CHECK: store i32 %l, ptr [[Slot]]
133  ; CHECK-NEXT: invoke void @f
134  %l = call i32 @g()
135  invoke void @f()
136          to label %join unwind label %catchpad.inner
137right:
138  ; CHECK: right:
139  ; CHECK-NOT: store
140  ; CHECK: store i32 %r, ptr [[Slot]]
141  ; CHECK-NEXT: invoke void @f
142  %r = call i32 @g()
143  invoke void @f()
144          to label %join unwind label %catchpad.inner
145catchpad.inner:
146   ; CHECK: catchpad.inner:
147   ; CHECK-NEXT: catchswitch within none
148   %phi.inner = phi i32 [ %l, %left ], [ %r, %right ]
149   %cs1 = catchswitch within none [label %catch.inner] unwind label %catchpad.outer
150catch.inner:
151   %cp1 = catchpad within %cs1 []
152   catchret from %cp1 to label %join
153join:
154  ; CHECK: join:
155  ; CHECK-NOT: store
156  ; CHECK: store i32 %j, ptr [[Slot]]
157  ; CHECK-NEXT: invoke void @f
158   %j = call i32 @g()
159   invoke void @f()
160           to label %exit unwind label %catchpad.outer
161
162catchpad.outer:
163   ; CHECK: catchpad.outer:
164   ; CHECK-NEXT: catchswitch within none
165   %phi.outer = phi i32 [ %phi.inner, %catchpad.inner ], [ %j, %join ]
166   %cs2 = catchswitch within none [label %catch.outer] unwind to caller
167catch.outer:
168   ; CHECK: catch.outer:
169   ; CHECK:   [[Reload:%[^ ]+]] = load i32, ptr [[Slot]]
170   ; CHECK:   call void @h(i32 [[Reload]])
171   %cp2 = catchpad within %cs2 []
172   call void @h(i32 %phi.outer) [ "funclet"(token %cp2) ]
173   catchret from %cp2 to label %exit
174exit:
175   ret void
176}
177
178; CHECK-LABEL: @test5(
179define void @test5() personality ptr @__CxxFrameHandler3 {
180entry:
181  ; need store for %phi.cleanup
182  ; CHECK:      entry:
183  ; CHECK:        store i32 1, ptr [[CleanupSlot:%[^ ]+]]
184  ; CHECK-NEXT:   invoke void @f
185  invoke void @f()
186          to label %invoke.cont unwind label %cleanup
187
188invoke.cont:
189  ; need store for %phi.cleanup
190  ; CHECK:      invoke.cont:
191  ; CHECK-NEXT:   store i32 2, ptr [[CleanupSlot]]
192  ; CHECK-NEXT:   invoke void @f
193  invoke void @f()
194          to label %invoke.cont2 unwind label %cleanup
195
196cleanup:
197  ; cleanup phi can be loaded at cleanup entry
198  ; CHECK: cleanup:
199  ; CHECK-NEXT: cleanuppad within none []
200  ; CHECK: [[CleanupReload:%[^ ]+]] = load i32, ptr [[CleanupSlot]]
201  %phi.cleanup = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
202  %cp = cleanuppad within none []
203  %b = call i1 @i() [ "funclet"(token %cp) ]
204  br i1 %b, label %left, label %right
205
206left:
207  ; CHECK: left:
208  ; CHECK:   call void @h(i32 [[CleanupReload]]
209  call void @h(i32 %phi.cleanup) [ "funclet"(token %cp) ]
210  br label %merge
211
212right:
213  ; CHECK: right:
214  ; CHECK:   call void @h(i32 [[CleanupReload]]
215  call void @h(i32 %phi.cleanup) [ "funclet"(token %cp) ]
216  br label %merge
217
218merge:
219  ; need store for %phi.catch
220  ; CHECK:      merge:
221  ; CHECK-NEXT:   store i32 [[CleanupReload]], ptr [[CatchSlot:%[^ ]+]]
222  ; CHECK-NEXT:   cleanupret
223  cleanupret from %cp unwind label %catchswitch
224
225invoke.cont2:
226  ; need store for %phi.catch
227  ; CHECK:      invoke.cont2:
228  ; CHECK-NEXT:   store i32 3, ptr [[CatchSlot]]
229  ; CHECK-NEXT:   invoke void @f
230  invoke void @f()
231          to label %exit unwind label %catchswitch
232
233catchswitch:
234  ; CHECK: catchswitch:
235  ; CHECK-NEXT: catchswitch within none
236  %phi.catch = phi i32 [ %phi.cleanup, %merge ], [ 3, %invoke.cont2 ]
237  %cs1 = catchswitch within none [label %catch] unwind to caller
238
239catch:
240  ; CHECK: catch:
241  ; CHECK:   catchpad within %cs1
242  ; CHECK:   [[CatchReload:%[^ ]+]] = load i32, ptr [[CatchSlot]]
243  ; CHECK:   call void @h(i32 [[CatchReload]]
244  %cp2 = catchpad within %cs1 []
245  call void @h(i32 %phi.catch) [ "funclet"(token %cp2) ]
246  catchret from %cp2 to label %exit
247
248exit:
249  ret void
250}
251
252; We used to demote %x, but we don't need to anymore.
253; CHECK-LABEL: @test6(
254define void @test6() personality ptr @__CxxFrameHandler3 {
255entry:
256  ; CHECK: entry:
257  ; CHECK: %x = invoke i32 @g()
258  ; CHECK-NEXT: to label %loop unwind label %to_caller
259  %x = invoke i32 @g()
260          to label %loop unwind label %to_caller
261to_caller:
262  %cp1 = cleanuppad within none []
263  cleanupret from %cp1 unwind to caller
264loop:
265  invoke void @f()
266          to label %loop unwind label %cleanup
267cleanup:
268  ; CHECK: cleanup:
269  ; CHECK:   call void @h(i32 %x)
270  %cp2 = cleanuppad within none []
271  call void @h(i32 %x) [ "funclet"(token %cp2) ]
272  cleanupret from %cp2 unwind to caller
273}
274
275; CHECK-LABEL: @test7(
276define void @test7() personality ptr @__CxxFrameHandler3 {
277entry:
278  ; %x is an EH pad phi, so gets stored in pred here
279  ; CHECK: entry:
280  ; CHECK:   store i32 1, ptr [[SlotX:%[^ ]+]]
281  ; CHECK:   invoke void @f()
282  invoke void @f()
283     to label %invoke.cont unwind label %catchpad
284invoke.cont:
285  ; %x is an EH pad phi, so gets stored in pred here
286  ; CHECK: invoke.cont:
287  ; CHECK:   store i32 2, ptr [[SlotX]]
288  ; CHECK:   invoke void @f()
289  invoke void @f()
290    to label %exit unwind label %catchpad
291catchpad:
292  ; %x phi should be eliminated
293  ; CHECK: catchpad:
294  ; CHECK-NEXT: catchswitch within none
295  %x = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
296  %cs1 = catchswitch within none [label %catch] unwind to caller
297catch:
298  ; CHECK: catch:
299  ; CHECK-NEXT: %[[CatchPad:[^ ]+]] = catchpad within %cs1 []
300  %cp = catchpad within %cs1 []
301  %b = call i1 @i() [ "funclet"(token %cp) ]
302  br i1 %b, label %left, label %right
303left:
304  ; Edge from %left to %join needs to be split so that
305  ; the load of %x can be inserted *after* the catchret
306  ; CHECK: left:
307  ; CHECK-NEXT: catchret from %[[CatchPad]] to label %[[SplitLeft:[^ ]+]]
308  catchret from %cp to label %join
309  ; CHECK: [[SplitLeft]]:
310  ; CHECK:   [[LoadX:%[^ ]+]] = load i32, ptr [[SlotX]]
311  ; CHECK:   br label %join
312right:
313  ; Edge from %right to %join needs to be split so that
314  ; the load of %y can be inserted *after* the catchret
315  ; CHECK: right:
316  ; CHECK:   %y = call i32 @g()
317  ; CHECK:   catchret from %[[CatchPad]] to label %join
318  %y = call i32 @g() [ "funclet"(token %cp) ]
319  catchret from %cp to label %join
320join:
321  ; CHECK: join:
322  ; CHECK:   %phi = phi i32 [ [[LoadX]], %[[SplitLeft]] ], [ %y, %right ]
323  %phi = phi i32 [ %x, %left ], [ %y, %right ]
324  call void @h(i32 %phi)
325  br label %exit
326exit:
327  ret void
328}
329
330; CHECK-LABEL: @test8(
331define void @test8() personality ptr @__CxxFrameHandler3 { entry:
332  invoke void @f()
333          to label %done unwind label %cleanup1
334  invoke void @f()
335          to label %done unwind label %cleanup2
336
337done:
338  ret void
339
340cleanup1:
341  ; CHECK: [[CleanupPad1:%[^ ]+]] = cleanuppad within none []
342  ; CHECK-NEXT: call void @llvm.bar()
343  ; CHECK-NEXT: cleanupret from [[CleanupPad1]]
344  %cp0 = cleanuppad within none []
345  br label %cleanupexit
346
347cleanup2:
348  ; CHECK: cleanuppad within none []
349  ; CHECK-NEXT: call void @llvm.bar()
350  ; CHECK-NEXT: unreachable
351  %cp1 = cleanuppad within none []
352  br label %cleanupexit
353
354cleanupexit:
355  call void @llvm.bar()
356  cleanupret from %cp0 unwind label %cleanup2
357}
358