diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index b55d5856d92d0..3edcfb21ef349 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -3689,7 +3689,22 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, return; } - args.add(EmitAnyExprToTemp(E), type); + AggValueSlot ArgSlot = AggValueSlot::ignored(); + if (hasAggregateEvaluationKind(E->getType())) { + ArgSlot = CreateAggTemp(E->getType(), "agg.tmp"); + + // Emit a lifetime start/end for this temporary. If the type has a + // destructor, then we need to keep it alive. FIXME: We should still be able + // to end the lifetime after the destructor returns. + if (!E->getType().isDestructedType()) { + 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) { @@ -4769,6 +4784,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall) LifetimeEnd.Emit(*this, /*Flags=*/{}); + for (auto < : CallArgs.getLifetimeCleanups()) + EmitLifetimeEnd(LT.Size, LT.Addr); + return Ret; } diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index 28121af946f9b..3c574a12953b3 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -283,6 +283,11 @@ class CallArgList : public SmallVector { 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 @@ class CallArgList : public SmallVector { 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 @@ class CallArgList : public SmallVector { /// 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 @@ class CallArgList : public SmallVector { /// 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; }; diff --git a/clang/test/CodeGen/lifetime-call-temp.c b/clang/test/CodeGen/lifetime-call-temp.c new file mode 100644 index 0000000000000..fa00980160c21 --- /dev/null +++ b/clang/test/CodeGen/lifetime-call-temp.c @@ -0,0 +1,83 @@ +// 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* +} + +struct Dtor { + ~Dtor(); +}; + +void takes_dtor(Dtor); +Dtor gives_dtor(); + +// CXX: define void @t3 +void t3() { + takes_dtor(gives_dtor()); + + // CXX-NOT @llvm.lifetime + // CXX: ret void +} + +#endif + +#ifdef __OBJC__ + +@interface X +-m:(aggregate)x; +@end + +// OBJC: define void @t4 +void t4(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 diff --git a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp index 4e824d94f510e..476a2019532af 100644 --- a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp +++ b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp @@ -26,6 +26,8 @@ const char * f(S s) // 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 @@ const char * f(S s) // // 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]]) //