| // 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. |