forked from OSchip/llvm-project
[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
This commit is contained in:
parent
e465a84f85
commit
003be7cbf4
|
@ -3790,7 +3790,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
|
|||
// 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 CGFunctionInfo &CallInfo,
|
|||
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 CGFunctionInfo &CallInfo,
|
|||
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 CGFunctionInfo &CallInfo,
|
|||
// 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 CGFunctionInfo &CallInfo,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue