Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CallLifetimeEnd>(NormalEHLifetimeMarker,
ArgSlotAlloca);
EmitLifetimeStart(ArgSlotAlloca.getPointer())) {
if (E->getType().isDestructedType()) {
pushFullExprCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker,
ArgSlotAlloca);
} else {
args.addLifetimeCleanup({ArgSlotAlloca.getPointer()});
if (getInvokeDest())
pushFullExprCleanup<CallLifetimeEnd>(CleanupKind::EHCleanup,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this still doesn't work quite right.

Consider the case where you have two calls which can throw in the expression. Something like f2(f1(X{})). If f1 throws an exception, you correctly end the lifetime, but if f2 throws an exception, you end up calling lifetime.end on an alloca where the lifetime already ended.

You can probably fudge this by deactivating the cleanup after the call. But it would be cleaner to introduce a new scope for the call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah, that's a good point. Thanks for the suggestion. That sounds promising.

ArgSlotAlloca);
}
}
}

args.add(EmitAnyExpr(E, ArgSlot), type);
Expand Down Expand Up @@ -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 &LT : CallArgs.getLifetimeCleanups())
EmitLifetimeEnd(LT.Addr);

if (!ReturnValue.isExternallyDestructed() &&
RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct)
pushDestroy(QualType::DK_nontrivial_c_struct, Ret.getAggregateAddress(),
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CodeGen/CGCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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());
Expand All @@ -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;
};
Expand Down
8 changes: 3 additions & 5 deletions clang/test/CodeGen/lifetime-invoke-c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
89 changes: 89 additions & 0 deletions clang/test/CodeGen/stack-usage-lifetimes.c
Original file line number Diff line number Diff line change
@@ -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
15 changes: 6 additions & 9 deletions clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenCXX/stack-reuse-miscompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading