190 lines
6.7 KiB
C++
190 lines
6.7 KiB
C++
|
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -o - -emit-llvm -O1 \
|
||
|
// RUN: -fexceptions -fcxx-exceptions -mllvm -simplifycfg-sink-common=false | 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.
|