https://github.com/ilovepi updated https://github.com/llvm/llvm-project/pull/170518
>From 448fa8e34242c407915a2f1b7221a6fc0b3ecf07 Mon Sep 17 00:00:00 2001 From: Paul Kirth <[email protected]> Date: Tue, 2 Dec 2025 15:14:32 -0800 Subject: [PATCH 1/2] [clang] Use tighter lifetime bounds for C temporary arguments In C, consecutive statements in the same scope are under CompoundStmt/CallExpr, while in C++ they typically fall under CompoundStmt/ExprWithCleanup. This leads to different behavior with respect to where pushFullExprCleanUp inserts the lifetime end markers (e.g., at the end of scope). For these cases, we can track and insert the lifetime end markers right after the call completes. Allowing the stack space to be reused immediately. This partially addresses #109204 and #43598 for improving stack usage. --- clang/lib/CodeGen/CGCall.cpp | 18 ++++++++++++++---- clang/lib/CodeGen/CGCall.h | 19 +++++++++++++++++++ clang/test/CodeGen/stack-usage-lifetimes.c | 12 ++++++------ .../CodeGenCXX/stack-reuse-miscompile.cpp | 2 +- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index bb81ddfc20fb4..00027b46ce928 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4960,11 +4960,16 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, 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<CallLifetimeEnd>(NormalAndEHCleanup, ArgSlotAlloca); + EmitLifetimeStart(ArgSlotAlloca.getPointer())) { + if (E->getType().isDestructedType()) { + pushFullExprCleanup<CallLifetimeEnd>(NormalAndEHCleanup, ArgSlotAlloca); + } else { + args.addLifetimeCleanup({ArgSlotAlloca.getPointer()}); + } + } } args.add(EmitAnyExpr(E, ArgSlot), type); @@ -6294,6 +6299,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall) LifetimeEnd.Emit(*this, /*Flags=*/{}); + if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries) + 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<CallArg, 8> { 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<CallArg, 8> { 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<CallArg, 8> { /// memory. bool isUsingInAlloca() const { return StackBase; } + void addLifetimeCleanup(EndLifetimeInfo Info) { + LifetimeCleanups.push_back(Info); + } + + ArrayRef<EndLifetimeInfo> 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<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 = nullptr; }; diff --git a/clang/test/CodeGen/stack-usage-lifetimes.c b/clang/test/CodeGen/stack-usage-lifetimes.c index 3787a29e4ce7d..189bc9c229ca4 100644 --- a/clang/test/CodeGen/stack-usage-lifetimes.c +++ b/clang/test/CodeGen/stack-usage-lifetimes.c @@ -40,11 +40,11 @@ void t1(int c) { } void t2(void) { - // x86-precise-remark@-1 {{72 stack bytes}} + // x86-precise-remark@-1 {{40 stack bytes}} // x86-sloppy-remark@-2 {{72 stack bytes}} - // aarch64-precise-remark@-3 {{80 stack bytes}} + // aarch64-precise-remark@-3 {{48 stack bytes}} // aarch64-sloppy-remark@-4 {{80 stack bytes}} - // riscv-precise-remark@-5 {{80 stack bytes}} + // riscv-precise-remark@-5 {{48 stack bytes}} // riscv-sloppy-remark@-6 {{80 stack bytes}} useA(genA()); @@ -52,11 +52,11 @@ void t2(void) { } void t3(void) { - // x86-precise-remark@-1 {{72 stack bytes}} + // x86-precise-remark@-1 {{40 stack bytes}} // x86-sloppy-remark@-2 {{72 stack bytes}} - // aarch64-precise-remark@-3 {{80 stack bytes}} + // aarch64-precise-remark@-3 {{48 stack bytes}} // aarch64-sloppy-remark@-4 {{80 stack bytes}} - // riscv-precise-remark@-5 {{80 stack bytes}} + // riscv-precise-remark@-5 {{48 stack bytes}} // riscv-sloppy-remark@-6 {{80 stack bytes}} useB(genB()); 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( >From 64b91cbc8d1f5b0d4f3e7d9c00613d28d1777845 Mon Sep 17 00:00:00 2001 From: Paul Kirth <[email protected]> Date: Wed, 3 Dec 2025 14:18:34 -0800 Subject: [PATCH 2/2] Update comment to be more accurate --- clang/lib/CodeGen/CGCall.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 00027b46ce928..aa3119267f778 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4950,12 +4950,10 @@ 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); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
