[clang] [clang-tools-extra] [codegen] Emit cleanups for lifetime-extended temporaries when an expr contains control-flow (PR #80698)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 12 03:29:58 PST 2024
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/80698
>From 28d90711ff8e4924135d4bd4e5f252d96ac41b93 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 23 Jan 2024 20:18:25 +0000
Subject: [PATCH 01/11] [misc-coroutine-hostile-raii] Use getOperand instead of
getCommonExpr
---
.../misc/CoroutineHostileRAIICheck.cpp | 2 +-
.../checkers/misc/coroutine-hostile-raii.cpp | 34 ++++++++++++++++++-
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp
index a0e8700b0522bc..360335b86c6418 100644
--- a/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/CoroutineHostileRAIICheck.cpp
@@ -56,7 +56,7 @@ AST_MATCHER_P(Stmt, forEachPrevStmt, ast_matchers::internal::Matcher<Stmt>,
// Matches the expression awaited by the `co_await`.
AST_MATCHER_P(CoawaitExpr, awaitable, ast_matchers::internal::Matcher<Expr>,
InnerMatcher) {
- if (Expr *E = Node.getCommonExpr())
+ if (Expr *E = Node.getOperand())
return InnerMatcher.matches(*E, Finder, Builder);
return false;
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp
index 55a7e4b8f2954a..c23c355dac1b2a 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp
@@ -1,7 +1,7 @@
// RUN: %check_clang_tidy -std=c++20 %s misc-coroutine-hostile-raii %t \
// RUN: -config="{CheckOptions: {\
// RUN: misc-coroutine-hostile-raii.RAIITypesList: 'my::Mutex; ::my::other::Mutex', \
-// RUN: misc-coroutine-hostile-raii.AllowedAwaitablesList: 'safe::awaitable; ::my::other::awaitable' \
+// RUN: misc-coroutine-hostile-raii.AllowedAwaitablesList: 'safe::awaitable; ::transformable::awaitable' \
// RUN: }}"
namespace std {
@@ -136,6 +136,9 @@ ReturnObject scopedLockableTest() {
absl::Mutex no_warning_5;
}
+// ================================================================================
+// Safe awaitable
+// ================================================================================
namespace safe {
struct awaitable {
bool await_ready() noexcept { return false; }
@@ -150,6 +153,32 @@ ReturnObject RAIISafeSuspendTest() {
co_await other{};
}
+// ================================================================================
+// Safe transformable awaitable
+// ================================================================================
+struct transformable { struct awaitable{}; };
+using alias_transformable_awaitable = transformable::awaitable;
+struct UseTransformAwaitable {
+ struct promise_type {
+ UseTransformAwaitable get_return_object() { return {}; }
+ std::suspend_always initial_suspend() { return {}; }
+ std::suspend_always final_suspend() noexcept { return {}; }
+ void unhandled_exception() {}
+ std::suspend_always await_transform(transformable::awaitable) { return {}; }
+ };
+};
+
+auto retAwaitable() { return transformable::awaitable{}; }
+UseTransformAwaitable RAIISafeSuspendTest2() {
+ absl::Mutex a;
+ co_await retAwaitable();
+ co_await transformable::awaitable{};
+ co_await alias_transformable_awaitable{};
+}
+
+// ================================================================================
+// Lambdas
+// ================================================================================
void lambda() {
absl::Mutex no_warning;
auto lambda = []() -> ReturnObject {
@@ -164,6 +193,9 @@ void lambda() {
absl::Mutex no_warning_2;
}
+// ================================================================================
+// Denylisted RAII
+// ================================================================================
template<class T>
ReturnObject raii_in_template(){
T a;
>From 442809ce6047f8f83c9f3f54b17182c18535bb03 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 5 Feb 2024 15:52:56 +0000
Subject: [PATCH 02/11] [codegen] Emit cleanups for lifetime-extended
temporaries when statment-expression has control-flow
---
clang/lib/CodeGen/CGCleanup.cpp | 31 +++++--
clang/lib/CodeGen/CodeGenFunction.h | 4 +
.../return-in-stmt-expr-cleanup.cpp | 37 +++++++++
.../coro-suspend-in-agg-init.cpp | 82 +++++++++++++++++++
4 files changed, 145 insertions(+), 9 deletions(-)
create mode 100644 clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp
create mode 100644 clang/test/CodeGenCoroutines/coro-suspend-in-agg-init.cpp
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index f87caf050eeaa7..da0528b271aa37 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -488,16 +488,11 @@ void CodeGenFunction::PopCleanupBlocks(
}
}
-/// Pops cleanup blocks until the given savepoint is reached, then add the
-/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
-void CodeGenFunction::PopCleanupBlocks(
- EHScopeStack::stable_iterator Old, size_t OldLifetimeExtendedSize,
- std::initializer_list<llvm::Value **> ValuesToReload) {
- PopCleanupBlocks(Old, ValuesToReload);
-
- // Move our deferred cleanups onto the EH stack.
+/// Adds deferred lifetime-extended cleanups onto the EH stack.
+void CodeGenFunction::AddLifetimeExtendedCleanups(size_t OldLifetimeExtendedSize) {
for (size_t I = OldLifetimeExtendedSize,
- E = LifetimeExtendedCleanupStack.size(); I != E; /**/) {
+ E = LifetimeExtendedCleanupStack.size();
+ I != E;) {
// Alignment should be guaranteed by the vptrs in the individual cleanups.
assert((I % alignof(LifetimeExtendedCleanupHeader) == 0) &&
"misaligned cleanup stack entry");
@@ -519,6 +514,17 @@ void CodeGenFunction::PopCleanupBlocks(
I += sizeof(ActiveFlag);
}
}
+}
+
+/// Pops cleanup blocks until the given savepoint is reached, then add the
+/// cleanups from the given savepoint in the lifetime-extended cleanups stack.
+void CodeGenFunction::PopCleanupBlocks(
+ EHScopeStack::stable_iterator Old, size_t OldLifetimeExtendedSize,
+ std::initializer_list<llvm::Value **> ValuesToReload) {
+ PopCleanupBlocks(Old, ValuesToReload);
+
+ // Move our deferred cleanups onto the EH stack.
+ AddLifetimeExtendedCleanups(OldLifetimeExtendedSize);
LifetimeExtendedCleanupStack.resize(OldLifetimeExtendedSize);
}
@@ -1102,6 +1108,13 @@ void CodeGenFunction::EmitBranchThroughCleanup(JumpDest Dest) {
if (!HaveInsertPoint())
return;
+ // If we have lifetime-extended (LE) cleanups, then we must be emitting a
+ // branch within an expression. Emit all the LE cleanups by adding them to the
+ // EHStack. Do not remove them from lifetime-extended stack, they need to be
+ // emitted again after the expression completes.
+ RunCleanupsScope LifetimeExtendedCleanups(*this);
+ AddLifetimeExtendedCleanups(0);
+
// Create the branch.
llvm::BranchInst *BI = Builder.CreateBr(Dest.getBlock());
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 143ad64e8816b1..ac55e84034bc60 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1143,6 +1143,10 @@ class CodeGenFunction : public CodeGenTypeCache {
PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize,
std::initializer_list<llvm::Value **> ValuesToReload = {});
+ /// Adds lifetime-extended cleanups from the given position to the stack.
+ /// (does not remove the cleanups from lifetime extended stack).
+ void AddLifetimeExtendedCleanups(size_t OldLifetimeExtendedSize);
+
/// Takes the old cleanup stack size and emits the cleanup blocks
/// that have been added, then adds all lifetime-extended cleanups from
/// the given position to the stack.
diff --git a/clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp b/clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp
new file mode 100644
index 00000000000000..214becd81e61af
--- /dev/null
+++ b/clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+// Context: GH63818
+struct Printy {
+ ~Printy() { }
+};
+
+struct Printies {
+ const Printy &a;
+ const Printy &b;
+ ~Printies() {}
+};
+
+bool foo();
+
+void bar() {
+ Printies p2{
+ // CHECK: store ptr %ref.tmp
+ Printy(),
+ ({
+ if(foo()) {
+ // CHECK-LABEL: if.then:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ return;
+ }
+ // CHECK-LABEL: if.end:
+ // CHECK-NEXT: store ptr %ref.tmp1
+ Printy();
+ })};
+ // CHECK-NEXT: call void @_ZN8PrintiesD1Ev
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ return;
+}
+
diff --git a/clang/test/CodeGenCoroutines/coro-suspend-in-agg-init.cpp b/clang/test/CodeGenCoroutines/coro-suspend-in-agg-init.cpp
new file mode 100644
index 00000000000000..362e212b7fba39
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-suspend-in-agg-init.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 --std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+// Context: GH63818
+
+#include "Inputs/coroutine.h"
+
+struct coroutine {
+ struct promise_type;
+ std::coroutine_handle<promise_type> handle;
+};
+
+struct coroutine::promise_type {
+ coroutine get_return_object() {
+ return {std::coroutine_handle<promise_type>::from_promise(*this)};
+ }
+ std::suspend_never initial_suspend() noexcept { return {}; }
+ std::suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+};
+
+struct Printy { ~Printy(); };
+
+struct Printies {
+ const Printy &a;
+ const Printy &b;
+ const Printy &c;
+};
+
+struct Awaiter : std::suspend_always {
+ Printy await_resume() { return {}; }
+};
+
+// CHECK: define dso_local ptr @_Z5test1v()
+coroutine test1() {
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+ Printies p1{
+ Printy(),
+ co_await Awaiter{},
+ // CHECK: await.cleanup:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %cleanup{{.*}}.from.await.cleanup
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+
+ co_await Awaiter{}
+ // CHECK: await2.cleanup:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %cleanup{{.*}}.from.await2.cleanup
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+ };
+
+ // CHECK-COUNT-3: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label
+
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+
+ // CHECK: unreachable:
+}
+
+void bar(const Printy& a, const Printy& b);
+
+// CHECK: define dso_local ptr @_Z5test2v()
+coroutine test2() {
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+ bar(
+ Printy(),
+ co_await Awaiter{}
+ // CHECK: await.cleanup:
+ // CHECK-NEXT: br label %cleanup{{.*}}.from.await.cleanup
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+ );
+ // CHECK: await.ready:
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+
+ // CHECK: cleanup{{.*}}:
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK-NOT: @_ZN6PrintyD1Ev
+
+ // CHECK: unreachable:
+}
>From 41258086773829db7f17afa9df192d6ca56b2bfa Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 5 Feb 2024 16:52:25 +0000
Subject: [PATCH 03/11] format
---
clang/lib/CodeGen/CGCleanup.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index da0528b271aa37..39c65c0b07c6f6 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -489,7 +489,8 @@ void CodeGenFunction::PopCleanupBlocks(
}
/// Adds deferred lifetime-extended cleanups onto the EH stack.
-void CodeGenFunction::AddLifetimeExtendedCleanups(size_t OldLifetimeExtendedSize) {
+void CodeGenFunction::AddLifetimeExtendedCleanups(
+ size_t OldLifetimeExtendedSize) {
for (size_t I = OldLifetimeExtendedSize,
E = LifetimeExtendedCleanupStack.size();
I != E;) {
>From 35437116444d9ac2827634456a449c19394434d7 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 6 Feb 2024 14:27:40 +0000
Subject: [PATCH 04/11] Emit not-all but only necessary lifetime-extended
cleanups for each branch
---
clang/lib/CodeGen/CGCleanup.cpp | 3 +-
clang/lib/CodeGen/CGStmt.cpp | 2 +-
clang/lib/CodeGen/CodeGenFunction.h | 17 ++++-
.../control-flow-in-stmt-expr-cleanup.cpp | 68 +++++++++++++++++++
.../return-in-stmt-expr-cleanup.cpp | 37 ----------
5 files changed, 85 insertions(+), 42 deletions(-)
create mode 100644 clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
delete mode 100644 clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index 39c65c0b07c6f6..a5a49917d108f9 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -1114,7 +1114,8 @@ void CodeGenFunction::EmitBranchThroughCleanup(JumpDest Dest) {
// EHStack. Do not remove them from lifetime-extended stack, they need to be
// emitted again after the expression completes.
RunCleanupsScope LifetimeExtendedCleanups(*this);
- AddLifetimeExtendedCleanups(0);
+ if(Dest.isValid())
+ AddLifetimeExtendedCleanups(Dest.getLifetimeExtendedDepth());
// Create the branch.
llvm::BranchInst *BI = Builder.CreateBr(Dest.getBlock());
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index beff0ad9da2709..8d7e2616c168c0 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -628,7 +628,7 @@ CodeGenFunction::getJumpDestForLabel(const LabelDecl *D) {
// Create, but don't insert, the new block.
Dest = JumpDest(createBasicBlock(D->getName()),
- EHScopeStack::stable_iterator::invalid(),
+ EHScopeStack::stable_iterator::invalid(), 0,
NextCleanupDestIndex++);
return Dest;
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index ac55e84034bc60..78bc45dca2936c 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -238,15 +238,20 @@ class CodeGenFunction : public CodeGenTypeCache {
/// A jump destination is an abstract label, branching to which may
/// require a jump out through normal cleanups.
struct JumpDest {
- JumpDest() : Block(nullptr), Index(0) {}
+ JumpDest() : Block(nullptr), LifetimeExtendedDepth(0), Index(0) {}
JumpDest(llvm::BasicBlock *Block, EHScopeStack::stable_iterator Depth,
- unsigned Index)
- : Block(Block), ScopeDepth(Depth), Index(Index) {}
+ unsigned LifetimeExtendedScopeDepth, unsigned Index)
+ : Block(Block), ScopeDepth(Depth),
+ LifetimeExtendedDepth(LifetimeExtendedScopeDepth), Index(Index) {
+ }
bool isValid() const { return Block != nullptr; }
llvm::BasicBlock *getBlock() const { return Block; }
EHScopeStack::stable_iterator getScopeDepth() const { return ScopeDepth; }
unsigned getDestIndex() const { return Index; }
+ unsigned getLifetimeExtendedDepth() const {
+ return LifetimeExtendedDepth;
+ }
// This should be used cautiously.
void setScopeDepth(EHScopeStack::stable_iterator depth) {
@@ -256,6 +261,11 @@ class CodeGenFunction : public CodeGenTypeCache {
private:
llvm::BasicBlock *Block;
EHScopeStack::stable_iterator ScopeDepth;
+
+ // Size of the lifetime-extended cleanup stack in destination scope.
+ // This can only occur when nested stmt-expr's contains branches.
+ // This is useful to emit only the necessary lifeitme-extended cleanups.
+ unsigned LifetimeExtendedDepth;
unsigned Index;
};
@@ -1163,6 +1173,7 @@ class CodeGenFunction : public CodeGenTypeCache {
JumpDest getJumpDestInCurrentScope(llvm::BasicBlock *Target) {
return JumpDest(Target,
EHStack.getInnermostNormalCleanup(),
+ LifetimeExtendedCleanupStack.size(),
NextCleanupDestIndex++);
}
diff --git a/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
new file mode 100644
index 00000000000000..a53b0fcf040927
--- /dev/null
+++ b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+// Context: GH63818
+struct Printy {
+ ~Printy() { }
+};
+
+struct Printies {
+ const Printy &a;
+ const Printy &b;
+ ~Printies() {}
+};
+
+bool foo();
+
+void bar() {
+// CHECK: define dso_local void @_Z3barv()
+// CHECK-NOT: call void @_ZN6PrintyD1Ev
+ Printies p2{
+ Printy(),
+ ({
+ if(foo()) {
+ // CHECK: if.then:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ // CHECK-NOT: call void @_ZN6PrintyD1Ev
+ return;
+ }
+ Printy();
+ })};
+ // CHECK: if.end:
+ // CHECK: call void @_ZN8PrintiesD1Ev
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ return;
+ // CHECK-NOT: call void @_ZN6PrintyD1Ev
+}
+
+
+void test_break() {
+// CHECK: define dso_local void @_Z10test_breakv()
+// CHECK-NOT: call void @_ZN6PrintyD1Ev
+ Printies p2{Printy(), ({
+ for (;;) {
+ Printies p3{Printy(), ({
+ if(foo()) {
+ break;
+ // CHECK: if.then:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %for.end
+ // CHECK-NOT: call void @_ZN6PrintyD1Ev
+ }
+ Printy();
+ })};
+ // CHECK: if.end:
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK-NOT: call void @_ZN6PrintyD1Ev
+ }
+ Printy();
+ })};
+ // CHECK: for.end:
+ // CHECK-COUNT-2: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: ret void
+ // CHECK-NOT: call void @_ZN6PrintyD1Ev
+}
+
diff --git a/clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp b/clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp
deleted file mode 100644
index 214becd81e61af..00000000000000
--- a/clang/test/CodeGenCXX/return-in-stmt-expr-cleanup.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
-
-// Context: GH63818
-struct Printy {
- ~Printy() { }
-};
-
-struct Printies {
- const Printy &a;
- const Printy &b;
- ~Printies() {}
-};
-
-bool foo();
-
-void bar() {
- Printies p2{
- // CHECK: store ptr %ref.tmp
- Printy(),
- ({
- if(foo()) {
- // CHECK-LABEL: if.then:
- // CHECK-NEXT: call void @_ZN6PrintyD1Ev
- // CHECK-NEXT: br label %return
- return;
- }
- // CHECK-LABEL: if.end:
- // CHECK-NEXT: store ptr %ref.tmp1
- Printy();
- })};
- // CHECK-NEXT: call void @_ZN8PrintiesD1Ev
- // CHECK-NEXT: call void @_ZN6PrintyD1Ev
- // CHECK-NEXT: call void @_ZN6PrintyD1Ev
- // CHECK-NEXT: br label %return
- return;
-}
-
>From d23cdb3fb5998cab7e7102ac715eca8e0bfe57c0 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 6 Feb 2024 14:34:25 +0000
Subject: [PATCH 05/11] format
---
clang/lib/CodeGen/CGCleanup.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index a5a49917d108f9..e97441310e34c6 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -1114,7 +1114,7 @@ void CodeGenFunction::EmitBranchThroughCleanup(JumpDest Dest) {
// EHStack. Do not remove them from lifetime-extended stack, they need to be
// emitted again after the expression completes.
RunCleanupsScope LifetimeExtendedCleanups(*this);
- if(Dest.isValid())
+ if (Dest.isValid())
AddLifetimeExtendedCleanups(Dest.getLifetimeExtendedDepth());
// Create the branch.
>From dc3659a4f061ab05fd51ed39d673c5a2710cd41f Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 6 Feb 2024 14:35:24 +0000
Subject: [PATCH 06/11] format
---
clang/lib/CodeGen/CodeGenFunction.h | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 78bc45dca2936c..11e4c6481776f7 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -249,9 +249,7 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::BasicBlock *getBlock() const { return Block; }
EHScopeStack::stable_iterator getScopeDepth() const { return ScopeDepth; }
unsigned getDestIndex() const { return Index; }
- unsigned getLifetimeExtendedDepth() const {
- return LifetimeExtendedDepth;
- }
+ unsigned getLifetimeExtendedDepth() const { return LifetimeExtendedDepth; }
// This should be used cautiously.
void setScopeDepth(EHScopeStack::stable_iterator depth) {
@@ -1171,8 +1169,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// target of a potentially scope-crossing jump; get a stable handle
/// to which we can perform this jump later.
JumpDest getJumpDestInCurrentScope(llvm::BasicBlock *Target) {
- return JumpDest(Target,
- EHStack.getInnermostNormalCleanup(),
+ return JumpDest(Target, EHStack.getInnermostNormalCleanup(),
LifetimeExtendedCleanupStack.size(),
NextCleanupDestIndex++);
}
>From ddf7c244e6d142cf61c7277d06ead2e0fb79f4a3 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 6 Feb 2024 14:52:44 +0000
Subject: [PATCH 07/11] format
---
clang/lib/CodeGen/CodeGenFunction.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 11e4c6481776f7..ec9f0de0c03385 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -242,8 +242,7 @@ class CodeGenFunction : public CodeGenTypeCache {
JumpDest(llvm::BasicBlock *Block, EHScopeStack::stable_iterator Depth,
unsigned LifetimeExtendedScopeDepth, unsigned Index)
: Block(Block), ScopeDepth(Depth),
- LifetimeExtendedDepth(LifetimeExtendedScopeDepth), Index(Index) {
- }
+ LifetimeExtendedDepth(LifetimeExtendedScopeDepth), Index(Index) {}
bool isValid() const { return Block != nullptr; }
llvm::BasicBlock *getBlock() const { return Block; }
>From a58284b872ca4118426d0e9339118c025ef8a0d3 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Fri, 9 Feb 2024 20:03:27 +0000
Subject: [PATCH 08/11] Add BranchInExpr cleanups kind to handle more missing
dtor calls
---
clang/lib/CodeGen/CGCleanup.cpp | 40 +++---
clang/lib/CodeGen/CGDecl.cpp | 15 ++-
clang/lib/CodeGen/CGExprAgg.cpp | 19 ++-
clang/lib/CodeGen/CGStmt.cpp | 6 +-
clang/lib/CodeGen/CodeGenFunction.cpp | 3 +
clang/lib/CodeGen/CodeGenFunction.h | 117 ++++++++++++-----
clang/lib/CodeGen/EHScopeStack.h | 10 ++
.../control-flow-in-stmt-expr-cleanup.cpp | 119 +++++++++++++++++-
8 files changed, 260 insertions(+), 69 deletions(-)
diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp
index e97441310e34c6..af787440f8847b 100644
--- a/clang/lib/CodeGen/CGCleanup.cpp
+++ b/clang/lib/CodeGen/CGCleanup.cpp
@@ -18,6 +18,8 @@
#include "CGCleanup.h"
#include "CodeGenFunction.h"
+#include "EHScopeStack.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
@@ -488,29 +490,22 @@ void CodeGenFunction::PopCleanupBlocks(
}
}
-/// Adds deferred lifetime-extended cleanups onto the EH stack.
-void CodeGenFunction::AddLifetimeExtendedCleanups(
- size_t OldLifetimeExtendedSize) {
- for (size_t I = OldLifetimeExtendedSize,
- E = LifetimeExtendedCleanupStack.size();
- I != E;) {
+void CodeGenFunction::AddDeferredCleanups(DeferredCleanupStack &Cleanups,
+ size_t OldSize) {
+ for (size_t I = OldSize, E = Cleanups.size(); I != E;) {
// Alignment should be guaranteed by the vptrs in the individual cleanups.
- assert((I % alignof(LifetimeExtendedCleanupHeader) == 0) &&
+ assert((I % alignof(DeferredCleanupHeader) == 0) &&
"misaligned cleanup stack entry");
- LifetimeExtendedCleanupHeader &Header =
- reinterpret_cast<LifetimeExtendedCleanupHeader&>(
- LifetimeExtendedCleanupStack[I]);
+ DeferredCleanupHeader &Header =
+ reinterpret_cast<DeferredCleanupHeader &>(Cleanups[I]);
I += sizeof(Header);
- EHStack.pushCopyOfCleanup(Header.getKind(),
- &LifetimeExtendedCleanupStack[I],
- Header.getSize());
+ EHStack.pushCopyOfCleanup(Header.getKind(), &Cleanups[I], Header.getSize());
I += Header.getSize();
if (Header.isConditional()) {
- Address ActiveFlag =
- reinterpret_cast<Address &>(LifetimeExtendedCleanupStack[I]);
+ Address ActiveFlag = reinterpret_cast<Address &>(Cleanups[I]);
initFullExprCleanupWithFlag(ActiveFlag);
I += sizeof(ActiveFlag);
}
@@ -524,8 +519,8 @@ void CodeGenFunction::PopCleanupBlocks(
std::initializer_list<llvm::Value **> ValuesToReload) {
PopCleanupBlocks(Old, ValuesToReload);
- // Move our deferred cleanups onto the EH stack.
- AddLifetimeExtendedCleanups(OldLifetimeExtendedSize);
+ // Move our deferred lifetime-extended cleanups onto the EH stack.
+ AddDeferredCleanups(LifetimeExtendedCleanupStack, OldLifetimeExtendedSize);
LifetimeExtendedCleanupStack.resize(OldLifetimeExtendedSize);
}
@@ -1109,13 +1104,12 @@ void CodeGenFunction::EmitBranchThroughCleanup(JumpDest Dest) {
if (!HaveInsertPoint())
return;
- // If we have lifetime-extended (LE) cleanups, then we must be emitting a
- // branch within an expression. Emit all the LE cleanups by adding them to the
- // EHStack. Do not remove them from lifetime-extended stack, they need to be
- // emitted again after the expression completes.
- RunCleanupsScope LifetimeExtendedCleanups(*this);
+ // If we are emitting a branch in a partial expression, add deferred cleanups
+ // to EHStack, which would otherwise have only been emitted after the full
+ // expression.
+ RunCleanupsScope BranchInExprCleanups(*this);
if (Dest.isValid())
- AddLifetimeExtendedCleanups(Dest.getLifetimeExtendedDepth());
+ AddDeferredCleanups(BranchInExprCleanupStack, Dest.getBranchInExprDepth());
// Create the branch.
llvm::BranchInst *BI = Builder.CreateBr(Dest.getBlock());
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index bbe14ef4c17244..66df9186155d9c 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "Address.h"
#include "CGBlocks.h"
#include "CGCXXABI.h"
#include "CGCleanup.h"
@@ -19,6 +20,7 @@
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "ConstantEmitter.h"
+#include "EHScopeStack.h"
#include "PatternInit.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
@@ -2209,8 +2211,11 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind,
static_cast<CleanupKind>(cleanupKind & ~NormalCleanup), addr, type,
destroyer, useEHCleanupForArray);
- return pushCleanupAfterFullExprWithActiveFlag<DestroyObject>(
- cleanupKind, Address::invalid(), addr, type, destroyer, useEHCleanupForArray);
+ pushDestroy(BranchInExprCleanup, addr, type, destroyer,
+ useEHCleanupForArray);
+ return pushDeferredCleanup<DestroyObject>(
+ LifetimeExtendedCleanupStack, cleanupKind, Address::invalid(), addr,
+ type, destroyer, useEHCleanupForArray);
}
// Otherwise, we should only destroy the object if it's been initialized.
@@ -2232,9 +2237,9 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind,
initFullExprCleanupWithFlag(ActiveFlag);
}
- pushCleanupAfterFullExprWithActiveFlag<ConditionalCleanupType>(
- cleanupKind, ActiveFlag, SavedAddr, type, destroyer,
- useEHCleanupForArray);
+ pushDeferredCleanup<ConditionalCleanupType>(
+ LifetimeExtendedCleanupStack, cleanupKind, ActiveFlag, SavedAddr, type,
+ destroyer, useEHCleanupForArray);
}
/// emitDestroy - Immediately perform the destruction of the given
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 22f55fe9aac904..bd2e9bc0f82a91 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -15,6 +15,7 @@
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "ConstantEmitter.h"
+#include "EHScopeStack.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
@@ -553,6 +554,7 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
Address endOfInit = Address::invalid();
EHScopeStack::stable_iterator cleanup;
llvm::Instruction *cleanupDominator = nullptr;
+ auto myDtorKind = dtorKind;
if (CGF.needsEHCleanup(dtorKind)) {
// In principle we could tell the cleanup where we are more
// directly, but the control flow can get so varied here that it
@@ -580,6 +582,7 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
// elements have been initialized.
llvm::Value *element = begin;
+ CodeGenFunction::RestoreBranchInExpr branchInExpr(CGF);
// Emit the explicit initializers.
for (uint64_t i = 0; i != NumInitElements; ++i) {
// Advance to the next element.
@@ -592,10 +595,14 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
// observed to be unnecessary.
if (endOfInit.isValid()) Builder.CreateStore(element, endOfInit);
}
-
- LValue elementLV = CGF.MakeAddrLValue(
- Address(element, llvmElementType, elementAlign), elementType);
+ Address address = Address(element, llvmElementType, elementAlign);
+ LValue elementLV = CGF.MakeAddrLValue(address, elementType);
EmitInitializationToLValue(Args[i], elementLV);
+ // Schedule to emit element cleanup if we see a branch in the array
+ // initialisation statement.
+ if (CGF.needsBranchCleanup(myDtorKind))
+ CGF.pushDestroy(BranchInExprCleanup, address, elementType,
+ CGF.getDestroyer(myDtorKind), false /*oder true ?*/);
}
// Check whether there's a non-trivial array-fill expression.
@@ -1754,7 +1761,7 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
return;
}
-
+ CodeGenFunction::RestoreBranchInExpr branchInExpr(CGF);
// Here we iterate over the fields; this makes it simpler to both
// default-initialize fields and skip over unnamed fields.
for (const auto *field : record->fields()) {
@@ -1793,6 +1800,10 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
if (QualType::DestructionKind dtorKind
= field->getType().isDestructedType()) {
assert(LV.isSimple());
+ if(CGF.needsBranchCleanup(dtorKind)) {
+ CGF.pushDestroy(BranchInExprCleanup, LV.getAddress(CGF),
+ field->getType(), CGF.getDestroyer(dtorKind), false);
+ }
if (CGF.needsEHCleanup(dtorKind)) {
CGF.pushDestroy(EHCleanup, LV.getAddress(CGF), field->getType(),
CGF.getDestroyer(dtorKind), false);
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 8d7e2616c168c0..9f1ab1e7817179 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -627,9 +627,9 @@ CodeGenFunction::getJumpDestForLabel(const LabelDecl *D) {
if (Dest.isValid()) return Dest;
// Create, but don't insert, the new block.
- Dest = JumpDest(createBasicBlock(D->getName()),
- EHScopeStack::stable_iterator::invalid(), 0,
- NextCleanupDestIndex++);
+ Dest = JumpDest(
+ createBasicBlock(D->getName()), EHScopeStack::stable_iterator::invalid(),
+ EHScopeStack::stable_iterator::invalid(), 0, NextCleanupDestIndex++);
return Dest;
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 1ad905078d349c..7bff6c89de563c 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -331,6 +331,9 @@ static void EmitIfUsed(CodeGenFunction &CGF, llvm::BasicBlock *BB) {
void CodeGenFunction::FinishFunction(SourceLocation EndLoc) {
assert(BreakContinueStack.empty() &&
"mismatched push/pop in break/continue stack!");
+ assert(LifetimeExtendedCleanupStack.empty() &&
+ "mismatched push/pop in stack!");
+ assert(BranchInExprCleanupStack.empty() && "mismatched push/pop in stack!");
bool OnlySimpleReturnStmts = NumSimpleReturnExprs > 0
&& NumSimpleReturnExprs == NumReturnExprs
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index ec9f0de0c03385..5310ec8440fd5a 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -238,17 +238,19 @@ class CodeGenFunction : public CodeGenTypeCache {
/// A jump destination is an abstract label, branching to which may
/// require a jump out through normal cleanups.
struct JumpDest {
- JumpDest() : Block(nullptr), LifetimeExtendedDepth(0), Index(0) {}
+ JumpDest() : Block(nullptr), BranchInExprDepth(0), Index(0) {}
JumpDest(llvm::BasicBlock *Block, EHScopeStack::stable_iterator Depth,
- unsigned LifetimeExtendedScopeDepth, unsigned Index)
- : Block(Block), ScopeDepth(Depth),
- LifetimeExtendedDepth(LifetimeExtendedScopeDepth), Index(Index) {}
+ EHScopeStack::stable_iterator EHScopeDepth,
+ unsigned BranchInExprDepth, unsigned Index)
+ : Block(Block), ScopeDepth(Depth), EHScopeDepth(EHScopeDepth),
+ BranchInExprDepth(BranchInExprDepth), Index(Index) {}
bool isValid() const { return Block != nullptr; }
llvm::BasicBlock *getBlock() const { return Block; }
EHScopeStack::stable_iterator getScopeDepth() const { return ScopeDepth; }
+ EHScopeStack::stable_iterator getEHScopeDepth() const { return EHScopeDepth; }
unsigned getDestIndex() const { return Index; }
- unsigned getLifetimeExtendedDepth() const { return LifetimeExtendedDepth; }
+ unsigned getBranchInExprDepth() const { return BranchInExprDepth; }
// This should be used cautiously.
void setScopeDepth(EHScopeStack::stable_iterator depth) {
@@ -258,11 +260,12 @@ class CodeGenFunction : public CodeGenTypeCache {
private:
llvm::BasicBlock *Block;
EHScopeStack::stable_iterator ScopeDepth;
+ EHScopeStack::stable_iterator EHScopeDepth;
- // Size of the lifetime-extended cleanup stack in destination scope.
- // This can only occur when nested stmt-expr's contains branches.
- // This is useful to emit only the necessary lifeitme-extended cleanups.
- unsigned LifetimeExtendedDepth;
+ // Size of the branch-in-expr cleanup stack in destination scope.
+ // All cleanups beyond this depth would be emitted on encountering a branch
+ // to this destination inside an expression.
+ unsigned BranchInExprDepth;
unsigned Index;
};
@@ -633,7 +636,33 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::DenseMap<const VarDecl *, llvm::Value *> NRVOFlags;
EHScopeStack EHStack;
- llvm::SmallVector<char, 256> LifetimeExtendedCleanupStack;
+
+ using DeferredCleanupStack = llvm::SmallVector<char, 256>;
+
+ // Deferred cleanups for lifetime-extended temporaries. Such cleanups are
+ // deferred until the end of the full expression, after which these are added
+ // to the EHStack.
+ DeferredCleanupStack LifetimeExtendedCleanupStack;
+
+ // Branch-in-expression cleanups include the cleanups which are not yet added
+ // to the EHStack while building an expression.
+ // Cleanups from this stack are only emitted when encountering a branch while
+ // building an expression (eg: branches in stmt-expr or coroutine
+ // suspensions). Otherwise, these should be cleared the end of the expression and
+ // added separately to the EHStack.
+ DeferredCleanupStack BranchInExprCleanupStack;
+
+ class RestoreBranchInExpr {
+ public:
+ RestoreBranchInExpr(CodeGenFunction &CGF)
+ : CGF(CGF), OldSize(CGF.BranchInExprCleanupStack.size()) {}
+ ~RestoreBranchInExpr() { CGF.BranchInExprCleanupStack.resize(OldSize); }
+
+ private:
+ CodeGenFunction &CGF;
+ size_t OldSize;
+ };
+
llvm::SmallVector<const JumpDest *, 2> SEHTryEpilogueStack;
llvm::Instruction *CurrentFuncletPad = nullptr;
@@ -653,8 +682,8 @@ class CodeGenFunction : public CodeGenTypeCache {
}
};
- /// Header for data within LifetimeExtendedCleanupStack.
- struct LifetimeExtendedCleanupHeader {
+ /// Header for data within deferred cleanup stacks.
+ struct DeferredCleanupHeader {
/// The size of the following cleanup object.
unsigned Size;
/// The kind of cleanup to push: a value from the CleanupKind enumeration.
@@ -784,6 +813,14 @@ class CodeGenFunction : public CodeGenTypeCache {
/// we're currently inside a conditionally-evaluated expression.
template <class T, class... As>
void pushFullExprCleanup(CleanupKind kind, As... A) {
+ if (kind & BranchInExprCleanup) {
+ // Defer BranchInExprCleanup as a NormalCleanup (emitted only if we see
+ // a branch). Do not add these to the EHStack as they should be added
+ // separately with a different CleanupKind.
+ pushDeferredCleanup<T>(BranchInExprCleanupStack, NormalCleanup,
+ Address::invalid(), A...);
+ return;
+ }
// If we're not in a conditional branch, or if none of the
// arguments requires saving, then use the unconditional cleanup.
if (!isInConditionalBranch())
@@ -803,8 +840,8 @@ class CodeGenFunction : public CodeGenTypeCache {
template <class T, class... As>
void pushCleanupAfterFullExpr(CleanupKind Kind, As... A) {
if (!isInConditionalBranch())
- return pushCleanupAfterFullExprWithActiveFlag<T>(Kind, Address::invalid(),
- A...);
+ return pushDeferredCleanup<T>(LifetimeExtendedCleanupStack, Kind,
+ Address::invalid(), A...);
Address ActiveFlag = createCleanupActiveFlag();
assert(!DominatingValue<Address>::needsSaving(ActiveFlag) &&
@@ -814,24 +851,23 @@ class CodeGenFunction : public CodeGenTypeCache {
SavedTuple Saved{saveValueInCond(A)...};
typedef EHScopeStack::ConditionalCleanup<T, As...> CleanupType;
- pushCleanupAfterFullExprWithActiveFlag<CleanupType>(Kind, ActiveFlag, Saved);
+ pushDeferredCleanup<CleanupType>(LifetimeExtendedCleanupStack, Kind,
+ ActiveFlag, Saved);
}
template <class T, class... As>
- void pushCleanupAfterFullExprWithActiveFlag(CleanupKind Kind,
- Address ActiveFlag, As... A) {
- LifetimeExtendedCleanupHeader Header = {sizeof(T), Kind,
- ActiveFlag.isValid()};
+ void pushDeferredCleanup(DeferredCleanupStack &Cleanups, CleanupKind Kind,
+ Address ActiveFlag, As... A) {
+ DeferredCleanupHeader Header = {sizeof(T), Kind, ActiveFlag.isValid()};
- size_t OldSize = LifetimeExtendedCleanupStack.size();
- LifetimeExtendedCleanupStack.resize(
- LifetimeExtendedCleanupStack.size() + sizeof(Header) + Header.Size +
- (Header.IsConditional ? sizeof(ActiveFlag) : 0));
+ size_t OldSize = Cleanups.size();
+ Cleanups.resize(Cleanups.size() + sizeof(Header) + Header.Size +
+ (Header.IsConditional ? sizeof(ActiveFlag) : 0));
static_assert(sizeof(Header) % alignof(T) == 0,
"Cleanup will be allocated on misaligned address");
- char *Buffer = &LifetimeExtendedCleanupStack[OldSize];
- new (Buffer) LifetimeExtendedCleanupHeader(Header);
+ char *Buffer = &Cleanups[OldSize];
+ new (Buffer) DeferredCleanupHeader(Header);
new (Buffer + sizeof(Header)) T(A...);
if (Header.IsConditional)
new (Buffer + sizeof(Header) + sizeof(T)) Address(ActiveFlag);
@@ -888,6 +924,8 @@ class CodeGenFunction : public CodeGenTypeCache {
class RunCleanupsScope {
EHScopeStack::stable_iterator CleanupStackDepth, OldCleanupScopeDepth;
size_t LifetimeExtendedCleanupStackSize;
+ // size_t BranchInExprCleanupStackSize;
+ RestoreBranchInExpr RestoreBranchInExpr;
bool OldDidCallStackSave;
protected:
bool PerformCleanup;
@@ -902,11 +940,11 @@ class CodeGenFunction : public CodeGenTypeCache {
public:
/// Enter a new cleanup scope.
explicit RunCleanupsScope(CodeGenFunction &CGF)
- : PerformCleanup(true), CGF(CGF)
- {
+ : RestoreBranchInExpr(CGF), PerformCleanup(true), CGF(CGF) {
CleanupStackDepth = CGF.EHStack.stable_begin();
LifetimeExtendedCleanupStackSize =
CGF.LifetimeExtendedCleanupStack.size();
+ // BranchInExprCleanupStackSize = CGF.BranchInExprCleanupStack.size();
OldDidCallStackSave = CGF.DidCallStackSave;
CGF.DidCallStackSave = false;
OldCleanupScopeDepth = CGF.CurrentCleanupScopeDepth;
@@ -1150,9 +1188,11 @@ class CodeGenFunction : public CodeGenTypeCache {
PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize,
std::initializer_list<llvm::Value **> ValuesToReload = {});
- /// Adds lifetime-extended cleanups from the given position to the stack.
- /// (does not remove the cleanups from lifetime extended stack).
- void AddLifetimeExtendedCleanups(size_t OldLifetimeExtendedSize);
+ /// Adds deferred cleanups from the given position to the EHStack.
+ /// These could be lifetime-extended cleanups or branch-in-expr cleanups.
+ /// (does not remove the cleanups from the original stack).
+ void AddDeferredCleanups(DeferredCleanupStack &DeferredCleanupsStack,
+ size_t OldSize);
/// Takes the old cleanup stack size and emits the cleanup blocks
/// that have been added, then adds all lifetime-extended cleanups from
@@ -1169,8 +1209,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// to which we can perform this jump later.
JumpDest getJumpDestInCurrentScope(llvm::BasicBlock *Target) {
return JumpDest(Target, EHStack.getInnermostNormalCleanup(),
- LifetimeExtendedCleanupStack.size(),
- NextCleanupDestIndex++);
+ EHStack.getInnermostEHScope(),
+ BranchInExprCleanupStack.size(), NextCleanupDestIndex++);
}
/// The given basic block lies in the current EH scope, but may be a
@@ -2163,6 +2203,19 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm_unreachable("bad destruction kind");
}
+ bool needsBranchCleanup(QualType::DestructionKind kind) {
+ switch (kind) {
+ case QualType::DK_none:
+ return false;
+ case QualType::DK_cxx_destructor:
+ case QualType::DK_objc_weak_lifetime:
+ case QualType::DK_nontrivial_c_struct:
+ case QualType::DK_objc_strong_lifetime:
+ return true;
+ }
+ llvm_unreachable("bad destruction kind");
+ }
+
CleanupKind getCleanupKind(QualType::DestructionKind kind) {
return (needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup);
}
diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h
index 0c667e80bb6d8c..feffa37c87f6b1 100644
--- a/clang/lib/CodeGen/EHScopeStack.h
+++ b/clang/lib/CodeGen/EHScopeStack.h
@@ -85,6 +85,13 @@ enum CleanupKind : unsigned {
NormalAndEHCleanup = EHCleanup | NormalCleanup,
+ // Denotes a deferred cleanup while building an expression. These cleanups are
+ // emitted on seeing a branch in an partially built expression (eg. branches
+ // in stmt-expr and coroutine suspensions).
+ // This cleanup type should not be used with other types. Cleanups of other
+ // types should be added separately to the EHStack.
+ BranchInExprCleanup = 0x4,
+
LifetimeMarker = 0x8,
NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup,
};
@@ -244,6 +251,9 @@ class EHScopeStack {
/// The innermost EH scope on the stack.
stable_iterator InnermostEHScope;
+ /// The innermost EH scope on the stack.
+ stable_iterator InnermostBranchExprCleanup;
+
/// The CGF this Stack belong to
CodeGenFunction* CGF;
diff --git a/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
index a53b0fcf040927..ee380a167e3fbe 100644
--- a/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
+++ b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 --std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
// Context: GH63818
struct Printy {
@@ -13,6 +13,9 @@ struct Printies {
bool foo();
+// ====================================
+// Init with lifetime extensions
+// ====================================
void bar() {
// CHECK: define dso_local void @_Z3barv()
// CHECK-NOT: call void @_ZN6PrintyD1Ev
@@ -37,7 +40,9 @@ void bar() {
// CHECK-NOT: call void @_ZN6PrintyD1Ev
}
-
+// ====================================
+// Break in stmt-expr
+// ====================================
void test_break() {
// CHECK: define dso_local void @_Z10test_breakv()
// CHECK-NOT: call void @_ZN6PrintyD1Ev
@@ -66,3 +71,113 @@ void test_break() {
// CHECK-NOT: call void @_ZN6PrintyD1Ev
}
+// =============================================
+// Initialisation without lifetime-extension
+// =============================================
+void test_init_with_no_ref_binding() {
+ // CHECK: define dso_local void @_Z29test_init_with_no_ref_bindingv()
+ struct PrintiesCopy {
+ Printy a;
+ Printy b;
+ Printy c;
+ };
+ PrintiesCopy ps(Printy(), ({
+ if (foo()) {
+ // CHECK: if.then:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ return;
+ }
+ Printy();
+ }),
+ ({
+ if (foo()) {
+ // CHECK: if.then2:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ return;
+ }
+ Printy();
+ }));
+}
+
+// ====================================
+// Array init
+// ====================================
+void test_array_init() {
+ // CHECK: define dso_local void @_Z15test_array_initv()
+ Printy arr[] = {
+ Printy(),
+ ({
+ if (foo()) {
+ // CHECK: if.then:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ return;
+ }
+ Printy();
+ }),
+ ({
+ if (foo()) {
+ return;
+ // CHECK: if.then3:
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: call void @_ZN6PrintyD1Ev
+ // CHECK-NEXT: br label %return
+ }
+ Printy();
+ })};
+ return;
+}
+
+// ====================================
+// Arrays as subobjects
+// ====================================
+void arrays_as_subobjects() {
+ // CHECK: define dso_local void @_Z20arrays_as_subobjectsv()
+ struct S {
+ Printy arr1[2];
+ Printy arr2[2];
+ Printy p;
+ };
+ S s{{Printy(), Printy()},
+ {Printy(), ({
+ if (foo()) {
+ /** One dtor followed by an array destruction **/
+ // CHECK: if.then:
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK: br label %arraydestroy.body
+
+ // CHECK: arraydestroy.body:
+ // CHECK: call void @_ZN6PrintyD1Ev
+
+ // CHECK: arraydestroy.done{{.*}}:
+ // CHECK: br label %return
+ return;
+ }
+ Printy();
+ })},
+ ({
+ if (foo()) {
+ /** Two array destructions **/
+ //CHECK: if.then{{.*}}:
+ //CHECK: br label %arraydestroy.body{{.*}}
+
+ //CHECK: arraydestroy.body{{.*}}:
+ //CHECK: call void @_ZN6PrintyD1Ev
+
+ //CHECK: arraydestroy.done{{.*}}:
+ //CHECK: br label %arraydestroy.body{{.*}}
+
+ //CHECK: arraydestroy.body{{.*}}:
+ //CHECK: call void @_ZN6PrintyD1Ev
+
+ //CHECK: arraydestroy.done{{.*}}:
+ //CHECK: br label %return
+ return;
+ }
+ Printy();
+ })};
+}
+
>From cf5024cd8a371e6a00cbe85fb0e91f476906f566 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Fri, 9 Feb 2024 20:18:04 +0000
Subject: [PATCH 09/11] remove dead code
---
clang/lib/CodeGen/CGStmt.cpp | 6 +++---
clang/lib/CodeGen/CodeGenFunction.h | 9 ++-------
clang/lib/CodeGen/EHScopeStack.h | 3 ---
3 files changed, 5 insertions(+), 13 deletions(-)
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 9f1ab1e7817179..8d7e2616c168c0 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -627,9 +627,9 @@ CodeGenFunction::getJumpDestForLabel(const LabelDecl *D) {
if (Dest.isValid()) return Dest;
// Create, but don't insert, the new block.
- Dest = JumpDest(
- createBasicBlock(D->getName()), EHScopeStack::stable_iterator::invalid(),
- EHScopeStack::stable_iterator::invalid(), 0, NextCleanupDestIndex++);
+ Dest = JumpDest(createBasicBlock(D->getName()),
+ EHScopeStack::stable_iterator::invalid(), 0,
+ NextCleanupDestIndex++);
return Dest;
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 5310ec8440fd5a..1713636b554adf 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -240,15 +240,13 @@ class CodeGenFunction : public CodeGenTypeCache {
struct JumpDest {
JumpDest() : Block(nullptr), BranchInExprDepth(0), Index(0) {}
JumpDest(llvm::BasicBlock *Block, EHScopeStack::stable_iterator Depth,
- EHScopeStack::stable_iterator EHScopeDepth,
unsigned BranchInExprDepth, unsigned Index)
- : Block(Block), ScopeDepth(Depth), EHScopeDepth(EHScopeDepth),
- BranchInExprDepth(BranchInExprDepth), Index(Index) {}
+ : Block(Block), ScopeDepth(Depth), BranchInExprDepth(BranchInExprDepth),
+ Index(Index) {}
bool isValid() const { return Block != nullptr; }
llvm::BasicBlock *getBlock() const { return Block; }
EHScopeStack::stable_iterator getScopeDepth() const { return ScopeDepth; }
- EHScopeStack::stable_iterator getEHScopeDepth() const { return EHScopeDepth; }
unsigned getDestIndex() const { return Index; }
unsigned getBranchInExprDepth() const { return BranchInExprDepth; }
@@ -260,7 +258,6 @@ class CodeGenFunction : public CodeGenTypeCache {
private:
llvm::BasicBlock *Block;
EHScopeStack::stable_iterator ScopeDepth;
- EHScopeStack::stable_iterator EHScopeDepth;
// Size of the branch-in-expr cleanup stack in destination scope.
// All cleanups beyond this depth would be emitted on encountering a branch
@@ -944,7 +941,6 @@ class CodeGenFunction : public CodeGenTypeCache {
CleanupStackDepth = CGF.EHStack.stable_begin();
LifetimeExtendedCleanupStackSize =
CGF.LifetimeExtendedCleanupStack.size();
- // BranchInExprCleanupStackSize = CGF.BranchInExprCleanupStack.size();
OldDidCallStackSave = CGF.DidCallStackSave;
CGF.DidCallStackSave = false;
OldCleanupScopeDepth = CGF.CurrentCleanupScopeDepth;
@@ -1209,7 +1205,6 @@ class CodeGenFunction : public CodeGenTypeCache {
/// to which we can perform this jump later.
JumpDest getJumpDestInCurrentScope(llvm::BasicBlock *Target) {
return JumpDest(Target, EHStack.getInnermostNormalCleanup(),
- EHStack.getInnermostEHScope(),
BranchInExprCleanupStack.size(), NextCleanupDestIndex++);
}
diff --git a/clang/lib/CodeGen/EHScopeStack.h b/clang/lib/CodeGen/EHScopeStack.h
index feffa37c87f6b1..2077eb5e315fbd 100644
--- a/clang/lib/CodeGen/EHScopeStack.h
+++ b/clang/lib/CodeGen/EHScopeStack.h
@@ -251,9 +251,6 @@ class EHScopeStack {
/// The innermost EH scope on the stack.
stable_iterator InnermostEHScope;
- /// The innermost EH scope on the stack.
- stable_iterator InnermostBranchExprCleanup;
-
/// The CGF this Stack belong to
CodeGenFunction* CGF;
>From db43f0271b18784b5d138a442f91ef59b1c42afe Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Fri, 9 Feb 2024 21:58:54 +0000
Subject: [PATCH 10/11] format
---
clang/lib/CodeGen/CGExprAgg.cpp | 2 +-
clang/lib/CodeGen/CodeGenFunction.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index bd2e9bc0f82a91..9ee0b94705ba06 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -1800,7 +1800,7 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr(
if (QualType::DestructionKind dtorKind
= field->getType().isDestructedType()) {
assert(LV.isSimple());
- if(CGF.needsBranchCleanup(dtorKind)) {
+ if (CGF.needsBranchCleanup(dtorKind)) {
CGF.pushDestroy(BranchInExprCleanup, LV.getAddress(CGF),
field->getType(), CGF.getDestroyer(dtorKind), false);
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 1713636b554adf..74317c3815a3f0 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -645,8 +645,8 @@ class CodeGenFunction : public CodeGenTypeCache {
// to the EHStack while building an expression.
// Cleanups from this stack are only emitted when encountering a branch while
// building an expression (eg: branches in stmt-expr or coroutine
- // suspensions). Otherwise, these should be cleared the end of the expression and
- // added separately to the EHStack.
+ // suspensions). Otherwise, these should be cleared the end of the expression
+ // and added separately to the EHStack.
DeferredCleanupStack BranchInExprCleanupStack;
class RestoreBranchInExpr {
>From 034ebda4cfd501796b82f0fe0d17141927f0a86e Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 12 Feb 2024 11:29:43 +0000
Subject: [PATCH 11/11] add branch-in-expr cleanup for lambda init
---
clang/lib/CodeGen/CGExprAgg.cpp | 4 ++++
clang/lib/CodeGen/CodeGenFunction.h | 1 -
.../control-flow-in-stmt-expr-cleanup.cpp | 15 +++++++++++++++
3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 9ee0b94705ba06..9c2596d0f30095 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -1367,6 +1367,7 @@ AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) {
SmallVector<EHScopeStack::stable_iterator, 16> Cleanups;
llvm::Instruction *CleanupDominator = nullptr;
+ CodeGenFunction::RestoreBranchInExpr RestoreBranchInExpr(CGF);
CXXRecordDecl::field_iterator CurField = E->getLambdaClass()->field_begin();
for (LambdaExpr::const_capture_init_iterator i = E->capture_init_begin(),
e = E->capture_init_end();
@@ -1384,6 +1385,9 @@ AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) {
if (QualType::DestructionKind DtorKind =
CurField->getType().isDestructedType()) {
assert(LV.isSimple());
+ if (CGF.needsBranchCleanup(DtorKind))
+ CGF.pushDestroy(BranchInExprCleanup, LV.getAddress(CGF),
+ CurField->getType(), CGF.getDestroyer(DtorKind), false);
if (CGF.needsEHCleanup(DtorKind)) {
if (!CleanupDominator)
CleanupDominator = CGF.Builder.CreateAlignedLoad(
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 74317c3815a3f0..176441a12d4db0 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -921,7 +921,6 @@ class CodeGenFunction : public CodeGenTypeCache {
class RunCleanupsScope {
EHScopeStack::stable_iterator CleanupStackDepth, OldCleanupScopeDepth;
size_t LifetimeExtendedCleanupStackSize;
- // size_t BranchInExprCleanupStackSize;
RestoreBranchInExpr RestoreBranchInExpr;
bool OldDidCallStackSave;
protected:
diff --git a/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
index ee380a167e3fbe..fe839316021e3b 100644
--- a/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
+++ b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp
@@ -181,3 +181,18 @@ void arrays_as_subobjects() {
})};
}
+// ====================================
+// Lambda capture initialisation
+// ====================================
+void lambda_init() {
+ // CHECK: define dso_local void @_Z11lambda_initv()
+ auto S = [a = Printy(), b = ({
+ if (foo()) {
+ return;
+ // CHECK: if.then:
+ // CHECK: call void @_ZN6PrintyD1Ev
+ // CHECK: br label %return
+ }
+ Printy();
+ })]() {};
+}
More information about the cfe-commits
mailing list