https://github.com/ilovepi updated 
https://github.com/llvm/llvm-project/pull/170518

>From 97f7f9d534ea8c919cded4b14402c036865b5ed2 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                  | 26 ++++--
 clang/lib/CodeGen/CGCall.h                    | 19 ++++
 clang/test/CodeGen/lifetime-bug.cpp           | 20 +++--
 clang/test/CodeGen/lifetime-invoke-c.c        | 48 ++++++----
 clang/test/CodeGen/stack-usage-lifetimes.c    | 89 +++++++++++++++++++
 .../CodeGenCXX/aggregate-lifetime-invoke.cpp  | 24 ++---
 .../CodeGenCXX/stack-reuse-miscompile.cpp     |  2 +-
 clang/test/CodeGenCoroutines/pr59181.cpp      |  2 +-
 8 files changed, 186 insertions(+), 44 deletions(-)
 create mode 100644 clang/test/CodeGen/stack-usage-lifetimes.c

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index ef99abd36406f..52326d0bb80ad 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4966,7 +4966,7 @@ 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.
   //
@@ -4976,12 +4976,20 @@ 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>(CleanupKind::NormalEHLifetimeMarker,
-                                           ArgSlotAlloca);
+        EmitLifetimeStart(ArgSlotAlloca.getPointer())) {
+      if (E->getType().isDestructedType()) {
+        pushFullExprCleanup<CallLifetimeEnd>(NormalEHLifetimeMarker,
+                                             ArgSlotAlloca);
+      } else {
+        args.addLifetimeCleanup({ArgSlotAlloca.getPointer()});
+        if (getInvokeDest())
+          pushFullExprCleanup<CallLifetimeEnd>(CleanupKind::EHCleanup,
+                                               ArgSlotAlloca);
+      }
+    }
   }
 
   args.add(EmitAnyExpr(E, ArgSlot), type);
@@ -6321,6 +6329,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(),
diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h
index 145992652934f..587d877d75435 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/lifetime-bug.cpp 
b/clang/test/CodeGen/lifetime-bug.cpp
index d9d5350fd4cbd..b66b4e88bf5db 100644
--- a/clang/test/CodeGen/lifetime-bug.cpp
+++ b/clang/test/CodeGen/lifetime-bug.cpp
@@ -28,20 +28,30 @@ struct e {
 // CHECK-NEXT:    store i1 true, ptr [[CLEANUP_ISACTIVE]], align 1
 // CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP]]) 
#[[ATTR6:[0-9]+]]
 // CHECK-NEXT:    invoke void @_ZN1eC1E1b(ptr noundef nonnull align 1 
dereferenceable(1) [[CALL]])
-// CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label 
%[[LPAD:.*]]
+// CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label 
%[[LPAD1:.*]]
 // CHECK:       [[INVOKE_CONT]]:
-// CHECK-NEXT:    store i1 false, ptr [[CLEANUP_ISACTIVE]], align 1
 // CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR6]]
+// CHECK-NEXT:    store i1 false, ptr [[CLEANUP_ISACTIVE]], align 1
 // CHECK-NEXT:    call void @_Z1av()
 // CHECK-NEXT:    br label %[[SW_EPILOG:.*]]
-// CHECK:       [[LPAD]]:
+// CHECK:       [[LPAD:.*:]]
 // CHECK-NEXT:    [[TMP1:%.*]] = landingpad { ptr, i32 }
 // CHECK-NEXT:            cleanup
 // CHECK-NEXT:    [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0
 // CHECK-NEXT:    store ptr [[TMP2]], ptr [[EXN_SLOT]], align 8
 // CHECK-NEXT:    [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 1
 // CHECK-NEXT:    store i32 [[TMP3]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NEXT:    br label %[[EHCLEANUP:.*]]
+// CHECK:       [[LPAD1]]:
+// CHECK-NEXT:    [[TMP4:%.*]] = landingpad { ptr, i32 }
+// CHECK-NEXT:            cleanup
+// CHECK-NEXT:    [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 0
+// CHECK-NEXT:    store ptr [[TMP5]], ptr [[EXN_SLOT]], align 8
+// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { ptr, i32 } [[TMP4]], 1
+// CHECK-NEXT:    store i32 [[TMP6]], ptr [[EHSELECTOR_SLOT]], align 4
 // CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR6]]
+// CHECK-NEXT:    br label %[[EHCLEANUP]]
+// CHECK:       [[EHCLEANUP]]:
 // CHECK-NEXT:    [[CLEANUP_IS_ACTIVE:%.*]] = load i1, ptr 
[[CLEANUP_ISACTIVE]], align 1
 // CHECK-NEXT:    br i1 [[CLEANUP_IS_ACTIVE]], label %[[CLEANUP_ACTION:.*]], 
label %[[CLEANUP_DONE:.*]]
 // CHECK:       [[CLEANUP_ACTION]]:
@@ -58,8 +68,8 @@ struct e {
 // CHECK-NEXT:    [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
 // CHECK-NEXT:    [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4
 // CHECK-NEXT:    [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr 
[[EXN]], 0
-// CHECK-NEXT:    [[LPAD_VAL1:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], 
i32 [[SEL]], 1
-// CHECK-NEXT:    resume { ptr, i32 } [[LPAD_VAL1]]
+// CHECK-NEXT:    [[LPAD_VAL2:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], 
i32 [[SEL]], 1
+// CHECK-NEXT:    resume { ptr, i32 } [[LPAD_VAL2]]
 //
 void f() {
   switch (d) {
diff --git a/clang/test/CodeGen/lifetime-invoke-c.c 
b/clang/test/CodeGen/lifetime-invoke-c.c
index 77514dc80e9e6..a53392d6b37cf 100644
--- a/clang/test/CodeGen/lifetime-invoke-c.c
+++ b/clang/test/CodeGen/lifetime-invoke-c.c
@@ -35,11 +35,11 @@ struct Trivial gen(void);
 // CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP]]) #[[ATTR3]]
 // CHECK-NEXT:    call void @gen(ptr dead_on_unwind writable 
sret([[STRUCT_TRIVIAL]]) align 4 [[AGG_TMP]])
 // CHECK-NEXT:    call void @func(ptr noundef byval([[STRUCT_TRIVIAL]]) align 
8 [[AGG_TMP]])
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR3]]
 // CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP1]]) 
#[[ATTR3]]
 // CHECK-NEXT:    call void @gen(ptr dead_on_unwind writable 
sret([[STRUCT_TRIVIAL]]) align 4 [[AGG_TMP1]])
 // CHECK-NEXT:    call void @func(ptr noundef byval([[STRUCT_TRIVIAL]]) align 
8 [[AGG_TMP1]])
 // CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP1]]) #[[ATTR3]]
-// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR3]]
 // CHECK-NEXT:    call void @cleanup(ptr noundef [[X]])
 // CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[X]]) #[[ATTR3]]
 // CHECK-NEXT:    ret void
@@ -51,46 +51,56 @@ struct Trivial gen(void);
 // EXCEPTIONS-NEXT:    [[AGG_TMP:%.*]] = alloca [[STRUCT_TRIVIAL:%.*]], align 8
 // EXCEPTIONS-NEXT:    [[EXN_SLOT:%.*]] = alloca ptr, align 8
 // EXCEPTIONS-NEXT:    [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
-// EXCEPTIONS-NEXT:    [[AGG_TMP2:%.*]] = alloca [[STRUCT_TRIVIAL]], align 8
+// EXCEPTIONS-NEXT:    [[AGG_TMP3:%.*]] = alloca [[STRUCT_TRIVIAL]], align 8
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.start.p0(ptr [[X]]) 
#[[ATTR4:[0-9]+]]
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP]]) 
#[[ATTR4]]
 // EXCEPTIONS-NEXT:    invoke void @gen(ptr dead_on_unwind writable 
sret([[STRUCT_TRIVIAL]]) align 4 [[AGG_TMP]])
-// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label 
%[[LPAD:.*]]
+// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label 
%[[LPAD1:.*]]
 // EXCEPTIONS:       [[INVOKE_CONT]]:
 // EXCEPTIONS-NEXT:    invoke void @func(ptr noundef byval([[STRUCT_TRIVIAL]]) 
align 8 [[AGG_TMP]])
-// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT1:.*]] unwind label 
%[[LPAD]]
-// EXCEPTIONS:       [[INVOKE_CONT1]]:
-// EXCEPTIONS-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP2]]) 
#[[ATTR4]]
-// EXCEPTIONS-NEXT:    invoke void @gen(ptr dead_on_unwind writable 
sret([[STRUCT_TRIVIAL]]) align 4 [[AGG_TMP2]])
-// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT4:.*]] unwind label 
%[[LPAD3:.*]]
-// EXCEPTIONS:       [[INVOKE_CONT4]]:
-// EXCEPTIONS-NEXT:    invoke void @func(ptr noundef byval([[STRUCT_TRIVIAL]]) 
align 8 [[AGG_TMP2]])
-// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT5:.*]] unwind label 
%[[LPAD3]]
-// EXCEPTIONS:       [[INVOKE_CONT5]]:
-// EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP2]]) 
#[[ATTR4]]
+// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT2:.*]] unwind label 
%[[LPAD1]]
+// EXCEPTIONS:       [[INVOKE_CONT2]]:
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) 
#[[ATTR4]]
+// EXCEPTIONS-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP3]]) 
#[[ATTR4]]
+// EXCEPTIONS-NEXT:    invoke void @gen(ptr dead_on_unwind writable 
sret([[STRUCT_TRIVIAL]]) align 4 [[AGG_TMP3]])
+// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT5:.*]] unwind label 
%[[LPAD4:.*]]
+// EXCEPTIONS:       [[INVOKE_CONT5]]:
+// EXCEPTIONS-NEXT:    invoke void @func(ptr noundef byval([[STRUCT_TRIVIAL]]) 
align 8 [[AGG_TMP3]])
+// EXCEPTIONS-NEXT:            to label %[[INVOKE_CONT6:.*]] unwind label 
%[[LPAD4]]
+// EXCEPTIONS:       [[INVOKE_CONT6]]:
+// EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP3]]) 
#[[ATTR4]]
 // EXCEPTIONS-NEXT:    call void @cleanup(ptr noundef [[X]])
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[X]]) #[[ATTR4]]
 // EXCEPTIONS-NEXT:    ret void
-// EXCEPTIONS:       [[LPAD]]:
+// EXCEPTIONS:       [[LPAD:.*:]]
 // EXCEPTIONS-NEXT:    [[TMP0:%.*]] = landingpad { ptr, i32 }
 // EXCEPTIONS-NEXT:            cleanup
 // EXCEPTIONS-NEXT:    [[TMP1:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 0
 // EXCEPTIONS-NEXT:    store ptr [[TMP1]], ptr [[EXN_SLOT]], align 8
 // EXCEPTIONS-NEXT:    [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP0]], 1
 // EXCEPTIONS-NEXT:    store i32 [[TMP2]], ptr [[EHSELECTOR_SLOT]], align 4
-// EXCEPTIONS-NEXT:    br label %[[EHCLEANUP:.*]]
-// EXCEPTIONS:       [[LPAD3]]:
+// EXCEPTIONS-NEXT:    br label %[[EHCLEANUP7:.*]]
+// EXCEPTIONS:       [[LPAD1]]:
 // EXCEPTIONS-NEXT:    [[TMP3:%.*]] = landingpad { ptr, i32 }
 // EXCEPTIONS-NEXT:            cleanup
 // EXCEPTIONS-NEXT:    [[TMP4:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 0
 // EXCEPTIONS-NEXT:    store ptr [[TMP4]], ptr [[EXN_SLOT]], align 8
 // EXCEPTIONS-NEXT:    [[TMP5:%.*]] = extractvalue { ptr, i32 } [[TMP3]], 1
 // EXCEPTIONS-NEXT:    store i32 [[TMP5]], ptr [[EHSELECTOR_SLOT]], align 4
-// EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP2]]) 
#[[ATTR4]]
+// EXCEPTIONS-NEXT:    br label %[[EHCLEANUP:.*]]
+// EXCEPTIONS:       [[LPAD4]]:
+// EXCEPTIONS-NEXT:    [[TMP6:%.*]] = landingpad { ptr, i32 }
+// EXCEPTIONS-NEXT:            cleanup
+// EXCEPTIONS-NEXT:    [[TMP7:%.*]] = extractvalue { ptr, i32 } [[TMP6]], 0
+// EXCEPTIONS-NEXT:    store ptr [[TMP7]], ptr [[EXN_SLOT]], align 8
+// EXCEPTIONS-NEXT:    [[TMP8:%.*]] = extractvalue { ptr, i32 } [[TMP6]], 1
+// EXCEPTIONS-NEXT:    store i32 [[TMP8]], ptr [[EHSELECTOR_SLOT]], align 4
+// EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP3]]) 
#[[ATTR4]]
 // EXCEPTIONS-NEXT:    br label %[[EHCLEANUP]]
 // EXCEPTIONS:       [[EHCLEANUP]]:
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) 
#[[ATTR4]]
+// EXCEPTIONS-NEXT:    br label %[[EHCLEANUP7]]
+// EXCEPTIONS:       [[EHCLEANUP7]]:
 // EXCEPTIONS-NEXT:    call void @cleanup(ptr noundef [[X]])
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[X]]) #[[ATTR4]]
 // EXCEPTIONS-NEXT:    br label %[[EH_RESUME:.*]]
@@ -98,8 +108,8 @@ struct Trivial gen(void);
 // EXCEPTIONS-NEXT:    [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
 // EXCEPTIONS-NEXT:    [[SEL:%.*]] = load i32, ptr [[EHSELECTOR_SLOT]], align 4
 // EXCEPTIONS-NEXT:    [[LPAD_VAL:%.*]] = insertvalue { ptr, i32 } poison, ptr 
[[EXN]], 0
-// EXCEPTIONS-NEXT:    [[LPAD_VAL8:%.*]] = insertvalue { ptr, i32 } 
[[LPAD_VAL]], i32 [[SEL]], 1
-// EXCEPTIONS-NEXT:    resume { ptr, i32 } [[LPAD_VAL8]]
+// EXCEPTIONS-NEXT:    [[LPAD_VAL9:%.*]] = insertvalue { ptr, i32 } 
[[LPAD_VAL]], i32 [[SEL]], 1
+// EXCEPTIONS-NEXT:    resume { ptr, i32 } [[LPAD_VAL9]]
 //
 void test() {
   int x __attribute__((cleanup(cleanup)));
diff --git a/clang/test/CodeGen/stack-usage-lifetimes.c 
b/clang/test/CodeGen/stack-usage-lifetimes.c
new file mode 100644
index 0000000000000..189bc9c229ca4
--- /dev/null
+++ b/clang/test/CodeGen/stack-usage-lifetimes.c
@@ -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
diff --git a/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp 
b/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
index 2d1075bbcbbb0..a08d9e5c047c0 100644
--- a/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
+++ b/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
@@ -18,31 +18,31 @@ void func_that_throws(Trivial t);
 // CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] personality ptr 
@__gxx_personality_v0 {
 // CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[AGG_TMP:%.*]] = alloca [[STRUCT_TRIVIAL:%.*]], align 8
-// CHECK-NEXT:    [[AGG_TMP1:%.*]] = alloca [[STRUCT_TRIVIAL]], align 8
+// CHECK-NEXT:    [[AGG_TMP2:%.*]] = alloca [[STRUCT_TRIVIAL]], align 8
 // CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr nonnull [[AGG_TMP]]) 
#[[ATTR4:[0-9]+]]
 // CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 
dereferenceable(400) [[AGG_TMP]], i8 0, i64 400, i1 false)
 // CHECK-NEXT:    invoke void @func_that_throws(ptr noundef nonnull 
byval([[STRUCT_TRIVIAL]]) align 8 [[AGG_TMP]])
-// CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label 
%[[LPAD:.*]]
+// CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label 
%[[LPAD1:.*]]
 // CHECK:       [[INVOKE_CONT]]:
-// CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr nonnull [[AGG_TMP1]]) 
#[[ATTR4]]
-// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 
dereferenceable(400) [[AGG_TMP1]], i8 0, i64 400, i1 false)
-// CHECK-NEXT:    invoke void @func_that_throws(ptr noundef nonnull 
byval([[STRUCT_TRIVIAL]]) align 8 [[AGG_TMP1]])
-// CHECK-NEXT:            to label %[[INVOKE_CONT4:.*]] unwind label 
%[[LPAD3:.*]]
-// CHECK:       [[INVOKE_CONT4]]:
-// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP1]]) 
#[[ATTR4]]
 // CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP]]) 
#[[ATTR4]]
+// CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr nonnull [[AGG_TMP2]]) 
#[[ATTR4]]
+// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 
dereferenceable(400) [[AGG_TMP2]], i8 0, i64 400, i1 false)
+// CHECK-NEXT:    invoke void @func_that_throws(ptr noundef nonnull 
byval([[STRUCT_TRIVIAL]]) align 8 [[AGG_TMP2]])
+// CHECK-NEXT:            to label %[[INVOKE_CONT5:.*]] unwind label 
%[[LPAD4:.*]]
+// CHECK:       [[INVOKE_CONT5]]:
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP2]]) 
#[[ATTR4]]
 // CHECK-NEXT:    br label %[[TRY_CONT:.*]]
-// CHECK:       [[LPAD]]:
+// CHECK:       [[LPAD1]]:
 // CHECK-NEXT:    [[TMP0:%.*]] = landingpad { ptr, i32 }
 // CHECK-NEXT:            catch ptr null
 // CHECK-NEXT:    br label %[[EHCLEANUP:.*]]
-// CHECK:       [[LPAD3]]:
+// CHECK:       [[LPAD4]]:
 // CHECK-NEXT:    [[TMP1:%.*]] = landingpad { ptr, i32 }
 // CHECK-NEXT:            catch ptr null
-// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP1]]) 
#[[ATTR4]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP2]]) 
#[[ATTR4]]
 // CHECK-NEXT:    br label %[[EHCLEANUP]]
 // CHECK:       [[EHCLEANUP]]:
-// CHECK-NEXT:    [[DOTPN:%.*]] = phi { ptr, i32 } [ [[TMP1]], %[[LPAD3]] ], [ 
[[TMP0]], %[[LPAD]] ]
+// CHECK-NEXT:    [[DOTPN:%.*]] = phi { ptr, i32 } [ [[TMP1]], %[[LPAD4]] ], [ 
[[TMP0]], %[[LPAD1]] ]
 // CHECK-NEXT:    [[EXN_SLOT_0:%.*]] = extractvalue { ptr, i32 } [[DOTPN]], 0
 // CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP]]) 
#[[ATTR4]]
 // CHECK-NEXT:    [[TMP2:%.*]] = tail call ptr @__cxa_begin_catch(ptr 
[[EXN_SLOT_0]]) #[[ATTR4]]
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(
diff --git a/clang/test/CodeGenCoroutines/pr59181.cpp 
b/clang/test/CodeGenCoroutines/pr59181.cpp
index bb5eba1c3252d..7c0868d6b82bd 100644
--- a/clang/test/CodeGenCoroutines/pr59181.cpp
+++ b/clang/test/CodeGenCoroutines/pr59181.cpp
@@ -59,5 +59,5 @@ void foo() {
 // CHECK: call void @llvm.coro.await.suspend.void(
 // CHECK-NEXT: %{{[0-9]+}} = call i8 @llvm.coro.suspend(
 
-// CHECK-LABEL: cleanup.done20:
+// CHECK-LABEL: cond.end:
 // CHECK: call void @llvm.lifetime.end.p0(ptr [[AGG]])

>From cef586b0fa55611d293c017a2562de906dd6ee9c Mon Sep 17 00:00:00 2001
From: Paul Kirth <[email protected]>
Date: Thu, 15 Jan 2026 07:22:24 -0800
Subject: [PATCH 2/2] Save test for conflicting cleanups

---
 clang/test/CodeGen/lifetime-bug-2.cpp | 36 +++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 clang/test/CodeGen/lifetime-bug-2.cpp

diff --git a/clang/test/CodeGen/lifetime-bug-2.cpp 
b/clang/test/CodeGen/lifetime-bug-2.cpp
new file mode 100644
index 0000000000000..229ff0255d1f1
--- /dev/null
+++ b/clang/test/CodeGen/lifetime-bug-2.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -emit-llvm 
-disable-llvm-passes -target-cpu x86-64 -fexceptions -fcxx-exceptions -x c++ %s 
-o - 2>&1 | FileCheck %s
+
+extern "C" {
+struct X {};
+
+X f1(X x);
+X f2(X x);
+
+void foo(){
+  try{
+  f2(f1(X{}));
+  } catch(int e){
+    return;
+  }
+  return;
+}
+}
+// CHECK-LABEL: define{{.*}} void @foo
+// CHECK: [[TMP1:%.*]] = alloca %struct.X
+// CHECK: [[TMP2:%.*]] = alloca %struct.X
+// CHECK: llvm.lifetime.start.p0(ptr [[TMP1]])
+// CHECK-NEXT: llvm.lifetime.start.p0(ptr [[TMP2]])
+// CHECK-NEXT: invoke void @f1
+// CHECK-NEXT: to label %[[CONT:.*]] unwind label %[[LPAD1:.*]]
+//
+// CHECK: [[CONT]]:
+// CHECK-NEXT: @llvm.lifetime.end.p0(ptr [[TMP2]])
+// CHECK-NEXT: invoke void @f2
+// CHECK-NEXT: to label %[[CONT2:.*]] unwind label %[[LPAD2:.*]]
+//
+// CHECK: [[CONT2]]:
+// CHECK-NEXT: lifetime.end.p0(ptr [[TMP1]])
+//
+// CHECK: [[LPAD1]]:
+// CHECK-NEXT: landingpad
+// CHECK: llvm.lifetime.end.p0(ptr [[TMP1]])
\ No newline at end of file

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to