Index: clang/lib/CodeGen/CGCall.h =================================================================== --- clang/lib/CodeGen/CGCall.h +++ clang/lib/CodeGen/CGCall.h @@ -283,6 +283,11 @@ llvm::Instruction *IsActiveIP; }; + struct EndLifetimeInfo { + llvm::Value *Addr; + llvm::Value *Size; + }; + void add(RValue rvalue, QualType type) { push_back(CallArg(rvalue, type)); } void addUncopiedAggregate(LValue LV, QualType type) { @@ -299,6 +304,9 @@ CleanupsToDeactivate.insert(CleanupsToDeactivate.end(), other.CleanupsToDeactivate.begin(), other.CleanupsToDeactivate.end()); + LifetimeCleanups.insert(LifetimeCleanups.end(), + other.LifetimeCleanups.begin(), + other.LifetimeCleanups.end()); assert(!(StackBase && other.StackBase) && "can't merge stackbases"); if (!StackBase) StackBase = other.StackBase; @@ -338,6 +346,14 @@ /// memory. bool isUsingInAlloca() const { return StackBase; } + void addLifetimeCleanup(EndLifetimeInfo Info) { + LifetimeCleanups.push_back(Info); + } + + ArrayRef getLifetimeCleanups() const { + return LifetimeCleanups; + } + private: SmallVector Writebacks; @@ -346,6 +362,10 @@ /// occurs. SmallVector CleanupsToDeactivate; + /// Lifetime information needed to call llvm.lifetime.end for any temporary + /// argument allocas. + SmallVector LifetimeCleanups; + /// The stacksave call. It dominates all of the argument evaluation. llvm::CallInst *StackBase; }; Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -3684,7 +3684,17 @@ return; } - args.add(EmitAnyExprToTemp(E), type); + AggValueSlot ArgSlot = AggValueSlot::ignored(); + if (hasAggregateEvaluationKind(E->getType())) { + ArgSlot = CreateAggTemp(E->getType(), "agg.tmp"); + + uint64_t size = + CGM.getDataLayout().getTypeAllocSize(ConvertTypeForMem(E->getType())); + if (auto *lifetimeSize = EmitLifetimeStart(size, ArgSlot.getPointer())) + args.addLifetimeCleanup({ArgSlot.getPointer(), lifetimeSize}); + } + + args.add(EmitAnyExpr(E, ArgSlot), type); } QualType CodeGenFunction::getVarArgType(const Expr *Arg) { @@ -4764,6 +4774,9 @@ for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall) LifetimeEnd.Emit(*this, /*Flags=*/{}); + for (auto < : CallArgs.getLifetimeCleanups()) + EmitLifetimeEnd(LT.Size, LT.Addr); + return Ret; } Index: clang/test/CodeGen/lifetime-call-temp.c =================================================================== --- /dev/null +++ clang/test/CodeGen/lifetime-call-temp.c @@ -0,0 +1,67 @@ +// RUN: %clang -cc1 -triple x86_64-apple-macos -O1 -disable-llvm-passes %s -S -emit-llvm -o - | FileCheck %s --implicit-check-not=llvm.lifetime +// RUN: %clang -cc1 -xc++ -std=c++17 -triple x86_64-apple-macos -O1 -disable-llvm-passes %s -S -emit-llvm -o - | FileCheck %s --implicit-check-not=llvm.lifetime --check-prefix=CHECK --check-prefix=CXX +// RUN: %clang -cc1 -xobjective-c -triple x86_64-apple-macos -O1 -disable-llvm-passes %s -S -emit-llvm -o - | FileCheck %s --implicit-check-not=llvm.lifetime --check-prefix=CHECK --check-prefix=OBJC + +typedef struct { int x[100]; } aggregate; + +#ifdef __cplusplus +extern "C" { +#endif + +void takes_aggregate(aggregate); +aggregate gives_aggregate(); + +// CHECK-LABEL: define void @t1 +void t1() { + takes_aggregate(gives_aggregate()); + + // CHECK: [[AGGTMP:%.*]] = alloca %struct.aggregate, align 8 + // CHECK: [[CAST:%.*]] = bitcast %struct.aggregate* [[AGGTMP]] to i8* + // CHECK: call void @llvm.lifetime.start.p0i8(i64 400, i8* [[CAST]]) + // CHECK: call void{{.*}} @gives_aggregate(%struct.aggregate* sret [[AGGTMP]]) + // CHECK: call void @takes_aggregate(%struct.aggregate* byval(%struct.aggregate) align 8 [[AGGTMP]]) + // CHECK: [[CAST:%.*]] = bitcast %struct.aggregate* [[AGGTMP]] to i8* + // CHECK: call void @llvm.lifetime.end.p0i8(i64 400, i8* [[CAST]]) +} + +// CHECK: declare {{.*}}llvm.lifetime.start +// CHECK: declare {{.*}}llvm.lifetime.end + +#ifdef __cplusplus +// CXX: define void @t2 +void t2() { + struct S { + S(aggregate) {} + }; + S{gives_aggregate()}; + + // CXX: [[AGG:%.*]] = alloca %struct.aggregate + // CXX: call void @llvm.lifetime.start.p0i8(i64 400, i8* + // CXX: call void @gives_aggregate(%struct.aggregate* sret [[AGG]]) + // CXX: call void @_ZZ2t2EN1SC1E9aggregate(%struct.S* {{.*}}, %struct.aggregate* byval(%struct.aggregate) align 8 [[AGG]]) + // CXX: call void @llvm.lifetime.end.p0i8(i64 400, i8* +} +#endif + +#ifdef __OBJC__ + +@interface X +-m:(aggregate)x; +@end + +// OBJC: define void @t3 +void t3(X *x) { + [x m: gives_aggregate()]; + + // OBJC: [[AGG:%.*]] = alloca %struct.aggregate + // OBJC: call void @llvm.lifetime.start.p0i8(i64 400, i8* + // OBJC: call void{{.*}} @gives_aggregate(%struct.aggregate* sret [[AGGTMP]]) + // OBJC: call {{.*}}@objc_msgSend + // OBJC: call void @llvm.lifetime.end.p0i8(i64 400, i8* +} + +#endif + +#ifdef __cplusplus +} +#endif Index: clang/test/CodeGenCXX/stack-reuse-miscompile.cpp =================================================================== --- clang/test/CodeGenCXX/stack-reuse-miscompile.cpp +++ clang/test/CodeGenCXX/stack-reuse-miscompile.cpp @@ -26,6 +26,8 @@ // CHECK: [[T2:%.*]] = alloca %class.T, align 4 // CHECK: [[T3:%.*]] = alloca %class.T, align 4 // +// CHECK: [[AGG:%.*]] = alloca %class.S, align 4 +// // FIXME: We could defer starting the lifetime of the return object of concat // until the call. // CHECK: [[T1i8:%.*]] = bitcast %class.T* [[T1]] to i8* @@ -37,8 +39,15 @@ // // CHECK: [[T3i8:%.*]] = bitcast %class.T* [[T3]] to i8* // CHECK: call void @llvm.lifetime.start.p0i8(i64 16, i8* [[T3i8]]) +// +// CHECK: [[AGGi8:%.*]] = bitcast %class.S* [[AGG]] to i8* +// CHECK: call void @llvm.lifetime.start.p0i8(i64 8, i8* [[AGGi8]]) +// // CHECK: [[T5:%.*]] = call %class.T* @_ZN1TC1E1S(%class.T* [[T3]], [2 x i32] %{{.*}}) // +// CHECK: [[AGGi8:%.*]] = bitcast %class.S* {{.*}} to i8* +// CHECK: call void @llvm.lifetime.end.p0i8(i64 8, i8* [[AGGi8]]) +// // CHECK: call void @_ZNK1T6concatERKS_(%class.T* sret [[T1]], %class.T* [[T2]], %class.T* dereferenceable(16) [[T3]]) // CHECK: [[T6:%.*]] = call i8* @_ZNK1T3strEv(%class.T* [[T1]]) //