[CodeGen] Reuse stack space from unused function results

Summary:
Space on stack allocated for unused structures returned by functions was unused
even when it's lifetime didn't intersect with lifetime of any other objects that
could use the same space.

The test added also checks for named and auto objects.  It seems to make sense
to have this all in one place.

Reviewers: aadg, rsmith, rjmccall, rnk

Reviewed By: rnk

Subscribers: asl, cfe-commits

Differential Revision: http://reviews.llvm.org/D9743

llvm-svn: 237385
This commit is contained in:
Sergey Dmitrouk 2015-05-14 19:58:03 +00:00
parent 38b54cb67b
commit 3e96fc08da
2 changed files with 145 additions and 3 deletions

View File

@ -3077,10 +3077,18 @@ 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.
llvm::Value *SRetPtr = nullptr;
size_t UnusedReturnSize = 0;
if (RetAI.isIndirect() || RetAI.isInAlloca()) {
SRetPtr = ReturnValue.getValue();
if (!SRetPtr)
if (!SRetPtr) {
SRetPtr = CreateMemTemp(RetTy);
if (HaveInsertPoint()) {
uint64_t size =
CGM.getDataLayout().getTypeAllocSize(ConvertTypeForMem(RetTy));
if (EmitLifetimeStart(size, SRetPtr))
UnusedReturnSize = size;
}
}
if (IRFunctionArgs.hasSRetArg()) {
IRCallArgs[IRFunctionArgs.getSRetArgNo()] = SRetPtr;
} else {
@ -3412,6 +3420,10 @@ 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);
Builder.CreateUnreachable();
Builder.ClearInsertionPoint();
@ -3440,8 +3452,13 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
RValue Ret = [&] {
switch (RetAI.getKind()) {
case ABIArgInfo::InAlloca:
case ABIArgInfo::Indirect:
return convertTempToRValue(SRetPtr, RetTy, SourceLocation());
case ABIArgInfo::Indirect: {
RValue ret = convertTempToRValue(SRetPtr, RetTy, SourceLocation());
if (UnusedReturnSize)
EmitLifetimeEnd(llvm::ConstantInt::get(Int64Ty, UnusedReturnSize),
SRetPtr);
return ret;
}
case ABIArgInfo::Ignore:
// If we are ignoring an argument that had a result, make sure to

View File

@ -0,0 +1,125 @@
// RUN: %clang -target armv7l-unknown-linux-gnueabihf -S %s -o - -emit-llvm -O1 -disable-llvm-optzns | FileCheck %s
// Stack should be reused when possible, no need to allocate two separate slots
// if they have disjoint lifetime.
// Sizes of objects are related to previously existed threshold of 32. In case
// of S_large stack size is rounded to 40 bytes.
// 32B
struct S_small {
int a[8];
};
// 36B
struct S_large {
int a[9];
};
extern S_small foo_small();
extern S_large foo_large();
extern void bar_small(S_small*);
extern void bar_large(S_large*);
// Prevent mangling of function names.
extern "C" {
void small_rvoed_unnamed_temporary_object() {
// CHECK-LABEL: define void @small_rvoed_unnamed_temporary_object
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_smallv
// CHECK: call void @llvm.lifetime.end
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_smallv
// CHECK: call void @llvm.lifetime.end
foo_small();
foo_small();
}
void large_rvoed_unnamed_temporary_object() {
// CHECK-LABEL: define void @large_rvoed_unnamed_temporary_object
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_largev
// CHECK: call void @llvm.lifetime.end
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_largev
// CHECK: call void @llvm.lifetime.end
foo_large();
foo_large();
}
void small_rvoed_named_temporary_object() {
// CHECK-LABEL: define void @small_rvoed_named_temporary_object
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_smallv
// CHECK: call void @llvm.lifetime.end
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_smallv
// CHECK: call void @llvm.lifetime.end
{
S_small s = foo_small();
}
{
S_small s = foo_small();
}
}
void large_rvoed_named_temporary_object() {
// CHECK-LABEL: define void @large_rvoed_named_temporary_object
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_largev
// CHECK: call void @llvm.lifetime.end
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9foo_largev
// CHECK: call void @llvm.lifetime.end
{
S_large s = foo_large();
}
{
S_large s = foo_large();
}
}
void small_auto_object() {
// CHECK-LABEL: define void @small_auto_object
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9bar_smallP7S_small
// CHECK: call void @llvm.lifetime.end
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9bar_smallP7S_small
// CHECK: call void @llvm.lifetime.end
{
S_small s;
bar_small(&s);
}
{
S_small s;
bar_small(&s);
}
}
void large_auto_object() {
// CHECK-LABEL: define void @large_auto_object
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9bar_largeP7S_large
// CHECK: call void @llvm.lifetime.end
// CHECK: call void @llvm.lifetime.start
// CHECK: call void @_Z9bar_largeP7S_large
// CHECK: call void @llvm.lifetime.end
{
S_large s;
bar_large(&s);
}
{
S_large s;
bar_large(&s);
}
}
}