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. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits