diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index f87caf050eeaa..af787440f8847 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,37 +490,37 @@ 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 ValuesToReload) { - PopCleanupBlocks(Old, ValuesToReload); - - // Move our deferred cleanups onto the EH stack. - 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( - LifetimeExtendedCleanupStack[I]); + DeferredCleanupHeader &Header = + reinterpret_cast(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
(LifetimeExtendedCleanupStack[I]); + Address ActiveFlag = reinterpret_cast
(Cleanups[I]); initFullExprCleanupWithFlag(ActiveFlag); 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 ValuesToReload) { + PopCleanupBlocks(Old, ValuesToReload); + + // Move our deferred lifetime-extended cleanups onto the EH stack. + AddDeferredCleanups(LifetimeExtendedCleanupStack, OldLifetimeExtendedSize); LifetimeExtendedCleanupStack.resize(OldLifetimeExtendedSize); } @@ -1102,6 +1104,13 @@ void CodeGenFunction::EmitBranchThroughCleanup(JumpDest Dest) { if (!HaveInsertPoint()) return; + // 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()) + 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 bbe14ef4c1724..66df9186155d9 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 & ~NormalCleanup), addr, type, destroyer, useEHCleanupForArray); - return pushCleanupAfterFullExprWithActiveFlag( - cleanupKind, Address::invalid(), addr, type, destroyer, useEHCleanupForArray); + pushDestroy(BranchInExprCleanup, addr, type, destroyer, + useEHCleanupForArray); + return pushDeferredCleanup( + 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( - cleanupKind, ActiveFlag, SavedAddr, type, destroyer, - useEHCleanupForArray); + pushDeferredCleanup( + LifetimeExtendedCleanupStack, cleanupKind, ActiveFlag, SavedAddr, type, + destroyer, useEHCleanupForArray); } /// emitDestroy - Immediately perform the destruction of the given diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 4a2f3caad6588..3519de8948249 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4961,7 +4961,7 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){ if (QualType::DestructionKind DtorKind = E->getType().isDestructedType()) pushLifetimeExtendedDestroy(getCleanupKind(DtorKind), DeclPtr, E->getType(), getDestroyer(DtorKind), - DtorKind & EHCleanup); + getCleanupKind(DtorKind) & EHCleanup); return Result; } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 22f55fe9aac90..b131c6b14b02e 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" @@ -565,10 +566,6 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, elementAlign, CGF.getDestroyer(dtorKind)); cleanup = CGF.EHStack.stable_begin(); - - // Otherwise, remember that we didn't need a cleanup. - } else { - dtorKind = QualType::DK_none; } llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1); @@ -580,6 +577,7 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, // elements have been initialized. llvm::Value *element = begin; + CodeGenFunction::RestoreBranchInExprRAII branchInExpr(CGF); // Emit the explicit initializers. for (uint64_t i = 0; i != NumInitElements; ++i) { // Advance to the next element. @@ -592,10 +590,15 @@ 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 expression. + // FIXME: Emit a single iterative cleanup instead of element-wise cleanup. + if (CGF.needsBranchCleanup(dtorKind)) + CGF.pushDestroy(BranchInExprCleanup, address, elementType, + CGF.getDestroyer(dtorKind), false); } // Check whether there's a non-trivial array-fill expression. @@ -666,7 +669,8 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, } // Leave the partial-array cleanup if we entered one. - if (dtorKind) CGF.DeactivateCleanupBlock(cleanup, cleanupDominator); + if (cleanupDominator) + CGF.DeactivateCleanupBlock(cleanup, cleanupDominator); } //===----------------------------------------------------------------------===// @@ -710,7 +714,7 @@ AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { if (QualType::DestructionKind DtorKind = E->getType().isDestructedType()) CGF.pushLifetimeExtendedDestroy( CGF.getCleanupKind(DtorKind), Slot.getAddress(), E->getType(), - CGF.getDestroyer(DtorKind), DtorKind & EHCleanup); + CGF.getDestroyer(DtorKind), CGF.getCleanupKind(DtorKind) & EHCleanup); } /// Attempt to look through various unimportant expressions to find a @@ -1360,6 +1364,7 @@ AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) { SmallVector Cleanups; llvm::Instruction *CleanupDominator = nullptr; + CodeGenFunction::RestoreBranchInExprRAII 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(); @@ -1377,6 +1382,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( @@ -1754,7 +1762,7 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr( return; } - + CodeGenFunction::RestoreBranchInExprRAII 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 +1801,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/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index d136bfc37278f..d872b645cda41 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1117,6 +1117,7 @@ void CodeGenFunction::EmitNewArrayInitializer( Cleanup = EHStack.stable_begin(); } + RestoreBranchInExprRAII BranchInExpr(*this); CharUnits StartAlign = CurPtr.getAlignment(); unsigned i = 0; for (const Expr *IE : InitExprs) { @@ -1131,6 +1132,12 @@ void CodeGenFunction::EmitNewArrayInitializer( // initialization loops. StoreAnyExprIntoOneUnit(*this, IE, IE->getType(), CurPtr, AggValueSlot::DoesNotOverlap); + // Schedule to emit element cleanup if we see a branch in the array + // initialisation expression. + // FIXME: Emit a single iterative cleanup instead of element-wise cleanup. + if (needsBranchCleanup(DtorKind)) + pushDestroy(BranchInExprCleanup, CurPtr, ElementType, + getDestroyer(DtorKind), false); CurPtr = Address(Builder.CreateInBoundsGEP( CurPtr.getElementType(), CurPtr.getPointer(), Builder.getSize(1), "array.exp.next"), diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index beff0ad9da270..6f40dc066796e 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -627,9 +627,11 @@ CodeGenFunction::getJumpDestForLabel(const LabelDecl *D) { if (Dest.isValid()) return Dest; // Create, but don't insert, the new block. + // FIXME: We do not know `BranchInExprDepth` for the destination and currently + // emit *all* the BranchInExpr cleanups. Dest = JumpDest(createBasicBlock(D->getName()), EHScopeStack::stable_iterator::invalid(), - NextCleanupDestIndex++); + /*BranchInExprDepth=*/0, NextCleanupDestIndex++); return Dest; } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 1ad905078d349..7bff6c89de563 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 143ad64e8816b..a8ca5b99b6e44 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -238,15 +238,17 @@ 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), BranchInExprDepth(0), Index(0) {} JumpDest(llvm::BasicBlock *Block, EHScopeStack::stable_iterator Depth, - unsigned Index) - : Block(Block), ScopeDepth(Depth), Index(Index) {} + unsigned BranchInExprDepth, unsigned 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; } unsigned getDestIndex() const { return Index; } + unsigned getBranchInExprDepth() const { return BranchInExprDepth; } // This should be used cautiously. void setScopeDepth(EHScopeStack::stable_iterator depth) { @@ -256,6 +258,11 @@ class CodeGenFunction : public CodeGenTypeCache { private: llvm::BasicBlock *Block; EHScopeStack::stable_iterator ScopeDepth; + + // 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; }; @@ -626,7 +633,37 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::DenseMap NRVOFlags; EHScopeStack EHStack; - llvm::SmallVector LifetimeExtendedCleanupStack; + + using DeferredCleanupStack = llvm::SmallVector; + + // 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; + + // RAII for restoring BranchInExprCleanupStack. + // All cleanups added to this stack during its scope are simply deleted. These + // cleanups should be added to the EHStack only on emitting a branch. + class RestoreBranchInExprRAII { + public: + RestoreBranchInExprRAII(CodeGenFunction &CGF) + : CGF(CGF), OldSize(CGF.BranchInExprCleanupStack.size()) {} + ~RestoreBranchInExprRAII() { CGF.BranchInExprCleanupStack.resize(OldSize); } + + private: + CodeGenFunction &CGF; + size_t OldSize; + }; + llvm::SmallVector SEHTryEpilogueStack; llvm::Instruction *CurrentFuncletPad = nullptr; @@ -646,8 +683,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. @@ -777,6 +814,14 @@ class CodeGenFunction : public CodeGenTypeCache { /// we're currently inside a conditionally-evaluated expression. template 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(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()) @@ -796,8 +841,8 @@ class CodeGenFunction : public CodeGenTypeCache { template void pushCleanupAfterFullExpr(CleanupKind Kind, As... A) { if (!isInConditionalBranch()) - return pushCleanupAfterFullExprWithActiveFlag(Kind, Address::invalid(), - A...); + return pushDeferredCleanup(LifetimeExtendedCleanupStack, Kind, + Address::invalid(), A...); Address ActiveFlag = createCleanupActiveFlag(); assert(!DominatingValue
::needsSaving(ActiveFlag) && @@ -807,24 +852,23 @@ class CodeGenFunction : public CodeGenTypeCache { SavedTuple Saved{saveValueInCond(A)...}; typedef EHScopeStack::ConditionalCleanup CleanupType; - pushCleanupAfterFullExprWithActiveFlag(Kind, ActiveFlag, Saved); + pushDeferredCleanup(LifetimeExtendedCleanupStack, Kind, + ActiveFlag, Saved); } template - 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); @@ -881,6 +925,7 @@ class CodeGenFunction : public CodeGenTypeCache { class RunCleanupsScope { EHScopeStack::stable_iterator CleanupStackDepth, OldCleanupScopeDepth; size_t LifetimeExtendedCleanupStackSize; + RestoreBranchInExprRAII RestoreBranchInExpr; bool OldDidCallStackSave; protected: bool PerformCleanup; @@ -895,8 +940,7 @@ 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(); @@ -1143,6 +1187,12 @@ class CodeGenFunction : public CodeGenTypeCache { PopCleanupBlocks(EHScopeStack::stable_iterator OldCleanupStackSize, std::initializer_list ValuesToReload = {}); + /// 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 /// the given position to the stack. @@ -1157,9 +1207,8 @@ 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(), - NextCleanupDestIndex++); + return JumpDest(Target, EHStack.getInnermostNormalCleanup(), + BranchInExprCleanupStack.size(), NextCleanupDestIndex++); } /// The given basic block lies in the current EH scope, but may be a @@ -2152,6 +2201,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 0c667e80bb6d8..2077eb5e315fb 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, }; 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 0000000000000..6ad6ecb08e2e5 --- /dev/null +++ b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp @@ -0,0 +1,213 @@ +// RUN: %clang_cc1 --std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s + +// Context: GH63818 +struct Printy { + Printy(const char *); + Printy(); + ~Printy(); +}; + +struct Printies { + const Printy &a; + const Printy &b; + ~Printies() {} +}; + +bool foo(); + +// ==================================== +// Init with lifetime extensions +// ==================================== +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 +} + +// ==================================== +// Break in stmt-expr +// ==================================== +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 +} + +// ============================================= +// 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; +} + +void new_array_init() { + // CHECK: define dso_local void @_Z14new_array_initv() + Printy *a = new Printy[]("1", ({ + if (foo()) { + return; + // CHECK: if.then: + // CHECK-NEXT: call void @_ZN6PrintyD1Ev + } + "2"; + })); + delete[] a; +} + +// ==================================== +// Arrays as sub-objects +// ==================================== +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(); + })}; +} + +// ==================================== +// 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(); + })]() {}; +} 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 0000000000000..362e212b7fba3 --- /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 handle; +}; + +struct coroutine::promise_type { + coroutine get_return_object() { + return {std::coroutine_handle::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: +}