xref: /llvm-project/clang/test/CodeGenCXX/eh.cpp (revision 10edb4991c12738e60843d55cd9edbf6d702d9eb)
1 // RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.13.99 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,UNALIGNED,THROWEND %s
2 // RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,ALIGNED,THROWEND %s
3 // RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s -fassume-nothrow-exception-dtor -DNOTHROWEND | FileCheck --check-prefixes=CHECK,ALIGNED,NOTHROWEND %s
4 
5 struct test1_D {
6   double d;
7 } d1;
8 
test1()9 void test1() {
10   throw d1;
11 }
12 
13 // CHECK-LABEL:     define{{.*}} void @_Z5test1v()
14 // CHECK:       [[EXNOBJ:%.*]] = call ptr @__cxa_allocate_exception(i64 8)
15 // UNALIGNED-NEXT:  call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[EXNOBJ]], ptr align 8 @d1, i64 8, i1 false)
16 // ALIGNED-NEXT:  call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[EXNOBJ]], ptr align 8 @d1, i64 8, i1 false)
17 // CHECK-NEXT:  call void @__cxa_throw(ptr [[EXNOBJ]], ptr @_ZTI7test1_D, ptr null) [[NR:#[0-9]+]]
18 // CHECK-NEXT:  unreachable
19 
20 
21 struct test2_D {
22   test2_D(const test2_D&o);
23   test2_D();
bartest2_D24   virtual void bar() { }
25   int i; int j;
26 } d2;
27 
test2()28 void test2() {
29   throw d2;
30 }
31 
32 // CHECK-LABEL:     define{{.*}} void @_Z5test2v()
33 // CHECK:       [[EXNVAR:%.*]] = alloca ptr
34 // CHECK-NEXT:  [[SELECTORVAR:%.*]] = alloca i32
35 // CHECK-NEXT:  [[EXNOBJ:%.*]] = call ptr @__cxa_allocate_exception(i64 16)
36 // CHECK-NEXT:  invoke void @_ZN7test2_DC1ERKS_(ptr {{[^,]*}} [[EXNOBJ]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @d2)
37 // CHECK-NEXT:     to label %[[CONT:.*]] unwind label %{{.*}}
38 //      :     [[CONT]]:   (can't check this in Release-Asserts builds)
39 // CHECK:       call void @__cxa_throw(ptr [[EXNOBJ]], ptr @_ZTI7test2_D, ptr null) [[NR]]
40 // CHECK-NEXT:  unreachable
41 
42 
43 struct test3_D {
test3_Dtest3_D44   test3_D() { }
45   test3_D(volatile test3_D&o);
46   virtual void bar();
47 };
48 
test3()49 void test3() {
50   throw (volatile test3_D *)0;
51 }
52 
53 // CHECK-LABEL:     define{{.*}} void @_Z5test3v()
54 // CHECK:       [[EXNOBJ:%.*]] = call ptr @__cxa_allocate_exception(i64 8)
55 // CHECK-NEXT:  store ptr null, ptr [[EXNOBJ]]
56 // CHECK-NEXT:  call void @__cxa_throw(ptr [[EXNOBJ]], ptr @_ZTIPV7test3_D, ptr null) [[NR]]
57 // CHECK-NEXT:  unreachable
58 
59 
test4()60 void test4() {
61   throw;
62 }
63 
64 // CHECK-LABEL:     define{{.*}} void @_Z5test4v()
65 // CHECK:        call void @__cxa_rethrow() [[NR]]
66 // CHECK-NEXT:   unreachable
67 
68 namespace test5 {
69   struct A {
70     A();
71     A(const A&);
72     ~A();
73   };
74 
test()75   void test() {
76     try { throw A(); } catch (A &x) {}
77   }
78 // CHECK-LABEL:      define{{.*}} void @_ZN5test54testEv()
79 // CHECK:      [[EXNOBJ:%.*]] = call ptr @__cxa_allocate_exception(i64 1)
80 // CHECK: invoke void @_ZN5test51AC1Ev(ptr {{[^,]*}} [[EXNOBJ]])
81 // CHECK:      invoke void @__cxa_throw(ptr [[EXNOBJ]], ptr @_ZTIN5test51AE, ptr @_ZN5test51AD1Ev) [[NR]]
82 // CHECK-NEXT:   to label {{%.*}} unwind label %[[HANDLER:[^ ]*]]
83 //      :    [[HANDLER]]:  (can't check this in Release-Asserts builds)
84 // CHECK:      {{%.*}} = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIN5test51AE)
85 }
86 
87 namespace test6 {
88   template <class T> struct allocator {
~allocatortest6::allocator89     ~allocator() throw() { }
90   };
91 
foo()92   void foo() {
93     allocator<int> a;
94   }
95 }
96 
97 // PR7127
98 namespace test7 {
99 // CHECK-LABEL:      define{{.*}} i32 @_ZN5test73fooEv()
100 // CHECK-SAME:  personality ptr @__gxx_personality_v0
foo()101   int foo() {
102 // CHECK:      [[CAUGHTEXNVAR:%.*]] = alloca ptr
103 // CHECK-NEXT: [[SELECTORVAR:%.*]] = alloca i32
104 // CHECK-NEXT: [[INTCATCHVAR:%.*]] = alloca i32
105     try {
106       try {
107 // CHECK-NEXT: [[EXNALLOC:%.*]] = call ptr @__cxa_allocate_exception
108 // CHECK-NEXT: store i32 1, ptr
109 // CHECK-NEXT: invoke void @__cxa_throw(ptr [[EXNALLOC]], ptr @_ZTIi, ptr null
110         throw 1;
111       }
112 
113 // CHECK:      [[CAUGHTVAL:%.*]] = landingpad { ptr, i32 }
114 // CHECK-NEXT:   catch ptr @_ZTIi
115 // CHECK-NEXT:   catch ptr null
116 // CHECK-NEXT: [[CAUGHTEXN:%.*]] = extractvalue { ptr, i32 } [[CAUGHTVAL]], 0
117 // CHECK-NEXT: store ptr [[CAUGHTEXN]], ptr [[CAUGHTEXNVAR]]
118 // CHECK-NEXT: [[SELECTOR:%.*]] = extractvalue { ptr, i32 } [[CAUGHTVAL]], 1
119 // CHECK-NEXT: store i32 [[SELECTOR]], ptr [[SELECTORVAR]]
120 // CHECK-NEXT: br label
121 // CHECK:      [[SELECTOR:%.*]] = load i32, ptr [[SELECTORVAR]]
122 // CHECK-NEXT: [[T0:%.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
123 // CHECK-NEXT: icmp eq i32 [[SELECTOR]], [[T0]]
124 // CHECK-NEXT: br i1
125 // CHECK:      [[T0:%.*]] = load ptr, ptr [[CAUGHTEXNVAR]]
126 // CHECK-NEXT: [[T1:%.*]] = call ptr @__cxa_begin_catch(ptr [[T0]])
127 // CHECK-NEXT: [[T3:%.*]] = load i32, ptr [[T1]]
128 // CHECK-NEXT: store i32 [[T3]], ptr {{%.*}}, align 4
129 // CHECK-NEXT: invoke void @__cxa_rethrow
130       catch (int) {
131         throw;
132       }
133     }
134 // CHECK:      [[CAUGHTVAL:%.*]] = landingpad { ptr, i32 }
135 // CHECK-NEXT:   catch ptr null
136 // CHECK-NEXT: [[CAUGHTEXN:%.*]] = extractvalue { ptr, i32 } [[CAUGHTVAL]], 0
137 // CHECK-NEXT: store ptr [[CAUGHTEXN]], ptr [[CAUGHTEXNVAR]]
138 // CHECK-NEXT: [[SELECTOR:%.*]] = extractvalue { ptr, i32 } [[CAUGHTVAL]], 1
139 // CHECK-NEXT: store i32 [[SELECTOR]], ptr [[SELECTORVAR]]
140 // CHECK-NEXT: call void @__cxa_end_catch()
141 // CHECK-NEXT: br label
142 // CHECK:      load ptr, ptr [[CAUGHTEXNVAR]]
143 // CHECK-NEXT: call ptr @__cxa_begin_catch
144 // CHECK-NEXT: call void @__cxa_end_catch
145     catch (...) {
146     }
147 // CHECK:      ret i32 0
148     return 0;
149   }
150 }
151 
152 // Ordering of destructors in a catch handler.
153 namespace test8 {
154   struct A { A(const A&); ~A(); };
155   void bar();
156 
157   // CHECK-LABEL: define{{.*}} void @_ZN5test83fooEv()
foo()158   void foo() {
159     try {
160       // CHECK:      invoke void @_ZN5test83barEv()
161       bar();
162     } catch (A a) {
163       // CHECK:      call ptr @__cxa_get_exception_ptr
164       // CHECK-NEXT: invoke void @_ZN5test81AC1ERKS0_(
165       // CHECK:      call ptr @__cxa_begin_catch
166       // CHECK-NEXT: call void @_ZN5test81AD1Ev(
167       // CHECK:      call void @__cxa_end_catch()
168       // CHECK:      ret void
169     }
170   }
171 }
172 
173 // Constructor function-try-block must rethrow on fallthrough.
174 namespace test9 {
175   void opaque();
176 
177   struct A { A(); };
178 
179 
180   // CHECK-LABEL: define{{.*}} void @_ZN5test91AC2Ev(ptr {{[^,]*}} %this) unnamed_addr
181   // CHECK-SAME:  personality ptr @__gxx_personality_v0
A()182   A::A() try {
183   // CHECK:      invoke void @_ZN5test96opaqueEv()
184     opaque();
185   } catch (int x) {
186   // CHECK:      landingpad { ptr, i32 }
187   // CHECK-NEXT:   catch ptr @_ZTIi
188 
189   // CHECK:      call ptr @__cxa_begin_catch
190   // CHECK:      invoke void @_ZN5test96opaqueEv()
191   // CHECK:      invoke void @__cxa_rethrow()
192 
193   // CHECK-LABEL:      define{{.*}} void @_ZN5test91AC1Ev(ptr {{[^,]*}} %this) unnamed_addr
194   // CHECK:      call void @_ZN5test91AC2Ev
195   // CHECK-NEXT: ret void
196     opaque();
197   }
198 }
199 
200 // __cxa_end_catch can throw for some kinds of caught exceptions.
201 namespace test10 {
202   void opaque();
203 
204   struct A { ~A(); };
205   struct B { int x; };
206 
207   // CHECK-LABEL: define{{.*}} void @_ZN6test103fooEv()
foo()208   void foo() {
209     A a; // force a cleanup context
210 
211     try {
212     // CHECK:      invoke void @_ZN6test106opaqueEv()
213       opaque();
214     } catch (int i) {
215     // CHECK:      call ptr @__cxa_begin_catch
216     // CHECK-NEXT: load i32, ptr
217     // CHECK-NEXT: store i32
218     // CHECK-NEXT: call void @__cxa_end_catch() [[NUW:#[0-9]+]]
219     } catch (B a) {
220     // CHECK:      call ptr @__cxa_begin_catch
221     // CHECK-NEXT: call void @llvm.memcpy
222     // THROWEND-NEXT:   invoke void @__cxa_end_catch()
223     // NOTHROWEND-NEXT: call void @__cxa_end_catch() [[NUW]]
224     } catch (...) {
225     // CHECK:      call ptr @__cxa_begin_catch
226     // THROWEND-NEXT:   invoke void @__cxa_end_catch()
227     // NOTHROWEND-NEXT: call void @__cxa_end_catch() [[NUW]]
228     }
229 
230     // THROWEND:       call void @_ZN6test101AD1Ev(
231     // NOTHROWEND-NOT: call void @_ZN6test101AD1Ev(
232   }
233 }
234 
235 // __cxa_begin_catch returns pointers by value, even when catching by reference
236 namespace test11 {
237   void opaque();
238 
239   // CHECK-LABEL: define{{.*}} void @_ZN6test113fooEv()
foo()240   void foo() {
241     try {
242       // CHECK:      invoke void @_ZN6test116opaqueEv()
243       opaque();
244     } catch (int**&p) {
245       // CHECK:      [[EXN:%.*]] = load ptr, ptr
246       // CHECK-NEXT: call ptr @__cxa_begin_catch(ptr [[EXN]]) [[NUW]]
247       // CHECK-NEXT: [[ADJ1:%.*]] = getelementptr i8, ptr [[EXN]], i32 32
248       // CHECK-NEXT: store ptr [[ADJ1]], ptr [[P:%.*]]
249       // CHECK-NEXT: call void @__cxa_end_catch() [[NUW]]
250     }
251   }
252 
253   struct A {};
254 
255   // CHECK-LABEL: define{{.*}} void @_ZN6test113barEv()
bar()256   void bar() {
257     try {
258       // CHECK:      [[EXNSLOT:%.*]] = alloca ptr
259       // CHECK-NEXT: [[SELECTORSLOT:%.*]] = alloca i32
260       // CHECK-NEXT: [[P:%.*]] = alloca ptr,
261       // CHECK-NEXT: [[TMP:%.*]] = alloca ptr
262       // CHECK-NEXT: invoke void @_ZN6test116opaqueEv()
263       opaque();
264     } catch (A*&p) {
265       // CHECK:      [[EXN:%.*]] = load ptr, ptr [[EXNSLOT]]
266       // CHECK-NEXT: [[ADJ1:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) [[NUW]]
267       // CHECK-NEXT: store ptr [[ADJ1]], ptr [[TMP]]
268       // CHECK-NEXT: store ptr [[TMP]], ptr [[P]]
269       // CHECK-NEXT: call void @__cxa_end_catch() [[NUW]]
270     }
271   }
272 }
273 
274 // PR7686
275 namespace test12 {
276   struct A { ~A() noexcept(false); };
277   bool opaque(const A&);
278 
279   // CHECK-LABEL: define{{.*}} void @_ZN6test124testEv()
test()280   void test() {
281     // CHECK: [[X:%.*]] = alloca [[A:%.*]],
282     // CHECK: [[EHCLEANUPDEST:%.*]] = alloca i32
283     // CHECK: [[Y:%.*]] = alloca [[A:%.*]]
284     // CHECK: [[Z:%.*]] = alloca [[A]]
285     // CHECK: [[CLEANUPDEST:%.*]] = alloca i32
286 
287     A x;
288     // CHECK: invoke noundef zeroext i1 @_ZN6test126opaqueERKNS_1AE(
289     if (opaque(x)) {
290       A y;
291       A z;
292 
293       // CHECK: invoke void @_ZN6test121AD1Ev(ptr {{[^,]*}} [[Z]])
294       // CHECK: invoke void @_ZN6test121AD1Ev(ptr {{[^,]*}} [[Y]])
295       // CHECK-NOT: switch
296       goto success;
297     }
298 
299   success:
300     bool _ = true;
301 
302     // CHECK: call void @_ZN6test121AD1Ev(ptr {{[^,]*}} [[X]])
303     // CHECK-NEXT: ret void
304   }
305 }
306 
307 // Reduced from some TableGen code that was causing a self-host crash.
308 namespace test13 {
309   struct A { ~A(); };
310 
test0(int x)311   void test0(int x) {
312     try {
313       switch (x) {
314       case 0:
315         break;
316       case 1:{
317         A a;
318         break;
319       }
320       default:
321         return;
322       }
323       return;
324     } catch (int x) {
325     }
326     return;
327   }
328 
test1(int x)329   void test1(int x) {
330     A y;
331     try {
332       switch (x) {
333       default: break;
334       }
335     } catch (int x) {}
336   }
337 }
338 
339 namespace test14 {
340   struct A { ~A(); };
341   struct B { ~B(); };
342 
343   B b();
344   void opaque();
345 
foo()346   void foo() {
347     A a;
348     try {
349       B str = b();
350       opaque();
351     } catch (int x) {
352     }
353   }
354 }
355 
356 // JumpDests shouldn't get confused by scopes that aren't normal cleanups.
357 namespace test15 {
358   struct A { ~A(); };
359 
360   bool opaque(int);
361 
362   // CHECK-LABEL: define{{.*}} void @_ZN6test153fooEv()
foo()363   void foo() {
364     A a;
365 
366     try {
367       // CHECK:      [[X:%.*]] = alloca i32
368       // CHECK:      store i32 10, ptr [[X]]
369       // CHECK-NEXT: br label
370       //   -> while.cond
371       int x = 10;
372 
373       while (true) {
374         // CHECK:      load i32, ptr [[X]]
375         // CHECK-NEXT: [[COND:%.*]] = invoke noundef zeroext i1 @_ZN6test156opaqueEi
376         // CHECK:      br i1 [[COND]]
377         if (opaque(x))
378         // CHECK:      br label
379           break;
380 
381         // CHECK:      br label
382       }
383       // CHECK:      br label
384     } catch (int x) { }
385 
386     // CHECK: call void @_ZN6test151AD1Ev
387   }
388 }
389 
390 namespace test16 {
391   struct A { A(); ~A() noexcept(false); };
392   struct B { int x; B(const A &); ~B() noexcept(false); };
393   void foo();
394   bool cond();
395 
396   // CHECK-LABEL: define{{.*}} void @_ZN6test163barEv()
bar()397   void bar() {
398     // THROWEND:      [[EXN_SAVE:%.*]] = alloca ptr
399     // THROWEND-NEXT: [[EXN_ACTIVE:%.*]] = alloca i1
400     // THROWEND-NEXT: [[TEMP:%.*]] = alloca [[A:%.*]],
401     // THROWEND-NEXT: [[EXNSLOT:%.*]] = alloca ptr
402     // THROWEND-NEXT: [[SELECTORSLOT:%.*]] = alloca i32
403     // THROWEND-NEXT: [[TEMP_ACTIVE:%.*]] = alloca i1
404 
405 #ifndef NOTHROWEND
406     cond() ? throw B(A()) : foo();
407 #endif
408 
409     // THROWEND-NEXT: [[COND:%.*]] = call noundef zeroext i1 @_ZN6test164condEv()
410     // THROWEND-NEXT: store i1 false, ptr [[EXN_ACTIVE]]
411     // THROWEND-NEXT: store i1 false, ptr [[TEMP_ACTIVE]]
412     // THROWEND-NEXT: br i1 [[COND]],
413 
414     // THROWEND:      [[EXN:%.*]] = call ptr @__cxa_allocate_exception(i64 4)
415     // THROWEND-NEXT: store ptr [[EXN]], ptr [[EXN_SAVE]]
416     // THROWEND-NEXT: store i1 true, ptr [[EXN_ACTIVE]]
417     // THROWEND-NEXT: invoke void @_ZN6test161AC1Ev(ptr {{[^,]*}} [[TEMP]])
418     // THROWEND:      store i1 true, ptr [[TEMP_ACTIVE]]
419     // THROWEND-NEXT: invoke void @_ZN6test161BC1ERKNS_1AE(ptr {{[^,]*}} [[EXN]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[TEMP]])
420     // THROWEND:      store i1 false, ptr [[EXN_ACTIVE]]
421     // THROWEND-NEXT: invoke void @__cxa_throw(ptr [[EXN]],
422 
423     // THROWEND:      invoke void @_ZN6test163fooEv()
424     // THROWEND:      br label
425 
426     // THROWEND:      invoke void @_ZN6test161AD1Ev(ptr {{[^,]*}} [[TEMP]])
427     // THROWEND:      ret void
428 
429     // THROWEND:      [[T0:%.*]] = load i1, ptr [[EXN_ACTIVE]]
430     // THROWEND-NEXT: br i1 [[T0]]
431     // THROWEND:      [[T1:%.*]] = load ptr, ptr [[EXN_SAVE]]
432     // THROWEND-NEXT: call void @__cxa_free_exception(ptr [[T1]])
433     // THROWEND-NEXT: br label
434   }
435 }
436 
437 namespace test17 {
438 class BaseException {
439 private:
440   int a[4];
441 public:
BaseException()442   BaseException() {};
443 };
444 
445 class DerivedException: public BaseException {
446 };
447 
foo()448 int foo() {
449   throw DerivedException();
450   // The alignment passed to memset is 16 on Darwin.
451 
452   // CHECK: [[T0:%.*]] = call ptr @__cxa_allocate_exception(i64 16)
453   // UNALIGNED-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[T0]], i8 0, i64 16, i1 false)
454   // ALIGNED-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[T0]], i8 0, i64 16, i1 false)
455 }
456 }
457 
458 // CHECK: attributes [[NUW]] = { nounwind }
459 // CHECK: attributes [[NR]] = { noreturn }
460