[llvm-branch-commits] [clang] [clang] Introduce scopes for arguments without destructors (PR #191019)

Paul Kirth via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Apr 8 10:51:04 PDT 2026


https://github.com/ilovepi created https://github.com/llvm/llvm-project/pull/191019

The current way we mark argument lifetimes is overly conservative for
most programs. To improve the status quo, we can introduce a new scope
for the arguments when it is safe to do so. This applies to aggregate
types that do not have destructors, and Objective-C types that are not
managed by reference counting (ARC). These rules may be possible to
refine more, but this recaptures a significant amount of the cases where
temporary objects never have their storage reused in stack coloring.

Note: I used an LLM to help draft test changes and suggest the scoping
rules for Objective-C.

>From 1cccdf78f4602775bc6cc2f45ece6edb5f06db2d Mon Sep 17 00:00:00 2001
From: Paul Kirth <paulkirth at google.com>
Date: Thu, 2 Apr 2026 13:28:39 -0700
Subject: [PATCH] [clang] Introduce scopes for arguments without destructors

The current way we mark argument lifetimes is overly conservative for
most programs. To improve the status quo, we can introduce a new scope
for the arguments when it is safe to do so. This applies to aggregate
types that do not have destructors, and Objective-C types that are not
managed by reference counting (ARC). These rules may be possible to
refine more, but this recaptures a significant amount of the cases where
temporary objects never have their storage reused in stack coloring.

Note: I used an LLM to help draft test changes and suggest the scoping
rules for Objective-C.
---
 clang/lib/CodeGen/CGCall.cpp                  |  6 +-
 clang/lib/CodeGen/CGExpr.cpp                  | 30 +++++++
 clang/test/CodeGen/lifetime-bug-2.cpp         | 10 ++-
 clang/test/CodeGen/lifetime-bug.cpp           |  2 +-
 clang/test/CodeGen/lifetime-invoke-c.c        | 14 +--
 clang/test/CodeGen/stack-usage-lifetimes.c    | 89 +++++++++++++++++++
 .../CodeGenCXX/aggregate-lifetime-invoke.cpp  | 32 +++----
 clang/test/CodeGenCoroutines/pr59181.cpp      |  2 +-
 8 files changed, 154 insertions(+), 31 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 419ad20a74465..fb241f37bdb1c 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5072,15 +5072,13 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
   // 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.
   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.
+    // expression. Note that we emit new scopes for these arguments in EmitCall,
+    // so cleanups will happen correctly on any error path.
     if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries &&
         EmitLifetimeStart(ArgSlotAlloca.getPointer()))
       pushFullExprCleanup<CallLifetimeEnd>(CleanupKind::NormalEHLifetimeMarker,
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 23802cdeb4811..9c199b86e7115 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6861,6 +6861,27 @@ LValue CodeGenFunction::EmitStmtExprLValue(const StmtExpr *E) {
                         AlignmentSource::Decl);
 }
 
+// Check if it is safe to tighten the lifetime of temporary aggregates for this
+// call. We can only do this if the call does not involve destructors or
+// Objective-C retainable types, as those push cleanups that must outlive the
+// call.
+static bool isSafeToTightenLifetime(const CallExpr *E) {
+  QualType RetTy = E->getType().getCanonicalType();
+  if (RetTy->isObjCRetainableType())
+    return false;
+
+  for (const auto *Arg : E->arguments()) {
+    if (Arg->getType().isDestructedType())
+      return false;
+
+    QualType Ty = Arg->getType().getNonReferenceType().getCanonicalType();
+    if (Ty->isObjCRetainableType())
+      return false;
+  }
+
+  return true;
+}
+
 RValue CodeGenFunction::EmitCall(QualType CalleeType,
                                  const CGCallee &OrigCallee, const CallExpr *E,
                                  ReturnValueSlot ReturnValue,
@@ -6872,6 +6893,15 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
   assert(CalleeType->isFunctionPointerType() &&
          "Call must have function pointer type!");
 
+  // For calls with trivial aggregate arguments, we want to tighten the
+  // lifetime of those aggregates to end immediately after the call returns,
+  // rather than at the end of the full-expression. We create a nested cleanup
+  // scope to intercept these lifetime markers. However, we only do this if the
+  // call is "safe".
+  std::optional<RunCleanupsScope> Scope;
+  if (isSafeToTightenLifetime(E))
+    Scope.emplace(*this);
+
   const Decl *TargetDecl =
       OrigCallee.getAbstractInfo().getCalleeDecl().getDecl();
 
diff --git a/clang/test/CodeGen/lifetime-bug-2.cpp b/clang/test/CodeGen/lifetime-bug-2.cpp
index aa7f0802fbad3..d13e20877e989 100644
--- a/clang/test/CodeGen/lifetime-bug-2.cpp
+++ b/clang/test/CodeGen/lifetime-bug-2.cpp
@@ -23,8 +23,14 @@ void foo(){
 // 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 [[TMP2]])
 // CHECK: llvm.lifetime.end.p0(ptr [[TMP1]])
-
diff --git a/clang/test/CodeGen/lifetime-bug.cpp b/clang/test/CodeGen/lifetime-bug.cpp
index d9d5350fd4cbd..a48128448c3d9 100644
--- a/clang/test/CodeGen/lifetime-bug.cpp
+++ b/clang/test/CodeGen/lifetime-bug.cpp
@@ -19,7 +19,7 @@ struct e {
 // CHECK-NEXT:    [[EXN_SLOT:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
 // CHECK-NEXT:    [[CLEANUP_ISACTIVE:%.*]] = alloca i1, align 1
-// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @d, align 4, !tbaa [[_ZTS1C_TBAA6:![0-9]+]]
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @d, align 4, !tbaa [[_ZTS1C_TBAA5:![0-9]+]]
 // CHECK-NEXT:    switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [
 // CHECK-NEXT:      i32 1, label %[[SW_BB:.*]]
 // CHECK-NEXT:    ]
diff --git a/clang/test/CodeGen/lifetime-invoke-c.c b/clang/test/CodeGen/lifetime-invoke-c.c
index 77514dc80e9e6..2c67f03b4f3f2 100644
--- a/clang/test/CodeGen/lifetime-invoke-c.c
+++ b/clang/test/CodeGen/lifetime-invoke-c.c
@@ -11,14 +11,14 @@ struct Trivial {
 // CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8, !tbaa [[INTPTR_TBAA6:![0-9]+]]
+// CHECK-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8, !tbaa [[INTPTR_TBAA5:![0-9]+]]
 // CHECK-NEXT:    ret void
 //
 // EXCEPTIONS-LABEL: define dso_local void @cleanup(
 // EXCEPTIONS-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] {
 // EXCEPTIONS-NEXT:  [[ENTRY:.*:]]
 // EXCEPTIONS-NEXT:    [[P_ADDR:%.*]] = alloca ptr, align 8
-// EXCEPTIONS-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8, !tbaa [[INTPTR_TBAA6:![0-9]+]]
+// EXCEPTIONS-NEXT:    store ptr [[P]], ptr [[P_ADDR]], align 8, !tbaa [[INTPTR_TBAA5:![0-9]+]]
 // EXCEPTIONS-NEXT:    ret void
 //
 void cleanup(int *p) {}
@@ -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
@@ -60,6 +60,7 @@ struct Trivial gen(void);
 // 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.end.p0(ptr [[AGG_TMP]]) #[[ATTR4]]
 // 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:.*]]
@@ -68,7 +69,6 @@ struct Trivial gen(void);
 // 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:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR4]]
 // EXCEPTIONS-NEXT:    call void @cleanup(ptr noundef [[X]])
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[X]]) #[[ATTR4]]
 // EXCEPTIONS-NEXT:    ret void
@@ -79,6 +79,7 @@ struct Trivial gen(void);
 // 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:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR4]]
 // EXCEPTIONS-NEXT:    br label %[[EHCLEANUP:.*]]
 // EXCEPTIONS:       [[LPAD3]]:
 // EXCEPTIONS-NEXT:    [[TMP3:%.*]] = landingpad { ptr, i32 }
@@ -90,7 +91,6 @@ struct Trivial gen(void);
 // EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP2]]) #[[ATTR4]]
 // EXCEPTIONS-NEXT:    br label %[[EHCLEANUP]]
 // EXCEPTIONS:       [[EHCLEANUP]]:
-// EXCEPTIONS-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR4]]
 // 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 +98,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_VAL7:%.*]] = insertvalue { ptr, i32 } [[LPAD_VAL]], i32 [[SEL]], 1
+// EXCEPTIONS-NEXT:    resume { ptr, i32 } [[LPAD_VAL7]]
 //
 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 at -1 {{40 stack bytes}}
+  // x86-sloppy-remark at -2 {{72 stack bytes}}
+  // aarch64-precise-remark at -3 {{48 stack bytes}}
+  // aarch64-sloppy-remark at -4 {{80 stack bytes}}
+  // riscv-precise-remark at -5 {{48 stack bytes}}
+  // riscv-sloppy-remark at -6 {{80 stack bytes}}
+
+  if (c)
+    useA(genA());
+  else
+    useA(genA());
+}
+
+void t2(void) {
+  // x86-precise-remark at -1 {{40 stack bytes}}
+  // x86-sloppy-remark at -2 {{72 stack bytes}}
+  // aarch64-precise-remark at -3 {{48 stack bytes}}
+  // aarch64-sloppy-remark at -4 {{80 stack bytes}}
+  // riscv-precise-remark at -5 {{48 stack bytes}}
+  // riscv-sloppy-remark at -6 {{80 stack bytes}}
+
+  useA(genA());
+  useA(genA());
+}
+
+void t3(void) {
+  // x86-precise-remark at -1 {{40 stack bytes}}
+  // x86-sloppy-remark at -2 {{72 stack bytes}}
+  // aarch64-precise-remark at -3 {{48 stack bytes}}
+  // aarch64-sloppy-remark at -4 {{80 stack bytes}}
+  // riscv-precise-remark at -5 {{48 stack bytes}}
+  // riscv-sloppy-remark at -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 at -1 {{40 stack bytes}}
+  // x86-sloppy-remark at -2 {{72 stack bytes}}
+  // aarch64-precise-remark at -3 {{48 stack bytes}}
+  // aarch64-sloppy-remark at -4 {{80 stack bytes}}
+  // riscv-precise-remark at -5 {{48 stack bytes}}
+  // riscv-sloppy-remark at -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..4fe5104e350ba 100644
--- a/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
+++ b/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
@@ -18,33 +18,33 @@ 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-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP]]) #[[ATTR4]]
+// CHECK-NEXT:    br label %[[CATCH:.*]]
+// 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:    br label %[[EHCLEANUP]]
-// CHECK:       [[EHCLEANUP]]:
-// CHECK-NEXT:    [[DOTPN:%.*]] = phi { ptr, i32 } [ [[TMP1]], %[[LPAD3]] ], [ [[TMP0]], %[[LPAD]] ]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr nonnull [[AGG_TMP2]]) #[[ATTR4]]
+// CHECK-NEXT:    br label %[[CATCH]]
+// CHECK:       [[CATCH]]:
+// 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]]
 // CHECK-NEXT:    tail call void @__cxa_end_catch()
 // CHECK-NEXT:    br label %[[TRY_CONT]]
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]])



More information about the llvm-branch-commits mailing list