[clang] fafc6e4 - [IRGen] Emit lifetime intrinsics around temporary aggregate argument allocas
Erik Pilkington via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 7 14:40:11 PST 2020
Author: Erik Pilkington
Date: 2020-02-07T14:39:31-08:00
New Revision: fafc6e4fdf3673dcf557d6c8ae0c0a4bb3184402
URL: https://github.com/llvm/llvm-project/commit/fafc6e4fdf3673dcf557d6c8ae0c0a4bb3184402
DIFF: https://github.com/llvm/llvm-project/commit/fafc6e4fdf3673dcf557d6c8ae0c0a4bb3184402.diff
LOG: [IRGen] Emit lifetime intrinsics around temporary aggregate argument allocas
These temporaries are only used in the callee, and their memory can be reused
after the call is complete.
rdar://58552124
Differential revision: https://reviews.llvm.org/D74094
Added:
clang/test/CodeGen/lifetime-call-temp.c
Modified:
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/CGCall.h
clang/test/CodeGenCXX/stack-reuse-miscompile.cpp
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index b55d5856d92d..3edcfb21ef34 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 28121af946f9..3c574a12953b 100644
--- a/clang/lib/CodeGen/CGCall.h
+++ b/clang/lib/CodeGen/CGCall.h
@@ -283,6 +283,11 @@ class CallArgList : public SmallVector<CallArg, 8> {
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<CallArg, 8> {
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<CallArg, 8> {
/// memory.
bool isUsingInAlloca() const { return StackBase; }
+ void addLifetimeCleanup(EndLifetimeInfo Info) {
+ LifetimeCleanups.push_back(Info);
+ }
+
+ ArrayRef<EndLifetimeInfo> getLifetimeCleanups() const {
+ return LifetimeCleanups;
+ }
+
private:
SmallVector<Writeback, 1> Writebacks;
@@ -346,6 +362,10 @@ class CallArgList : public SmallVector<CallArg, 8> {
/// occurs.
SmallVector<CallArgCleanup, 1> CleanupsToDeactivate;
+ /// Lifetime information needed to call llvm.lifetime.end for any temporary
+ /// argument allocas.
+ SmallVector<EndLifetimeInfo, 2> 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 000000000000..fa00980160c2
--- /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__
+
+ at interface X
+-m:(aggregate)x;
+ at 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 4e824d94f510..476a2019532a 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]])
//
More information about the cfe-commits
mailing list