[clang] [Clang][CodeGen] Do not emit lifetime intrinsics for coro promise alloca (PR #140548)

2025-05-20 Thread Weibo He via cfe-commits

NewSigma wrote:

After further consideration, I think this is not a satisfactory solution to the 
issue. I may revisit it if I develop a concrete plan. Apologies for any 
inconvenience.

https://github.com/llvm/llvm-project/pull/140548
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][CodeGen] Do not emit lifetime intrinsics for coro promise alloca (PR #140548)

2025-05-20 Thread Weibo He via cfe-commits

https://github.com/NewSigma closed 
https://github.com/llvm/llvm-project/pull/140548
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][CodeGen] Do not emit lifetime intrinsics for coro promise alloca (PR #140548)

2025-05-19 Thread Weibo He via cfe-commits

https://github.com/NewSigma created 
https://github.com/llvm/llvm-project/pull/140548

Coro promise has same lifetime as coro frame. It do not need explicit lifetime 
guarding. If we add lifetimes to it, middle end passes may assume promise dead 
after lifetime.end, leading to mis-optimizations.

Fix #120200 

>From d2e1a8d8a650f2a38387c500f942a0c6722c8a84 Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Mon, 19 May 2025 22:12:45 +0800
Subject: [PATCH] Do not emit lifetime intrinsics for coro promise alloca

---
 clang/lib/CodeGen/CGDecl.cpp   | 5 +
 clang/test/CodeGenCoroutines/coro-params.cpp   | 3 ---
 clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp | 2 +-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 4a8f7f6a42ecb..556adebc6fa39 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1343,6 +1343,11 @@ llvm::Value 
*CodeGenFunction::EmitLifetimeStart(llvm::TypeSize Size,
   if (!ShouldEmitLifetimeMarkers)
 return nullptr;
 
+  // No lifetimes on promise alloca, or middle end passes will assume promise
+  // dead after lifetime.end, leading to mis-optimization
+  if (Addr->getName() == "__promise")
+return nullptr;
+
   assert(Addr->getType()->getPointerAddressSpace() ==
  CGM.getDataLayout().getAllocaAddrSpace() &&
  "Pointer should be in alloca address space");
diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp 
b/clang/test/CodeGenCoroutines/coro-params.cpp
index 719726cca29c5..4f13e093197ff 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -84,7 +84,6 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam, 
TrivialABI trivialParam)
   // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], 
ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) #
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN10TrivialABIC1EOS_(ptr {{[^,]*}} 
%[[TrivialCopy]], ptr {{[^,]*}} %[[TrivialAlloca]])
-  // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: invoke void 
@_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeC1Ev(
 
   // CHECK: call void @_ZN14suspend_always12await_resumeEv(
@@ -106,7 +105,6 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam, 
TrivialABI trivialParam)
 
   // Destroy promise, then parameter copies:
   // CHECK: call void 
@_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeD1Ev(ptr
 {{[^,]*}} %__promise)
-  // CHECK-NEXT: call void @llvm.lifetime.end.p0(
   // CHECK-NEXT: call void @_ZN10TrivialABID1Ev(ptr {{[^,]*}} %[[TrivialCopy]])
   // CHECK-NEXT: call void @llvm.lifetime.end.p0(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(ptr {{[^,]*}} %[[McCopy]])
@@ -135,7 +133,6 @@ void dependent_params(T x, U, U y) {
   // CHECK-NEXT: call void @_ZN1BC1EOS_(ptr {{[^,]*}} %[[unnamed_copy]], ptr 
noundef nonnull align 4 dereferenceable(512) %0)
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN1BC1EOS_(ptr {{[^,]*}} %[[y_copy]], ptr noundef 
nonnull align 4 dereferenceable(512) %y)
-  // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: invoke void 
@_ZNSt16coroutine_traitsIJv1A1BS1_EE12promise_typeC1Ev(
 
   co_return;
diff --git a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp 
b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
index d71c2c558996a..d028c127e1e21 100644
--- a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
+++ b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
@@ -52,7 +52,7 @@ coroutine ArrayInitCoro() {
 // CHECK-NEXT:  store ptr %arrayinit.element, ptr 
%arrayinit.endOfInit.reload.addr, align 8
 co_await Awaiter{}
 // CHECK-NEXT:  @_ZNSt14suspend_always11await_readyEv
-// CHECK-NEXT:  br i1 %{{.+}}, label %await.ready, label %CoroSave30
+// CHECK-NEXT:  br i1 %{{.+}}, label %await.ready, label %CoroSave29
   };
   // CHECK:   await.cleanup:; preds = 
%AfterCoroSuspend{{.*}}
   // CHECK-NEXT:br label %cleanup{{.*}}.from.await.cleanup

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][CodeGen] Do not emit lifetime intrinsics for coro promise alloca (PR #140548)

2025-05-19 Thread Weibo He via cfe-commits

NewSigma wrote:

Here is a example

``` LLVM
define i32 @fn() {
entry:
  %__promise = alloca i32, align 4
  %id = call token @llvm.coro.id(i32 16, ptr nonnull %__promise, ptr nonnull 
@fn, ptr null)
  %hdl = call ptr @llvm.coro.begin(token %id, ptr null) #14
  %promise.addr = call ptr @llvm.coro.promise(ptr %hdl, i32 4, i1 false) #14
  call void @llvm.lifetime.start.p0(i64 4, ptr %promise.addr) #2
  store i32 5, ptr %promise.addr, align 4 ; DSE eliminates
  call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %promise.addr) #2
  %0 = call i1 @llvm.coro.end(ptr null, i1 false, token none) #14
  %value = load i32, ptr %promise.addr, align 4
  ret i32 %value
}
```

https://github.com/llvm/llvm-project/pull/140548
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [Clang][CodeGen] Do not emit lifetime intrinsics for coro promise alloca (PR #140548)

2025-05-19 Thread Weibo He via cfe-commits

https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/140548

>From d2e1a8d8a650f2a38387c500f942a0c6722c8a84 Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Mon, 19 May 2025 22:12:45 +0800
Subject: [PATCH 1/2] Do not emit lifetime intrinsics for coro promise alloca

---
 clang/lib/CodeGen/CGDecl.cpp   | 5 +
 clang/test/CodeGenCoroutines/coro-params.cpp   | 3 ---
 clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp | 2 +-
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 4a8f7f6a42ecb..556adebc6fa39 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1343,6 +1343,11 @@ llvm::Value 
*CodeGenFunction::EmitLifetimeStart(llvm::TypeSize Size,
   if (!ShouldEmitLifetimeMarkers)
 return nullptr;
 
+  // No lifetimes on promise alloca, or middle end passes will assume promise
+  // dead after lifetime.end, leading to mis-optimization
+  if (Addr->getName() == "__promise")
+return nullptr;
+
   assert(Addr->getType()->getPointerAddressSpace() ==
  CGM.getDataLayout().getAllocaAddrSpace() &&
  "Pointer should be in alloca address space");
diff --git a/clang/test/CodeGenCoroutines/coro-params.cpp 
b/clang/test/CodeGenCoroutines/coro-params.cpp
index 719726cca29c5..4f13e093197ff 100644
--- a/clang/test/CodeGenCoroutines/coro-params.cpp
+++ b/clang/test/CodeGenCoroutines/coro-params.cpp
@@ -84,7 +84,6 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam, 
TrivialABI trivialParam)
   // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(ptr {{[^,]*}} %[[McCopy]], 
ptr noundef nonnull align 4 dereferenceable(4) %[[McParam]]) #
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN10TrivialABIC1EOS_(ptr {{[^,]*}} 
%[[TrivialCopy]], ptr {{[^,]*}} %[[TrivialAlloca]])
-  // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: invoke void 
@_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeC1Ev(
 
   // CHECK: call void @_ZN14suspend_always12await_resumeEv(
@@ -106,7 +105,6 @@ void f(int val, MoveOnly moParam, MoveAndCopy mcParam, 
TrivialABI trivialParam)
 
   // Destroy promise, then parameter copies:
   // CHECK: call void 
@_ZNSt16coroutine_traitsIJvi8MoveOnly11MoveAndCopy10TrivialABIEE12promise_typeD1Ev(ptr
 {{[^,]*}} %__promise)
-  // CHECK-NEXT: call void @llvm.lifetime.end.p0(
   // CHECK-NEXT: call void @_ZN10TrivialABID1Ev(ptr {{[^,]*}} %[[TrivialCopy]])
   // CHECK-NEXT: call void @llvm.lifetime.end.p0(
   // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(ptr {{[^,]*}} %[[McCopy]])
@@ -135,7 +133,6 @@ void dependent_params(T x, U, U y) {
   // CHECK-NEXT: call void @_ZN1BC1EOS_(ptr {{[^,]*}} %[[unnamed_copy]], ptr 
noundef nonnull align 4 dereferenceable(512) %0)
   // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: call void @_ZN1BC1EOS_(ptr {{[^,]*}} %[[y_copy]], ptr noundef 
nonnull align 4 dereferenceable(512) %y)
-  // CHECK-NEXT: call void @llvm.lifetime.start.p0(
   // CHECK-NEXT: invoke void 
@_ZNSt16coroutine_traitsIJv1A1BS1_EE12promise_typeC1Ev(
 
   co_return;
diff --git a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp 
b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
index d71c2c558996a..d028c127e1e21 100644
--- a/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
+++ b/clang/test/CodeGenCoroutines/coro-suspend-cleanups.cpp
@@ -52,7 +52,7 @@ coroutine ArrayInitCoro() {
 // CHECK-NEXT:  store ptr %arrayinit.element, ptr 
%arrayinit.endOfInit.reload.addr, align 8
 co_await Awaiter{}
 // CHECK-NEXT:  @_ZNSt14suspend_always11await_readyEv
-// CHECK-NEXT:  br i1 %{{.+}}, label %await.ready, label %CoroSave30
+// CHECK-NEXT:  br i1 %{{.+}}, label %await.ready, label %CoroSave29
   };
   // CHECK:   await.cleanup:; preds = 
%AfterCoroSuspend{{.*}}
   // CHECK-NEXT:br label %cleanup{{.*}}.from.await.cleanup

>From 8f3a3bdc40d418d1b9325c63236b5d5741d0d760 Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Tue, 20 May 2025 08:03:02 +0800
Subject: [PATCH 2/2] Use VarDecl::getName instead of Value::getName

---
 clang/lib/CodeGen/CGDecl.cpp | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 556adebc6fa39..a308b8bf2c387 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -1343,11 +1343,6 @@ llvm::Value 
*CodeGenFunction::EmitLifetimeStart(llvm::TypeSize Size,
   if (!ShouldEmitLifetimeMarkers)
 return nullptr;
 
-  // No lifetimes on promise alloca, or middle end passes will assume promise
-  // dead after lifetime.end, leading to mis-optimization
-  if (Addr->getName() == "__promise")
-return nullptr;
-
   assert(Addr->getType()->getPointerAddressSpace() ==
  CGM.getDataLayout().getAllocaAddrSpace() &&
  "Poin

[clang] [Clang][CodeGen] Do not emit lifetime intrinsics for coro promise alloca (PR #140548)

2025-05-19 Thread Weibo He via cfe-commits


@@ -1343,6 +1343,11 @@ llvm::Value 
*CodeGenFunction::EmitLifetimeStart(llvm::TypeSize Size,
   if (!ShouldEmitLifetimeMarkers)
 return nullptr;
 
+  // No lifetimes on promise alloca, or middle end passes will assume promise
+  // dead after lifetime.end, leading to mis-optimization
+  if (Addr->getName() == "__promise")

NewSigma wrote:

Thank you for catching that. I've now used VarDecl::getName and tested it on 
non-assert builds.

https://github.com/llvm/llvm-project/pull/140548
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [LLVM][Coro] Add LifetimeMovePass (PR #144319)

2025-06-16 Thread Weibo He via cfe-commits

https://github.com/NewSigma created 
https://github.com/llvm/llvm-project/pull/144319

As suggested by @ChuanqiXu9 in #14 , I propose LifetimeMovePass, which was 
previously part of the CoroSplit pass, should now appear as a general pass. 
Lifetime markers determine whether we place alloca on the frame or on the 
stack. By moving these markers to optimized positions, we can reduce the 
coroutine frame size, leading to significant memory savings.
The LifetimeMovePass is positioned between SimplifyCFG and InstCombine. 
Currently, it only applies to pre-split coroutines, as I have not yet developed 
a concrete plan for its interaction with non-coroutine code. This patch is WIP, 
feel free to share your feedback or suggestions.

Close #49716 

>From 584d47295f7719f96ee77d32a6b4329f82be Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Mon, 16 Jun 2025 15:36:53 +0800
Subject: [PATCH 1/3] Add LifetimeMovePass

---
 clang/test/CodeGenCoroutines/pr56919.cpp  |   6 +-
 .../llvm/Transforms/Scalar/LifetimeMove.h |  23 ++
 llvm/lib/Passes/PassBuilder.cpp   |   1 +
 llvm/lib/Passes/PassRegistry.def  |   1 +
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  |  88 +--
 llvm/lib/Transforms/Scalar/CMakeLists.txt |   1 +
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp   | 223 ++
 .../Transforms/Coroutines/coro-alloca-07.ll   |   4 +-
 .../Coroutines/coro-split-rise-lifetime-01.ll |  39 +++
 .../Coroutines/coro-split-rise-lifetime-02.ll |  61 +
 .../Coroutines/coro-split-rise-lifetime-03.ll |  62 +
 .../Coroutines/coro-split-sink-lifetime-01.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-02.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-03.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-04.ll |   2 +-
 15 files changed, 422 insertions(+), 95 deletions(-)
 create mode 100644 llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
 create mode 100644 llvm/lib/Transforms/Scalar/LifetimeMove.cpp
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$40, %esi
+// CHECK-NEXT: movl$32, %esi
 // CHECK-NEXT: jmp _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$80, %esi
+// CHECK-NEXT: movl$64, %esi
 // CHECK-NEXT: jmp _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$120, %esi
+// CHECK-NEXT: movl$96, %esi
 // CHECK-NEXT: jmp _ZdlPvm
diff --git a/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h 
b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
new file mode 100644
index 0..f9a690433cb77
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
@@ -0,0 +1,23 @@
+//===- LifetimeMove.h - Narrowing lifetimes -*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+#define LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct LifetimeMovePass : PassInfoMixin {
+  /// Run the pass over the function.
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index a6c59c1ca846e..03ed9c51c2b74 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -279,6 +279,7 @@
 #include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
 #include "llvm/Transforms/Scalar/JumpThreading.h"
 #include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
 #include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
 #include "llvm/Transforms/Scalar/LoopBoundSplit.h"
 #include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index fe6f13477bb12..c457efc307bb4 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -402,6 +402,7 @@ FUNCTION_PASS("kcfi", KCFIPass())
 FUNCTION_PASS("kernel-info", KernelInfoPrinter(TM))
 FUNCTION_PASS("lcssa", LCSSAPass())
 FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrink

[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-06-19 Thread Weibo He via cfe-commits

https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/144319

>From 584d47295f7719f96ee77d32a6b4329f82be Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Mon, 16 Jun 2025 15:36:53 +0800
Subject: [PATCH 1/8] Add LifetimeMovePass

---
 clang/test/CodeGenCoroutines/pr56919.cpp  |   6 +-
 .../llvm/Transforms/Scalar/LifetimeMove.h |  23 ++
 llvm/lib/Passes/PassBuilder.cpp   |   1 +
 llvm/lib/Passes/PassRegistry.def  |   1 +
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  |  88 +--
 llvm/lib/Transforms/Scalar/CMakeLists.txt |   1 +
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp   | 223 ++
 .../Transforms/Coroutines/coro-alloca-07.ll   |   4 +-
 .../Coroutines/coro-split-rise-lifetime-01.ll |  39 +++
 .../Coroutines/coro-split-rise-lifetime-02.ll |  61 +
 .../Coroutines/coro-split-rise-lifetime-03.ll |  62 +
 .../Coroutines/coro-split-sink-lifetime-01.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-02.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-03.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-04.ll |   2 +-
 15 files changed, 422 insertions(+), 95 deletions(-)
 create mode 100644 llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
 create mode 100644 llvm/lib/Transforms/Scalar/LifetimeMove.cpp
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$40, %esi
+// CHECK-NEXT: movl$32, %esi
 // CHECK-NEXT: jmp _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$80, %esi
+// CHECK-NEXT: movl$64, %esi
 // CHECK-NEXT: jmp _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$120, %esi
+// CHECK-NEXT: movl$96, %esi
 // CHECK-NEXT: jmp _ZdlPvm
diff --git a/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h 
b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
new file mode 100644
index 0..f9a690433cb77
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
@@ -0,0 +1,23 @@
+//===- LifetimeMove.h - Narrowing lifetimes -*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+#define LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct LifetimeMovePass : PassInfoMixin {
+  /// Run the pass over the function.
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index a6c59c1ca846e..03ed9c51c2b74 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -279,6 +279,7 @@
 #include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
 #include "llvm/Transforms/Scalar/JumpThreading.h"
 #include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
 #include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
 #include "llvm/Transforms/Scalar/LoopBoundSplit.h"
 #include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index fe6f13477bb12..c457efc307bb4 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -402,6 +402,7 @@ FUNCTION_PASS("kcfi", KCFIPass())
 FUNCTION_PASS("kernel-info", KernelInfoPrinter(TM))
 FUNCTION_PASS("lcssa", LCSSAPass())
 FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass())
+FUNCTION_PASS("lifetime-move", LifetimeMovePass())
 FUNCTION_PASS("load-store-vectorizer", LoadStoreVectorizerPass())
 FUNCTION_PASS("loop-data-prefetch", LoopDataPrefetchPass())
 FUNCTION_PASS("loop-distribute", LoopDistributePass())
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 53d78edda2e9f..a7d7e6ef6d710 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
 #include "CoroInternal.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "ll

[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-06-22 Thread Weibo He via cfe-commits

https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/144319

>From 584d47295f7719f96ee77d32a6b4329f82be Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Mon, 16 Jun 2025 15:36:53 +0800
Subject: [PATCH 1/9] Add LifetimeMovePass

---
 clang/test/CodeGenCoroutines/pr56919.cpp  |   6 +-
 .../llvm/Transforms/Scalar/LifetimeMove.h |  23 ++
 llvm/lib/Passes/PassBuilder.cpp   |   1 +
 llvm/lib/Passes/PassRegistry.def  |   1 +
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  |  88 +--
 llvm/lib/Transforms/Scalar/CMakeLists.txt |   1 +
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp   | 223 ++
 .../Transforms/Coroutines/coro-alloca-07.ll   |   4 +-
 .../Coroutines/coro-split-rise-lifetime-01.ll |  39 +++
 .../Coroutines/coro-split-rise-lifetime-02.ll |  61 +
 .../Coroutines/coro-split-rise-lifetime-03.ll |  62 +
 .../Coroutines/coro-split-sink-lifetime-01.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-02.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-03.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-04.ll |   2 +-
 15 files changed, 422 insertions(+), 95 deletions(-)
 create mode 100644 llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
 create mode 100644 llvm/lib/Transforms/Scalar/LifetimeMove.cpp
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$40, %esi
+// CHECK-NEXT: movl$32, %esi
 // CHECK-NEXT: jmp _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$80, %esi
+// CHECK-NEXT: movl$64, %esi
 // CHECK-NEXT: jmp _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$120, %esi
+// CHECK-NEXT: movl$96, %esi
 // CHECK-NEXT: jmp _ZdlPvm
diff --git a/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h 
b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
new file mode 100644
index 0..f9a690433cb77
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
@@ -0,0 +1,23 @@
+//===- LifetimeMove.h - Narrowing lifetimes -*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+#define LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct LifetimeMovePass : PassInfoMixin {
+  /// Run the pass over the function.
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index a6c59c1ca846e..03ed9c51c2b74 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -279,6 +279,7 @@
 #include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
 #include "llvm/Transforms/Scalar/JumpThreading.h"
 #include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
 #include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
 #include "llvm/Transforms/Scalar/LoopBoundSplit.h"
 #include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index fe6f13477bb12..c457efc307bb4 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -402,6 +402,7 @@ FUNCTION_PASS("kcfi", KCFIPass())
 FUNCTION_PASS("kernel-info", KernelInfoPrinter(TM))
 FUNCTION_PASS("lcssa", LCSSAPass())
 FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass())
+FUNCTION_PASS("lifetime-move", LifetimeMovePass())
 FUNCTION_PASS("load-store-vectorizer", LoadStoreVectorizerPass())
 FUNCTION_PASS("loop-data-prefetch", LoopDataPrefetchPass())
 FUNCTION_PASS("loop-distribute", LoopDistributePass())
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 53d78edda2e9f..a7d7e6ef6d710 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
 #include "CoroInternal.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "ll

[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-06-22 Thread Weibo He via cfe-commits

NewSigma wrote:

RFC: https://discourse.llvm.org/t/rfc-add-lifetimemovepass/87005

https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-06-24 Thread Weibo He via cfe-commits

https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/144319

>From 584d47295f7719f96ee77d32a6b4329f82be Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Mon, 16 Jun 2025 15:36:53 +0800
Subject: [PATCH 01/10] Add LifetimeMovePass

---
 clang/test/CodeGenCoroutines/pr56919.cpp  |   6 +-
 .../llvm/Transforms/Scalar/LifetimeMove.h |  23 ++
 llvm/lib/Passes/PassBuilder.cpp   |   1 +
 llvm/lib/Passes/PassRegistry.def  |   1 +
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  |  88 +--
 llvm/lib/Transforms/Scalar/CMakeLists.txt |   1 +
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp   | 223 ++
 .../Transforms/Coroutines/coro-alloca-07.ll   |   4 +-
 .../Coroutines/coro-split-rise-lifetime-01.ll |  39 +++
 .../Coroutines/coro-split-rise-lifetime-02.ll |  61 +
 .../Coroutines/coro-split-rise-lifetime-03.ll |  62 +
 .../Coroutines/coro-split-sink-lifetime-01.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-02.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-03.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-04.ll |   2 +-
 15 files changed, 422 insertions(+), 95 deletions(-)
 create mode 100644 llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
 create mode 100644 llvm/lib/Transforms/Scalar/LifetimeMove.cpp
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$40, %esi
+// CHECK-NEXT: movl$32, %esi
 // CHECK-NEXT: jmp _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$80, %esi
+// CHECK-NEXT: movl$64, %esi
 // CHECK-NEXT: jmp _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$120, %esi
+// CHECK-NEXT: movl$96, %esi
 // CHECK-NEXT: jmp _ZdlPvm
diff --git a/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h 
b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
new file mode 100644
index 0..f9a690433cb77
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
@@ -0,0 +1,23 @@
+//===- LifetimeMove.h - Narrowing lifetimes -*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+#define LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct LifetimeMovePass : PassInfoMixin {
+  /// Run the pass over the function.
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index a6c59c1ca846e..03ed9c51c2b74 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -279,6 +279,7 @@
 #include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
 #include "llvm/Transforms/Scalar/JumpThreading.h"
 #include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
 #include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
 #include "llvm/Transforms/Scalar/LoopBoundSplit.h"
 #include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index fe6f13477bb12..c457efc307bb4 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -402,6 +402,7 @@ FUNCTION_PASS("kcfi", KCFIPass())
 FUNCTION_PASS("kernel-info", KernelInfoPrinter(TM))
 FUNCTION_PASS("lcssa", LCSSAPass())
 FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass())
+FUNCTION_PASS("lifetime-move", LifetimeMovePass())
 FUNCTION_PASS("load-store-vectorizer", LoadStoreVectorizerPass())
 FUNCTION_PASS("loop-data-prefetch", LoopDataPrefetchPass())
 FUNCTION_PASS("loop-distribute", LoopDistributePass())
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 53d78edda2e9f..a7d7e6ef6d710 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
 #include "CoroInternal.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "

[clang] [llvm] [LLVM][Coro] Add LifetimeMovePass (PR #144319)

2025-06-16 Thread Weibo He via cfe-commits

NewSigma wrote:

cc @ChuanqiXu9 @nikic 

https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [LLVM][Coro] Add LifetimeMovePass (PR #144319)

2025-06-16 Thread Weibo He via cfe-commits

https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/144319

>From 584d47295f7719f96ee77d32a6b4329f82be Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Mon, 16 Jun 2025 15:36:53 +0800
Subject: [PATCH 1/4] Add LifetimeMovePass

---
 clang/test/CodeGenCoroutines/pr56919.cpp  |   6 +-
 .../llvm/Transforms/Scalar/LifetimeMove.h |  23 ++
 llvm/lib/Passes/PassBuilder.cpp   |   1 +
 llvm/lib/Passes/PassRegistry.def  |   1 +
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  |  88 +--
 llvm/lib/Transforms/Scalar/CMakeLists.txt |   1 +
 llvm/lib/Transforms/Scalar/LifetimeMove.cpp   | 223 ++
 .../Transforms/Coroutines/coro-alloca-07.ll   |   4 +-
 .../Coroutines/coro-split-rise-lifetime-01.ll |  39 +++
 .../Coroutines/coro-split-rise-lifetime-02.ll |  61 +
 .../Coroutines/coro-split-rise-lifetime-03.ll |  62 +
 .../Coroutines/coro-split-sink-lifetime-01.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-02.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-03.ll |   2 +-
 .../Coroutines/coro-split-sink-lifetime-04.ll |   2 +-
 15 files changed, 422 insertions(+), 95 deletions(-)
 create mode 100644 llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
 create mode 100644 llvm/lib/Transforms/Scalar/LifetimeMove.cpp
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$40, %esi
+// CHECK-NEXT: movl$32, %esi
 // CHECK-NEXT: jmp _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$80, %esi
+// CHECK-NEXT: movl$64, %esi
 // CHECK-NEXT: jmp _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$120, %esi
+// CHECK-NEXT: movl$96, %esi
 // CHECK-NEXT: jmp _ZdlPvm
diff --git a/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h 
b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
new file mode 100644
index 0..f9a690433cb77
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Scalar/LifetimeMove.h
@@ -0,0 +1,23 @@
+//===- LifetimeMove.h - Narrowing lifetimes -*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+
+#ifndef LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+#define LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+struct LifetimeMovePass : PassInfoMixin {
+  /// Run the pass over the function.
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_LIFETIMEMOVER_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index a6c59c1ca846e..03ed9c51c2b74 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -279,6 +279,7 @@
 #include "llvm/Transforms/Scalar/JumpTableToSwitch.h"
 #include "llvm/Transforms/Scalar/JumpThreading.h"
 #include "llvm/Transforms/Scalar/LICM.h"
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
 #include "llvm/Transforms/Scalar/LoopAccessAnalysisPrinter.h"
 #include "llvm/Transforms/Scalar/LoopBoundSplit.h"
 #include "llvm/Transforms/Scalar/LoopDataPrefetch.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index fe6f13477bb12..c457efc307bb4 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -402,6 +402,7 @@ FUNCTION_PASS("kcfi", KCFIPass())
 FUNCTION_PASS("kernel-info", KernelInfoPrinter(TM))
 FUNCTION_PASS("lcssa", LCSSAPass())
 FUNCTION_PASS("libcalls-shrinkwrap", LibCallsShrinkWrapPass())
+FUNCTION_PASS("lifetime-move", LifetimeMovePass())
 FUNCTION_PASS("load-store-vectorizer", LoadStoreVectorizerPass())
 FUNCTION_PASS("loop-data-prefetch", LoopDataPrefetchPass())
 FUNCTION_PASS("loop-distribute", LoopDistributePass())
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 53d78edda2e9f..a7d7e6ef6d710 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
 #include "CoroInternal.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "ll

[clang] [llvm] [LLVM][Coro] Add LifetimeMovePass (PR #144319)

2025-06-16 Thread Weibo He via cfe-commits


@@ -0,0 +1,223 @@
+//===- LifetimeMove.cpp - Narrowing lifetimes 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// The LifetimeMovePass identifies the precise lifetime range of allocas
+//
+//===--===//
+
+#include "llvm/Transforms/Scalar/LifetimeMove.h"
+#include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/Transforms/Coroutines/CoroInstr.h"
+
+#define DEBUG_TYPE "lifetime-move"
+
+namespace llvm {
+static bool mayEscape(Value *V, User *U) {
+  if (V == U->stripInBoundsOffsets() || isa(U))
+return true;
+
+  if (auto *SI = dyn_cast(U))
+return SI->getValueOperand() == V;
+
+  if (auto *CB = dyn_cast(U)) {
+unsigned OpCount = CB->arg_size();
+for (unsigned Op = 0; Op < OpCount; ++Op)
+  if (V == CB->getArgOperand(Op) && !CB->doesNotCapture(Op))
+return true;
+  }
+  return false;
+}
+
+namespace {
+class LifetimeMover {
+  const DominatorTree &DT;
+
+  SmallVector Allocas;
+  // Critical points are instructions where the crossing of a variable's
+  // lifetime makes a difference. We attempt to move lifetime.end
+  // before critical points and lifetime.start after them.
+  SmallVector CriticalPoints;
+
+  SmallVector LifetimeStarts;
+  SmallVector LifetimeEnds;
+  SmallVector OtherUsers;
+  SmallPtrSet UserBBs;
+
+public:
+  LifetimeMover(Function &F, const DominatorTree &DT);
+
+  void run();
+
+private:
+  void sinkLifetimeStartMarkers(AllocaInst *AI);
+  void riseLifetimeEndMarkers();
+  bool collectLifetime(Instruction *I);
+  void reset();
+};
+} // namespace
+
+LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT) : DT(DT) {
+  for (Instruction &I : instructions(F)) {
+if (auto *AI = dyn_cast(&I))
+  Allocas.push_back(AI);
+else if (isa(I))
+  CriticalPoints.push_back(&I);
+  }
+}
+
+void LifetimeMover::run() {
+  for (auto *AI : Allocas) {
+reset();
+
+bool Escape = false;
+for (User *U : AI->users()) {
+  auto *I = cast(U);
+  // lifetime markers are not actual uses
+  if (collectLifetime(I))
+continue;
+
+  // GEP and bitcast used by lifetime markers
+  if (U->hasOneUse() && U->stripPointerCasts() == AI) {
+auto *U1 = cast(U->user_back());
+if (collectLifetime(U1))
+  continue;
+  }
+
+  Escape |= mayEscape(AI, U);
+  OtherUsers.push_back(I);
+  UserBBs.insert(I->getParent());
+}
+
+if (!LifetimeStarts.empty())
+  sinkLifetimeStartMarkers(AI);
+
+// Do not move lifetime.end if alloca escapes
+if (!LifetimeEnds.empty() && !Escape)
+  riseLifetimeEndMarkers();
+  }
+}
+/// For each local variable that all of its user are dominated by one of the
+/// critical point, we sink their lifetime.start markers to the place where
+/// after the critical point. Doing so minimizes the lifetime of each variable.
+void LifetimeMover::sinkLifetimeStartMarkers(AllocaInst *AI) {
+  auto Update = [this](Instruction *Old, Instruction *New) {
+// Reject if the new proposal lengthens the lifetime
+if (DT.dominates(New, Old))
+  return Old;
+
+bool DomAll = llvm::all_of(UserBBs, [this, New](BasicBlock *UserBB) {
+  // Instruction level analysis if lifetime and users share a common BB
+  if (UserBB == New->getParent()) {
+return llvm::all_of(OtherUsers, [this, New, UserBB](Instruction *I) {
+  return UserBB != I->getParent() || DT.dominates(New, I);
+});
+  }
+  // Otherwise, BB level analysis is enough
+  return DT.dominates(New, UserBB);
+});
+
+return DomAll ? New : Old;
+  };
+
+  // AllocaInst is a trivial critical point
+  Instruction *DomPoint = AI;
+  for (auto *P : CriticalPoints)
+DomPoint = Update(DomPoint, P);
+
+  // Sink lifetime.start markers to dominate block when they are
+  // only used outside the region.
+  if (DomPoint != AI) {
+// If existing position is better, do nothing
+for (auto *P : LifetimeStarts)
+  if (isPotentiallyReachable(DomPoint, P) && (P == Update(DomPoint, P)))
+return;
+
+auto *NewStart = LifetimeStarts[0]->clone();
+NewStart->replaceUsesOfWith(NewStart->getOperand(1), AI);
+NewStart->insertAfter(DomPoint->getIterator());
+
+// All the outsided lifetime.start markers are no longer necessary.
+for (auto *I : LifetimeStarts)
+  if (DT.dominates(I, DomPoint))
+I->eraseFromParent();
+  }
+}
+// Find the critical point that is dominated by all users of alloca,
+// we will rise lifetime.end markers before the critical point.
+void Lifetime

[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-06-16 Thread Weibo He via cfe-commits

https://github.com/NewSigma edited 
https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [CoroSplit] Rise lifetime.end to get smaller frame size (PR #143333)

2025-06-08 Thread Weibo He via cfe-commits

https://github.com/NewSigma created 
https://github.com/llvm/llvm-project/pull/14

Lifetime markers determine whether we place alloca on the frame or on the 
stack. We can move `lifetime.end` to an optimized position to reduce the 
coroutine frame size. I propose finding the suspend point that dominates all 
uses of alloca and raising the `lifetime.end` markers to the end of the 
corresponding save block. This should significantly reduce the frame size.

Close #49716 

>From 771ff1c9cc7d4b7f95d78d4219288d5c7ca0b83e Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Thu, 5 Jun 2025 21:51:06 +0800
Subject: [PATCH] Rise lifetime.end to get smaller frame size

---
 clang/test/CodeGenCoroutines/pr56919.cpp  |  6 +-
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  | 94 ++-
 .../Transforms/Coroutines/coro-alloca-06.ll   |  2 +
 .../Transforms/Coroutines/coro-alloca-07.ll   |  2 +
 .../Coroutines/coro-split-rise-lifetime-01.ll | 39 
 .../Coroutines/coro-split-rise-lifetime-02.ll | 61 
 .../Coroutines/coro-split-rise-lifetime-03.ll | 62 
 7 files changed, 259 insertions(+), 7 deletions(-)
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$40, %esi
+// CHECK-NEXT: movl$32, %esi
 // CHECK-NEXT: jmp _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$80, %esi
+// CHECK-NEXT: movl$64, %esi
 // CHECK-NEXT: jmp _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$120, %esi
+// CHECK-NEXT: movl$96, %esi
 // CHECK-NEXT: jmp _ZdlPvm
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 53d78edda2e9f..fb56832dec9e3 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
 #include "CoroInternal.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "llvm/Analysis/StackLifetime.h"
 #include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DebugInfo.h"
@@ -1767,6 +1768,18 @@ static void eliminateSwiftError(Function &F, coro::Shape 
&Shape) {
   }
 }
 
+static bool isLifetimeStart(Instruction *I) {
+  if (auto *II = dyn_cast(I))
+return II->getIntrinsicID() == Intrinsic::lifetime_start;
+  return false;
+}
+
+static bool isLifetimeEnd(Instruction *I) {
+  if (auto *II = dyn_cast(I))
+return II->getIntrinsicID() == Intrinsic::lifetime_end;
+  return false;
+}
+
 /// For each local variable that all of its user are only used inside one of
 /// suspended region, we sink their lifetime.start markers to the place where
 /// after the suspend block. Doing so minimizes the lifetime of each variable,
@@ -1819,7 +1832,7 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
 
   for (User *U : AI->users()) {
 Instruction *UI = cast(U);
-// For all users except lifetime.start markers, if they are all
+// For all users except lifetime markers, if they are all
 // dominated by one of the basic blocks and do not cross
 // suspend points as well, then there is no need to spill the
 // instruction.
@@ -1827,7 +1840,7 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
 Checker.isDefinitionAcrossSuspend(DomBB, UI)) {
   // Skip lifetime.start, GEP and bitcast used by lifetime.start
   // markers.
-  if (collectLifetimeStart(UI, AI))
+  if (collectLifetimeStart(UI, AI) || isLifetimeEnd(UI))
 continue;
   Valid = false;
   break;
@@ -1850,6 +1863,78 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
   }
 }
 
+static bool mayEscape(Value *V, User *U) {
+  if (V == U->stripInBoundsOffsets() || isa(U))
+return true;
+
+  if (auto *SI = dyn_cast(U))
+return SI->getValueOperand() == V;
+
+  if (auto *CB = dyn_cast(U)) {
+unsigned OpCount = CB->arg_size();
+for (unsigned Op = 0; Op < OpCount; ++Op)
+  if (V == CB->getArgOperand(Op) && !CB->doesNotCapture(Op))
+return true;
+  }
+  return false;
+}
+
+// Find the suspend point that dominate all uses of alloca,
+// we will rise lifetime.end markers to the end of corresponding save block.
+static void riseLifetimeEndMarkers(Function &F, const coro::Shape &Shape) {
+  const PostDominatorTree PDT(F

[clang] [llvm] [CoroSplit] Rise lifetime.end to get smaller frame size (PR #143333)

2025-06-08 Thread Weibo He via cfe-commits

https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/14

>From 771ff1c9cc7d4b7f95d78d4219288d5c7ca0b83e Mon Sep 17 00:00:00 2001
From: NewSigma 
Date: Thu, 5 Jun 2025 21:51:06 +0800
Subject: [PATCH 1/2] Rise lifetime.end to get smaller frame size

---
 clang/test/CodeGenCoroutines/pr56919.cpp  |  6 +-
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  | 94 ++-
 .../Transforms/Coroutines/coro-alloca-06.ll   |  2 +
 .../Transforms/Coroutines/coro-alloca-07.ll   |  2 +
 .../Coroutines/coro-split-rise-lifetime-01.ll | 39 
 .../Coroutines/coro-split-rise-lifetime-02.ll | 61 
 .../Coroutines/coro-split-rise-lifetime-03.ll | 62 
 7 files changed, 259 insertions(+), 7 deletions(-)
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$40, %esi
+// CHECK-NEXT: movl$32, %esi
 // CHECK-NEXT: jmp _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$80, %esi
+// CHECK-NEXT: movl$64, %esi
 // CHECK-NEXT: jmp _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl$120, %esi
+// CHECK-NEXT: movl$96, %esi
 // CHECK-NEXT: jmp _ZdlPvm
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 53d78edda2e9f..fb56832dec9e3 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
 #include "CoroInternal.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "llvm/Analysis/StackLifetime.h"
 #include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DebugInfo.h"
@@ -1767,6 +1768,18 @@ static void eliminateSwiftError(Function &F, coro::Shape 
&Shape) {
   }
 }
 
+static bool isLifetimeStart(Instruction *I) {
+  if (auto *II = dyn_cast(I))
+return II->getIntrinsicID() == Intrinsic::lifetime_start;
+  return false;
+}
+
+static bool isLifetimeEnd(Instruction *I) {
+  if (auto *II = dyn_cast(I))
+return II->getIntrinsicID() == Intrinsic::lifetime_end;
+  return false;
+}
+
 /// For each local variable that all of its user are only used inside one of
 /// suspended region, we sink their lifetime.start markers to the place where
 /// after the suspend block. Doing so minimizes the lifetime of each variable,
@@ -1819,7 +1832,7 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
 
   for (User *U : AI->users()) {
 Instruction *UI = cast(U);
-// For all users except lifetime.start markers, if they are all
+// For all users except lifetime markers, if they are all
 // dominated by one of the basic blocks and do not cross
 // suspend points as well, then there is no need to spill the
 // instruction.
@@ -1827,7 +1840,7 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
 Checker.isDefinitionAcrossSuspend(DomBB, UI)) {
   // Skip lifetime.start, GEP and bitcast used by lifetime.start
   // markers.
-  if (collectLifetimeStart(UI, AI))
+  if (collectLifetimeStart(UI, AI) || isLifetimeEnd(UI))
 continue;
   Valid = false;
   break;
@@ -1850,6 +1863,78 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
   }
 }
 
+static bool mayEscape(Value *V, User *U) {
+  if (V == U->stripInBoundsOffsets() || isa(U))
+return true;
+
+  if (auto *SI = dyn_cast(U))
+return SI->getValueOperand() == V;
+
+  if (auto *CB = dyn_cast(U)) {
+unsigned OpCount = CB->arg_size();
+for (unsigned Op = 0; Op < OpCount; ++Op)
+  if (V == CB->getArgOperand(Op) && !CB->doesNotCapture(Op))
+return true;
+  }
+  return false;
+}
+
+// Find the suspend point that dominate all uses of alloca,
+// we will rise lifetime.end markers to the end of corresponding save block.
+static void riseLifetimeEndMarkers(Function &F, const coro::Shape &Shape) {
+  const PostDominatorTree PDT(F);
+  for (Instruction &I : instructions(F)) {
+AllocaInst *AI = dyn_cast(&I);
+if (!AI)
+  continue;
+
+SmallVector LifetimeEnds;
+SmallPtrSet UserBBs{};
+bool Escape = false;
+for (User *U : AI->users()) {
+  auto *I = cast(U);
+  // lifetime markers are not actual uses
+  if (isLifetimeStart(I))
+continue;
+
+  if (isLifetimeEnd(I

[clang] [llvm] [CoroSplit] Rise lifetime.end to get smaller frame size (PR #143333)

2025-06-09 Thread Weibo He via cfe-commits

NewSigma wrote:

It sounds interesting, though I still don’t have a clear idea about it. What 
are the benefits for non-coroutine code, and where exactly should we place it 
in the pipeline?

https://github.com/llvm/llvm-project/pull/14
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [CoroSplit] Rise lifetime.end to get smaller frame size (PR #143333)

2025-06-09 Thread Weibo He via cfe-commits

NewSigma wrote:

Prefer make it a general pass

https://github.com/llvm/llvm-project/pull/14
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [CoroSplit] Rise lifetime.end to get smaller frame size (PR #143333)

2025-06-09 Thread Weibo He via cfe-commits

https://github.com/NewSigma closed 
https://github.com/llvm/llvm-project/pull/14
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-07-03 Thread Weibo He via cfe-commits


@@ -0,0 +1,341 @@
+//===- LifetimeMove.cpp - Narrowing lifetimes 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// The LifetimeMovePass identifies the precise lifetime range of allocas and
+// repositions lifetime markers to stricter positions.
+//
+//===--===//
+
+#include "llvm/Transforms/Utils/LifetimeMove.h"
+#include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/PtrUseVisitor.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/Transforms/Coroutines/CoroInstr.h"
+
+#define DEBUG_TYPE "lifetime-move"
+
+namespace llvm {
+namespace {
+class LifetimeMover : public PtrUseVisitor {
+  using This = LifetimeMover;
+  using Base = PtrUseVisitor;
+
+  const DominatorTree &DT;
+  const LoopInfo &LI;
+
+  SmallVector Allocas;
+  // Critical points are instructions where the crossing of a variable's
+  // lifetime makes a difference. We attempt to rise lifetime.end
+  // before critical points and sink lifetime.start after them.
+  SmallVector CriticalPoints;
+
+  SmallVector LifetimeStarts;
+  SmallVector LifetimeEnds;
+  SmallVector OtherUsers;
+  SmallPtrSet LifetimeStartBBs;
+  SmallPtrSet UserBBs;
+
+public:
+  LifetimeMover(Function &F, const DominatorTree &DT, const LoopInfo &LI);
+
+  bool run();
+
+  void visitInstruction(Instruction &I);
+  void visitPHINode(PHINode &I);
+  void visitSelectInst(SelectInst &I);
+  void visitStoreInst(StoreInst &SI);
+  void visitIntrinsicInst(IntrinsicInst &II);
+  void visitMemIntrinsic(MemIntrinsic &I);
+  void visitCallBase(CallBase &CB);
+
+private:
+  bool sinkLifetimeStartMarkers(AllocaInst *AI);
+  bool riseLifetimeEndMarkers();
+  void reset();
+};
+} // namespace
+
+LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT,
+ const LoopInfo &LI)
+: Base(F.getDataLayout()), DT(DT), LI(LI) {
+  for (Instruction &I : instructions(F)) {
+if (auto *AI = dyn_cast(&I))
+  Allocas.push_back(AI);
+else if (isa(I))
+  continue;
+else if (isa(I))
+  CriticalPoints.push_back(&I);
+else if (isa(I))
+  CriticalPoints.push_back(&I);
+else if (isa(I))
+  CriticalPoints.push_back(&I);
+  }
+}
+
+bool LifetimeMover::run() {
+  bool Changed = false;
+  for (auto *AI : Allocas) {
+reset();
+Base::visitPtr(*AI);
+
+if (!LifetimeStarts.empty())
+  Changed |= sinkLifetimeStartMarkers(AI);
+
+// Do not move lifetime.end if alloca escapes
+if (!LifetimeEnds.empty() && !PI.isEscaped())
+  Changed |= riseLifetimeEndMarkers();

NewSigma wrote:

> Should both of those transformation be limited only to allocas that don't 
> have their address captured?

We do not have to check it if alloca has not been captured. And this is the 
case for the very first `lifetime.start`, as we are doing here.

> There are no executions where allocas are live at the same time and their 
> storage overlaps.

You mean 'does not overlap'?

> Alive2 demonstration

The community has not yet reached a consensus on comparing a pointer outside 
its lifetime range, according to the RFC above. Therefore, the input IR should 
be considered invalid. What do we expect transform passes do on an invalid 
input? Specifically for the demo, LifetimeMovePass does nothing.

https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-07-06 Thread Weibo He via cfe-commits

https://github.com/NewSigma edited 
https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-06-29 Thread Weibo He via cfe-commits

https://github.com/NewSigma ready_for_review 
https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-06-29 Thread Weibo He via cfe-commits

NewSigma wrote:

CI fail seems not related.

I'm marking it ready for review because it works in my tests with ASAN enabled, 
both for coro code and non-coro code. The non-coro part should be landed in 
another PR if current patch looks good.

https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [Transforms] Add LifetimeMovePass (PR #144319)

2025-07-04 Thread Weibo He via cfe-commits


@@ -0,0 +1,341 @@
+//===- LifetimeMove.cpp - Narrowing lifetimes 
-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===--===//
+//
+// The LifetimeMovePass identifies the precise lifetime range of allocas and
+// repositions lifetime markers to stricter positions.
+//
+//===--===//
+
+#include "llvm/Transforms/Utils/LifetimeMove.h"
+#include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/PtrUseVisitor.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/Transforms/Coroutines/CoroInstr.h"
+
+#define DEBUG_TYPE "lifetime-move"
+
+namespace llvm {
+namespace {
+class LifetimeMover : public PtrUseVisitor {
+  using This = LifetimeMover;
+  using Base = PtrUseVisitor;
+
+  const DominatorTree &DT;
+  const LoopInfo &LI;
+
+  SmallVector Allocas;
+  // Critical points are instructions where the crossing of a variable's
+  // lifetime makes a difference. We attempt to rise lifetime.end
+  // before critical points and sink lifetime.start after them.
+  SmallVector CriticalPoints;
+
+  SmallVector LifetimeStarts;
+  SmallVector LifetimeEnds;
+  SmallVector OtherUsers;
+  SmallPtrSet LifetimeStartBBs;
+  SmallPtrSet UserBBs;
+
+public:
+  LifetimeMover(Function &F, const DominatorTree &DT, const LoopInfo &LI);
+
+  bool run();
+
+  void visitInstruction(Instruction &I);
+  void visitPHINode(PHINode &I);
+  void visitSelectInst(SelectInst &I);
+  void visitStoreInst(StoreInst &SI);
+  void visitIntrinsicInst(IntrinsicInst &II);
+  void visitMemIntrinsic(MemIntrinsic &I);
+  void visitCallBase(CallBase &CB);
+
+private:
+  bool sinkLifetimeStartMarkers(AllocaInst *AI);
+  bool riseLifetimeEndMarkers();
+  void reset();
+};
+} // namespace
+
+LifetimeMover::LifetimeMover(Function &F, const DominatorTree &DT,
+ const LoopInfo &LI)
+: Base(F.getDataLayout()), DT(DT), LI(LI) {
+  for (Instruction &I : instructions(F)) {
+if (auto *AI = dyn_cast(&I))
+  Allocas.push_back(AI);
+else if (isa(I))
+  continue;
+else if (isa(I))
+  CriticalPoints.push_back(&I);
+else if (isa(I))
+  CriticalPoints.push_back(&I);
+else if (isa(I))
+  CriticalPoints.push_back(&I);
+  }
+}
+
+bool LifetimeMover::run() {
+  bool Changed = false;
+  for (auto *AI : Allocas) {
+reset();
+Base::visitPtr(*AI);
+
+if (!LifetimeStarts.empty())
+  Changed |= sinkLifetimeStartMarkers(AI);
+
+// Do not move lifetime.end if alloca escapes
+if (!LifetimeEnds.empty() && !PI.isEscaped())
+  Changed |= riseLifetimeEndMarkers();

NewSigma wrote:

If we want to align with Alive2, restricting to allocas that never escape shall 
not work. Because

1. Escape is a subset of capturing
2. We do not consider the lifetime of other allocas when optimizing a specific 
alloca.

> I think Alive2 quite correctly points out that this changes observable 
> behavior.

The demo is essentially doing

``` C
int src() {
char a[1000];
char* p_b;
{
char b[1000];
p_b = b;
}
return a == p_b;
}

int tgt() {
char* p_b;
{
char b[1000];
p_b = b;
}
char a[1000];
return a == p_b;
}
```

The transform looks OK to me. Anyway, what’s the point of comparing alloca 
pointers except to demonstrate that the optimization works? We might understand 
the behavior from the backend's perspective. Lifetime markers are hints for 
optimization, not a requirement, and it is the backend's freedom not to respect 
them. Correct behavior should not rely on it.

https://github.com/llvm/llvm-project/pull/144319
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits