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

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Apr 8 15:48:54 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-codegen

Author: Paul Kirth (ilovepi)

<details>
<summary>Changes</summary>

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.

---

Patch is 21.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/191019.diff


9 Files Affected:

- (modified) clang/lib/CodeGen/CGCall.cpp (+2-4) 
- (modified) clang/lib/CodeGen/CGExpr.cpp (+31) 
- (modified) clang/test/CodeGen/lifetime-bug-2.cpp (+8-2) 
- (modified) clang/test/CodeGen/lifetime-bug.cpp (+1-1) 
- (modified) clang/test/CodeGen/lifetime-invoke-c.c (+7-7) 
- (added) clang/test/CodeGen/stack-usage-lifetimes.c (+89) 
- (modified) clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp (+16-16) 
- (modified) clang/test/CodeGenCXX/conditional-temporaries.cpp (+29-17) 
- (modified) clang/test/CodeGenCoroutines/pr59181.cpp (+1-1) 


``````````diff
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..094002c106576 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,16 @@ 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 (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries &&
+      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/CodeGenCXX/conditional-temporaries.cpp b/clang/test/CodeGenCXX/conditional-temporaries.cpp
index beac2d10291f0..160f9f148ff4f 100644
--- a/clang/test/CodeGenCXX/conditional-temporaries.cpp
+++ b/clang/test/CodeGenCXX/conditional-temporaries.cpp
@@ -67,8 +67,9 @@ int lifetime_nontriv(bool cond) {
   // CHECK-NOOPT: store i1 false,
   // CHECK-NOOPT: store i1 false,
   // CHECK-NOOPT: store i1 false,
-  // CHECK-NOOPT: br i1
+  // CHECK-NOOPT: br i1 {{.*}}, label %[[COND_TRUE:.*]], label %[[COND_FALSE:.*]]
   //
+  // CHECK-NOOPT: [[COND_TRUE]]:
   // CHECK-NOOPT: call void @llvm.lifetime.start
   // CHECK-NOOPT: store i1 true,
   // CHECK-NOOPT: store i1 true,
@@ -82,10 +83,6 @@ int lifetime_nontriv(bool cond) {
   // CHECK-NOOPT: store i1 true,
   // CHECK-NOOPT: call noundef i32 @_ZN1X1fEv(
   // CHECK-NOOPT: call noundef i32 @_Z1giii(
-  // CHECK-NOOPT: br label
-  //
-  // CHECK-NOOPT: call noundef i32 @_Z1giii(i32 noundef 1, i32 noundef 2, i32 noundef 3)
-  // CHECK-NOOPT: br label
   //
   // CHECK-NOOPT: load i1,
   // CHECK-NOOPT: br i1
@@ -117,6 +114,10 @@ int lifetime_nontriv(bool cond) {
   // CHECK-NOOPT: call void @llvm.lifetime.end
   // CHECK-NOOPT: br label
   //
+  // CHECK-NOOPT: [[COND_FALSE]]:
+  // CHECK-NOOPT: call noundef i32 @_Z1giii(i32 noundef 1, i32 noundef 2, i32 noundef 3)
+  // CHECK-NOOPT: br label
+  //
   // CHECK-NOOPT: ret
 
   // CHECK-OPT: br i1
@@ -151,36 +152,39 @@ int lifetime_triv(bool cond) {
   // CHECK-NOOPT: call noundef i32 @_ZN1Y1fEv(
   // CHECK-NOOPT: call noundef i32 @_ZN1Y1fEv(
   // CHECK-NOOPT: call noundef i32 @_Z1giii(
-  // CHECK-NOOPT: br label
-  //
-  // CHECK-NOOPT: call noundef i32 @_Z1giii(i32 noundef 1, i32 noundef 2, i32 noundef 3)
-  // CHECK-NOOPT: br label
-  //
   // CHECK-NOOPT: call void @llvm.lifetime.end
   // CHECK-NOOPT-NOT: br
   // CHECK-NOOPT: call void @llvm.lifetime.end
   // CHECK-NOOPT-NOT: br
   // CHECK-NOOPT: call void @llvm.lifetime.end
+  // CHECK-NOOPT: br label
+  //
+  // CHECK-NOOPT: cond.false:
+  // CHECK-NOOPT-NOT: call void @llvm.lifetime.end
+  // CHECK-NOOPT: call noundef i32 @_Z1giii(i32 noundef 1, i32 noundef 2, i32 noundef 3)
+  // CHECK-NOOPT: br label
   //
   // CHECK-NOOPT: ret
 
-  // FIXME: LLVM isn't smart enough to remove the lifetime markers from the
-  // g(1, 2, 3) path here.
-
+  // CHECK-OPT: entry:
   // CHECK-OPT: call void @llvm.lifetime.start
   // CHECK-OPT: call void @llvm.lifetime.start
   // CHECK-OPT: call void @llvm.lifetime.start
   // CHECK-OPT: br i1
   //
+  // CHECK-OPT: [[COND_TRUE:.*]]:
   // CHECK-OPT: call noundef i32 @_ZN1Y1fEv(
   // CHECK-OPT: call noundef i32 @_ZN1Y1fEv(
   // CHECK-OPT: call noundef i32 @_ZN1Y1fEv(
   // CHECK-OPT: call noundef i32 @_Z1giii(
-  // CHECK-OPT: br label
-  //
   // CHECK-OPT: call void @llvm.lifetime.end
   // CHECK-OPT: call void @llvm.lifetime.end
   // CHECK-OPT: call void @llvm.lifetime.end
+  // CHECK-OPT: br label %[[COND_END:.*]]
+  //
+  // CHECK-OPT: [[COND_END]]:
+  // CHECK-OPT: phi
+  // CHECK-OPT: ret
   return cond ? g(Y().f(), Y().f(), Y().f()) : g(1, 2, 3);
 }
 
@@ -188,8 +192,9 @@ struct Z { ~Z() {} int f(); };
 int g(int, int, int);
 // CHECK-LABEL: @_Z22lifetime_nontriv_...
[truncated]

``````````

</details>


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


More information about the llvm-branch-commits mailing list