From 003be7cbf4a4c95f1cc005ec96cbd1fbf9724391 Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Thu, 8 Mar 2018 05:32:30 +0000 Subject: [CodeGen] Emit lifetime.ends in both EH and non-EH blocks Before this, we'd only emit lifetime.ends for these temps in non-exceptional paths. This potentially made our stack larger than it needed to be for any code that follows an EH cleanup. e.g. in ``` struct Foo { char cs[32]; }; void escape(void *); struct Bar { ~Bar() { char cs[64]; escape(cs); } }; Foo getFoo(); void baz() { Bar b; getFoo(); } ``` baz() would require 96 bytes of stack, since the temporary from getFoo() only had a lifetime.end on the non-exceptional path. This also makes us keep hold of the Value* returned by EmitLifetimeStart, so we don't have to remake it later. llvm-svn: 326988 --- clang/test/CodeGenCXX/stack-reuse-exceptions.cpp | 189 +++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 clang/test/CodeGenCXX/stack-reuse-exceptions.cpp (limited to 'clang/test/CodeGenCXX/stack-reuse-exceptions.cpp') diff --git a/clang/test/CodeGenCXX/stack-reuse-exceptions.cpp b/clang/test/CodeGenCXX/stack-reuse-exceptions.cpp new file mode 100644 index 00000000000..de870c53050 --- /dev/null +++ b/clang/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. -- cgit v1.2.3