https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/85398
>From 0654f624b3b60026398e8b0168623a85e3129630 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <u...@google.com> Date: Fri, 15 Mar 2024 13:19:36 +0000 Subject: [PATCH 1/5] [codegen] Emit cleanups for branch in stmt-expr and coro suspensions --- clang/lib/CodeGen/CGCleanup.cpp | 7 +++++-- clang/lib/CodeGen/CGExprAgg.cpp | 13 +++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index f87caf050eeaa7..c22d4da0fefc3c 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -694,7 +694,8 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // - whether there's a fallthrough llvm::BasicBlock *FallthroughSource = Builder.GetInsertBlock(); - bool HasFallthrough = (FallthroughSource != nullptr && IsActive); + bool HasFallthrough = + FallthroughSource != nullptr && (IsActive || HasExistingBranches); // Branch-through fall-throughs leave the insertion point set to the // end of the last cleanup, which points to the current scope. The @@ -719,7 +720,9 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // If we have a prebranched fallthrough into an inactive normal // cleanup, rewrite it so that it leads to the appropriate place. - if (Scope.isNormalCleanup() && HasPrebranchedFallthrough && !IsActive) { + if (Scope.isNormalCleanup() && HasPrebranchedFallthrough && + !RequiresNormalCleanup) { + assert(!IsActive); llvm::BasicBlock *prebranchDest; // If the prebranch is semantically branching through the next diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 5190b22bcc1622..7e62599089b8df 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" @@ -1389,15 +1390,15 @@ AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) { if (QualType::DestructionKind DtorKind = CurField->getType().isDestructedType()) { assert(LV.isSimple()); - if (CGF.needsEHCleanup(DtorKind)) { + if (DtorKind) { if (!CleanupDominator) CleanupDominator = CGF.Builder.CreateAlignedLoad( CGF.Int8Ty, llvm::Constant::getNullValue(CGF.Int8PtrTy), CharUnits::One()); // placeholder - CGF.pushDestroy(EHCleanup, LV.getAddress(CGF), CurField->getType(), - CGF.getDestroyer(DtorKind), false); + CGF.pushDestroy(NormalAndEHCleanup, LV.getAddress(CGF), + CurField->getType(), CGF.getDestroyer(DtorKind), false); Cleanups.push_back(CGF.EHStack.stable_begin()); } } @@ -1806,9 +1807,9 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr( if (QualType::DestructionKind dtorKind = field->getType().isDestructedType()) { assert(LV.isSimple()); - if (CGF.needsEHCleanup(dtorKind)) { - CGF.pushDestroy(EHCleanup, LV.getAddress(CGF), field->getType(), - CGF.getDestroyer(dtorKind), false); + if (dtorKind) { + CGF.pushDestroy(NormalAndEHCleanup, LV.getAddress(CGF), + field->getType(), CGF.getDestroyer(dtorKind), false); addCleanup(CGF.EHStack.stable_begin()); pushedCleanup = true; } >From 1b7c160b030f3a765d40723bbbf9a7daa4a1e090 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <u...@google.com> Date: Fri, 15 Mar 2024 15:22:07 +0000 Subject: [PATCH 2/5] add tests --- .../control-flow-in-expr-cleanup.cpp | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp diff --git a/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp b/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp new file mode 100644 index 00000000000000..d703532b5a10b9 --- /dev/null +++ b/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp @@ -0,0 +1,172 @@ +// RUN: %clang_cc1 --std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + +#include "Inputs/coroutine.h" + +struct Printy { + Printy(const char *name) : name(name) {} + ~Printy() {} + const char *name; +}; + +struct coroutine { + struct promise_type; + std::coroutine_handle<promise_type> handle; + ~coroutine() { + if (handle) handle.destroy(); + } +}; + +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 Awaiter : std::suspend_always { + Printy await_resume() { return {"awaited"}; } +}; + +int foo() { return 2; } + +struct PrintiesCopy { + Printy a; + Printy b; + Printy c; +}; + +void ParenInit() { + // CHECK: define dso_local void @_Z9ParenInitv() + // CHECK: [[CLEANUP_DEST:%.+]] = alloca i32, align 4 + PrintiesCopy ps(Printy("a"), + // CHECK: call void @_ZN6PrintyC1EPKc + ({ + if (foo()) return; + // CHECK: if.then: + // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 + // CHECK-NEXT: br label %cleanup + Printy("b"); + // CHECK: if.end: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + }), + ({ + if (foo()) return; + // CHECK: if.then{{.*}}: + // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: br label %cleanup + Printy("c"); + // CHECK: if.end{{.*}}: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: call void @_ZN12PrintiesCopyD1Ev + // CHECK-NEXT: br label %return + })); + // CHECK: cleanup: + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: br label %return +} + +coroutine ParenInitCoro() { + // CHECK: define dso_local void @_Z13ParenInitCorov + // CHECK: [[ACTIVE1:%.+]] = alloca i1, align 1 + // CHECK: [[ACTIVE2:%.+]] = alloca i1, align 1 + PrintiesCopy ps(Printy("a"), Printy("b"), + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: store i1 true, ptr [[ACTIVE1]].reload.addr, align 1 + // CHECK-NEXT: store i1 true, ptr [[ACTIVE2]].reload.addr, align 1 + // CHECK: call void @_ZN6PrintyC1EPKc + co_await Awaiter{} + + // CHECK: await.cleanup: + // CHECK-NEXT: br label %[[CLEANUP:.+]].from.await.cleanup + + // CHECK: [[CLEANUP]].from.await.cleanup: + // CHECK: br label %[[CLEANUP]] + + // CHECK: await.ready: + // CHECK: store i1 false, ptr [[ACTIVE1]].reload.addr, align 1 + // CHECK-NEXT: store i1 false, ptr [[ACTIVE2]].reload.addr, align 1 + // CHECK-NEXT: br label %[[CLEANUP]].from.await.ready + + // CHECK: [[CLEANUP]].from.await.ready: + // CHECK: br label %[[CLEANUP]] + + // CHECK: [[CLEANUP]]: + // CHECK: [[IS_ACTIVE1:%.+]] = load i1, ptr [[ACTIVE1]].reload.addr, align 1 + // CHECK-NEXT: br i1 [[IS_ACTIVE1]], label %[[ACTION1:.+]], label %[[DONE1:.+]] + + // CHECK: [[ACTION1]]: + // CHECK: call void @_ZN6PrintyD1Ev + // CHECK: br label %[[DONE1]] + + // CHECK: [[DONE1]]: + // CHECK: [[IS_ACTIVE2:%.+]] = load i1, ptr [[ACTIVE2]].reload.addr, align 1 + // CHECK-NEXT: br i1 [[IS_ACTIVE2]], label %[[ACTION2:.+]], label %[[DONE2:.+]] + + // CHECK: [[ACTION2]]: + // CHECK: call void @_ZN6PrintyD1Ev + // CHECK: br label %[[DONE2]] + ); +} + +// TODO: Add more assertions after preliminary review. +// struct S { +// Printy arr1[2]; +// Printy arr2[2]; +// Printy p; +// }; + +// void ArraySubobjects() { +// S s{{Printy("a"), Printy("b")}, +// {Printy("a"), ({ +// if (foo() == 1) { +// return; +// } +// Printy("b"); +// })}, +// ({ +// if (foo() == 2) { +// return; +// } +// Printy("b"); +// })}; +// } + +// coroutine ArraySubobjectsCoro() { +// S s{{Printy("a"), Printy("b")}, +// {Printy("a"), co_await Awaiter{}}, +// co_await Awaiter{}}; +// } + +// struct A { +// Printy a; +// }; +// struct B : A { +// Printy b; +// Printy c; +// }; + +// void BaseClassCtors() { +// auto S = B({Printy("a")}, Printy("b"), ({ +// return; +// Printy("c"); +// })); +// } + +// coroutine BaseClassCtorsCoro() { +// auto S = B({Printy("a")}, Printy("b"), co_await Awaiter{}); +// } + +// void LambdaInit() { +// auto S = [a = Printy("a"), b = ({ +// return; +// Printy("b"); +// })]() { return a; }; +// } + +// coroutine LambdaInitCoro() { +// auto S = [a = Printy("a"), b = co_await Awaiter{}]() { return a; }; +// } >From b5c5fe685df999668890aab0209427bea6eca2f3 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <u...@google.com> Date: Mon, 18 Mar 2024 12:23:57 +0000 Subject: [PATCH 3/5] Handle array init cleanups and lifetime extended dtors --- clang/include/clang/AST/Expr.h | 2 ++ clang/lib/AST/Expr.cpp | 17 +++++++++++++++++ clang/lib/CodeGen/CGCleanup.cpp | 8 ++++++++ clang/lib/CodeGen/CGDecl.cpp | 22 ++++++++++++++-------- clang/lib/CodeGen/CGExprAgg.cpp | 3 ++- clang/lib/CodeGen/CGExprCXX.cpp | 2 +- clang/lib/CodeGen/CodeGenFunction.h | 12 +++++++++++- 7 files changed, 55 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 446bec4081e869..26d0d589cd2550 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -246,6 +246,8 @@ class Expr : public ValueStmt { return static_cast<bool>(getDependence() & ExprDependence::Error); } + bool containsControlFlow() const; + /// getExprLoc - Return the preferred location for the arrow when diagnosing /// a problem with a generic expression. SourceLocation getExprLoc() const LLVM_READONLY; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index b4de2155adcebd..40cf06847528b8 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -24,6 +24,7 @@ #include "clang/AST/IgnoreExpr.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.h" @@ -2503,6 +2504,22 @@ Stmt *BlockExpr::getBody() { // Generic Expression Routines //===----------------------------------------------------------------------===// +bool Expr::containsControlFlow() const { + struct BranchDetector : public RecursiveASTVisitor<BranchDetector> { + bool HasBranch = false; + bool activate() { + HasBranch = true; + return false; + } + bool VisitCoawaitExpr(CoawaitExpr *) { return activate(); } + bool VisitCoyieldExpr(CoyieldExpr *) { return activate(); } + bool VisitStmtExpr(StmtExpr *) { return activate(); } + }; + BranchDetector detector; + detector.TraverseStmt(const_cast<Expr *>(this)); + return detector.HasBranch; +} + bool Expr::isReadIfDiscardedInCPlusPlus11() const { // In C++11, discarded-value expressions of a certain form are special, // according to [expr]p10: diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index c22d4da0fefc3c..5e51f263930848 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -492,7 +492,15 @@ void CodeGenFunction::PopCleanupBlocks( /// cleanups from the given savepoint in the lifetime-extended cleanups stack. void CodeGenFunction::PopCleanupBlocks( EHScopeStack::stable_iterator Old, size_t OldLifetimeExtendedSize, + size_t OldDeactivateAfterFullExprStackSize, std::initializer_list<llvm::Value **> ValuesToReload) { + for (size_t I = DeactivateAfterFullExprStack.size(); + I > OldDeactivateAfterFullExprStackSize; I--) { + DeactivateCleanupBlock(DeactivateAfterFullExprStack[I - 1].Cleanup, + DeactivateAfterFullExprStack[I - 1].DominatingIP); + DeactivateAfterFullExprStack[I - 1].DominatingIP->eraseFromParent(); + } + DeactivateAfterFullExprStack.resize(OldDeactivateAfterFullExprStackSize); PopCleanupBlocks(Old, ValuesToReload); // Move our deferred cleanups onto the EH stack. diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index bbe14ef4c17244..24760727d966aa 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -19,6 +19,7 @@ #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" +#include "EHScopeStack.h" #include "PatternInit.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" @@ -2204,10 +2205,16 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind, // Push an EH-only cleanup for the object now. // FIXME: When popping normal cleanups, we need to keep this EH cleanup // around in case a temporary's destructor throws an exception. - if (cleanupKind & EHCleanup) - EHStack.pushCleanup<DestroyObject>( - static_cast<CleanupKind>(cleanupKind & ~NormalCleanup), addr, type, - destroyer, useEHCleanupForArray); + if (cleanupKind) { + // Placeholder dominating IP for this cleanup. + llvm::Instruction *CleanupDominator = Builder.CreateAlignedLoad( + Int8Ty, llvm::Constant::getNullValue(Int8PtrTy), CharUnits::One()); + EHStack.pushCleanup<DestroyObject>(static_cast<CleanupKind>(cleanupKind), + addr, type, destroyer, + useEHCleanupForArray); + DeactivateAfterFullExprStack.push_back( + {EHStack.stable_begin(), CleanupDominator}); + } return pushCleanupAfterFullExprWithActiveFlag<DestroyObject>( cleanupKind, Address::invalid(), addr, type, destroyer, useEHCleanupForArray); @@ -2437,10 +2444,9 @@ void CodeGenFunction::pushIrregularPartialArrayCleanup(llvm::Value *arrayBegin, QualType elementType, CharUnits elementAlign, Destroyer *destroyer) { - pushFullExprCleanup<IrregularPartialArrayDestroy>(EHCleanup, - arrayBegin, arrayEndPointer, - elementType, elementAlign, - destroyer); + pushFullExprCleanup<IrregularPartialArrayDestroy>( + NormalAndEHCleanup, arrayBegin, arrayEndPointer, elementType, + elementAlign, destroyer); } /// pushRegularPartialArrayCleanup - Push an EH cleanup to destroy diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 7e62599089b8df..8b502596a215c2 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -558,7 +558,8 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, Address endOfInit = Address::invalid(); EHScopeStack::stable_iterator cleanup; llvm::Instruction *cleanupDominator = nullptr; - if (CGF.needsEHCleanup(dtorKind)) { + if (CGF.needsEHCleanup(dtorKind) || + (dtorKind && ExprToVisit->containsControlFlow())) { // In principle we could tell the cleanup where we are more // directly, but the control flow can get so varied here that it // would actually be quite complex. Therefore we go through an diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 2adbef6d55122c..fd413dcfc236c6 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1103,7 +1103,7 @@ void CodeGenFunction::EmitNewArrayInitializer( } // Enter a partial-destruction Cleanup if necessary. - if (needsEHCleanup(DtorKind)) { + if (needsEHCleanup(DtorKind) || (DtorKind && E->containsControlFlow())) { // In principle we could tell the Cleanup where we are more // directly, but the control flow can get so varied here that it // would actually be quite complex. Therefore we go through an diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 6c825a302913df..8f2d09ba017738 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -648,6 +648,12 @@ class CodeGenFunction : public CodeGenTypeCache { EHScopeStack EHStack; llvm::SmallVector<char, 256> LifetimeExtendedCleanupStack; + struct DeactivateAfterFullExprCleanup { + EHScopeStack::stable_iterator Cleanup; + llvm::Instruction *DominatingIP; + }; + llvm::SmallVector<DeactivateAfterFullExprCleanup> + DeactivateAfterFullExprStack; llvm::SmallVector<const JumpDest *, 2> SEHTryEpilogueStack; llvm::Instruction *CurrentFuncletPad = nullptr; @@ -904,6 +910,7 @@ class CodeGenFunction : public CodeGenTypeCache { class RunCleanupsScope { EHScopeStack::stable_iterator CleanupStackDepth, OldCleanupScopeDepth; size_t LifetimeExtendedCleanupStackSize; + size_t DeactivateAfterFullExprStackSize; bool OldDidCallStackSave; protected: bool PerformCleanup; @@ -923,6 +930,8 @@ class CodeGenFunction : public CodeGenTypeCache { CleanupStackDepth = CGF.EHStack.stable_begin(); LifetimeExtendedCleanupStackSize = CGF.LifetimeExtendedCleanupStack.size(); + DeactivateAfterFullExprStackSize = + CGF.DeactivateAfterFullExprStack.size(); OldDidCallStackSave = CGF.DidCallStackSave; CGF.DidCallStackSave = false; OldCleanupScopeDepth = CGF.CurrentCleanupScopeDepth; @@ -950,7 +959,7 @@ class CodeGenFunction : public CodeGenTypeCache { assert(PerformCleanup && "Already forced cleanup"); CGF.DidCallStackSave = OldDidCallStackSave; CGF.PopCleanupBlocks(CleanupStackDepth, LifetimeExtendedCleanupStackSize, - ValuesToReload); + DeactivateAfterFullExprStackSize, ValuesToReload); PerformCleanup = false; CGF.CurrentCleanupScopeDepth = OldCleanupScopeDepth; } @@ -1172,6 +1181,7 @@ class CodeGenFunction : public CodeGenTypeCache { void PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize, size_t OldLifetimeExtendedStackSize, + size_t OldDeactivateAfterFullExprStackSize, std::initializer_list<llvm::Value **> ValuesToReload = {}); void ResolveBranchFixups(llvm::BasicBlock *Target); >From 801feaaffe5a0375160e81b48b08694f99ec813a Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <u...@google.com> Date: Wed, 20 Mar 2024 14:28:41 +0000 Subject: [PATCH 4/5] Add more tests. Reuse CleanupDeactivationScope tosimplify previous deferred deactivations --- clang/include/clang/AST/Expr.h | 8 +- clang/lib/AST/Expr.cpp | 9 +- clang/lib/CodeGen/CGCleanup.cpp | 10 +- clang/lib/CodeGen/CGDecl.cpp | 56 ++- clang/lib/CodeGen/CGExprAgg.cpp | 82 +-- clang/lib/CodeGen/CGExprCXX.cpp | 29 +- clang/lib/CodeGen/CodeGenFunction.cpp | 6 + clang/lib/CodeGen/CodeGenFunction.h | 74 ++- .../control-flow-in-expr-cleanup.cpp | 465 ++++++++++++++---- 9 files changed, 535 insertions(+), 204 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 26d0d589cd2550..36e4c8acdc6736 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -246,7 +246,13 @@ class Expr : public ValueStmt { return static_cast<bool>(getDependence() & ExprDependence::Error); } - bool containsControlFlow() const; + /// Determines whether the expression contains primitives which may branch out + /// of the expr through a non-exceptional normal control flow. This is + /// possible if the expression contains: + /// - a statement expression having a control flow out of the expr (break, + /// goto, return). + /// - a coroutine suspension. + bool mayBranchOut() const; /// getExprLoc - Return the preferred location for the arrow when diagnosing /// a problem with a generic expression. diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 40cf06847528b8..b2d5df116ca9e8 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -25,6 +25,7 @@ #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.h" @@ -2504,16 +2505,20 @@ Stmt *BlockExpr::getBody() { // Generic Expression Routines //===----------------------------------------------------------------------===// -bool Expr::containsControlFlow() const { +bool Expr::mayBranchOut() const { struct BranchDetector : public RecursiveASTVisitor<BranchDetector> { bool HasBranch = false; bool activate() { HasBranch = true; return false; } + // Coroutine suspensions. bool VisitCoawaitExpr(CoawaitExpr *) { return activate(); } bool VisitCoyieldExpr(CoyieldExpr *) { return activate(); } - bool VisitStmtExpr(StmtExpr *) { return activate(); } + // Control flow in stmt-expressions. + bool VisitBreakStmt(BreakStmt *) { return activate(); } + bool VisitReturnStmt(ReturnStmt *) { return activate(); } + bool VisitGotoStmt(GotoStmt *) { return activate(); } }; BranchDetector detector; detector.TraverseStmt(const_cast<Expr *>(this)); diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index 5e51f263930848..8f74a06e4f96d4 100644 --- a/clang/lib/CodeGen/CGCleanup.cpp +++ b/clang/lib/CodeGen/CGCleanup.cpp @@ -492,15 +492,7 @@ void CodeGenFunction::PopCleanupBlocks( /// cleanups from the given savepoint in the lifetime-extended cleanups stack. void CodeGenFunction::PopCleanupBlocks( EHScopeStack::stable_iterator Old, size_t OldLifetimeExtendedSize, - size_t OldDeactivateAfterFullExprStackSize, std::initializer_list<llvm::Value **> ValuesToReload) { - for (size_t I = DeactivateAfterFullExprStack.size(); - I > OldDeactivateAfterFullExprStackSize; I--) { - DeactivateCleanupBlock(DeactivateAfterFullExprStack[I - 1].Cleanup, - DeactivateAfterFullExprStack[I - 1].DominatingIP); - DeactivateAfterFullExprStack[I - 1].DominatingIP->eraseFromParent(); - } - DeactivateAfterFullExprStack.resize(OldDeactivateAfterFullExprStackSize); PopCleanupBlocks(Old, ValuesToReload); // Move our deferred cleanups onto the EH stack. @@ -730,6 +722,8 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // cleanup, rewrite it so that it leads to the appropriate place. if (Scope.isNormalCleanup() && HasPrebranchedFallthrough && !RequiresNormalCleanup) { + // FIXME: Come up with a program which would need forwarding prebranched + // fallthrough and add tests. Otherwise delete this and assert against it. assert(!IsActive); llvm::BasicBlock *prebranchDest; diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 24760727d966aa..02c597bd999dc5 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -2186,6 +2186,24 @@ void CodeGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr, destroyer, useEHCleanupForArray); } +// Pushes a destroy and defers its deactivation until its +// DeferredDeactivationCleanupScope is exited. +void CodeGenFunction::pushDestroyAndDeferDeactivation( + QualType::DestructionKind dtorKind, Address addr, QualType type) { + assert(dtorKind && "cannot push destructor for trivial type"); + + CleanupKind cleanupKind = getCleanupKind(dtorKind); + pushDestroyAndDeferDeactivation( + cleanupKind, addr, type, getDestroyer(dtorKind), cleanupKind & EHCleanup); +} + +void CodeGenFunction::pushDestroyAndDeferDeactivation( + CleanupKind cleanupKind, Address addr, QualType type, Destroyer *destroyer, + bool useEHCleanupForArray) { + pushCleanupAndDeferDeactivation<DestroyObject>( + cleanupKind, addr, type, destroyer, useEHCleanupForArray); +} + void CodeGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) { EHStack.pushCleanup<CallStackRestore>(Kind, SPMem); } @@ -2202,22 +2220,19 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind, // If we're not in a conditional branch, we don't need to bother generating a // conditional cleanup. if (!isInConditionalBranch()) { - // Push an EH-only cleanup for the object now. // FIXME: When popping normal cleanups, we need to keep this EH cleanup // around in case a temporary's destructor throws an exception. - if (cleanupKind) { - // Placeholder dominating IP for this cleanup. - llvm::Instruction *CleanupDominator = Builder.CreateAlignedLoad( - Int8Ty, llvm::Constant::getNullValue(Int8PtrTy), CharUnits::One()); - EHStack.pushCleanup<DestroyObject>(static_cast<CleanupKind>(cleanupKind), - addr, type, destroyer, - useEHCleanupForArray); - DeactivateAfterFullExprStack.push_back( - {EHStack.stable_begin(), CleanupDominator}); - } + // Add the cleanup to the EHStack. After the full-expr, this would be + // deactivated before being popped from the stack. + pushDestroyAndDeferDeactivation(cleanupKind, addr, type, destroyer, + useEHCleanupForArray); + + // Since this is lifetime-extended, push it once again to the EHStack after + // the full expression. return pushCleanupAfterFullExprWithActiveFlag<DestroyObject>( - cleanupKind, Address::invalid(), addr, type, destroyer, useEHCleanupForArray); + cleanupKind, Address::invalid(), addr, type, destroyer, + useEHCleanupForArray); } // Otherwise, we should only destroy the object if it's been initialized. @@ -2232,13 +2247,12 @@ void CodeGenFunction::pushLifetimeExtendedDestroy(CleanupKind cleanupKind, Address ActiveFlag = createCleanupActiveFlag(); SavedType SavedAddr = saveValueInCond(addr); - if (cleanupKind & EHCleanup) { - EHStack.pushCleanup<ConditionalCleanupType>( - static_cast<CleanupKind>(cleanupKind & ~NormalCleanup), SavedAddr, type, - destroyer, useEHCleanupForArray); - initFullExprCleanupWithFlag(ActiveFlag); - } + pushCleanupAndDeferDeactivation<ConditionalCleanupType>( + cleanupKind, SavedAddr, type, destroyer, useEHCleanupForArray); + initFullExprCleanupWithFlag(ActiveFlag); + // Since this is lifetime-extended, push it once again to the EHStack after + // the full expression. pushCleanupAfterFullExprWithActiveFlag<ConditionalCleanupType>( cleanupKind, ActiveFlag, SavedAddr, type, destroyer, useEHCleanupForArray); @@ -2433,9 +2447,9 @@ namespace { }; } // end anonymous namespace -/// pushIrregularPartialArrayCleanup - Push an EH cleanup to destroy -/// already-constructed elements of the given array. The cleanup -/// may be popped with DeactivateCleanupBlock or PopCleanupBlock. +/// pushIrregularPartialArrayCleanup - Push a NormalAndEHCleanup to +/// destroy already-constructed elements of the given array. The cleanup may be +/// popped with DeactivateCleanupBlock or PopCleanupBlock. /// /// \param elementType - the immediate element type of the array; /// possibly still an array type diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 8b502596a215c2..bedd83e1b99cea 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -25,6 +25,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instruction.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" using namespace clang; @@ -556,25 +557,24 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, // For that, we'll need an EH cleanup. QualType::DestructionKind dtorKind = elementType.isDestructedType(); Address endOfInit = Address::invalid(); - EHScopeStack::stable_iterator cleanup; - llvm::Instruction *cleanupDominator = nullptr; + CodeGenFunction::CleanupDeactivationScope deactivation(CGF); + if (CGF.needsEHCleanup(dtorKind) || - (dtorKind && ExprToVisit->containsControlFlow())) { + (dtorKind && ExprToVisit->mayBranchOut())) { // In principle we could tell the cleanup where we are more // directly, but the control flow can get so varied here that it // would actually be quite complex. Therefore we go through an // alloca. + llvm::Instruction *DominatingIP = + Builder.CreateFlagLoad(llvm::ConstantInt::getNullValue(CGF.Int8PtrTy)); endOfInit = CGF.CreateTempAlloca(begin->getType(), CGF.getPointerAlign(), "arrayinit.endOfInit"); - cleanupDominator = Builder.CreateStore(begin, endOfInit); + Builder.CreateStore(begin, endOfInit); CGF.pushIrregularPartialArrayCleanup(begin, endOfInit, elementType, elementAlign, CGF.getDestroyer(dtorKind)); - cleanup = CGF.EHStack.stable_begin(); - - // Otherwise, remember that we didn't need a cleanup. - } else { - dtorKind = QualType::DK_none; + CGF.DeferredDeactivationCleanupStack.push_back( + {CGF.EHStack.stable_begin(), DominatingIP}); } llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1); @@ -670,9 +670,6 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, CGF.EmitBlock(endBB); } - - // Leave the partial-array cleanup if we entered one. - if (dtorKind) CGF.DeactivateCleanupBlock(cleanup, cleanupDominator); } //===----------------------------------------------------------------------===// @@ -1370,9 +1367,8 @@ AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) { LValue SlotLV = CGF.MakeAddrLValue(Slot.getAddress(), E->getType()); // We'll need to enter cleanup scopes in case any of the element - // initializers throws an exception. - SmallVector<EHScopeStack::stable_iterator, 16> Cleanups; - llvm::Instruction *CleanupDominator = nullptr; + // initializers throws an exception or contains branch out of the expressions. + CodeGenFunction::CleanupDeactivationScope scope(CGF); CXXRecordDecl::field_iterator CurField = E->getLambdaClass()->field_begin(); for (LambdaExpr::const_capture_init_iterator i = E->capture_init_begin(), @@ -1391,28 +1387,12 @@ AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) { if (QualType::DestructionKind DtorKind = CurField->getType().isDestructedType()) { assert(LV.isSimple()); - if (DtorKind) { - if (!CleanupDominator) - CleanupDominator = CGF.Builder.CreateAlignedLoad( - CGF.Int8Ty, - llvm::Constant::getNullValue(CGF.Int8PtrTy), - CharUnits::One()); // placeholder - - CGF.pushDestroy(NormalAndEHCleanup, LV.getAddress(CGF), - CurField->getType(), CGF.getDestroyer(DtorKind), false); - Cleanups.push_back(CGF.EHStack.stable_begin()); - } + if (DtorKind) + CGF.pushDestroyAndDeferDeactivation( + NormalAndEHCleanup, LV.getAddress(CGF), CurField->getType(), + CGF.getDestroyer(DtorKind), false); } } - - // Deactivate all the partial cleanups in reverse order, which - // generally means popping them. - for (unsigned i = Cleanups.size(); i != 0; --i) - CGF.DeactivateCleanupBlock(Cleanups[i-1], CleanupDominator); - - // Destroy the placeholder if we made one. - if (CleanupDominator) - CleanupDominator->eraseFromParent(); } void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *E) { @@ -1700,14 +1680,7 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr( // We'll need to enter cleanup scopes in case any of the element // initializers throws an exception. SmallVector<EHScopeStack::stable_iterator, 16> cleanups; - llvm::Instruction *cleanupDominator = nullptr; - auto addCleanup = [&](const EHScopeStack::stable_iterator &cleanup) { - cleanups.push_back(cleanup); - if (!cleanupDominator) // create placeholder once needed - cleanupDominator = CGF.Builder.CreateAlignedLoad( - CGF.Int8Ty, llvm::Constant::getNullValue(CGF.Int8PtrTy), - CharUnits::One()); - }; + CodeGenFunction::CleanupDeactivationScope DeactivateCleanups(CGF); unsigned curInitIndex = 0; @@ -1730,10 +1703,8 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr( CGF.EmitAggExpr(InitExprs[curInitIndex++], AggSlot); if (QualType::DestructionKind dtorKind = - Base.getType().isDestructedType()) { - CGF.pushDestroy(dtorKind, V, Base.getType()); - addCleanup(CGF.EHStack.stable_begin()); - } + Base.getType().isDestructedType()) + CGF.pushDestroyAndDeferDeactivation(dtorKind, V, Base.getType()); } } @@ -1809,9 +1780,9 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr( = field->getType().isDestructedType()) { assert(LV.isSimple()); if (dtorKind) { - CGF.pushDestroy(NormalAndEHCleanup, LV.getAddress(CGF), - field->getType(), CGF.getDestroyer(dtorKind), false); - addCleanup(CGF.EHStack.stable_begin()); + CGF.pushDestroyAndDeferDeactivation( + NormalAndEHCleanup, LV.getAddress(CGF), field->getType(), + CGF.getDestroyer(dtorKind), false); pushedCleanup = true; } } @@ -1824,17 +1795,6 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr( if (GEP->use_empty()) GEP->eraseFromParent(); } - - // Deactivate all the partial cleanups in reverse order, which - // generally means popping them. - assert((cleanupDominator || cleanups.empty()) && - "Missing cleanupDominator before deactivating cleanup blocks"); - for (unsigned i = cleanups.size(); i != 0; --i) - CGF.DeactivateCleanupBlock(cleanups[i-1], cleanupDominator); - - // Destroy the placeholder if we made one. - if (cleanupDominator) - cleanupDominator->eraseFromParent(); } void AggExprEmitter::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E, diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index fd413dcfc236c6..f10437d6592421 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1005,8 +1005,8 @@ void CodeGenFunction::EmitNewArrayInitializer( const Expr *Init = E->getInitializer(); Address EndOfInit = Address::invalid(); QualType::DestructionKind DtorKind = ElementType.isDestructedType(); - EHScopeStack::stable_iterator Cleanup; - llvm::Instruction *CleanupDominator = nullptr; + CleanupDeactivationScope deactivation(*this); + bool pushedCleanup = false; CharUnits ElementSize = getContext().getTypeSizeInChars(ElementType); CharUnits ElementAlign = @@ -1103,18 +1103,21 @@ void CodeGenFunction::EmitNewArrayInitializer( } // Enter a partial-destruction Cleanup if necessary. - if (needsEHCleanup(DtorKind) || (DtorKind && E->containsControlFlow())) { + if (needsEHCleanup(DtorKind) || (DtorKind && E->mayBranchOut())) { // In principle we could tell the Cleanup where we are more // directly, but the control flow can get so varied here that it // would actually be quite complex. Therefore we go through an // alloca. + llvm::Instruction *DominatingIP = + Builder.CreateFlagLoad(llvm::ConstantInt::getNullValue(Int8PtrTy)); EndOfInit = CreateTempAlloca(BeginPtr.getType(), getPointerAlign(), "array.init.end"); - CleanupDominator = Builder.CreateStore(BeginPtr.getPointer(), EndOfInit); pushIrregularPartialArrayCleanup(BeginPtr.getPointer(), EndOfInit, ElementType, ElementAlign, getDestroyer(DtorKind)); - Cleanup = EHStack.stable_begin(); + DeferredDeactivationCleanupStack.push_back( + {EHStack.stable_begin(), DominatingIP}); + pushedCleanup = true; } CharUnits StartAlign = CurPtr.getAlignment(); @@ -1160,9 +1163,6 @@ void CodeGenFunction::EmitNewArrayInitializer( // initialization. llvm::ConstantInt *ConstNum = dyn_cast<llvm::ConstantInt>(NumElements); if (ConstNum && ConstNum->getZExtValue() <= InitListElements) { - // If there was a Cleanup, deactivate it. - if (CleanupDominator) - DeactivateCleanupBlock(Cleanup, CleanupDominator); return; } @@ -1277,12 +1277,14 @@ void CodeGenFunction::EmitNewArrayInitializer( Builder.CreateStore(CurPtr.getPointer(), EndOfInit); // Enter a partial-destruction Cleanup if necessary. - if (!CleanupDominator && needsEHCleanup(DtorKind)) { + if (!pushedCleanup && needsEHCleanup(DtorKind)) { + llvm::Instruction *DominatingIP = + Builder.CreateFlagLoad(llvm::ConstantInt::getNullValue(Int8PtrTy)); pushRegularPartialArrayCleanup(BeginPtr.getPointer(), CurPtr.getPointer(), ElementType, ElementAlign, getDestroyer(DtorKind)); - Cleanup = EHStack.stable_begin(); - CleanupDominator = Builder.CreateUnreachable(); + DeferredDeactivationCleanupStack.push_back( + {EHStack.stable_begin(), DominatingIP}); } // Emit the initializer into this element. @@ -1290,10 +1292,7 @@ void CodeGenFunction::EmitNewArrayInitializer( AggValueSlot::DoesNotOverlap); // Leave the Cleanup if we entered one. - if (CleanupDominator) { - DeactivateCleanupBlock(Cleanup, CleanupDominator); - CleanupDominator->eraseFromParent(); - } + deactivation.ForceDeactivate(); // Advance to the next element by adjusting the pointer type as necessary. llvm::Value *NextPtr = diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 4a3ff49b0007a3..ae0acc6e0a3e56 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -91,6 +91,8 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) CodeGenFunction::~CodeGenFunction() { assert(LifetimeExtendedCleanupStack.empty() && "failed to emit a cleanup"); + assert(DeferredDeactivationCleanupStack.empty() && + "missed to deactivate a cleanup"); if (getLangOpts().OpenMP && CurFn) CGM.getOpenMPRuntime().functionFinished(*this); @@ -336,6 +338,10 @@ 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 of cleanups in EHStack!"); + assert(DeferredDeactivationCleanupStack.empty() && + "mismatched activate/deactivate of cleanups!"); bool OnlySimpleReturnStmts = NumSimpleReturnExprs > 0 && NumSimpleReturnExprs == NumReturnExprs diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 8f2d09ba017738..c848d7ae777ccf 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -648,12 +648,51 @@ class CodeGenFunction : public CodeGenTypeCache { EHScopeStack EHStack; llvm::SmallVector<char, 256> LifetimeExtendedCleanupStack; - struct DeactivateAfterFullExprCleanup { + + // A stack of cleanups which were added to EHStack but have to be deactivated + // later before being popped or emitted. These are usually deactivated on + // exiting a `CleanupDeactivationScope` scope. For instance, after a + // full-expr. + // + // These are specially useful for correctly emitting cleanups while + // encountering branches out of expression (through stmt-expr or coroutine + // suspensions). + struct DeferredDeactivateCleanup { EHScopeStack::stable_iterator Cleanup; llvm::Instruction *DominatingIP; }; - llvm::SmallVector<DeactivateAfterFullExprCleanup> - DeactivateAfterFullExprStack; + llvm::SmallVector<DeferredDeactivateCleanup> DeferredDeactivationCleanupStack; + + // Enters a new scope for capturing cleanups which are deferred to be + // deactivated, all of which will be deactivated once the scope is exited. + struct CleanupDeactivationScope { + CodeGenFunction &CGF; + size_t OldDeactivateCleanupStackSize; + bool Deactivated; + CleanupDeactivationScope(CodeGenFunction &CGF) + : CGF(CGF), OldDeactivateCleanupStackSize( + CGF.DeferredDeactivationCleanupStack.size()), + Deactivated(false) {} + + void ForceDeactivate() { + assert(!Deactivated && "Deactivating already deactivated scope"); + auto &Stack = CGF.DeferredDeactivationCleanupStack; + for (size_t I = Stack.size(); I > OldDeactivateCleanupStackSize; I--) { + CGF.DeactivateCleanupBlock(Stack[I - 1].Cleanup, + Stack[I - 1].DominatingIP); + Stack[I - 1].DominatingIP->eraseFromParent(); + } + Stack.resize(OldDeactivateCleanupStackSize); + Deactivated = true; + } + + ~CleanupDeactivationScope() { + if (Deactivated) + return; + ForceDeactivate(); + } + }; + llvm::SmallVector<const JumpDest *, 2> SEHTryEpilogueStack; llvm::Instruction *CurrentFuncletPad = nullptr; @@ -859,6 +898,19 @@ class CodeGenFunction : public CodeGenTypeCache { new (Buffer + sizeof(Header) + sizeof(T)) Address(ActiveFlag); } + // Push a cleanup onto EHStack and deactivate it later. It is usually + // deactivated when exiting a `CleanupDeactivationScope` (for example: after a + // full expression). + template <class T, class... As> + void pushCleanupAndDeferDeactivation(CleanupKind Kind, As... A) { + // Placeholder dominating IP for this cleanup. + llvm::Instruction *DominatingIP = + Builder.CreateFlagLoad(llvm::Constant::getNullValue(Int8PtrTy)); + EHStack.pushCleanup<T>(Kind, A...); + DeferredDeactivationCleanupStack.push_back( + {EHStack.stable_begin(), DominatingIP}); + } + /// Set up the last cleanup that was pushed as a conditional /// full-expression cleanup. void initFullExprCleanup() { @@ -910,7 +962,7 @@ class CodeGenFunction : public CodeGenTypeCache { class RunCleanupsScope { EHScopeStack::stable_iterator CleanupStackDepth, OldCleanupScopeDepth; size_t LifetimeExtendedCleanupStackSize; - size_t DeactivateAfterFullExprStackSize; + CleanupDeactivationScope DeactivateCleanups; bool OldDidCallStackSave; protected: bool PerformCleanup; @@ -925,13 +977,10 @@ class CodeGenFunction : public CodeGenTypeCache { public: /// Enter a new cleanup scope. explicit RunCleanupsScope(CodeGenFunction &CGF) - : PerformCleanup(true), CGF(CGF) - { + : DeactivateCleanups(CGF), PerformCleanup(true), CGF(CGF) { CleanupStackDepth = CGF.EHStack.stable_begin(); LifetimeExtendedCleanupStackSize = CGF.LifetimeExtendedCleanupStack.size(); - DeactivateAfterFullExprStackSize = - CGF.DeactivateAfterFullExprStack.size(); OldDidCallStackSave = CGF.DidCallStackSave; CGF.DidCallStackSave = false; OldCleanupScopeDepth = CGF.CurrentCleanupScopeDepth; @@ -958,8 +1007,9 @@ class CodeGenFunction : public CodeGenTypeCache { void ForceCleanup(std::initializer_list<llvm::Value**> ValuesToReload = {}) { assert(PerformCleanup && "Already forced cleanup"); CGF.DidCallStackSave = OldDidCallStackSave; + DeactivateCleanups.ForceDeactivate(); CGF.PopCleanupBlocks(CleanupStackDepth, LifetimeExtendedCleanupStackSize, - DeactivateAfterFullExprStackSize, ValuesToReload); + ValuesToReload); PerformCleanup = false; CGF.CurrentCleanupScopeDepth = OldCleanupScopeDepth; } @@ -1181,7 +1231,6 @@ class CodeGenFunction : public CodeGenTypeCache { void PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize, size_t OldLifetimeExtendedStackSize, - size_t OldDeactivateAfterFullExprStackSize, std::initializer_list<llvm::Value **> ValuesToReload = {}); void ResolveBranchFixups(llvm::BasicBlock *Target); @@ -2146,6 +2195,11 @@ class CodeGenFunction : public CodeGenTypeCache { Address addr, QualType type); void pushDestroy(CleanupKind kind, Address addr, QualType type, Destroyer *destroyer, bool useEHCleanupForArray); + void pushDestroyAndDeferDeactivation(QualType::DestructionKind dtorKind, + Address addr, QualType type); + void pushDestroyAndDeferDeactivation(CleanupKind cleanupKind, Address addr, + QualType type, Destroyer *destroyer, + bool useEHCleanupForArray); void pushLifetimeExtendedDestroy(CleanupKind kind, Address addr, QualType type, Destroyer *destroyer, bool useEHCleanupForArray); diff --git a/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp b/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp index d703532b5a10b9..b3c6d8f65a4fba 100644 --- a/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp +++ b/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp @@ -32,52 +32,52 @@ struct Awaiter : std::suspend_always { int foo() { return 2; } -struct PrintiesCopy { +struct Printies { Printy a; Printy b; Printy c; }; void ParenInit() { - // CHECK: define dso_local void @_Z9ParenInitv() + // CHECK-LABEL: define dso_local void @_Z9ParenInitv() // CHECK: [[CLEANUP_DEST:%.+]] = alloca i32, align 4 - PrintiesCopy ps(Printy("a"), - // CHECK: call void @_ZN6PrintyC1EPKc - ({ - if (foo()) return; - // CHECK: if.then: - // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 - // CHECK-NEXT: br label %cleanup - Printy("b"); - // CHECK: if.end: - // CHECK-NEXT: call void @_ZN6PrintyC1EPKc - }), - ({ - if (foo()) return; - // CHECK: if.then{{.*}}: - // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 - // CHECK-NEXT: call void @_ZN6PrintyD1Ev - // CHECK-NEXT: br label %cleanup - Printy("c"); - // CHECK: if.end{{.*}}: - // CHECK-NEXT: call void @_ZN6PrintyC1EPKc - // CHECK-NEXT: call void @_ZN12PrintiesCopyD1Ev - // CHECK-NEXT: br label %return - })); + Printies ps(Printy("a"), + // CHECK: call void @_ZN6PrintyC1EPKc + ({ + if (foo()) return; + // CHECK: if.then: + // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 + // CHECK-NEXT: br label %cleanup + Printy("b"); + // CHECK: if.end: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + }), + ({ + if (foo()) return; + // CHECK: if.then{{.*}}: + // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: br label %cleanup + Printy("c"); + // CHECK: if.end{{.*}}: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: call void @_ZN8PrintiesD1Ev + // CHECK-NEXT: br label %return + })); // CHECK: cleanup: // CHECK-NEXT: call void @_ZN6PrintyD1Ev // CHECK-NEXT: br label %return } coroutine ParenInitCoro() { - // CHECK: define dso_local void @_Z13ParenInitCorov + // CHECK-LABEL: define dso_local void @_Z13ParenInitCorov // CHECK: [[ACTIVE1:%.+]] = alloca i1, align 1 // CHECK: [[ACTIVE2:%.+]] = alloca i1, align 1 - PrintiesCopy ps(Printy("a"), Printy("b"), + Printies ps(Printy("a"), Printy("b"), // CHECK: call void @_ZN6PrintyC1EPKc - // CHECK-NEXT: store i1 true, ptr [[ACTIVE1]].reload.addr, align 1 // CHECK-NEXT: store i1 true, ptr [[ACTIVE2]].reload.addr, align 1 // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: store i1 true, ptr [[ACTIVE1]].reload.addr, align 1 co_await Awaiter{} // CHECK: await.cleanup: @@ -112,61 +112,354 @@ coroutine ParenInitCoro() { ); } -// TODO: Add more assertions after preliminary review. -// struct S { -// Printy arr1[2]; -// Printy arr2[2]; -// Printy p; -// }; - -// void ArraySubobjects() { -// S s{{Printy("a"), Printy("b")}, -// {Printy("a"), ({ -// if (foo() == 1) { -// return; -// } -// Printy("b"); -// })}, -// ({ -// if (foo() == 2) { -// return; -// } -// Printy("b"); -// })}; -// } - -// coroutine ArraySubobjectsCoro() { -// S s{{Printy("a"), Printy("b")}, -// {Printy("a"), co_await Awaiter{}}, -// co_await Awaiter{}}; -// } - -// struct A { -// Printy a; -// }; -// struct B : A { -// Printy b; -// Printy c; -// }; - -// void BaseClassCtors() { -// auto S = B({Printy("a")}, Printy("b"), ({ -// return; -// Printy("c"); -// })); -// } - -// coroutine BaseClassCtorsCoro() { -// auto S = B({Printy("a")}, Printy("b"), co_await Awaiter{}); -// } - -// void LambdaInit() { -// auto S = [a = Printy("a"), b = ({ -// return; -// Printy("b"); -// })]() { return a; }; -// } - -// coroutine LambdaInitCoro() { -// auto S = [a = Printy("a"), b = co_await Awaiter{}]() { return a; }; -// } +void break_in_stmt_expr() { + // Verify that the "break" in "if.then".calls dtor before jumping to "for.end". + + // CHECK-LABEL: define dso_local void @_Z18break_in_stmt_exprv() + Printies p{Printy("a"), + // CHECK: call void @_ZN6PrintyC1EPKc + ({ + for (;;) { + Printies ps{ + Printy("b"), + // CHECK: for.cond: + // CHECK: call void @_ZN6PrintyC1EPKc + ({ + if (foo()) { + break; + // CHECK: if.then: + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: br label %for.end + } + Printy("c"); + // CHECK: if.end: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + }), + Printy("d")}; + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: call void @_ZN8PrintiesD1Ev + // CHECK-NEXT: br label %for.cond + } + Printy("e"); + // CHECK: for.end: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + }), + Printy("f")}; + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: call void @_ZN8PrintiesD1Ev +} + +void goto_in_stmt_expr() { + // Verify that: + // - correct branch fixups for deactivated normal cleanups are generated correctly. + + // CHECK-LABEL: define dso_local void @_Z17goto_in_stmt_exprv() + // CHECK: [[CLEANUP_DEST_SLOT:%cleanup.dest.slot.*]] = alloca i32, align 4 + { + Printies p1{Printy("a"), // CHECK: call void @_ZN6PrintyC1EPKc + ({ + { + Printies p2{Printy("b"), + // CHECK: call void @_ZN6PrintyC1EPKc + ({ + if (foo() == 1) { + goto in; + // CHECK: if.then: + // CHECK-NEXT: store i32 2, ptr [[CLEANUP_DEST_SLOT]], align 4 + // CHECK-NEXT: br label %[[CLEANUP1:.+]] + } + if (foo() == 2) { + goto out; + // CHECK: if.then{{.*}}: + // CHECK-NEXT: store i32 3, ptr [[CLEANUP_DEST_SLOT]], align 4 + // CHECK-NEXT: br label %[[CLEANUP1]] + } + Printy("c"); + // CHECK: if.end{{.*}}: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + }), + Printy("d")}; + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: call void @_ZN8PrintiesD1Ev + // CHECK-NEXT: br label %in + + } + in: + Printy("e"); + // CHECK: in: ; preds = %if.end{{.*}}, %[[CLEANUP1]] + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + }), + Printy("f")}; + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: call void @_ZN8PrintiesD1Ev + // CHECK-NEXT: br label %out + } +out: + return; + // CHECK: out: + // CHECK-NEXT: ret void + + // CHECK: [[CLEANUP1]]: ; preds = %if.then{{.*}}, %if.then + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: %cleanup.dest = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4 + // CHECK-NEXT: switch i32 %cleanup.dest, label %[[CLEANUP2:.+]] [ + // CHECK-NEXT: i32 2, label %in + // CHECK-NEXT: ] + + // CHECK: [[CLEANUP2]]: ; preds = %[[CLEANUP1]] + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: %cleanup.dest{{.*}} = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4 + // CHECK-NEXT: switch i32 %cleanup.dest{{.*}}, label %unreachable [ + // CHECK-NEXT: i32 3, label %out + // CHECK-NEXT: ] +} + +void ArrayInit() { + // Printy arr[4] = {ctorA, ctorB, stmt-exprC, stmt-exprD}; + // Verify that: + // - We do the necessary stores for array cleanups (endOfInit and last constructed element). + // - We update the array init element correctly for ctorA, ctorB and stmt-exprC. + // - stmt-exprC and stmt-exprD share the array body dtor code (see %cleanup). + + // CHECK-LABEL: define dso_local void @_Z9ArrayInitv() + // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 + // CHECK: %cleanup.dest.slot = alloca i32, align 4 + // CHECK: %arrayinit.begin = getelementptr inbounds [4 x %struct.Printy], ptr %arr, i64 0, i64 0 + // CHECK: store ptr %arrayinit.begin, ptr %arrayinit.endOfInit, align 8 + Printy arr[4] = { + Printy("a"), + // CHECK: call void @_ZN6PrintyC1EPKc(ptr noundef nonnull align 8 dereferenceable(8) %arrayinit.begin, ptr noundef @.str) + // CHECK: [[ARRAYINIT_ELEMENT1:%.+]] = getelementptr inbounds %struct.Printy, ptr %arrayinit.begin, i64 1 + // CHECK: store ptr [[ARRAYINIT_ELEMENT1]], ptr %arrayinit.endOfInit, align 8 + Printy("b"), + // CHECK: call void @_ZN6PrintyC1EPKc(ptr noundef nonnull align 8 dereferenceable(8) [[ARRAYINIT_ELEMENT1]], ptr noundef @.str.1) + // CHECK: [[ARRAYINIT_ELEMENT2:%.+]] = getelementptr inbounds %struct.Printy, ptr [[ARRAYINIT_ELEMENT1]], i64 1 + // CHECK: store ptr [[ARRAYINIT_ELEMENT2]], ptr %arrayinit.endOfInit, align 8 + ({ + // CHECK: br i1 {{.*}}, label %if.then, label %if.end + if (foo()) { + return; + // CHECK: if.then: + // CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot, align 4 + // CHECK-NEXT: br label %cleanup + } + // CHECK: if.end: + Printy("c"); + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: %arrayinit.element2 = getelementptr inbounds %struct.Printy, ptr %arrayinit.element1, i64 1 + // CHECK-NEXT: store ptr %arrayinit.element2, ptr %arrayinit.endOfInit, align 8 + }), + ({ + // CHECK: br i1 {{%.+}} label %[[IF_THEN2:.+]], label %[[IF_END2:.+]] + if (foo()) { + return; + // CHECK: [[IF_THEN2]]: + // CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot, align 4 + // CHECK-NEXT: br label %cleanup + } + // CHECK: [[IF_END2]]: + Printy("d"); + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: %array.begin = getelementptr inbounds [4 x %struct.Printy], ptr %arr, i32 0, i32 0 + // CHECK-NEXT: %0 = getelementptr inbounds %struct.Printy, ptr %array.begin, i64 4 + // CHECK-NEXT: br label %[[ARRAY_DESTROY_BODY1:.+]] + }), + }; + + // CHECK: [[ARRAY_DESTROY_BODY1]]: + // CHECK-NEXT: %arraydestroy.elementPast{{.*}} = phi ptr [ %0, %[[IF_END2]] ], [ %arraydestroy.element{{.*}}, %[[ARRAY_DESTROY_BODY1]] ] + // CHECK-NEXT: %arraydestroy.element{{.*}} = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast{{.*}}, i64 -1 + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: %arraydestroy.done{{.*}} = icmp eq ptr %arraydestroy.element{{.*}}, %array.begin + // CHECK-NEXT: br i1 %arraydestroy.done{{.*}}, label %[[ARRAY_DESTROY_DONE1:.+]], label %[[ARRAY_DESTROY_BODY1]] + + // CHECK: [[ARRAY_DESTROY_DONE1]]: + // CHECK-NEXT: ret void + + // CHECK: cleanup: + // CHECK-NEXT: %1 = load ptr, ptr %arrayinit.endOfInit, align 8 + // CHECK-NEXT: %arraydestroy.isempty = icmp eq ptr %arrayinit.begin, %1 + // CHECK-NEXT: br i1 %arraydestroy.isempty, label %[[ARRAY_DESTROY_DONE2:.+]], label %[[ARRAY_DESTROY_BODY2:.+]] + + // CHECK: [[ARRAY_DESTROY_BODY2]]: + // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %1, %cleanup ], [ %arraydestroy.element, %[[ARRAY_DESTROY_BODY2]] ] + // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1 + // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element) + // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %arrayinit.begin + // CHECK-NEXT: br i1 %arraydestroy.done, label %[[ARRAY_DESTROY_DONE2]], label %[[ARRAY_DESTROY_BODY2]] + + // CHECK: [[ARRAY_DESTROY_DONE2]]: + // CHECK-NEXT: br label %[[ARRAY_DESTROY_DONE1]] +} + +coroutine ArrayInitCoro() { + // Verify that: + // - We do the necessary stores for array cleanups. + // - Array cleanups are called by await.cleanup. + // - We activate the cleanup after the first element and deactivate it in await.ready (see cleanup.isactive). + + // CHECK-LABEL: define dso_local void @_Z13ArrayInitCorov + // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 + // CHECK: %cleanup.isactive = alloca i1, align 1 + Printy arr[2] = { + Printy("a"), + // CHECK: %arrayinit.begin = getelementptr inbounds [2 x %struct.Printy], ptr %arr.reload.addr, i64 0, i64 0 + // CHECK-NEXT: %arrayinit.begin.spill.addr = getelementptr inbounds %_Z13ArrayInitCorov.Frame, ptr %0, i32 0, i32 10 + // CHECK-NEXT: store ptr %arrayinit.begin, ptr %arrayinit.begin.spill.addr, align 8 + // CHECK-NEXT: store i1 true, ptr %cleanup.isactive.reload.addr, align 1 + // CHECK-NEXT: store ptr %arrayinit.begin, ptr %arrayinit.endOfInit.reload.addr, align 8 + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc(ptr noundef nonnull align 8 dereferenceable(8) %arrayinit.begin, ptr noundef @.str) + // CHECK-NEXT: %arrayinit.element = getelementptr inbounds %struct.Printy, ptr %arrayinit.begin, i64 1 + // CHECK-NEXT: %arrayinit.element.spill.addr = getelementptr inbounds %_Z13ArrayInitCorov.Frame, ptr %0, i32 0, i32 11 + // CHECK-NEXT: store ptr %arrayinit.element, ptr %arrayinit.element.spill.addr, align 8 + // 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: await.cleanup: ; preds = %AfterCoroSuspend{{.*}} + // CHECK-NEXT: br label %cleanup{{.*}}.from.await.cleanup + + // CHECK: cleanup{{.*}}.from.await.cleanup: ; preds = %await.cleanup + // CHECK: br label %cleanup{{.*}} + + // CHECK: await.ready: + // CHECK-NEXT: %arrayinit.element.reload.addr = getelementptr inbounds %_Z13ArrayInitCorov.Frame, ptr %0, i32 0, i32 11 + // CHECK-NEXT: %arrayinit.element.reload = load ptr, ptr %arrayinit.element.reload.addr, align 8 + // CHECK-NEXT: call void @_ZN7Awaiter12await_resumeEv + // CHECK-NEXT: store i1 false, ptr %cleanup.isactive.reload.addr, align 1 + // CHECK-NEXT: br label %cleanup{{.*}}.from.await.ready + + // CHECK: cleanup{{.*}}: ; preds = %cleanup{{.*}}.from.await.ready, %cleanup{{.*}}.from.await.cleanup + // CHECK: %cleanup.is_active = load i1, ptr %cleanup.isactive.reload.addr, align 1 + // CHECK-NEXT: br i1 %cleanup.is_active, label %cleanup.action, label %cleanup.done + + // CHECK: cleanup.action: + // CHECK: %arraydestroy.isempty = icmp eq ptr %arrayinit.begin.reload{{.*}}, %{{.*}} + // CHECK-NEXT: br i1 %arraydestroy.isempty, label %arraydestroy.done{{.*}}, label %arraydestroy.body.from.cleanup.action + // Ignore rest of the array cleanup. +} + +void ArraySubobjects() { + struct S { + Printy arr1[2]; + Printy arr2[2]; + Printy p; + }; + // CHECK-LABEL: define dso_local void @_Z15ArraySubobjectsv() + // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 + S s{{Printy("a"), Printy("b")}, + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK: call void @_ZN6PrintyC1EPKc + {Printy("a"), + // CHECK: [[ARRAYINIT_BEGIN:%.+]] = getelementptr inbounds [2 x %struct.Printy] + // CHECK: store ptr [[ARRAYINIT_BEGIN]], ptr %arrayinit.endOfInit, align 8 + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK: [[ARRAYINIT_ELEMENT:%.+]] = getelementptr inbounds %struct.Printy + // CHECK: store ptr [[ARRAYINIT_ELEMENT]], ptr %arrayinit.endOfInit, align 8 + ({ + if (foo()) { + return; + // CHECK: if.then: + // CHECK-NEXT: [[V0:%.+]] = load ptr, ptr %arrayinit.endOfInit, align 8 + // CHECK-NEXT: %arraydestroy.isempty = icmp eq ptr [[ARRAYINIT_BEGIN]], [[V0]] + // CHECK-NEXT: br i1 %arraydestroy.isempty, label %[[ARRAY_DESTROY_DONE:.+]], label %[[ARRAY_DESTROY_BODY:.+]] + } + Printy("b"); + }) + }, + Printy("c") + // CHECK: if.end: + // CHECK-NEXT: call void @_ZN6PrintyC1EPKc + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK-NEXT: call void @_ZZ15ArraySubobjectsvEN1SD1Ev + // CHECK-NEXT: br label %return + }; + // CHECK: return: + // CHECK-NEXT: ret void + + // CHECK: [[ARRAY_DESTROY_BODY]]: + // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %0, %if.then ], [ %arraydestroy.element, %[[ARRAY_DESTROY_BODY]] ] + // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1 + // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element) + // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, [[ARRAYINIT_BEGIN]] + // CHECK-NEXT: br i1 %arraydestroy.done, label %[[ARRAY_DESTROY_DONE]], label %[[ARRAY_DESTROY_BODY]] + + // CHECK: [[ARRAY_DESTROY_DONE]] + // CHECK-NEXT: [[ARRAY_BEGIN:%.+]] = getelementptr inbounds [2 x %struct.Printy], ptr %arr1, i32 0, i32 0 + // CHECK-NEXT: [[V1:%.+]] = getelementptr inbounds %struct.Printy, ptr [[ARRAY_BEGIN]], i64 2 + // CHECK-NEXT: br label %[[ARRAY_DESTROY_BODY2:.+]] + + // CHECK: [[ARRAY_DESTROY_BODY2]]: + // CHECK-NEXT: %arraydestroy.elementPast5 = phi ptr [ %1, %[[ARRAY_DESTROY_DONE]] ], [ %arraydestroy.element6, %[[ARRAY_DESTROY_BODY2]] ] + // CHECK-NEXT: %arraydestroy.element6 = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast5, i64 -1 + // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element6) + // CHECK-NEXT: %arraydestroy.done7 = icmp eq ptr %arraydestroy.element6, [[ARRAY_BEGIN]] + // CHECK-NEXT: br i1 %arraydestroy.done7, label %[[ARRAY_DESTROY_DONE2:.+]], label %[[ARRAY_DESTROY_BODY2]] + + + // CHECK: [[ARRAY_DESTROY_DONE2]]: + // CHECK-NEXT: br label %return +} + +void LambdaInit() { + // CHECK-LABEL: define dso_local void @_Z10LambdaInitv() + auto S = [a = Printy("a"), b = ({ + if (foo()) { + return; + // CHECK: if.then: + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: br label %return + } + Printy("b"); + })]() { return a; }; +} + +void LifetimeExtended() { + // CHECK-LABEL: define dso_local void @_Z16LifetimeExtendedv + struct PrintyRefBind { + const Printy &a; + const Printy &b; + }; + PrintyRefBind ps = {Printy("a"), ({ + if (foo()) { + return; + // CHECK: if.then: + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + // CHECK-NEXT: br label %return + } + Printy("b"); + })}; +} + +void NewArrayInit() { + // CHECK-LABEL: define dso_local void @_Z12NewArrayInitv() + // CHECK: %array.init.end = alloca ptr, align 8 + // CHECK: store ptr %0, ptr %array.init.end, align 8 + Printy *array = new Printy[3]{ + "a", + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK: store ptr %array.exp.next, ptr %array.init.end, align 8 + "b", + // CHECK: call void @_ZN6PrintyC1EPKc + // CHECK: store ptr %array.exp.next1, ptr %array.init.end, align 8 + ({ + if (foo()) { + return; + // CHECK: if.then: + // CHECK: br i1 %arraydestroy.isempty, label %arraydestroy.done{{.*}}, label %arraydestroy.body + } + "b"; + // CHECK: if.end: + // CHECK: call void @_ZN6PrintyC1EPKc + })}; + // CHECK: arraydestroy.body: + // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %{{.*}}, %if.then ], [ %arraydestroy.element, %arraydestroy.body ] + // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1 + // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element) + // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %0 + // CHECK-NEXT: br i1 %arraydestroy.done, label %arraydestroy.done{{.*}}, label %arraydestroy.body + + // CHECK: arraydestroy.done{{.*}}: ; preds = %arraydestroy.body, %if.then + // CHECK-NEXT: br label %return +} \ No newline at end of file >From 0702c966bbe526c3aff5e5db6c6d82470de5c22b Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena <u...@google.com> Date: Mon, 25 Mar 2024 14:15:14 +0000 Subject: [PATCH 5/5] Added co_return, continue as branches out of expr. Added tests --- clang/lib/AST/Expr.cpp | 4 +++ clang/lib/CodeGen/CGDecl.cpp | 2 +- .../control-flow-in-expr-cleanup.cpp | 28 ++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index b2d5df116ca9e8..9b94da06b511e2 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -26,6 +26,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.h" @@ -2515,10 +2516,13 @@ bool Expr::mayBranchOut() const { // Coroutine suspensions. bool VisitCoawaitExpr(CoawaitExpr *) { return activate(); } bool VisitCoyieldExpr(CoyieldExpr *) { return activate(); } + bool VisitCoreturnStmt(CoreturnStmt *) { return activate(); } // Control flow in stmt-expressions. bool VisitBreakStmt(BreakStmt *) { return activate(); } bool VisitReturnStmt(ReturnStmt *) { return activate(); } bool VisitGotoStmt(GotoStmt *) { return activate(); } + bool VisitIndirectGotoStmt(IndirectGotoStmt *) { return activate(); } + bool VisitContinueStmt(ContinueStmt *) { return activate(); } }; BranchDetector detector; detector.TraverseStmt(const_cast<Expr *>(this)); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 02c597bd999dc5..4dce1f53841547 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -2187,7 +2187,7 @@ void CodeGenFunction::pushDestroy(CleanupKind cleanupKind, Address addr, } // Pushes a destroy and defers its deactivation until its -// DeferredDeactivationCleanupScope is exited. +// CleanupDeactivationScope is exited. void CodeGenFunction::pushDestroyAndDeferDeactivation( QualType::DestructionKind dtorKind, Address addr, QualType type) { assert(dtorKind && "cannot push destructor for trivial type"); diff --git a/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp b/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp index b3c6d8f65a4fba..5673c30eb40d06 100644 --- a/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp +++ b/clang/test/CodeGenCoroutines/control-flow-in-expr-cleanup.cpp @@ -462,4 +462,30 @@ void NewArrayInit() { // CHECK: arraydestroy.done{{.*}}: ; preds = %arraydestroy.body, %if.then // CHECK-NEXT: br label %return -} \ No newline at end of file +} + +void ArrayInitWithContinue() { + // CHECK-LABEL: @_Z21ArrayInitWithContinuev + // Verify that we start to emit the array destructor. + // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 + for (int i = 0; i < 1; ++i) { + Printy arr[2] = {"a", ({ + if (foo()) { + continue; + } + "b"; + })}; + } +} + +coroutine ArrayInitWithCoReturn() { + // CHECK-LABEL: define dso_local void @_Z21ArrayInitWithCoReturnv + // Verify that we start to emit the array destructor. + // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 + Printy arr[2] = {"a", ({ + if (foo()) { + co_return; + } + "b"; + })}; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits