Skip to content

Commit 231df8a

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 e0f9bcc commit 231df8a

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

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

49674972
args.add(EmitAnyExpr(E, ArgSlot), type);
@@ -6291,6 +6296,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
62916296
for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall)
62926297
LifetimeEnd.Emit(*this, /*Flags=*/{});
62936298

6299+
if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries)
6300+
for (const CallArgList::EndLifetimeInfo &LT :
6301+
CallArgs.getLifetimeCleanups())
6302+
EmitLifetimeEnd(LT.Addr);
6303+
62946304
if (!ReturnValue.isExternallyDestructed() &&
62956305
RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct)
62966306
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)