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

This reverts commit 6d38c876478dac4a42f9d6e37692348deabf6a25. The
current version only works when exceptions are not enabled until we
determine how to resolve issues around broken dominance relationships
with the def-use chain.

>From cb88594cc80087a5858da23de2fef36a456e901d Mon Sep 17 00:00:00 2001
From: Paul Kirth <[email protected]>
Date: Fri, 12 Dec 2025 09:03:44 -0800
Subject: [PATCH] Reapply "[clang] Limit lifetimes of temporaries to the full
 expression (#170517)"

This reverts commit 6d38c876478dac4a42f9d6e37692348deabf6a25. The
current version only works when exceptions are not enabled until we
determine how to resolve issues around broken dominance relationships
with the def-use chain.
---
 clang/docs/ReleaseNotes.rst                   | 11 +++
 clang/include/clang/Basic/CodeGenOptions.def  |  4 +
 clang/include/clang/Options/Options.td        |  5 +
 clang/lib/CodeGen/CGCall.cpp                  | 25 ++++-
 clang/lib/CodeGen/CGCleanup.cpp               |  7 +-
 clang/test/CodeGen/lifetime-bug-2.c           | 58 +++++++++++
 clang/test/CodeGen/lifetime-bug.cpp           | 58 +++++++++++
 clang/test/CodeGen/lifetime-call-temp.c       | 98 +++++++++++++++++++
 clang/test/CodeGen/lifetime-invoke-c.c        | 36 +++++++
 .../CodeGenCXX/aggregate-lifetime-invoke.cpp  | 40 ++++++++
 .../CodeGenCXX/amdgcn-call-with-aggarg.cc     | 19 ++++
 .../CodeGenCXX/stack-reuse-miscompile.cpp     |  4 +
 clang/test/CodeGenCoroutines/pr59181.cpp      |  4 +
 13 files changed, 365 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CodeGen/lifetime-bug-2.c
 create mode 100644 clang/test/CodeGen/lifetime-bug.cpp
 create mode 100644 clang/test/CodeGen/lifetime-call-temp.c
 create mode 100644 clang/test/CodeGen/lifetime-invoke-c.c
 create mode 100644 clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
 create mode 100644 clang/test/CodeGenCXX/amdgcn-call-with-aggarg.cc

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 85d2e562d1ecd..c40710e14098d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -36,6 +36,17 @@ latest release, please see the `Clang Web Site 
<https://clang.llvm.org>`_ or the
 
 Potentially Breaking Changes
 ============================
+- When exceptions are disabled, Clang is now more precise with regards to the
+  lifetime of temporary objects such as when aggregates are passed by value to
+  a function, resulting in better sharing of stack slots and reduced stack
+  usage. This change can lead to use-after-scope related issues in code that
+  unintentionally relied on the previous behavior. If recompiling with
+  ``-fsanitize=address`` shows a use-after-scope warning, then this is likely
+  the case, and the report printed should be able to help users pinpoint where
+  the use-after-scope is occurring. Users can use ``-Xclang
+  -sloppy-temporary-lifetimes`` to retain the old behavior until they are able
+  to find and resolve issues in their code.
+
 
 C/C++ Language Potentially Breaking Changes
 -------------------------------------------
diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index baf8b093c10e6..a690956bdec1f 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -486,6 +486,10 @@ ENUM_CODEGENOPT(ZeroCallUsedRegs, ZeroCallUsedRegsKind,
 /// non-deleting destructors. (No effect on Microsoft ABI.)
 CODEGENOPT(CtorDtorReturnThis, 1, 0, Benign)
 
+/// Set via -Xclang -sloppy-temporary-lifetimes to disable emission of lifetime
+/// marker intrinsic calls.
+CODEGENOPT(NoLifetimeMarkersForTemporaries, 1, 0, Benign)
+
 /// Enables emitting Import Call sections on supported targets that can be used
 /// by the Windows kernel to enable import call optimization.
 CODEGENOPT(ImportCallOptimization, 1, 0, Benign)
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 2f57a5b13b917..289544b49b7f1 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -8224,6 +8224,11 @@ def import_call_optimization : Flag<["-"], 
"import-call-optimization">,
 def replaceable_function: Joined<["-"], "loader-replaceable-function=">,
   MarshallingInfoStringVector<CodeGenOpts<"LoaderReplaceableFunctionNames">>;
 
+def sloppy_temporary_lifetimes
+    : Flag<["-"], "sloppy-temporary-lifetimes">,
+      HelpText<"Don't emit lifetime markers for temporary objects">,
+      MarshallingInfoFlag<CodeGenOpts<"NoLifetimeMarkersForTemporaries">>;
+
 } // let Visibility = [CC1Option]
 
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index d7bdeb3981cf8..d4fe113d52ec8 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4960,7 +4960,30 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, 
const Expr *E,
     return;
   }
 
-  args.add(EmitAnyExprToTemp(E), type);
+  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
+  // 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.
+  // FIXME: This should work fine w/ exceptions, but somehow breaks the
+  // dominance relationship in the def-use chain.
+  if (!CGM.getLangOpts().Exceptions &&
+      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.
+    if (!CGM.getCodeGenOpts().NoLifetimeMarkersForTemporaries &&
+        EmitLifetimeStart(ArgSlotAlloca.getPointer()))
+      pushFullExprCleanup<CallLifetimeEnd>(CleanupKind::NormalEHLifetimeMarker,
+                                           ArgSlotAlloca);
+  }
+
+  args.add(EmitAnyExpr(E, ArgSlot), type);
 }
 
 QualType CodeGenFunction::getVarArgType(const Expr *Arg) {
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index 28ac9bf396356..5f37305aa8c70 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -701,11 +701,12 @@ void CodeGenFunction::PopCleanupBlock(bool 
FallthroughIsBranchThrough,
 
   // If this is a normal cleanup, then having a prebranched
   // fallthrough implies that the fallthrough source unconditionally
-  // jumps here.
+  // jumps here, unless its for a lifetime marker.
   assert(!Scope.isNormalCleanup() || !HasPrebranchedFallthrough ||
+         Scope.isLifetimeMarker() ||
          (Scope.getNormalBlock() &&
-          FallthroughSource->getTerminator()->getSuccessor(0)
-            == Scope.getNormalBlock()));
+          FallthroughSource->getTerminator()->getSuccessor(0) ==
+              Scope.getNormalBlock()));
 
   bool RequiresNormalCleanup = false;
   if (Scope.isNormalCleanup() &&
diff --git a/clang/test/CodeGen/lifetime-bug-2.c 
b/clang/test/CodeGen/lifetime-bug-2.c
new file mode 100644
index 0000000000000..76dd5b95d0216
--- /dev/null
+++ b/clang/test/CodeGen/lifetime-bug-2.c
@@ -0,0 +1,58 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --check-globals none --version 6
+// RUN: %clang_cc1 -O2 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
+
+struct {
+} __trans_tmp_85;
+struct bkey_s_c {
+} bch2_trans_update_extent_k;
+int bch2_trans_update_extent_k_0;
+struct bkey_s_c bch2_btree_iter_peek_max();
+// CHECK-LABEL: define dso_local void @bch2_trans_update_extent(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[DONE:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    [[TMP:%.*]] = alloca [[STRUCT_BKEY_S_C:%.*]], align 1
+// CHECK-NEXT:    [[AGG_TMP:%.*]] = alloca [[STRUCT_ANON:%.*]], align 1
+// CHECK-NEXT:    [[UNDEF_AGG_TMP:%.*]] = alloca [[STRUCT_BKEY_S_C]], align 1
+// CHECK-NEXT:    [[CLEANUP_DEST_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[DONE]]) 
#[[ATTR4:[0-9]+]]
+// CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[TMP]]) #[[ATTR4]]
+// CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP]]) #[[ATTR4]]
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[AGG_TMP]], 
ptr align 1 @__trans_tmp_85, i64 0, i1 false), !tbaa.struct 
[[TBAA_STRUCT6:![0-9]+]]
+// CHECK-NEXT:    call void (...) @bch2_btree_iter_peek_max()
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[AGG_TMP]]) #[[ATTR4]]
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 1 
@bch2_trans_update_extent_k, ptr align 1 [[UNDEF_AGG_TMP]], i64 0, i1 false), 
!tbaa.struct [[TBAA_STRUCT6]]
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[TMP]]) #[[ATTR4]]
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @bch2_trans_update_extent_k_0, 
align 4, !tbaa [[INT_TBAA2:![0-9]+]]
+// CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0
+// CHECK-NEXT:    br i1 [[TOBOOL]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+// CHECK:       [[IF_THEN]]:
+// CHECK-NEXT:    store i32 2, ptr [[CLEANUP_DEST_SLOT]], align 4
+// CHECK-NEXT:    br label %[[CLEANUP:.*]]
+// CHECK:       [[IF_END]]:
+// CHECK-NEXT:    store i32 0, ptr [[CLEANUP_DEST_SLOT]], align 4
+// CHECK-NEXT:    br label %[[CLEANUP]]
+// CHECK:       [[CLEANUP]]:
+// CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[DONE]]) #[[ATTR4]]
+// CHECK-NEXT:    [[CLEANUP_DEST:%.*]] = load i32, ptr [[CLEANUP_DEST_SLOT]], 
align 4
+// CHECK-NEXT:    switch i32 [[CLEANUP_DEST]], label %[[UNREACHABLE:.*]] [
+// CHECK-NEXT:      i32 0, label %[[CLEANUP_CONT:.*]]
+// CHECK-NEXT:      i32 2, label %[[ERR:.*]]
+// CHECK-NEXT:    ]
+// CHECK:       [[CLEANUP_CONT]]:
+// CHECK-NEXT:    br label %[[ERR]]
+// CHECK:       [[ERR]]:
+// CHECK-NEXT:    ret void
+// CHECK:       [[UNREACHABLE]]:
+// CHECK-NEXT:    unreachable
+//
+void bch2_trans_update_extent() {
+  {
+    _Bool done;
+    bch2_trans_update_extent_k = bch2_btree_iter_peek_max((0, __trans_tmp_85));
+    if (bch2_trans_update_extent_k_0)
+      goto err;
+  }
+err:
+}
+
diff --git a/clang/test/CodeGen/lifetime-bug.cpp 
b/clang/test/CodeGen/lifetime-bug.cpp
new file mode 100644
index 0000000000000..641b3ae7762ae
--- /dev/null
+++ b/clang/test/CodeGen/lifetime-bug.cpp
@@ -0,0 +1,58 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --check-globals none --version 6
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O1 -emit-llvm 
-disable-llvm-passes -target-cpu x86-64 -fexceptions -x c++ %s -o - | FileCheck 
%s
+
+void a();
+struct b {};
+typedef enum {} c;
+c d;
+struct e {
+  e(b = b());
+};
+// CHECK-LABEL: define dso_local void @_Z1fv(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[AGG_TEMP:%.*]] = alloca [[STRUCT_B:%.*]], align 1
+// CHECK-NEXT:    [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @d, align 4, !tbaa 
[[_ZTS1C_TBAA6:![0-9]+]]
+// CHECK-NEXT:    switch i32 [[TMP0]], label %[[SW_DEFAULT:.*]] [
+// CHECK-NEXT:      i32 1, label %[[SW_BB:.*]]
+// CHECK-NEXT:    ]
+// CHECK:       [[SW_BB]]:
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 
noundef 1) #[[ATTR4:[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:       [[INVOKE_CONT]]:
+// CHECK-NEXT:    call void @_Z1av()
+// CHECK-NEXT:    br label %[[SW_EPILOG:.*]]
+// 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:    call void @_ZdlPvm(ptr noundef [[CALL]], i64 noundef 1) 
#[[ATTR5:[0-9]+]]
+// CHECK-NEXT:    br label %[[EH_RESUME:.*]]
+// CHECK:       [[SW_DEFAULT]]:
+// CHECK-NEXT:    call void @_Z1av()
+// CHECK-NEXT:    br label %[[SW_EPILOG]]
+// CHECK:       [[SW_EPILOG]]:
+// CHECK-NEXT:    ret void
+// CHECK:       [[EH_RESUME]]:
+// 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]]
+//
+void f() {
+  switch (d) {
+  case 1:
+    new e;
+    a();
+    break;
+  default:
+    a();
+  }
+}
diff --git a/clang/test/CodeGen/lifetime-call-temp.c 
b/clang/test/CodeGen/lifetime-call-temp.c
new file mode 100644
index 0000000000000..3bc68b5e8024a
--- /dev/null
+++ b/clang/test/CodeGen/lifetime-call-temp.c
@@ -0,0 +1,98 @@
+// RUN: %clang -cc1 -triple x86_64-apple-macos -O1 -disable-llvm-passes %s \
+// RUN:   -emit-llvm -o - | FileCheck %s --implicit-check-not=llvm.lifetime
+// RUN: %clang -cc1 -xc++ -std=c++17 -triple x86_64-apple-macos -O1 \
+// RUN:   -disable-llvm-passes %s -emit-llvm -o - -Wno-return-type-c-linkage | 
\
+// RUN:   FileCheck %s --implicit-check-not=llvm.lifetime 
--check-prefixes=CHECK,CXX
+// RUN: %clang -cc1 -xobjective-c -triple x86_64-apple-macos -O1 \
+// RUN:   -disable-llvm-passes %s -emit-llvm -o - | \
+// RUN:   FileCheck %s --implicit-check-not=llvm.lifetime 
--check-prefixes=CHECK,OBJC
+// RUN: %clang -cc1 -triple x86_64-apple-macos -O1 -disable-llvm-passes %s \
+// RUN:   -emit-llvm -o - -sloppy-temporary-lifetimes | \
+// RUN:   FileCheck %s --implicit-check-not=llvm.lifetime 
--check-prefixes=SLOPPY
+
+typedef struct { int x[100]; } aggregate;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void takes_aggregate(aggregate);
+aggregate gives_aggregate();
+
+// CHECK-LABEL: define void @t1
+void t1() {
+  takes_aggregate(gives_aggregate());
+
+  // CHECK: [[AGGTMP:%.*]] = alloca %struct.aggregate, align 8
+  // CHECK: call void @llvm.lifetime.start.p0(ptr [[AGGTMP]])
+  // CHECK: call void{{.*}} @gives_aggregate(ptr{{.*}}sret(%struct.aggregate) 
align 4 [[AGGTMP]])
+  // CHECK: call void @takes_aggregate(ptr noundef byval(%struct.aggregate) 
align 8 [[AGGTMP]])
+  // CHECK: call void @llvm.lifetime.end.p0(ptr [[AGGTMP]])
+
+  // SLOPPY: [[AGGTMP:%.*]] = alloca %struct.aggregate, align 8
+  // SLOPPY-NEXT: call void (ptr, ...) 
@gives_aggregate(ptr{{.*}}sret(%struct.aggregate) align 4 [[AGGTMP]])
+  // SLOPPY-NEXT: call void @takes_aggregate(ptr noundef 
byval(%struct.aggregate) align 8 [[AGGTMP]])
+}
+
+// CHECK: declare {{.*}}llvm.lifetime.start
+// CHECK: declare {{.*}}llvm.lifetime.end
+
+#ifdef __cplusplus
+// CXX: define void @t2
+void t2() {
+  struct S {
+    S(aggregate) {}
+  };
+  S{gives_aggregate()};
+
+  // CXX: [[AGG:%.*]] = alloca %struct.aggregate
+  // CXX: call void @llvm.lifetime.start.p0(ptr [[AGG]]
+  // CXX: call void @gives_aggregate(ptr{{.*}}sret(%struct.aggregate) align 4 
[[AGG]])
+  // CXX: call void @_ZZ2t2EN1SC1E9aggregate(ptr {{.*}}, ptr {{.*}} 
byval(%struct.aggregate) align 8 [[AGG]])
+  // CXX: call void @llvm.lifetime.end.p0(ptr [[AGG]]
+}
+
+struct Dtor {
+  ~Dtor();
+};
+
+void takes_dtor(Dtor);
+Dtor gives_dtor();
+
+// CXX: define void @t3
+void t3() {
+  takes_dtor(gives_dtor());
+
+  // CXX: [[AGG:%.*]] = alloca %struct.Dtor
+  // CXX: call void @llvm.lifetime.start.p0(ptr [[AGG]])
+  // CXX: call void @gives_dtor(ptr{{.*}}sret(%struct.Dtor) align 1 [[AGG]])
+  // CXX: call void @takes_dtor(ptr noundef [[AGG]])
+  // CXX: call void @_ZN4DtorD1Ev(ptr {{.*}} [[AGG]])
+  // CXX: call void @llvm.lifetime.end.p0(ptr [[AGG]])
+  // CXX: ret void
+}
+
+#endif
+
+#ifdef __OBJC__
+
+@interface X
+-m:(aggregate)x;
+@end
+
+// OBJC: define void @t4
+void t4(X *x) {
+  [x m: gives_aggregate()];
+
+  // OBJC: [[AGG:%.*]] = alloca %struct.aggregate
+  // OBJC: call void @llvm.lifetime.start.p0(ptr [[AGG]]
+  // OBJC: call void{{.*}} @gives_aggregate(ptr{{.*}}sret(%struct.aggregate) 
align 4 [[AGGTMP]])
+  // OBJC: call {{.*}}@objc_msgSend
+  // OBJC: call void @llvm.lifetime.end.p0(ptr [[AGG]]
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/clang/test/CodeGen/lifetime-invoke-c.c 
b/clang/test/CodeGen/lifetime-invoke-c.c
new file mode 100644
index 0000000000000..6a43b3562a2e8
--- /dev/null
+++ b/clang/test/CodeGen/lifetime-invoke-c.c
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s -O1 
-disable-llvm-passes | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s -O1 
-disable-llvm-passes -fexceptions | FileCheck %s --check-prefix=EXCEPTIONS
+
+struct Trivial {
+  int x[100];
+};
+
+void cleanup(int *p) {}
+void func(struct Trivial t);
+struct Trivial gen(void);
+
+// CHECK-LABEL: define dso_local void @test()
+void test() {
+  int x __attribute__((cleanup(cleanup)));
+
+  // CHECK: %[[AGG1:.*]] = alloca %struct.Trivial
+  // CHECK: %[[AGG2:.*]] = alloca %struct.Trivial
+  // CHECK: call void @llvm.lifetime.start.p0(ptr %[[AGG1]])
+  // CHECK: call void @gen(ptr{{.*}} sret(%struct.Trivial){{.*}} %[[AGG1]])
+  // CHECK: call void @func(ptr{{.*}} %[[AGG1]])
+  // CHECK: call void @llvm.lifetime.start.p0(ptr %[[AGG2]])
+  // CHECK: call void @gen(ptr{{.*}} sret(%struct.Trivial){{.*}} %[[AGG2]])
+  // CHECK: call void @func(ptr{{.*}} %[[AGG2]])
+  // CHECK: call void @llvm.lifetime.end.p0(ptr %[[AGG2]])
+  // CHECK: call void @llvm.lifetime.end.p0(ptr %[[AGG1]])
+  // CHECK: call void @cleanup
+
+  // EXCEPTIONS: %[[AGG1:.*]] = alloca %struct.Trivial
+  // EXCEPTIONS: %[[AGG2:.*]] = alloca %struct.Trivial
+  // EXCEPTIONS-NOT: call void @llvm.lifetime.start.p0(ptr %[[AGG1]])
+  // EXCEPTIONS-NOT: call void @llvm.lifetime.start.p0(ptr %[[AGG2]])
+  // EXCEPTIONS-NOT: call void @llvm.lifetime.end.p0(ptr %[[AGG2]])
+  // EXCEPTIONS-NOT: call void @llvm.lifetime.end.p0(ptr %[[AGG1]])
+  func(gen());
+  func(gen());
+}
diff --git a/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp 
b/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
new file mode 100644
index 0000000000000..c3ce91aea73f3
--- /dev/null
+++ b/clang/test/CodeGenCXX/aggregate-lifetime-invoke.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s -O1 
-fexceptions -fcxx-exceptions | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s -O1 
-fexceptions -fcxx-exceptions -sloppy-temporary-lifetimes | FileCheck %s
+
+// COM: Note that this test case would break if we allowed tighter lifetimes to
+// run when exceptions were enabled. If we make them work together this test
+// will need to be updated.
+
+extern "C" {
+
+struct Trivial {
+  int x[100];
+};
+
+void func_that_throws(Trivial t);
+
+// CHECK-LABEL: define{{.*}} void @test()
+void test() {
+  // CHECK: %[[AGG1:.*]] = alloca %struct.Trivial
+  // CHECK: %[[AGG2:.*]] = alloca %struct.Trivial
+
+  // CHECK: invoke void @func_that_throws(ptr{{.*}} %[[AGG1]])
+  // CHECK-NEXT: to label %[[CONT:.*]] unwind label %[[LPAD:.*]]
+
+  // CHECK: [[CONT]]:
+  // CHECK: invoke void @func_that_throws(ptr{{.*}} %[[AGG2]])
+  // CHECK-NEXT: to label %[[CONT:.*]] unwind label %[[LPAD:.*]]
+
+  // CHECK: [[LPAD]]:
+  // CHECK: landingpad
+
+  // CHECK-NOT: llvm.lifetime.start
+  // CHECK-NOT: llvm.lifetime.end
+
+  try {
+    func_that_throws(Trivial{0});
+    func_that_throws(Trivial{0});
+  } catch (...) {
+  }
+}
+} // end extern "C"
diff --git a/clang/test/CodeGenCXX/amdgcn-call-with-aggarg.cc 
b/clang/test/CodeGenCXX/amdgcn-call-with-aggarg.cc
new file mode 100644
index 0000000000000..9b598a48f6436
--- /dev/null
+++ b/clang/test/CodeGenCXX/amdgcn-call-with-aggarg.cc
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -emit-llvm -O3 
-disable-llvm-passes -o - %s | FileCheck %s
+
+struct A {
+  float x, y, z, w;
+};
+
+void foo(A a);
+
+// CHECK-LABEL: @_Z4testv
+// CHECK:         [[A:%.*]] = alloca [[STRUCT_A:%.*]], align 4, addrspace(5)
+// CHECK-NEXT:    [[AGG_TMP:%.*]] = alloca [[STRUCT_A]], align 4, addrspace(5)
+// CHECK-NEXT:    [[A_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[A]] to 
ptr
+// CHECK-NEXT:    [[AGG_TMP_ASCAST:%.*]] = addrspacecast ptr addrspace(5) 
[[AGG_TMP]] to ptr
+// CHECK-NEXT:    call void @llvm.lifetime.start.p5(i64 16, ptr addrspace(5) 
[[A]]) #[[ATTR4:[0-9]+]]
+// CHECK-NEXT:    call void @llvm.lifetime.start.p5(i64 16, ptr addrspace(5) 
[[AGG_TMP]]) #[[ATTR4]]
+void test() {
+  A a;
+  foo(a);
+}
diff --git a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp 
b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp
index 67fa9f9c9cd98..50c374d2710f4 100644
--- a/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp
+++ b/clang/test/CodeGenCXX/stack-reuse-miscompile.cpp
@@ -26,6 +26,8 @@ const char * f(S s)
 // CHECK: [[T2:%.*]] = alloca %class.T, align 4
 // CHECK: [[T3:%.*]] = alloca %class.T, align 4
 //
+// CHECK: [[AGG:%.*]] = alloca %class.S, align 4
+//
 // FIXME: We could defer starting the lifetime of the return object of concat
 // until the call.
 // CHECK: call void @llvm.lifetime.start.p0(ptr [[T1]])
@@ -34,10 +36,12 @@ const char * f(S s)
 // CHECK: [[T4:%.*]] = call noundef ptr @_ZN1TC1EPKc(ptr {{[^,]*}} [[T2]], ptr 
noundef @.str)
 //
 // 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 @_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 21e784e0031de..bb5eba1c3252d 100644
--- a/clang/test/CodeGenCoroutines/pr59181.cpp
+++ b/clang/test/CodeGenCoroutines/pr59181.cpp
@@ -49,6 +49,7 @@ void foo() {
 }
 
 // CHECK: cleanup.cont:{{.*}}
+// CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[AGG:%agg.tmp]])
 // CHECK-NEXT: load i8
 // CHECK-NEXT: trunc
 // CHECK-NEXT: store i1 false
@@ -57,3 +58,6 @@ void foo() {
 // CHECK-NOT: call void @llvm.lifetime
 // CHECK: call void @llvm.coro.await.suspend.void(
 // CHECK-NEXT: %{{[0-9]+}} = call i8 @llvm.coro.suspend(
+
+// CHECK-LABEL: cleanup.done20:
+// CHECK: call void @llvm.lifetime.end.p0(ptr [[AGG]])

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

Reply via email to