Skip to content

Commit f914b1f

Browse files
committed
[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.
1 parent 08189ad commit f914b1f

File tree

4 files changed

+40
-11
lines changed

4 files changed

+40
-11
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4960,11 +4960,16 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
49604960
RawAddress ArgSlotAlloca = Address::invalid();
49614961
ArgSlot = CreateAggTemp(E->getType(), "agg.tmp", &ArgSlotAlloca);
49624962

4963-
// Emit a lifetime start/end for this temporary at the end of the full
4964-
// expression.
4963+
// Emit a lifetime start/end for this temporary. If the type has a
4964+
// destructor, then we need to keep it alive for the full expression.
49654965
if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries &&
4966-
EmitLifetimeStart(ArgSlotAlloca.getPointer()))
4967-
pushFullExprCleanup<CallLifetimeEnd>(NormalAndEHCleanup, ArgSlotAlloca);
4966+
EmitLifetimeStart(ArgSlotAlloca.getPointer())) {
4967+
if (E->getType().isDestructedType()) {
4968+
pushFullExprCleanup<CallLifetimeEnd>(NormalAndEHCleanup, ArgSlotAlloca);
4969+
} else {
4970+
args.addLifetimeCleanup({ArgSlotAlloca.getPointer()});
4971+
}
4972+
}
49684973
}
49694974

49704975
args.add(EmitAnyExpr(E, ArgSlot), type);
@@ -6294,6 +6299,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
62946299
for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall)
62956300
LifetimeEnd.Emit(*this, /*Flags=*/{});
62966301

6302+
if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries)
6303+
for (const CallArgList::EndLifetimeInfo &LT :
6304+
CallArgs.getLifetimeCleanups())
6305+
EmitLifetimeEnd(LT.Addr);
6306+
62976307
if (!ReturnValue.isExternallyDestructed() &&
62986308
RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct)
62996309
pushDestroy(QualType::DK_nontrivial_c_struct, Ret.getAggregateAddress(),

clang/lib/CodeGen/CGCall.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,10 @@ class CallArgList : public SmallVector<CallArg, 8> {
299299
llvm::Instruction *IsActiveIP;
300300
};
301301

302+
struct EndLifetimeInfo {
303+
llvm::Value *Addr;
304+
};
305+
302306
void add(RValue rvalue, QualType type) { push_back(CallArg(rvalue, type)); }
303307

304308
void addUncopiedAggregate(LValue LV, QualType type) {
@@ -312,6 +316,9 @@ class CallArgList : public SmallVector<CallArg, 8> {
312316
llvm::append_range(*this, other);
313317
llvm::append_range(Writebacks, other.Writebacks);
314318
llvm::append_range(CleanupsToDeactivate, other.CleanupsToDeactivate);
319+
LifetimeCleanups.insert(LifetimeCleanups.end(),
320+
other.LifetimeCleanups.begin(),
321+
other.LifetimeCleanups.end());
315322
assert(!(StackBase && other.StackBase) && "can't merge stackbases");
316323
if (!StackBase)
317324
StackBase = other.StackBase;
@@ -352,6 +359,14 @@ class CallArgList : public SmallVector<CallArg, 8> {
352359
/// memory.
353360
bool isUsingInAlloca() const { return StackBase; }
354361

362+
void addLifetimeCleanup(EndLifetimeInfo Info) {
363+
LifetimeCleanups.push_back(Info);
364+
}
365+
366+
ArrayRef<EndLifetimeInfo> getLifetimeCleanups() const {
367+
return LifetimeCleanups;
368+
}
369+
355370
// Support reversing writebacks for MSVC ABI.
356371
void reverseWritebacks() {
357372
std::reverse(Writebacks.begin(), Writebacks.end());
@@ -365,6 +380,10 @@ class CallArgList : public SmallVector<CallArg, 8> {
365380
/// occurs.
366381
SmallVector<CallArgCleanup, 1> CleanupsToDeactivate;
367382

383+
/// Lifetime information needed to call llvm.lifetime.end for any temporary
384+
/// argument allocas.
385+
SmallVector<EndLifetimeInfo, 2> LifetimeCleanups;
386+
368387
/// The stacksave call. It dominates all of the argument evaluation.
369388
llvm::CallInst *StackBase = nullptr;
370389
};

clang/test/CodeGen/stack-usage-lifetimes.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,23 @@ void t1(int c) {
4040
}
4141

4242
void t2(void) {
43-
// x86-precise-remark@-1 {{72 stack bytes}}
43+
// x86-precise-remark@-1 {{40 stack bytes}}
4444
// x86-sloppy-remark@-2 {{72 stack bytes}}
45-
// aarch64-precise-remark@-3 {{80 stack bytes}}
45+
// aarch64-precise-remark@-3 {{48 stack bytes}}
4646
// aarch64-sloppy-remark@-4 {{80 stack bytes}}
47-
// riscv-precise-remark@-5 {{80 stack bytes}}
47+
// riscv-precise-remark@-5 {{48 stack bytes}}
4848
// riscv-sloppy-remark@-6 {{80 stack bytes}}
4949

5050
useA(genA());
5151
useA(genA());
5252
}
5353

5454
void t3(void) {
55-
// x86-precise-remark@-1 {{72 stack bytes}}
55+
// x86-precise-remark@-1 {{40 stack bytes}}
5656
// x86-sloppy-remark@-2 {{72 stack bytes}}
57-
// aarch64-precise-remark@-3 {{80 stack bytes}}
57+
// aarch64-precise-remark@-3 {{48 stack bytes}}
5858
// aarch64-sloppy-remark@-4 {{80 stack bytes}}
59-
// riscv-precise-remark@-5 {{80 stack bytes}}
59+
// riscv-precise-remark@-5 {{48 stack bytes}}
6060
// riscv-sloppy-remark@-6 {{80 stack bytes}}
6161

6262
useB(genB());

clang/test/CodeGenCXX/stack-reuse-miscompile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ const char * f(S s)
3838
// CHECK: call void @llvm.lifetime.start.p0(ptr [[T3]])
3939
// CHECK: call void @llvm.lifetime.start.p0(ptr [[AGG]])
4040
// CHECK: [[T5:%.*]] = call noundef ptr @_ZN1TC1E1S(ptr {{[^,]*}} [[T3]], [2 x i32] %{{.*}})
41+
// CHECK: call void @llvm.lifetime.end.p0(ptr [[AGG]])
4142
//
4243
// 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]])
4344
// CHECK: [[T6:%.*]] = call noundef ptr @_ZNK1T3strEv(ptr {{[^,]*}} [[T1]])
44-
// CHECK: call void @llvm.lifetime.end.p0(ptr [[AGG]])
4545
//
4646
// CHECK: call void @llvm.lifetime.end.p0(
4747
// CHECK: call void @llvm.lifetime.end.p0(

0 commit comments

Comments
 (0)