Skip to content

Commit acdccf1

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 564e13b commit acdccf1

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
@@ -4973,11 +4973,16 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
49734973
RawAddress ArgSlotAlloca = Address::invalid();
49744974
ArgSlot = CreateAggTemp(E->getType(), "agg.tmp", &ArgSlotAlloca);
49754975

4976-
// Emit a lifetime start/end for this temporary at the end of the full
4977-
// expression.
4976+
// Emit a lifetime start/end for this temporary. If the type has a
4977+
// destructor, then we need to keep it alive for the full expression.
49784978
if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries &&
4979-
EmitLifetimeStart(ArgSlotAlloca.getPointer()))
4980-
pushFullExprCleanup<CallLifetimeEnd>(NormalAndEHCleanup, ArgSlotAlloca);
4979+
EmitLifetimeStart(ArgSlotAlloca.getPointer())) {
4980+
if (E->getType().isDestructedType()) {
4981+
pushFullExprCleanup<CallLifetimeEnd>(NormalAndEHCleanup, ArgSlotAlloca);
4982+
} else {
4983+
args.addLifetimeCleanup({ArgSlotAlloca.getPointer()});
4984+
}
4985+
}
49814986
}
49824987

49834988
args.add(EmitAnyExpr(E, ArgSlot), type);
@@ -6307,6 +6312,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
63076312
for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall)
63086313
LifetimeEnd.Emit(*this, /*Flags=*/{});
63096314

6315+
if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries)
6316+
for (const CallArgList::EndLifetimeInfo &LT :
6317+
CallArgs.getLifetimeCleanups())
6318+
EmitLifetimeEnd(LT.Addr);
6319+
63106320
if (!ReturnValue.isExternallyDestructed() &&
63116321
RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct)
63126322
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)