r326988 - [CodeGen] Emit lifetime.ends in both EH and non-EH blocks
George Burgess IV via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 7 21:32:30 PST 2018
Author: gbiv
Date: Wed Mar 7 21:32:30 2018
New Revision: 326988
URL: http://llvm.org/viewvc/llvm-project?rev=326988&view=rev
Log:
[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.
Added:
cfe/trunk/test/CodeGenCXX/stack-reuse-exceptions.cpp
Modified:
cfe/trunk/lib/CodeGen/CGCall.cpp
Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=326988&r1=326987&r2=326988&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Wed Mar 7 21:32:30 2018
@@ -3790,7 +3790,7 @@ RValue CodeGenFunction::EmitCall(const C
// If the call returns a temporary with struct return, create a temporary
// alloca to hold the result, unless one is given to us.
Address SRetPtr = Address::invalid();
- size_t UnusedReturnSize = 0;
+ llvm::Value *UnusedReturnSizePtr = nullptr;
if (RetAI.isIndirect() || RetAI.isInAlloca() || RetAI.isCoerceAndExpand()) {
if (!ReturnValue.isNull()) {
SRetPtr = ReturnValue.getValue();
@@ -3799,8 +3799,7 @@ RValue CodeGenFunction::EmitCall(const C
if (HaveInsertPoint() && ReturnValue.isUnused()) {
uint64_t size =
CGM.getDataLayout().getTypeAllocSize(ConvertTypeForMem(RetTy));
- if (EmitLifetimeStart(size, SRetPtr.getPointer()))
- UnusedReturnSize = size;
+ UnusedReturnSizePtr = EmitLifetimeStart(size, SRetPtr.getPointer());
}
}
if (IRFunctionArgs.hasSRetArg()) {
@@ -4231,6 +4230,15 @@ RValue CodeGenFunction::EmitCall(const C
CannotThrow = Attrs.hasAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::NoUnwind);
}
+
+ // If we made a temporary, be sure to clean up after ourselves. Note that we
+ // can't depend on being inside of an ExprWithCleanups, so we need to manually
+ // pop this cleanup later on. Being eager about this is OK, since this
+ // temporary is 'invisible' outside of the callee.
+ if (UnusedReturnSizePtr)
+ pushFullExprCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker, SRetPtr,
+ UnusedReturnSizePtr);
+
llvm::BasicBlock *InvokeDest = CannotThrow ? nullptr : getInvokeDest();
SmallVector<llvm::OperandBundleDef, 1> BundleList =
@@ -4284,9 +4292,8 @@ RValue CodeGenFunction::EmitCall(const C
// insertion point; this allows the rest of IRGen to discard
// unreachable code.
if (CS.doesNotReturn()) {
- if (UnusedReturnSize)
- EmitLifetimeEnd(llvm::ConstantInt::get(Int64Ty, UnusedReturnSize),
- SRetPtr.getPointer());
+ if (UnusedReturnSizePtr)
+ PopCleanupBlock();
// Strip away the noreturn attribute to better diagnose unreachable UB.
if (SanOpts.has(SanitizerKind::Unreachable)) {
@@ -4355,9 +4362,8 @@ RValue CodeGenFunction::EmitCall(const C
case ABIArgInfo::InAlloca:
case ABIArgInfo::Indirect: {
RValue ret = convertTempToRValue(SRetPtr, RetTy, SourceLocation());
- if (UnusedReturnSize)
- EmitLifetimeEnd(llvm::ConstantInt::get(Int64Ty, UnusedReturnSize),
- SRetPtr.getPointer());
+ if (UnusedReturnSizePtr)
+ PopCleanupBlock();
return ret;
}
Added: cfe/trunk/test/CodeGenCXX/stack-reuse-exceptions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/stack-reuse-exceptions.cpp?rev=326988&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/stack-reuse-exceptions.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/stack-reuse-exceptions.cpp Wed Mar 7 21:32:30 2018
@@ -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.
More information about the cfe-commits
mailing list