diff options
Diffstat (limited to 'test/CodeGenCXX/stack-reuse-exceptions.cpp')
-rw-r--r-- | test/CodeGenCXX/stack-reuse-exceptions.cpp | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/test/CodeGenCXX/stack-reuse-exceptions.cpp b/test/CodeGenCXX/stack-reuse-exceptions.cpp new file mode 100644 index 000000000000..de870c530504 --- /dev/null +++ b/test/CodeGenCXX/stack-reuse-exceptions.cpp @@ -0,0 +1,189 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -o - -emit-llvm -O1 \ +// RUN: -fexceptions -fcxx-exceptions | FileCheck %s +// +// We should emit lifetime.ends for these temporaries in both the 'exception' +// and 'normal' paths in functions. +// +// -O1 is necessary to make lifetime markers appear. + +struct Large { + int cs[32]; +}; + +Large getLarge(); + +// Used to ensure we emit invokes. +struct NontrivialDtor { + int i; + ~NontrivialDtor(); +}; + +// CHECK-LABEL: define void @_Z33cleanupsAreEmittedWithoutTryCatchv +void cleanupsAreEmittedWithoutTryCatch() { +// CHECK: %[[CLEAN:[^ ]+]] = bitcast %struct.NontrivialDtor* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[CLEAN]]) +// CHECK: %[[T1:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[CONT:[^ ]+]] unwind label %[[LPAD:[^ ]+]] +// +// CHECK: [[CONT]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK: %[[T2:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[CONT2:[^ ]+]] unwind label %[[LPAD2:.+]] +// +// CHECK: [[CONT2]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[CLEAN]]) +// CHECK: ret void +// +// CHECK: [[LPAD]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK: br label %[[EHCLEANUP:.+]] +// +// CHECK: [[LPAD2]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK: br label %[[EHCLEANUP]] +// +// CHECK: [[EHCLEANUP]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[CLEAN]]) + + NontrivialDtor clean; + + getLarge(); + getLarge(); +} + +// CHECK-LABEL: define void @_Z30cleanupsAreEmittedWithTryCatchv +void cleanupsAreEmittedWithTryCatch() { +// CHECK: %[[CLEAN:[^ ]+]] = bitcast %struct.NontrivialDtor* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[CLEAN]]) +// CHECK: %[[T1:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[CONT:[^ ]+]] unwind label %[[LPAD:[^ ]+]] +// +// CHECK: [[CONT]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK: %[[T2:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[CONT2:[^ ]+]] unwind label %[[LPAD2:.+]] +// +// CHECK: [[CONT2]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK: br label %[[TRY_CONT:.+]] +// +// CHECK: [[LPAD]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK: br label %[[CATCH:.+]] +// +// CHECK: [[LPAD2]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK: br label %[[CATCH]] +// +// CHECK: [[CATCH]]: +// CHECK-NOT: call void @llvm.lifetime +// CHECK: invoke void +// CHECK-NEXT: to label %[[TRY_CONT]] unwind label %[[OUTER_LPAD:.+]] +// +// CHECK: [[TRY_CONT]]: +// CHECK: %[[T_OUTER:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T_OUTER]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[OUTER_CONT:[^ ]+]] unwind label %[[OUTER_LPAD2:.+]] +// +// CHECK: [[OUTER_CONT]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T_OUTER]]) +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[CLEAN]]) +// CHECK: ret void +// +// CHECK: [[OUTER_LPAD]]: +// CHECK-NOT: call void @llvm.lifetime +// CHECK: br label %[[EHCLEANUP:.+]] +// +// CHECK: [[OUTER_LPAD2]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T_OUTER]]) +// CHECK: br label %[[EHCLEANUP]] +// +// CHECK: [[EHCLEANUP]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[CLEAN]]) + + NontrivialDtor clean; + + try { + getLarge(); + getLarge(); + } catch (...) {} + + getLarge(); +} + +// CHECK-LABEL: define void @_Z39cleanupInTryHappensBeforeCleanupInCatchv +void cleanupInTryHappensBeforeCleanupInCatch() { +// CHECK: %[[T1:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[CONT:[^ ]+]] unwind label %[[LPAD:[^ ]+]] +// +// CHECK: [[CONT]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK: br label %[[TRY_CONT]] +// +// CHECK: [[LPAD]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T1]]) +// CHECK: br i1 {{[^,]+}}, label %[[CATCH_INT_MATCH:[^,]+]], label %[[CATCH_ALL:.+]] +// +// CHECK: [[CATCH_INT_MATCH]]: +// CHECK: %[[T2:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[CATCH_INT_CONT:[^ ]+]] unwind label %[[CATCH_INT_LPAD:[^ ]+]] +// +// CHECK: [[CATCH_INT_CONT]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK: br label %[[TRY_CONT]] +// +// CHECK: [[TRY_CONT]]: +// CHECK: ret void +// +// CHECK: [[CATCH_ALL]]: +// CHECK: %[[T3:[^ ]+]] = bitcast %struct.Large* %{{[^ ]+}} to i8* +// CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* nonnull %[[T3]]) +// CHECK-NEXT: invoke void @_Z8getLargev +// CHECK-NEXT: to label %[[CATCH_ALL_CONT:[^ ]+]] unwind label %[[CATCH_ALL_LPAD:[^ ]+]] +// +// CHECK: [[CATCH_ALL_CONT]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T3]]) +// CHECK: br label %[[TRY_CONT]] +// +// CHECK: [[CATCH_ALL_LPAD]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T3]]) +// +// CHECK: [[CATCH_INT_LPAD]]: +// CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* nonnull %[[T2]]) +// CHECK-NOT: call void @llvm.lifetime + + try { + getLarge(); + } catch (const int &) { + getLarge(); + } catch (...) { + getLarge(); + } +} + +// FIXME: We don't currently emit lifetime markers for aggregate by-value +// temporaries (e.g. given a function `Large combine(Large, Large);` +// combine(getLarge(), getLarge()) "leaks" two `Large`s). We probably should. We +// also don't emit markers for things like: +// +// { +// Large L = getLarge(); +// combine(L, L); +// } +// +// Though this arguably isn't as bad, since we pass a pointer to `L` as one of +// the two args. |