-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Use pushFullExprCleanup for deferred destroy #88670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: Utkarsh Saxena (usx95) ChangesInstead of directly pushing the Tested: CLANG_DIR=/usr/local/google/home/usx/build
$CLANG_DIR/bin/clang++ \
-Itools/clang/tools/extra/clangd \
-I/usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/clangd \
-I/usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/clangd/../include-cleaner/include \
-Itools/clang/tools/extra/clangd/../clang-tidy \
-I/usr/local/google/home/usx/src/llvm/llvm-project/clang/include \
-Itools/clang/include \
-Iinclude \
-I/usr/local/google/home/usx/src/llvm/llvm-project/llvm/include \
-I/usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/pseudo/lib/../include \
-isystem$CLANG_DIR/lib/clang/19/include \
-o /usr/local/google/home/usx/build/bootstrap/tools/clang/tools/extra/clangd/CMakeFiles/obj.clangDaemon.dir/ClangdLSPServer.cpp.o \
-c /usr/local/google/home/usx/src/llvm/llvm-project/clang-tools-extra/clangd/ClangdLSPServer.cpp Full diff: https://github.com/llvm/llvm-project/pull/88670.diff 1 Files Affected:
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8bdafa7c569b08..3f05ebb561da57 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -2216,8 +2216,11 @@ void CodeGenFunction::pushDestroyAndDeferDeactivation(
void CodeGenFunction::pushDestroyAndDeferDeactivation(
CleanupKind cleanupKind, Address addr, QualType type, Destroyer *destroyer,
bool useEHCleanupForArray) {
- pushCleanupAndDeferDeactivation<DestroyObject>(
- cleanupKind, addr, type, destroyer, useEHCleanupForArray);
+ llvm::Instruction *DominatingIP =
+ Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy));
+ pushDestroy(cleanupKind, addr, type, destroyer, useEHCleanupForArray);
+ DeferredDeactivationCleanupStack.push_back(
+ {EHStack.stable_begin(), DominatingIP});
}
void CodeGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) {
|
This fix is for a wrong refactoring change in 89ba7e1 and |
@usx95 Could you maybe reduce a test from the crash? Thanks! |
@cor3ntin Added tests for crash. |
|
||
class Value { | ||
public: | ||
Value(std::initializer_list<Value> Elements); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of initializer_list here doesn't seem necessary; you could remove it to reduce the size of the testcase.
Can we integrate this into an existing test file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you try this out ? I cannot reproduce the original crash without std::initializer_list
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class Object {
public:
struct KV;
Object(KV);
};
struct A { A(); ~A(); };
class Value {
public:
Value(A);
Value(const char *V);
~Value();
};
class ObjectKey {
public:
ObjectKey(const char *S);
~ObjectKey();
};
struct Object::KV {
ObjectKey K;
Value V;
};
bool foo();
void DestroyInConditionalCleanup() {
foo() ? Object{{"key1", A()}} : Object{{"key2", "val2"}};
}
Not really a big deal either way, though; just a minor cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great. Thanks. I was able to reduce it further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Instead of directly pushing the `Destroy`, we should use `pushFullExprCleanup` which handles conditional branches. This fixes a backend crash due to 89ba7e1. Added minimized crash reproducer.
This reverts commit 43b4e5b.
) Latest diff: https://github.com/llvm/llvm-project/pull/89154/files/f1ab4c2677394bbfc985d9680d5eecd7b2e6a882..adf9bc902baddb156c83ce0f8ec03c142e806d45 We address two additional bugs here: ### Problem 1: Deactivated normal cleanup still runs, leading to double-free Consider the following: ```cpp struct A { }; struct B { B(const A&); }; struct S { A a; B b; }; int AcceptS(S s); void Accept2(int x, int y); void Test() { Accept2(AcceptS({.a = A{}, .b = A{}}), ({ return; 0; })); } ``` We add cleanups as follows: 1. push dtor for field `S::a` 2. push dtor for temp `A{}` (used by ` B(const A&)` in `.b = A{}`) 3. push dtor for field `S::b` 4. Deactivate 3 `S::b`-> This pops the cleanup. 5. Deactivate 1 `S::a` -> Does not pop the cleanup as *2* is top. Should create _active flag_!! 6. push dtor for `~S()`. 7. ... It is important to deactivate **5** using active flags. Without the active flags, the `return` will fallthrough it and would run both `~S()` and dtor `S::a` leading to **double free** of `~A()`. In this patch, we unconditionally emit active flags while deactivating normal cleanups. These flags are deleted later by the `AllocaTracker` if the cleanup is not emitted. ### Problem 2: Missing cleanup for conditional lifetime extension We push 2 cleanups for lifetime-extended cleanup. The first cleanup is useful if we exit from the middle of the expression (stmt-expr/coro suspensions). This is deactivated after full-expr, and a new cleanup is pushed, extending the lifetime of the temporaries (to the scope of the reference being initialized). If this lifetime extension happens to be conditional, then we use active flags to remember whether the branch was taken and if the object was initialized. Previously, we used a **single** active flag, which was used by both cleanups. This is wrong because the first cleanup will be forced to deactivate after the full-expr and therefore this **active** flag will always be **inactive**. The dtor for the lifetime extended entity would not run as it always sees an **inactive** flag. In this patch, we solve this using two separate active flags for both cleanups. Both of them are activated if the conditional branch is taken, but only one of them is deactivated after the full-expr. --- Fixes #63818 Fixes #88478 --- Previous PR logs: 1. #85398 2. #88670 3. #88751 4. #88884
Instead of directly pushing the
Destroy
, we should usepushFullExprCleanup
which handles conditional branches.This fixes a backend crash due to 89ba7e1.
Added minimized crash reproducer.