diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 84d3229863fe9..779c372613488 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4962,22 +4962,28 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, AggValueSlot ArgSlot = AggValueSlot::ignored(); // For arguments with aggregate type, create an alloca to store - // the value. If the argument's type has a destructor, that destructor + // the value. If the argument's type has a destructor, that destructor // will run at the end of the full-expression; emit matching lifetime - // markers. - // - // FIXME: For types which don't have a destructor, consider using a - // narrower lifetime bound. + // markers. For types which don't have a destructor, we use a narrower + // lifetime bound. if (hasAggregateEvaluationKind(E->getType())) { RawAddress ArgSlotAlloca = Address::invalid(); ArgSlot = CreateAggTemp(E->getType(), "agg.tmp", &ArgSlotAlloca); - // Emit a lifetime start/end for this temporary at the end of the full - // expression. + // Emit a lifetime start/end for this temporary. If the type has a + // destructor, then we need to keep it alive for the full expression. if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries && - EmitLifetimeStart(ArgSlotAlloca.getPointer())) - pushFullExprCleanup(NormalEHLifetimeMarker, - ArgSlotAlloca); + EmitLifetimeStart(ArgSlotAlloca.getPointer())) { + if (E->getType().isDestructedType()) { + pushFullExprCleanup(NormalEHLifetimeMarker, + ArgSlotAlloca); + } else { + args.addLifetimeCleanup({ArgSlotAlloca.getPointer()}); + if (getInvokeDest()) + pushFullExprCleanup(CleanupKind::EHCleanup, + ArgSlotAlloca); + } + } } args.add(EmitAnyExpr(E, ArgSlot), type); @@ -6307,6 +6313,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall) LifetimeEnd.Emit(*this, /*Flags=*/{}); + // Add lifetime end markers for any temporary aggregates. Under + // NoLifetimeMarkersForTemporaries LifetimeCleanups will be empty, so this is + // still correct. + for (const CallArgList::EndLifetimeInfo < : CallArgs.getLifetimeCleanups()) + EmitLifetimeEnd(LT.Addr); + if (!ReturnValue.isExternallyDestructed() && RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct) pushDestroy(QualType::DK_nontrivial_c_struct, Ret.getAggregateAddress(), diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h index 1ef8a3f114573..aab4b64d6a4a8 100644 --- a/clang/lib/CodeGen/CGCall.h +++ b/clang/lib/CodeGen/CGCall.h @@ -299,6 +299,10 @@ class CallArgList : public SmallVector { llvm::Instruction *IsActiveIP; }; + struct EndLifetimeInfo { + llvm::Value *Addr; + }; + void add(RValue rvalue, QualType type) { push_back(CallArg(rvalue, type)); } void addUncopiedAggregate(LValue LV, QualType type) { @@ -312,6 +316,9 @@ class CallArgList : public SmallVector { llvm::append_range(*this, other); llvm::append_range(Writebacks, other.Writebacks); llvm::append_range(CleanupsToDeactivate, other.CleanupsToDeactivate); + LifetimeCleanups.insert(LifetimeCleanups.end(), + other.LifetimeCleanups.begin(), + other.LifetimeCleanups.end()); assert(!(StackBase && other.StackBase) && "can't merge stackbases"); if (!StackBase) StackBase = other.StackBase; @@ -352,6 +359,14 @@ class CallArgList : public SmallVector { /// memory. bool isUsingInAlloca() const { return StackBase; } + void addLifetimeCleanup(EndLifetimeInfo Info) { + LifetimeCleanups.push_back(Info); + } + + ArrayRef getLifetimeCleanups() const { + return LifetimeCleanups; + } + // Support reversing writebacks for MSVC ABI. void reverseWritebacks() { std::reverse(Writebacks.begin(), Writebacks.end()); @@ -365,6 +380,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 = nullptr; }; diff --git a/clang/test/CodeGen/lifetime-invoke-c.c b/clang/test/CodeGen/lifetime-invoke-c.c index f3573f4b2ec17..5d38ad9e0c408 100644 --- a/clang/test/CodeGen/lifetime-invoke-c.c +++ b/clang/test/CodeGen/lifetime-invoke-c.c @@ -22,16 +22,14 @@ void test() { // CHECK-NEXT: to label %[[CONT1:.*]] unwind label %[[LPAD1:.*]] // CHECK: [[CONT1]]: - // CHECK-NOT: call void @llvm.lifetime.end.p0(ptr %[[AGG1]]) - - // CHECK: call void @llvm.lifetime.start.p0(ptr %[[AGG2]]) + // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[AGG1]]) + // CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[AGG2]]) // CHECK: invoke void @gen(ptr{{.*}} sret(%struct.Trivial){{.*}} %[[AGG2]]) // CHECK: invoke void @func(ptr{{.*}} %[[AGG2]]) // CHECK-NEXT: to label %[[CONT2:.*]] unwind label %[[LPAD2:.*]] // CHECK: [[CONT2]]: - // CHECK-DAG: call void @llvm.lifetime.end.p0(ptr %[[AGG2]]) - // CHECK-DAG: call void @llvm.lifetime.end.p0(ptr %[[AGG1]]) + // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %[[AGG2]]) // CHECK: [[LPAD1]]: // CHECK: landingpad diff --git a/clang/test/CodeGen/stack-usage-lifetimes.c b/clang/test/CodeGen/stack-usage-lifetimes.c new file mode 100644 index 0000000000000..189bc9c229ca4 --- /dev/null +++ b/clang/test/CodeGen/stack-usage-lifetimes.c @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog %s -verify=x86-precise +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog -sloppy-temporary-lifetimes %s -verify=x86-sloppy + +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog %s -verify=aarch64-precise +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog -sloppy-temporary-lifetimes %s -verify=aarch64-sloppy + +// RUN: %clang_cc1 -triple riscv64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog %s -verify=riscv-precise +// RUN: %clang_cc1 -triple riscv64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog -sloppy-temporary-lifetimes %s -verify=riscv-sloppy + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog %s -verify=x86-precise -xc++ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog -sloppy-temporary-lifetimes %s -verify=x86-sloppy -xc++ + +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog %s -verify=aarch64-precise -xc++ +// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog -sloppy-temporary-lifetimes %s -verify=aarch64-sloppy -xc++ + +// RUN: %clang_cc1 -triple riscv64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog %s -verify=riscv-precise -xc++ +// RUN: %clang_cc1 -triple riscv64-unknown-linux-gnu -O1 -emit-codegen-only -Rpass-analysis=prologepilog -sloppy-temporary-lifetimes %s -verify=riscv-sloppy -xc++ + + +typedef struct { char x[32]; } A; +typedef struct { char *w, *x, *y, *z; } B; + +void useA(A); +void useB(B); +A genA(void); +B genB(void); + +void t1(int c) { + // x86-precise-remark@-1 {{40 stack bytes}} + // x86-sloppy-remark@-2 {{72 stack bytes}} + // aarch64-precise-remark@-3 {{48 stack bytes}} + // aarch64-sloppy-remark@-4 {{80 stack bytes}} + // riscv-precise-remark@-5 {{48 stack bytes}} + // riscv-sloppy-remark@-6 {{80 stack bytes}} + + if (c) + useA(genA()); + else + useA(genA()); +} + +void t2(void) { + // x86-precise-remark@-1 {{40 stack bytes}} + // x86-sloppy-remark@-2 {{72 stack bytes}} + // aarch64-precise-remark@-3 {{48 stack bytes}} + // aarch64-sloppy-remark@-4 {{80 stack bytes}} + // riscv-precise-remark@-5 {{48 stack bytes}} + // riscv-sloppy-remark@-6 {{80 stack bytes}} + + useA(genA()); + useA(genA()); +} + +void t3(void) { + // x86-precise-remark@-1 {{40 stack bytes}} + // x86-sloppy-remark@-2 {{72 stack bytes}} + // aarch64-precise-remark@-3 {{48 stack bytes}} + // aarch64-sloppy-remark@-4 {{80 stack bytes}} + // riscv-precise-remark@-5 {{48 stack bytes}} + // riscv-sloppy-remark@-6 {{80 stack bytes}} + + useB(genB()); + useB(genB()); +} + +#ifdef __cplusplus +struct C { + char x[24]; + char *ptr; + ~C() {}; +}; + +void useC(C); +C genC(void); + +// This case works in C++, since its AST is structured slightly differently +// than it is in C (CompundStmt/ExprWithCleanup/CallExpr vs CompundStmt/CallExpr). +void t4() { + // x86-precise-remark@-1 {{40 stack bytes}} + // x86-sloppy-remark@-2 {{72 stack bytes}} + // aarch64-precise-remark@-3 {{48 stack bytes}} + // aarch64-sloppy-remark@-4 {{80 stack bytes}} + // riscv-precise-remark@-5 {{48 stack bytes}} + // riscv-sloppy-remark@-6 {{80 stack bytes}} + + useC(genC()); + useC(genC()); +} +#endif diff --git a/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp b/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp index 32af6b51fe9c8..adcc357cb2e45 100644 --- a/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp +++ b/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp @@ -15,23 +15,20 @@ void test() { // CHECK: call void @llvm.lifetime.start.p0(ptr{{.*}} %[[AGG1]]) // CHECK: invoke void @func_that_throws(ptr{{.*}} %[[AGG1]]) - // CHECK-NEXT: to label %[[CONT1:.*]] unwind label %[[LPAD1:.*]] - // CHECK: [[CONT1]]: + // CHECK: [[CONT1:.*]]: + // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr{{.*}} %[[AGG1]]) // CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr{{.*}} %[[AGG2]]) // CHECK: invoke void @func_that_throws(ptr{{.*}} %[[AGG2]]) - // CHECK-NEXT: to label %[[CONT2:.*]] unwind label %[[LPAD2:.*]] - // CHECK: [[CONT2]]: - // CHECK-DAG: call void @llvm.lifetime.end.p0(ptr{{.*}} %[[AGG2]]) - // CHECK-DAG: call void @llvm.lifetime.end.p0(ptr{{.*}} %[[AGG1]]) - // CHECK: br label %[[TRY_CONT:.*]] + // CHECK: [[CONT2:.*]]: + // CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr{{.*}} %[[AGG2]]) - // CHECK: [[LPAD1]]: + // CHECK: [[LPAD1:lpad.*]]: // CHECK: landingpad // CHECK: br label %[[EHCLEANUP:.*]] - // CHECK: [[LPAD2]]: + // CHECK: [[LPAD2:lpad.*]]: // CHECK: landingpad // CHECK: call void @llvm.lifetime.end.p0(ptr{{.*}} %[[AGG2]]) // CHECK: br label %[[EHCLEANUP]] diff --git a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp index 50c374d2710f4..4aef39119c94a 100644 --- a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp +++ b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp @@ -38,10 +38,10 @@ const char * f(S s) // CHECK: call void @llvm.lifetime.start.p0(ptr [[T3]]) // CHECK: call void @llvm.lifetime.start.p0(ptr [[AGG]]) // CHECK: [[T5:%.*]] = call noundef ptr @_ZN1TC1E1S(ptr {{[^,]*}} [[T3]], [2 x i32] %{{.*}}) +// CHECK: call void @llvm.lifetime.end.p0(ptr [[AGG]]) // // CHECK: call void @_ZNK1T6concatERKS_(ptr dead_on_unwind writable sret(%class.T) align 4 [[T1]], ptr {{[^,]*}} [[T2]], ptr noundef nonnull align 4 dereferenceable(16) [[T3]]) // CHECK: [[T6:%.*]] = call noundef ptr @_ZNK1T3strEv(ptr {{[^,]*}} [[T1]]) -// CHECK: call void @llvm.lifetime.end.p0(ptr [[AGG]]) // // CHECK: call void @llvm.lifetime.end.p0( // CHECK: call void @llvm.lifetime.end.p0(