diff --git a/clang/lib/CodeGen/Address.h b/clang/lib/CodeGen/Address.h index cf48df8f5e736..94fdf56632f21 100644 --- a/clang/lib/CodeGen/Address.h +++ b/clang/lib/CodeGen/Address.h @@ -17,6 +17,7 @@ #include "clang/AST/CharUnits.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/Instructions.h" #include "llvm/Support/MathExtras.h" namespace clang { @@ -53,6 +54,10 @@ class Address { return PointerAndKnownNonNull.getPointer(); } + llvm::AllocaInst *getAllocaInst() const { + return llvm::dyn_cast(getPointer()); + } + /// Return the type of the pointer value. llvm::PointerType *getType() const { return llvm::cast(getPointer()->getType()); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 34319381901af..9f37a631e6482 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -671,7 +671,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, // Ensure that we destroy the objects if an exception is thrown later in // the constructor. QualType::DestructionKind dtorKind = FieldType.isDestructedType(); - if (CGF.needsEHCleanup(dtorKind)) + if (dtorKind) CGF.pushEHDestroy(dtorKind, LHS.getAddress(CGF), FieldType); return; } @@ -710,7 +710,7 @@ void CodeGenFunction::EmitInitializerForField(FieldDecl *Field, LValue LHS, // Ensure that we destroy this object if an exception is thrown // later in the constructor. QualType::DestructionKind dtorKind = FieldType.isDestructedType(); - if (needsEHCleanup(dtorKind)) + if (dtorKind) pushEHDestroy(dtorKind, LHS.getAddress(*this), FieldType); } diff --git a/clang/lib/CodeGen/CGCleanup.cpp b/clang/lib/CodeGen/CGCleanup.cpp index f87caf050eeaa..8a1879dc07954 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; @@ -294,18 +296,22 @@ void EHScopeStack::popNullFixups() { BranchFixups.pop_back(); } -Address CodeGenFunction::createCleanupActiveFlag() { +Address CodeGenFunction::createCleanupActiveFlag(EHCleanupScope *TopCleanup) { // Create a variable to decide whether the cleanup needs to be run. Address active = CreateTempAllocaWithoutCast( Builder.getInt1Ty(), CharUnits::One(), "cleanup.cond"); - + if (TopCleanup) + TopCleanup->AddAuxInst(active.getAllocaInst()); // Initialize it to false at a site that's guaranteed to be run // before each evaluation. - setBeforeOutermostConditional(Builder.getFalse(), active); + auto *store1 = setBeforeOutermostConditional(Builder.getFalse(), active); + if (TopCleanup) + TopCleanup->AddAuxInst(store1); // Initialize it to true at the current location. - Builder.CreateStore(Builder.getTrue(), active); - + auto *store = Builder.CreateStore(Builder.getTrue(), active); + if (TopCleanup) + TopCleanup->AddAuxInst(store); return active; } @@ -488,37 +494,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); } @@ -672,15 +678,16 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { Address NormalActiveFlag = Scope.shouldTestFlagInNormalCleanup() ? Scope.getActiveFlag() : Address::invalid(); - Address EHActiveFlag = - Scope.shouldTestFlagInEHCleanup() ? Scope.getActiveFlag() - : Address::invalid(); - + Address EHActiveFlag = Scope.shouldTestFlagInEHCleanup() + ? Scope.getActiveFlag() + : Address::invalid(); // Check whether we need an EH cleanup. This is only true if we've // generated a lazy EH cleanup block. llvm::BasicBlock *EHEntry = Scope.getCachedEHDispatchBlock(); assert(Scope.hasEHBranches() == (EHEntry != nullptr)); bool RequiresEHCleanup = (EHEntry != nullptr); + bool EmitEHCleanup = + RequiresEHCleanup && (EHActiveFlag.isValid() || IsActive); EHScopeStack::stable_iterator EHParent = Scope.getEnclosingEHScope(); // Check the three conditions which might require a normal cleanup: @@ -792,6 +799,8 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { EmitSehCppScopeEnd(); } destroyOptimisticNormalEntry(*this, Scope); + if (EmitEHCleanup) + Scope.MarkEmitted(); EHStack.popCleanup(); } else { // If we have a fallthrough and no other need for the cleanup, @@ -808,6 +817,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { } destroyOptimisticNormalEntry(*this, Scope); + Scope.MarkEmitted(); EHStack.popCleanup(); EmitCleanup(*this, Fn, cleanupFlags, NormalActiveFlag); @@ -944,6 +954,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { } // IV. Pop the cleanup and emit it. + Scope.MarkEmitted(); EHStack.popCleanup(); assert(EHStack.hasNormalCleanups() == HasEnclosingCleanups); @@ -1047,7 +1058,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // We only actually emit the cleanup code if the cleanup is either // active or was used before it was deactivated. - if (EHActiveFlag.isValid() || IsActive) { + if (EmitEHCleanup) { cleanupFlags.setIsForEHCleanup(); EmitCleanup(*this, Fn, cleanupFlags, EHActiveFlag); } @@ -1089,6 +1100,30 @@ bool CodeGenFunction::isObviouslyBranchWithoutCleanups(JumpDest Dest) const { return false; } +void CodeGenFunction::EmitEHCleanupsForBranch(JumpDest Dest) { + if (!Dest.isValid()) + return; + EHScopeStack::stable_iterator TopCleanup = EHStack.getInnermostEHScope(); + std::vector Cleanups; + while (TopCleanup != EHStack.stable_end() && + Dest.getEHDepth().strictlyEncloses(TopCleanup)) { + if (EHStack.find(TopCleanup)->getKind() != EHScope::Cleanup) + break; + EHCleanupScope &Scope = cast(*EHStack.find(TopCleanup)); + if (!Scope.isNormalCleanup() && !Scope.shouldSkipBranchInExpr()) + Cleanups.push_back(TopCleanup); + TopCleanup = Scope.getEnclosingEHScope(); + } + for (size_t I = Cleanups.size(); I > 0; I--) { + EHCleanupScope &Scope = + cast(*EHStack.find(Cleanups[I - 1])); + assert(Scope.isEHCleanup()); + EHStack.pushCopyOfCleanup(NormalCleanup, Scope.getCleanup(), + Scope.getCleanupSize()); + cast(*EHStack.begin()) + .SetExternalAuxInsts(&Scope.getAuxillaryInstructions()); + } +} /// Terminate the current block by emitting a branch which might leave /// the current cleanup-protected scope. The target scope may not yet @@ -1102,6 +1137,12 @@ 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()) + EmitEHCleanupsForBranch(Dest); // Create the branch. llvm::BranchInst *BI = Builder.CreateBr(Dest.getBlock()); diff --git a/clang/lib/CodeGen/CGCleanup.h b/clang/lib/CodeGen/CGCleanup.h index fcfbf41b0eaff..ca7d0aa812e82 100644 --- a/clang/lib/CodeGen/CGCleanup.h +++ b/clang/lib/CodeGen/CGCleanup.h @@ -16,8 +16,10 @@ #include "EHScopeStack.h" #include "Address.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Instruction.h" namespace llvm { class BasicBlock; @@ -81,6 +83,8 @@ class EHScope { /// Whether the EH cleanup should test the activation flag. unsigned TestFlagInEHCleanup : 1; + unsigned SkipBranchInExpr : 1; + /// The amount of extra storage needed by the Cleanup. /// Always a multiple of the scope-stack alignment. unsigned CleanupSize : 12; @@ -256,6 +260,30 @@ class alignas(8) EHCleanupScope : public EHScope { BranchAfters; }; mutable struct ExtInfo *ExtInfo; + // Erases unused auxillary instructions for the cleanup. + // Cleanups should mark these instructions 'used' if the cleanup is + // emitted, otherwise these instructions would be erased. + struct AuxillaryInsts { + SmallVector AuxInsts; + bool used = false; + + // Records a potentially unused instruction to be erased later. + void Add(llvm::Instruction *Inst) { AuxInsts.push_back(Inst); } + + // Mark all recorded instructions as used. These will not be erased later. + void MarkUsed() { + used = true; + AuxInsts.clear(); + } + ~AuxillaryInsts() { + if (used) + return; + for (auto *Inst : llvm::reverse(AuxInsts)) + Inst->eraseFromParent(); + } + }; + mutable struct AuxillaryInsts *AuxInsts; + bool AuxInstsIsOwned = true; /// The number of fixups required by enclosing scopes (not including /// this one). If this is the top cleanup scope, all the fixups @@ -279,6 +307,19 @@ class alignas(8) EHCleanupScope : public EHScope { return sizeof(EHCleanupScope) + Size; } + AuxillaryInsts &getAuxillaryInstructions() { + if (!AuxInsts) { + assert(AuxInstsIsOwned); + AuxInsts = new struct AuxillaryInsts(); + } + return *AuxInsts; + } + + void SetExternalAuxInsts(AuxillaryInsts *NewAuxInsts) { + AuxInsts = NewAuxInsts; + AuxInstsIsOwned = false; + } + size_t getAllocatedSize() const { return sizeof(EHCleanupScope) + CleanupBits.CleanupSize; } @@ -289,7 +330,7 @@ class alignas(8) EHCleanupScope : public EHScope { EHScopeStack::stable_iterator enclosingEH) : EHScope(EHScope::Cleanup, enclosingEH), EnclosingNormal(enclosingNormal), NormalBlock(nullptr), - ActiveFlag(Address::invalid()), ExtInfo(nullptr), + ActiveFlag(Address::invalid()), ExtInfo(nullptr), AuxInsts(nullptr), FixupDepth(fixupDepth) { CleanupBits.IsNormalCleanup = isNormal; CleanupBits.IsEHCleanup = isEH; @@ -297,12 +338,16 @@ class alignas(8) EHCleanupScope : public EHScope { CleanupBits.IsLifetimeMarker = false; CleanupBits.TestFlagInNormalCleanup = false; CleanupBits.TestFlagInEHCleanup = false; + CleanupBits.SkipBranchInExpr = false; CleanupBits.CleanupSize = cleanupSize; + AuxInstsIsOwned = true; assert(CleanupBits.CleanupSize == cleanupSize && "cleanup size overflow"); } void Destroy() { + if (AuxInstsIsOwned) + delete AuxInsts; delete ExtInfo; } // Objects of EHCleanupScope are not destructed. Use Destroy(). @@ -320,6 +365,9 @@ class alignas(8) EHCleanupScope : public EHScope { bool isLifetimeMarker() const { return CleanupBits.IsLifetimeMarker; } void setLifetimeMarker() { CleanupBits.IsLifetimeMarker = true; } + void setShouldSkipBranchInExpr() { CleanupBits.SkipBranchInExpr = true; } + bool shouldSkipBranchInExpr() const { return CleanupBits.SkipBranchInExpr; } + bool hasActiveFlag() const { return ActiveFlag.isValid(); } Address getActiveFlag() const { return ActiveFlag; @@ -348,6 +396,12 @@ class alignas(8) EHCleanupScope : public EHScope { return EnclosingNormal; } + void AddAuxInst(llvm::Instruction *Inst) { + getAuxillaryInstructions().Add(Inst); + } + + void MarkEmitted() { getAuxillaryInstructions().MarkUsed(); } + size_t getCleanupSize() const { return CleanupBits.CleanupSize; } void *getCleanupBuffer() { return this + 1; } diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index 888d30bfb3e1d..5f0dc3e95cdbf 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -12,9 +12,10 @@ #include "CGCleanup.h" #include "CodeGenFunction.h" -#include "llvm/ADT/ScopeExit.h" +#include "EHScopeStack.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtVisitor.h" +#include "llvm/ADT/ScopeExit.h" using namespace clang; using namespace CodeGen; @@ -743,6 +744,11 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { GroManager.EmitGroInit(); EHStack.pushCleanup(EHCleanup); + { + EHCleanupScope &Scope = + cast(*EHStack.find(EHStack.stable_begin())); + Scope.setShouldSkipBranchInExpr(); + } CurCoro.Data->CurrentAwaitKind = AwaitKind::Init; CurCoro.Data->ExceptionHandler = S.getExceptionHandler(); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index bbe14ef4c1724..1de0fe0072cae 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" @@ -32,9 +34,12 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/Sema/Sema.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instruction.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Type.h" #include @@ -2162,7 +2167,6 @@ CodeGenFunction::getDestroyer(QualType::DestructionKind kind) { void CodeGenFunction::pushEHDestroy(QualType::DestructionKind dtorKind, Address addr, QualType type) { assert(dtorKind && "cannot push destructor for trivial type"); - assert(needsEHCleanup(dtorKind)); pushDestroy(EHCleanup, addr, type, getDestroyer(dtorKind), true); } @@ -2209,8 +2213,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 +2239,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..c9077b22873a2 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -15,15 +15,20 @@ #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" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" using namespace clang; @@ -551,24 +556,30 @@ 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(); + EHCleanupScope *arrayCleanup = nullptr; EHScopeStack::stable_iterator cleanup; llvm::Instruction *cleanupDominator = nullptr; - if (CGF.needsEHCleanup(dtorKind)) { + if (dtorKind) { // 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. - endOfInit = CGF.CreateTempAlloca(begin->getType(), CGF.getPointerAlign(), - "arrayinit.endOfInit"); + Address AllocaInst = Address::invalid(); + endOfInit = + CGF.CreateTempAlloca(begin->getType(), CGF.getPointerAlign(), + "arrayinit.endOfInit", nullptr, &AllocaInst); cleanupDominator = 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; + arrayCleanup = + &cast(*CGF.EHStack.find(CGF.EHStack.stable_begin())); + arrayCleanup->AddAuxInst(AllocaInst.getAllocaInst()); + if (auto *AddrSpaceCast = + llvm::dyn_cast(endOfInit.getPointer())) + arrayCleanup->AddAuxInst(AddrSpaceCast); + arrayCleanup->AddAuxInst(cleanupDominator); } llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1); @@ -590,11 +601,11 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially // observed to be unnecessary. - if (endOfInit.isValid()) Builder.CreateStore(element, endOfInit); + if (endOfInit.isValid()) + arrayCleanup->AddAuxInst(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); } @@ -615,7 +626,8 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, if (NumInitElements) { element = Builder.CreateInBoundsGEP( llvmElementType, element, one, "arrayinit.start"); - if (endOfInit.isValid()) Builder.CreateStore(element, endOfInit); + if (endOfInit.isValid()) + arrayCleanup->AddAuxInst(Builder.CreateStore(element, endOfInit)); } // Compute the end of the array. @@ -653,7 +665,8 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, llvmElementType, currentElement, one, "arrayinit.next"); // Tell the EH cleanup that we finished with the last element. - if (endOfInit.isValid()) Builder.CreateStore(nextElement, endOfInit); + if (endOfInit.isValid()) + arrayCleanup->AddAuxInst(Builder.CreateStore(nextElement, endOfInit)); // Leave the loop if we're done. llvm::Value *done = Builder.CreateICmpEQ(nextElement, end, @@ -666,7 +679,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 +724,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 @@ -1377,7 +1391,7 @@ 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, @@ -1789,21 +1803,19 @@ void AggExprEmitter::VisitCXXParenListOrInitListExpr( // Push a destructor if necessary. // FIXME: if we have an array of structures, all explicitly // initialized, we can end up pushing a linear number of cleanups. - bool pushedCleanup = false; if (QualType::DestructionKind dtorKind = field->getType().isDestructedType()) { assert(LV.isSimple()); - if (CGF.needsEHCleanup(dtorKind)) { + if (dtorKind) { CGF.pushDestroy(EHCleanup, LV.getAddress(CGF), field->getType(), CGF.getDestroyer(dtorKind), false); addCleanup(CGF.EHStack.stable_begin()); - pushedCleanup = true; } } // If the GEP didn't get used because of a dead zero init or something // else, clean it up for -O0 builds and general tidiness. - if (!pushedCleanup && LV.isSimple()) + if (!cleanupDominator && LV.isSimple()) if (llvm::GetElementPtrInst *GEP = dyn_cast(LV.getPointer(CGF))) if (GEP->use_empty()) diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index d136bfc37278f..b88e692fc250a 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -16,9 +16,11 @@ #include "CGObjCRuntime.h" #include "CodeGenFunction.h" #include "ConstantEmitter.h" +#include "EHScopeStack.h" #include "TargetInfo.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "llvm/IR/Instruction.h" #include "llvm/IR/Intrinsics.h" using namespace clang; @@ -1004,6 +1006,8 @@ void CodeGenFunction::EmitNewArrayInitializer( const Expr *Init = E->getInitializer(); Address EndOfInit = Address::invalid(); + + EHCleanupScope *ArrayCleanup = nullptr; QualType::DestructionKind DtorKind = ElementType.isDestructedType(); EHScopeStack::stable_iterator Cleanup; llvm::Instruction *CleanupDominator = nullptr; @@ -1103,18 +1107,26 @@ void CodeGenFunction::EmitNewArrayInitializer( } // Enter a partial-destruction Cleanup if necessary. - if (needsEHCleanup(DtorKind)) { + if (DtorKind) { // 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. + Address AllocaInst = Address::invalid(); EndOfInit = CreateTempAlloca(BeginPtr.getType(), getPointerAlign(), - "array.init.end"); + "array.init.end", nullptr, &AllocaInst); CleanupDominator = Builder.CreateStore(BeginPtr.getPointer(), EndOfInit); pushIrregularPartialArrayCleanup(BeginPtr.getPointer(), EndOfInit, ElementType, ElementAlign, getDestroyer(DtorKind)); Cleanup = EHStack.stable_begin(); + ArrayCleanup = + &cast(*EHStack.find(EHStack.stable_begin())); + ArrayCleanup->AddAuxInst(AllocaInst.getAllocaInst()); + if (auto *AddrSpaceCast = + llvm::dyn_cast(EndOfInit.getPointer())) + ArrayCleanup->AddAuxInst(AddrSpaceCast); + ArrayCleanup->AddAuxInst(CleanupDominator); } CharUnits StartAlign = CurPtr.getAlignment(); @@ -1124,7 +1136,8 @@ void CodeGenFunction::EmitNewArrayInitializer( // element. TODO: some of these stores can be trivially // observed to be unnecessary. if (EndOfInit.isValid()) { - Builder.CreateStore(CurPtr.getPointer(), EndOfInit); + ArrayCleanup->AddAuxInst( + Builder.CreateStore(CurPtr.getPointer(), EndOfInit)); } // FIXME: If the last initializer is an incomplete initializer list for // an array, and we have an array filler, we can fold together the two @@ -1187,7 +1200,8 @@ void CodeGenFunction::EmitNewArrayInitializer( // FIXME: Share this cleanup with the constructor call emission rather than // having it create a cleanup of its own. if (EndOfInit.isValid()) - Builder.CreateStore(CurPtr.getPointer(), EndOfInit); + ArrayCleanup->AddAuxInst( + Builder.CreateStore(CurPtr.getPointer(), EndOfInit)); // Emit a constructor call loop to initialize the remaining elements. if (InitListElements) @@ -1274,7 +1288,8 @@ void CodeGenFunction::EmitNewArrayInitializer( // Store the new Cleanup position for irregular Cleanups. if (EndOfInit.isValid()) - Builder.CreateStore(CurPtr.getPointer(), EndOfInit); + ArrayCleanup->AddAuxInst( + Builder.CreateStore(CurPtr.getPointer(), EndOfInit)); // Enter a partial-destruction Cleanup if necessary. if (!CleanupDominator && needsEHCleanup(DtorKind)) { diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index beff0ad9da270..78dc295b568e5 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. - Dest = JumpDest(createBasicBlock(D->getName()), - EHScopeStack::stable_iterator::invalid(), - NextCleanupDestIndex++); + // FIXME: We do not know `EHDepth` for the destination and currently + // emit *all* the EHCleanups when a branch in expr is encountered. + Dest = JumpDest( + createBasicBlock(D->getName()), EHScopeStack::stable_iterator::invalid(), + EHScopeStack::stable_iterator::invalid(), NextCleanupDestIndex++); return Dest; } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 1ad905078d349..ebbc38bef2ab8 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -331,6 +331,8 @@ 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!"); bool OnlySimpleReturnStmts = NumSimpleReturnExprs > 0 && NumSimpleReturnExprs == NumReturnExprs diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 143ad64e8816b..2d436299f44d4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -14,6 +14,7 @@ #define LLVM_CLANG_LIB_CODEGEN_CODEGENFUNCTION_H #include "CGBuilder.h" +#include "CGCleanup.h" #include "CGDebugInfo.h" #include "CGLoopInfo.h" #include "CGValue.h" @@ -240,12 +241,13 @@ class CodeGenFunction : public CodeGenTypeCache { struct JumpDest { JumpDest() : Block(nullptr), Index(0) {} JumpDest(llvm::BasicBlock *Block, EHScopeStack::stable_iterator Depth, - unsigned Index) - : Block(Block), ScopeDepth(Depth), Index(Index) {} + EHScopeStack::stable_iterator EHDepth, unsigned Index) + : Block(Block), ScopeDepth(Depth), EHDepth(EHDepth), Index(Index) {} bool isValid() const { return Block != nullptr; } llvm::BasicBlock *getBlock() const { return Block; } EHScopeStack::stable_iterator getScopeDepth() const { return ScopeDepth; } + EHScopeStack::stable_iterator getEHDepth() const { return EHDepth; } unsigned getDestIndex() const { return Index; } // This should be used cautiously. @@ -256,6 +258,8 @@ class CodeGenFunction : public CodeGenTypeCache { private: llvm::BasicBlock *Block; EHScopeStack::stable_iterator ScopeDepth; + EHScopeStack::stable_iterator EHDepth; + unsigned Index; }; @@ -626,7 +630,40 @@ 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,12 @@ 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. + return; + } // If we're not in a conditional branch, or if none of the // arguments requires saving, then use the unconditional cleanup. if (!isInConditionalBranch()) @@ -788,7 +831,10 @@ class CodeGenFunction : public CodeGenTypeCache { typedef EHScopeStack::ConditionalCleanup CleanupType; EHStack.pushCleanupTuple(kind, Saved); - initFullExprCleanup(); + EHCleanupScope *TopCleanup = nullptr; + if (EHStack.begin()->getKind() == EHScope::Kind::Cleanup) + TopCleanup = &cast(*EHStack.begin()); + initFullExprCleanupWithFlag(createCleanupActiveFlag(TopCleanup)); } /// Queue a cleanup to be pushed after finishing the current full-expression, @@ -796,8 +842,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 +853,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); @@ -837,7 +882,9 @@ class CodeGenFunction : public CodeGenTypeCache { } void initFullExprCleanupWithFlag(Address ActiveFlag); - Address createCleanupActiveFlag(); + // Also attaches the produced instructions to the cleanup auxillary + // instructions. + Address createCleanupActiveFlag(EHCleanupScope *TopCleanup = nullptr); /// PushDestructorCleanup - Push a cleanup to call the /// complete-object destructor of an object of the given type at the @@ -895,8 +942,7 @@ class CodeGenFunction : public CodeGenTypeCache { public: /// Enter a new cleanup scope. explicit RunCleanupsScope(CodeGenFunction &CGF) - : PerformCleanup(true), CGF(CGF) - { + : PerformCleanup(true), CGF(CGF) { CleanupStackDepth = CGF.EHStack.stable_begin(); LifetimeExtendedCleanupStackSize = CGF.LifetimeExtendedCleanupStack.size(); @@ -1143,6 +1189,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 +1209,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(), + EHStack.getInnermostEHScope(), NextCleanupDestIndex++); } /// The given basic block lies in the current EH scope, but may be a @@ -1173,7 +1224,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// block through the normal cleanup handling code (if any) and then /// on to \arg Dest. void EmitBranchThroughCleanup(JumpDest Dest); - + void EmitEHCleanupsForBranch(JumpDest Dest); /// isObviouslyBranchWithoutCleanups - Return true if a branch to the /// specified destination obviously has no cleanups to run. 'false' is always /// a conservatively correct answer for this method. @@ -1220,11 +1271,13 @@ class CodeGenFunction : public CodeGenTypeCache { /// one branch or the other of a conditional expression. bool isInConditionalBranch() const { return OutermostConditional != nullptr; } - void setBeforeOutermostConditional(llvm::Value *value, Address addr) { + llvm::StoreInst *setBeforeOutermostConditional(llvm::Value *value, + Address addr) { assert(isInConditionalBranch()); llvm::BasicBlock *block = OutermostConditional->getStartingBlock(); - auto store = new llvm::StoreInst(value, addr.getPointer(), &block->back()); + auto *store = new llvm::StoreInst(value, addr.getPointer(), &block->back()); store->setAlignment(addr.getAlignment().getAsAlign()); + return store; } /// An RAII object to record that we're evaluating a statement 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..310de6ce15d0c --- /dev/null +++ b/clang/test/CodeGenCXX/control-flow-in-stmt-expr-cleanup.cpp @@ -0,0 +1,259 @@ +// 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.then{{.*}}: + // 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(), + // CHECK: entry: + // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 + // CHECK-NEXT: %arrayinit.begin = getelementptr inbounds + // CHECK-NEXT: store ptr %arrayinit.begin, ptr %arrayinit.endOfInit, align 8 + // CHECK-NEXT: call void @_ZN6PrintyC1Ev + // CHECK-NEXT: %arrayinit.element = getelementptr inbounds %struct.Printy, ptr %arrayinit.begin, i64 1 + // CHECK-NEXT: store ptr %arrayinit.element, ptr %arrayinit.endOfInit, align 8 + // CHECK-NEXT: @_Z3foov() + // CHECK-NEXT: br i1 %call, label %if.then, label %if.end + ({ + if (foo()) { + // CHECK: if.then: + // CHECK-NEXT: load ptr, ptr %arrayinit.endOfInit, + // CHECK-NEXT: %arraydestroy.isempty = icmp eq ptr %arrayinit.begin, {{.*}} + // CHECK-NEXT: br i1 %arraydestroy.isempty, label %arraydestroy.done{{.*}}, label %arraydestroy.body + + // CHECK: arraydestroy.body: + // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %0, %if.then ], [ %arraydestroy.element, %arraydestroy.body ] + // 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, %arrayinit.begin + // CHECK-NEXT: br i1 %arraydestroy.done, label %arraydestroy.done{{.*}}, label %arraydestroy.body + + // CHECK: arraydestroy.done{{.*}}: + // CHECK-NEXT: br label %return + return; + } + Printy(); + }), + ({ + if (foo()) { + return; + // CHECK: if.then{{.*}}: + // CHECK-NEXT: load ptr, ptr %arrayinit.endOfInit + // CHECK-NEXT: %arraydestroy.isempty{{.*}} = icmp eq ptr %arrayinit.begin, {{.*}} + // CHECK-NEXT: br i1 %arraydestroy.isempty{{.*}}, label %arraydestroy.done{{.*}}, label %arraydestroy.body{{.*}} + + // CHECK: arraydestroy.body{{.*}}: + // CHECK-NEXT: %arraydestroy.elementPast{{.*}} = phi ptr [ %1, %if.then{{.*}} ], [ %arraydestroy.element{{.*}}, %arraydestroy.body{{.*}} ] + // 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{{.*}}, %arrayinit.begin + // CHECK-NEXT: br i1 %arraydestroy.done{{.*}}, label %arraydestroy.done{{.*}}, label %arraydestroy.body{{.*}} + + // CHECK: arraydestroy.done{{.*}}: + // CHECK-NEXT: br label %return + } + Printy(); + })}; + return; +} + +void new_array_init() { + // CHECK: define dso_local void @_Z14new_array_initv() + Printy *a = new Printy[]("1", + // CHECK: entry: + // CHECK: %array.init.end = alloca ptr, align 8 + // CHECK: store ptr %0, ptr %array.init.end, align 8 + // CHECK-NEXT: store ptr %0, ptr %array.init.end, align 8 + // CHECK: store ptr %array.exp.next, ptr %array.init.end, align 8 + ({ + if (foo()) { + return; + // CHECK: if.then{{.*}}: + // CHECK-NEXT: load ptr, ptr %array.init.end, align 8 + // CHECK-NEXT: %arraydestroy.isempty = icmp + // CHECK-NEXT: br i1 %arraydestroy.isempty, label %arraydestroy.done{{.*}}, label %arraydestroy.body + + // 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 + // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %0 + // CHECK-NEXT: br i1 %arraydestroy.done, label %arraydestroy.done2, label %arraydestroy.body + + // CHECK: arraydestroy.done{{.*}}: + // CHECK-NEXT: br label %delete.end + } + "2"; + })); + // CHECK: delete.end{{.*}}: + // CHECK-NEXT: ret void + 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]; + }; + S s{{Printy(), + // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 + // CHECK: %arrayinit.endOfInit{{.*}} = alloca ptr, align 8 + // CHECK: store ptr %arrayinit.begin, ptr %arrayinit.endOfInit + // CHECK: call void @_ZN6PrintyC1Ev + // CHECK: store ptr %arrayinit.element, ptr %arrayinit.endOfInit + ({ + if (foo()) { + return; + // CHECK: if.then{{.*}}: + // CHECK-NEXT: load ptr, ptr %arrayinit.endOfInit + // CHECK: arraydestroy.body: + // CHECK: arraydestroy.done1: + // CHECK-NEXT: br label %return + } + // CHECK: if.end: + // CHECK: call void @_ZN6PrintyC1Ev + // CHECK: store ptr %arrayinit.element4, ptr %arrayinit.endOfInit3, align 8 + Printy(); + })}, + {Printy(), ({ + if (foo()) { + /** One dtor followed by an array destruction **/ + // CHECK: if.then{{.*}}: + // CHECK-NEXT: load ptr, ptr %arrayinit.endOfInit3 + // CHECK: arraydestroy.body{{.*}}: + // CHECK: arraydestroy.done{{.*}}: + // CHECK: arraydestroy.body{{.*}}: + // CHECK: arraydestroy.done{{.*}}: + // CHECK-NEXT: 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/CodeGenCXX/cxx1y-init-captures-eh.cpp b/clang/test/CodeGenCXX/cxx1y-init-captures-eh.cpp index b0b938191a023..12cf4e42b9871 100644 --- a/clang/test/CodeGenCXX/cxx1y-init-captures-eh.cpp +++ b/clang/test/CodeGenCXX/cxx1y-init-captures-eh.cpp @@ -50,9 +50,7 @@ void y() noexcept; // CHECK-LABEL: define{{.*}} void @_Z1hbb( void h(bool b1, bool b2) { - // CHECK: {{.*}} = alloca i1, // CHECK: %[[S_ISACTIVE:.*]] = alloca i1, - // CHECK: {{.*}} = alloca i1, // lambda init: s and t, branch on b1 // CHECK: call void @_ZN1SC1Ev( diff --git a/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp b/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp index f00cf90762756..3c643039e848c 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp @@ -124,7 +124,6 @@ int HasConditionalDeactivatedCleanups(bool cond) { // WIN32-O0: store i1 false // WIN32-O0: store i1 false // WIN32-O0: store i1 false -// WIN32-O0: store i1 false // WIN32-O0: br i1 // True condition. // WIN32-O0: call x86_thiscallcc noundef ptr @"??0A@@QAE@XZ" @@ -136,7 +135,6 @@ int HasConditionalDeactivatedCleanups(bool cond) { // WIN32-O0: store i1 true // WIN32-O0: invoke void @"?TakeRef@@YAXABUA@@@Z" // WIN32-O0: invoke x86_thiscallcc noundef ptr @"??0A@@QAE@XZ" -// WIN32-O0: store i1 true // WIN32-O0: store i1 false, ptr %[[arg1_cond]] // WIN32-O0: invoke noundef i32 @"?TakesTwo@@YAHUA@@0@Z" // False condition. @@ -162,7 +160,6 @@ int HasConditionalDeactivatedCleanups(bool cond) { // WIN32-O3: store i1 false // WIN32-O3: store i1 false // WIN32-O3: store i1 false -// WIN32-O3: store i1 false // WIN32-O3: br i1 // True condition. // WIN32-O3: call x86_thiscallcc noundef ptr @"??0A@@QAE@XZ" @@ -174,7 +171,6 @@ int HasConditionalDeactivatedCleanups(bool cond) { // WIN32-O3: store i1 true // WIN32-O3: invoke void @"?TakeRef@@YAXABUA@@@Z" // WIN32-O3: invoke x86_thiscallcc noundef ptr @"??0A@@QAE@XZ" -// WIN32-O3: store i1 true // WIN32-O3: store i1 false, ptr %[[arg1_cond]] // WIN32-O3: invoke noundef i32 @"?TakesTwo@@YAHUA@@0@Z" // False condition. 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: +}