From: John McCall <rjmccall@apple.com> Date: Tue, 14 Sep 2010 07:57:04 +0000 (+0000) Subject: Implement the EH cleanup to call 'operator delete' if a new-expression throws X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7d8647f194ae4f2499e5bcd40dcfea34cd21ebc6;p=clang Implement the EH cleanup to call 'operator delete' if a new-expression throws (but not if destructors associated with the full-expression throw). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@113836 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGException.h b/lib/CodeGen/CGException.h index f1294746a4..ce8bd49751 100644 --- a/lib/CodeGen/CGException.h +++ b/lib/CodeGen/CGException.h @@ -160,11 +160,14 @@ class EHCleanupScope : public EHScope { /// Whether this cleanup needs to be run along exception edges. bool IsEHCleanup : 1; - /// Whether this cleanup was activated before all normal uses. - bool ActivatedBeforeNormalUse : 1; + /// Whether this cleanup is currently active. + bool IsActive : 1; - /// Whether this cleanup was activated before all EH uses. - bool ActivatedBeforeEHUse : 1; + /// Whether the normal cleanup should test the activation flag. + bool TestFlagInNormalCleanup : 1; + + /// Whether the EH cleanup should test the activation flag. + bool TestFlagInEHCleanup : 1; /// The amount of extra storage needed by the Cleanup. /// Always a multiple of the scope-stack alignment. @@ -173,7 +176,7 @@ class EHCleanupScope : public EHScope { /// The number of fixups required by enclosing scopes (not including /// this one). If this is the top cleanup scope, all the fixups /// from this index onwards belong to this scope. - unsigned FixupDepth : BitsRemaining - 16; + unsigned FixupDepth : BitsRemaining - 17; // currently 13 /// The nearest normal cleanup scope enclosing this one. EHScopeStack::stable_iterator EnclosingNormal; @@ -190,12 +193,8 @@ class EHCleanupScope : public EHScope { llvm::BasicBlock *EHBlock; /// An optional i1 variable indicating whether this cleanup has been - /// activated yet. This has one of three states: - /// - it is null if the cleanup is inactive - /// - it is activeSentinel() if the cleanup is active and was not - /// required before activation - /// - it points to a valid variable - llvm::AllocaInst *ActiveVar; + /// activated yet. + llvm::AllocaInst *ActiveFlag; /// Extra information required for cleanups that have resolved /// branches through them. This has to be allocated on the side @@ -246,14 +245,11 @@ public: EHScopeStack::stable_iterator EnclosingNormal, EHScopeStack::stable_iterator EnclosingEH) : EHScope(EHScope::Cleanup), - IsNormalCleanup(IsNormal), IsEHCleanup(IsEH), - ActivatedBeforeNormalUse(IsActive), - ActivatedBeforeEHUse(IsActive), + IsNormalCleanup(IsNormal), IsEHCleanup(IsEH), IsActive(IsActive), + TestFlagInNormalCleanup(false), TestFlagInEHCleanup(false), CleanupSize(CleanupSize), FixupDepth(FixupDepth), EnclosingNormal(EnclosingNormal), EnclosingEH(EnclosingEH), - NormalBlock(0), EHBlock(0), - ActiveVar(IsActive ? activeSentinel() : 0), - ExtInfo(0) + NormalBlock(0), EHBlock(0), ActiveFlag(0), ExtInfo(0) { assert(this->CleanupSize == CleanupSize && "cleanup size overflow"); } @@ -270,19 +266,17 @@ public: llvm::BasicBlock *getEHBlock() const { return EHBlock; } void setEHBlock(llvm::BasicBlock *BB) { EHBlock = BB; } - static llvm::AllocaInst *activeSentinel() { - return reinterpret_cast<llvm::AllocaInst*>(1); - } + bool isActive() const { return IsActive; } + void setActive(bool A) { IsActive = A; } - bool isActive() const { return ActiveVar != 0; } - llvm::AllocaInst *getActiveVar() const { return ActiveVar; } - void setActiveVar(llvm::AllocaInst *Var) { ActiveVar = Var; } + llvm::AllocaInst *getActiveFlag() const { return ActiveFlag; } + void setActiveFlag(llvm::AllocaInst *Var) { ActiveFlag = Var; } - bool wasActivatedBeforeNormalUse() const { return ActivatedBeforeNormalUse; } - void setActivatedBeforeNormalUse(bool B) { ActivatedBeforeNormalUse = B; } + void setTestFlagInNormalCleanup() { TestFlagInNormalCleanup = true; } + bool shouldTestFlagInNormalCleanup() const { return TestFlagInNormalCleanup; } - bool wasActivatedBeforeEHUse() const { return ActivatedBeforeEHUse; } - void setActivatedBeforeEHUse(bool B) { ActivatedBeforeEHUse = B; } + void setTestFlagInEHCleanup() { TestFlagInEHCleanup = true; } + bool shouldTestFlagInEHCleanup() const { return TestFlagInEHCleanup; } unsigned getFixupDepth() const { return FixupDepth; } EHScopeStack::stable_iterator getEnclosingNormalCleanup() const { diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index acdbe06556..3c0023a606 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -677,6 +677,62 @@ static void EmitNewInitializer(CodeGenFunction &CGF, const CXXNewExpr *E, StoreAnyExprIntoOneUnit(CGF, E, NewPtr); } +namespace { + /// A cleanup to call the given 'operator delete' function upon + /// abnormal exit from a new expression. + class CallDeleteDuringNew : public EHScopeStack::Cleanup { + size_t NumPlacementArgs; + const FunctionDecl *OperatorDelete; + llvm::Value *Ptr; + llvm::Value *AllocSize; + + RValue *getPlacementArgs() { return reinterpret_cast<RValue*>(this+1); } + + public: + static size_t getExtraSize(size_t NumPlacementArgs) { + return NumPlacementArgs * sizeof(RValue); + } + + CallDeleteDuringNew(size_t NumPlacementArgs, + const FunctionDecl *OperatorDelete, + llvm::Value *Ptr, + llvm::Value *AllocSize) + : NumPlacementArgs(NumPlacementArgs), OperatorDelete(OperatorDelete), + Ptr(Ptr), AllocSize(AllocSize) {} + + void setPlacementArg(unsigned I, RValue Arg) { + assert(I < NumPlacementArgs && "index out of range"); + getPlacementArgs()[I] = Arg; + } + + void Emit(CodeGenFunction &CGF, bool IsForEH) { + const FunctionProtoType *FPT + = OperatorDelete->getType()->getAs<FunctionProtoType>(); + assert(FPT->getNumArgs() == NumPlacementArgs + 1 || + FPT->getNumArgs() == NumPlacementArgs + 2); + + CallArgList DeleteArgs; + + // The first argument is always a void*. + FunctionProtoType::arg_type_iterator AI = FPT->arg_type_begin(); + DeleteArgs.push_back(std::make_pair(RValue::get(Ptr), *AI++)); + + // A member 'operator delete' can take an extra 'size_t' argument. + if (FPT->getNumArgs() == NumPlacementArgs + 2) + DeleteArgs.push_back(std::make_pair(RValue::get(AllocSize), *AI++)); + + // Pass the rest of the arguments, which must match exactly. + for (unsigned I = 0; I != NumPlacementArgs; ++I) + DeleteArgs.push_back(std::make_pair(getPlacementArgs()[I], *AI++)); + + // Call 'operator delete'. + CGF.EmitCall(CGF.CGM.getTypes().getFunctionInfo(DeleteArgs, FPT), + CGF.CGM.GetAddrOfFunction(OperatorDelete), + ReturnValueSlot(), DeleteArgs, OperatorDelete); + } + }; +} + llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { QualType AllocType = E->getAllocatedType(); if (AllocType->isArrayType()) @@ -769,9 +825,24 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { AllocType); } + // If there's an operator delete, enter a cleanup to call it if an + // exception is thrown. + EHScopeStack::stable_iterator CallOperatorDelete; + if (E->getOperatorDelete()) { + CallDeleteDuringNew *Cleanup = CGF.EHStack + .pushCleanupWithExtra<CallDeleteDuringNew>(EHCleanup, + E->getNumPlacementArgs(), + E->getOperatorDelete(), + NewPtr, AllocSize); + for (unsigned I = 0, N = E->getNumPlacementArgs(); I != N; ++I) + Cleanup->setPlacementArg(I, NewArgs[I+1].first); + CallOperatorDelete = EHStack.stable_begin(); + } + const llvm::Type *ElementPtrTy = ConvertTypeForMem(AllocType)->getPointerTo(AS); NewPtr = Builder.CreateBitCast(NewPtr, ElementPtrTy); + if (E->isArray()) { EmitNewInitializer(*this, E, NewPtr, NumElements, AllocSizeWithoutCookie); @@ -784,6 +855,11 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { } else { EmitNewInitializer(*this, E, NewPtr, NumElements, AllocSizeWithoutCookie); } + + // Deactivate the 'operator delete' cleanup if we finished + // initialization. + if (CallOperatorDelete.isValid()) + DeactivateCleanupBlock(CallOperatorDelete); if (NullCheckResult) { Builder.CreateBr(NewEnd); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 51d084e1d3..0dbc6120ed 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -759,11 +759,51 @@ static llvm::BasicBlock *SimplifyCleanupEntry(CodeGenFunction &CGF, static void EmitCleanup(CodeGenFunction &CGF, EHScopeStack::Cleanup *Fn, - bool ForEH) { + bool ForEH, + llvm::Value *ActiveFlag) { + // EH cleanups always occur within a terminate scope. if (ForEH) CGF.EHStack.pushTerminate(); + + // If there's an active flag, load it and skip the cleanup if it's + // false. + llvm::BasicBlock *ContBB = 0; + if (ActiveFlag) { + ContBB = CGF.createBasicBlock("cleanup.done"); + llvm::BasicBlock *CleanupBB = CGF.createBasicBlock("cleanup.action"); + llvm::Value *IsActive + = CGF.Builder.CreateLoad(ActiveFlag, "cleanup.is_active"); + CGF.Builder.CreateCondBr(IsActive, CleanupBB, ContBB); + CGF.EmitBlock(CleanupBB); + } + + // Ask the cleanup to emit itself. Fn->Emit(CGF, ForEH); - if (ForEH) CGF.EHStack.popTerminate(); assert(CGF.HaveInsertPoint() && "cleanup ended with no insertion point?"); + + // Emit the continuation block if there was an active flag. + if (ActiveFlag) + CGF.EmitBlock(ContBB); + + // Leave the terminate scope. + if (ForEH) CGF.EHStack.popTerminate(); +} + +static void ForwardPrebranchedFallthrough(llvm::BasicBlock *Exit, + llvm::BasicBlock *From, + llvm::BasicBlock *To) { + // Exit is the exit block of a cleanup, so it always terminates in + // an unconditional branch or a switch. + llvm::TerminatorInst *Term = Exit->getTerminator(); + + if (llvm::BranchInst *Br = dyn_cast<llvm::BranchInst>(Term)) { + assert(Br->isUnconditional() && Br->getSuccessor(0) == From); + Br->setSuccessor(0, To); + } else { + llvm::SwitchInst *Switch = cast<llvm::SwitchInst>(Term); + for (unsigned I = 0, E = Switch->getNumSuccessors(); I != E; ++I) + if (Switch->getSuccessor(I) == From) + Switch->setSuccessor(I, To); + } } /// Pops a cleanup block. If the block includes a normal cleanup, the @@ -774,7 +814,13 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { assert(isa<EHCleanupScope>(*EHStack.begin()) && "top not a cleanup!"); EHCleanupScope &Scope = cast<EHCleanupScope>(*EHStack.begin()); assert(Scope.getFixupDepth() <= EHStack.getNumBranchFixups()); - assert(Scope.isActive() && "cleanup was still inactive when popped!"); + + // Remember activation information. + bool IsActive = Scope.isActive(); + llvm::Value *NormalActiveFlag = + Scope.shouldTestFlagInNormalCleanup() ? Scope.getActiveFlag() : 0; + llvm::Value *EHActiveFlag = + Scope.shouldTestFlagInEHCleanup() ? Scope.getActiveFlag() : 0; // Check whether we need an EH cleanup. This is only true if we've // generated a lazy EH cleanup block. @@ -791,7 +837,12 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // - whether there's a fallthrough llvm::BasicBlock *FallthroughSource = Builder.GetInsertBlock(); - bool HasFallthrough = (FallthroughSource != 0); + bool HasFallthrough = (FallthroughSource != 0 && IsActive); + + // As a kindof crazy internal case, branch-through fall-throughs + // leave the insertion point set to the end of the last cleanup. + bool HasPrebranchedFallthrough = + (FallthroughSource && FallthroughSource->getTerminator()); bool RequiresNormalCleanup = false; if (Scope.isNormalCleanup() && @@ -799,6 +850,40 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { RequiresNormalCleanup = true; } + assert(!HasPrebranchedFallthrough || RequiresNormalCleanup || !IsActive); + assert(!HasPrebranchedFallthrough || + (Scope.isNormalCleanup() && Scope.getNormalBlock() && + FallthroughSource->getTerminator()->getSuccessor(0) + == Scope.getNormalBlock())); + + // Even if we don't need the normal cleanup, we might still have + // prebranched fallthrough to worry about. + if (!RequiresNormalCleanup && HasPrebranchedFallthrough) { + assert(!IsActive); + + llvm::BasicBlock *NormalEntry = Scope.getNormalBlock(); + + // If we're branching through this cleanup, just forward the + // prebranched fallthrough to the next cleanup, leaving the insert + // point in the old block. + if (FallthroughIsBranchThrough) { + EHScope &S = *EHStack.find(Scope.getEnclosingNormalCleanup()); + llvm::BasicBlock *EnclosingEntry = + CreateNormalEntry(*this, cast<EHCleanupScope>(S)); + + ForwardPrebranchedFallthrough(FallthroughSource, + NormalEntry, EnclosingEntry); + assert(NormalEntry->use_empty() && + "uses of entry remain after forwarding?"); + delete NormalEntry; + + // Otherwise, we're branching out; just emit the next block. + } else { + EmitBlock(NormalEntry); + SimplifyCleanupEntry(*this, NormalEntry); + } + } + // If we don't need the cleanup at all, we're done. if (!RequiresNormalCleanup && !RequiresEHCleanup) { EHStack.popCleanup(); // safe because there are no fixups @@ -877,14 +962,6 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { if (!RequiresNormalCleanup) { EHStack.popCleanup(); } else { - // As a kindof crazy internal case, branch-through fall-throughs - // leave the insertion point set to the end of the last cleanup. - bool HasPrebranchedFallthrough = - (HasFallthrough && FallthroughSource->getTerminator()); - assert(!HasPrebranchedFallthrough || - FallthroughSource->getTerminator()->getSuccessor(0) - == Scope.getNormalBlock()); - // If we have a fallthrough and no other need for the cleanup, // emit it directly. if (HasFallthrough && !HasPrebranchedFallthrough && @@ -901,7 +978,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { EHStack.popCleanup(); - EmitCleanup(*this, Fn, /*ForEH*/ false); + EmitCleanup(*this, Fn, /*ForEH*/ false, NormalActiveFlag); // Otherwise, the best approach is to thread everything through // the cleanup block and then try to clean up after ourselves. @@ -909,16 +986,30 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // Force the entry block to exist. llvm::BasicBlock *NormalEntry = CreateNormalEntry(*this, Scope); + // I. Set up the fallthrough edge in. + // If there's a fallthrough, we need to store the cleanup // destination index. For fall-throughs this is always zero. - if (HasFallthrough && !HasPrebranchedFallthrough) - Builder.CreateStore(Builder.getInt32(0), getNormalCleanupDestSlot()); + if (HasFallthrough) { + if (!HasPrebranchedFallthrough) + Builder.CreateStore(Builder.getInt32(0), getNormalCleanupDestSlot()); + + // Otherwise, clear the IP if we don't have fallthrough because + // the cleanup is inactive. We don't need to save it because + // it's still just FallthroughSource. + } else if (FallthroughSource) { + assert(!IsActive && "source without fallthrough for active cleanup"); + Builder.ClearInsertionPoint(); + } - // Emit the entry block. This implicitly branches to it if we - // have fallthrough. All the fixups and existing branches should - // already be branched to it. + // II. Emit the entry block. This implicitly branches to it if + // we have fallthrough. All the fixups and existing branches + // should already be branched to it. EmitBlock(NormalEntry); + // III. Figure out where we're going and build the cleanup + // epilogue. + bool HasEnclosingCleanups = (Scope.getEnclosingNormalCleanup() != EHStack.stable_end()); @@ -929,7 +1020,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // to the enclosing cleanup llvm::BasicBlock *BranchThroughDest = 0; if (Scope.hasBranchThroughs() || - (HasFallthrough && FallthroughIsBranchThrough) || + (FallthroughSource && FallthroughIsBranchThrough) || (HasFixups && HasEnclosingCleanups)) { assert(HasEnclosingCleanups); EHScope &S = *EHStack.find(Scope.getEnclosingNormalCleanup()); @@ -943,7 +1034,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { // we can route it without a switch. if (!Scope.hasBranchThroughs() && !HasFixups && !HasFallthrough && Scope.getNumBranchAfters() == 1) { - assert(!BranchThroughDest); + assert(!BranchThroughDest || !IsActive); // TODO: clean up the possibly dead stores to the cleanup dest slot. llvm::BasicBlock *BranchAfter = Scope.getBranchAfterBlock(0); @@ -973,9 +1064,10 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { InstsToAppend.push_back(Switch); // Branch-after fallthrough. - if (HasFallthrough && !FallthroughIsBranchThrough) { + if (FallthroughSource && !FallthroughIsBranchThrough) { FallthroughDest = createBasicBlock("cleanup.cont"); - Switch->addCase(Builder.getInt32(0), FallthroughDest); + if (HasFallthrough) + Switch->addCase(Builder.getInt32(0), FallthroughDest); } for (unsigned I = 0, E = Scope.getNumBranchAfters(); I != E; ++I) { @@ -991,11 +1083,11 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { InstsToAppend.push_back(llvm::BranchInst::Create(BranchThroughDest)); } - // We're finally ready to pop the cleanup. + // IV. Pop the cleanup and emit it. EHStack.popCleanup(); assert(EHStack.hasNormalCleanups() == HasEnclosingCleanups); - EmitCleanup(*this, Fn, /*ForEH*/ false); + EmitCleanup(*this, Fn, /*ForEH*/ false, NormalActiveFlag); // Append the prepared cleanup prologue from above. llvm::BasicBlock *NormalExit = Builder.GetInsertBlock(); @@ -1015,11 +1107,47 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { } Fixup.OptimisticBranchBlock = NormalExit; } + + // V. Set up the fallthrough edge out. - if (FallthroughDest) + // Case 1: a fallthrough source exists but shouldn't branch to + // the cleanup because the cleanup is inactive. + if (!HasFallthrough && FallthroughSource) { + assert(!IsActive); + + // If we have a prebranched fallthrough, that needs to be + // forwarded to the right block. + if (HasPrebranchedFallthrough) { + llvm::BasicBlock *Next; + if (FallthroughIsBranchThrough) { + Next = BranchThroughDest; + assert(!FallthroughDest); + } else { + Next = FallthroughDest; + } + + ForwardPrebranchedFallthrough(FallthroughSource, NormalEntry, Next); + } + Builder.SetInsertPoint(FallthroughSource); + + // Case 2: a fallthrough source exists and should branch to the + // cleanup, but we're not supposed to branch through to the next + // cleanup. + } else if (HasFallthrough && FallthroughDest) { + assert(!FallthroughIsBranchThrough); EmitBlock(FallthroughDest); - else if (!HasFallthrough) + + // Case 3: a fallthrough source exists and should branch to the + // cleanup and then through to the next. + } else if (HasFallthrough) { + // Everything is already set up for this. + + // Case 4: no fallthrough source exists. + } else { Builder.ClearInsertionPoint(); + } + + // VI. Assorted cleaning. // Check whether we can merge NormalEntry into a single predecessor. // This might invalidate (non-IR) pointers to NormalEntry. @@ -1042,7 +1170,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); EmitBlock(EHEntry); - EmitCleanup(*this, Fn, /*ForEH*/ true); + EmitCleanup(*this, Fn, /*ForEH*/ true, EHActiveFlag); // Append the prepared cleanup prologue from above. llvm::BasicBlock *EHExit = Builder.GetInsertBlock(); @@ -1252,71 +1380,121 @@ void CodeGenFunction::ResolveBranchFixups(llvm::BasicBlock *Block) { EHStack.popNullFixups(); } -/// Activate a cleanup that was created in an inactivated state. -void CodeGenFunction::ActivateCleanup(EHScopeStack::stable_iterator C) { - assert(C != EHStack.stable_end() && "activating bottom of stack?"); - EHCleanupScope &Scope = cast<EHCleanupScope>(*EHStack.find(C)); - assert(!Scope.isActive() && "double activation"); +static bool IsUsedAsNormalCleanup(EHScopeStack &EHStack, + EHScopeStack::stable_iterator C) { + // If we needed a normal block for any reason, that counts. + if (cast<EHCleanupScope>(*EHStack.find(C)).getNormalBlock()) + return true; + + // Check whether any enclosed cleanups were needed. + for (EHScopeStack::stable_iterator + I = EHStack.getInnermostNormalCleanup(); + I != C; ) { + assert(C.strictlyEncloses(I)); + EHCleanupScope &S = cast<EHCleanupScope>(*EHStack.find(I)); + if (S.getNormalBlock()) return true; + I = S.getEnclosingNormalCleanup(); + } + + return false; +} + +static bool IsUsedAsEHCleanup(EHScopeStack &EHStack, + EHScopeStack::stable_iterator C) { + // If we needed an EH block for any reason, that counts. + if (cast<EHCleanupScope>(*EHStack.find(C)).getEHBlock()) + return true; + + // Check whether any enclosed cleanups were needed. + for (EHScopeStack::stable_iterator + I = EHStack.getInnermostEHCleanup(); I != C; ) { + assert(C.strictlyEncloses(I)); + EHCleanupScope &S = cast<EHCleanupScope>(*EHStack.find(I)); + if (S.getEHBlock()) return true; + I = S.getEnclosingEHCleanup(); + } + + return false; +} + +enum ForActivation_t { + ForActivation, + ForDeactivation +}; + +/// The given cleanup block is changing activation state. Configure a +/// cleanup variable if necessary. +/// +/// It would be good if we had some way of determining if there were +/// extra uses *after* the change-over point. +static void SetupCleanupBlockActivation(CodeGenFunction &CGF, + EHScopeStack::stable_iterator C, + ForActivation_t Kind) { + EHCleanupScope &Scope = cast<EHCleanupScope>(*CGF.EHStack.find(C)); + assert(!Scope.getActiveFlag() && "scope already has activation flag"); + + bool NeedFlag = false; // Calculate whether the cleanup was used: - bool Used = false; // - as a normal cleanup - if (Scope.isNormalCleanup()) { - bool NormalUsed = false; - if (Scope.getNormalBlock()) { - NormalUsed = true; - } else { - // Check whether any enclosed cleanups were needed. - for (EHScopeStack::stable_iterator - I = EHStack.getInnermostNormalCleanup(); I != C; ) { - assert(C.strictlyEncloses(I)); - EHCleanupScope &S = cast<EHCleanupScope>(*EHStack.find(I)); - if (S.getNormalBlock()) { - NormalUsed = true; - break; - } - I = S.getEnclosingNormalCleanup(); - } - } - - if (NormalUsed) - Used = true; - else - Scope.setActivatedBeforeNormalUse(true); + if (Scope.isNormalCleanup() && IsUsedAsNormalCleanup(CGF.EHStack, C)) { + Scope.setTestFlagInNormalCleanup(); + NeedFlag = true; } // - as an EH cleanup - if (Scope.isEHCleanup()) { - bool EHUsed = false; - if (Scope.getEHBlock()) { - EHUsed = true; - } else { - // Check whether any enclosed cleanups were needed. - for (EHScopeStack::stable_iterator - I = EHStack.getInnermostEHCleanup(); I != C; ) { - assert(C.strictlyEncloses(I)); - EHCleanupScope &S = cast<EHCleanupScope>(*EHStack.find(I)); - if (S.getEHBlock()) { - EHUsed = true; - break; - } - I = S.getEnclosingEHCleanup(); - } - } + if (Scope.isEHCleanup() && IsUsedAsEHCleanup(CGF.EHStack, C)) { + Scope.setTestFlagInEHCleanup(); + NeedFlag = true; + } - if (EHUsed) - Used = true; - else - Scope.setActivatedBeforeEHUse(true); + // If it hasn't yet been used as either, we're done. + if (!NeedFlag) return; + + llvm::AllocaInst *Var = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty()); + Scope.setActiveFlag(Var); + + if (Kind == ForActivation) { + CGF.InitTempAlloca(Var, CGF.Builder.getFalse()); + CGF.Builder.CreateStore(CGF.Builder.getTrue(), Var); + } else { + CGF.InitTempAlloca(Var, CGF.Builder.getTrue()); + CGF.Builder.CreateStore(CGF.Builder.getFalse(), Var); } - - llvm::AllocaInst *Var = EHCleanupScope::activeSentinel(); - if (Used) { - Var = CreateTempAlloca(Builder.getInt1Ty()); - InitTempAlloca(Var, Builder.getFalse()); +} + +/// Activate a cleanup that was created in an inactivated state. +void CodeGenFunction::ActivateCleanupBlock(EHScopeStack::stable_iterator C) { + assert(C != EHStack.stable_end() && "activating bottom of stack?"); + EHCleanupScope &Scope = cast<EHCleanupScope>(*EHStack.find(C)); + assert(!Scope.isActive() && "double activation"); + + SetupCleanupBlockActivation(*this, C, ForActivation); + + Scope.setActive(true); +} + +/// Deactive a cleanup that was created in an active state. +void CodeGenFunction::DeactivateCleanupBlock(EHScopeStack::stable_iterator C) { + assert(C != EHStack.stable_end() && "deactivating bottom of stack?"); + EHCleanupScope &Scope = cast<EHCleanupScope>(*EHStack.find(C)); + assert(Scope.isActive() && "double activation"); + + // If it's the top of the stack, just pop it. + if (C == EHStack.stable_begin()) { + // If it's a normal cleanup, we need to pretend that the + // fallthrough is unreachable. + CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); + PopCleanupBlock(); + Builder.restoreIP(SavedIP); + return; } - Scope.setActiveVar(Var); + + // Otherwise, follow the general case. + SetupCleanupBlockActivation(*this, C, ForDeactivation); + + Scope.setActive(false); } llvm::Value *CodeGenFunction::getNormalCleanupDestSlot() { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index acf1e4d7b3..715be3ce17 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -285,6 +285,25 @@ public: (void) Obj; } + // Feel free to add more variants of the following: + + /// Push a cleanup with non-constant storage requirements on the + /// stack. The cleanup type must provide an additional static method: + /// static size_t getExtraSize(size_t); + /// The argument to this method will be the value N, which will also + /// be passed as the first argument to the constructor. + /// + /// The data stored in the extra storage must obey the same + /// restrictions as normal cleanup member data. + /// + /// The pointer returned from this method is valid until the cleanup + /// stack is modified. + template <class T, class A0, class A1, class A2> + T *pushCleanupWithExtra(CleanupKind Kind, size_t N, A0 a0, A1 a1, A2 a2) { + void *Buffer = pushCleanup(Kind, sizeof(T) + T::getExtraSize(N)); + return new (Buffer) T(N, a0, a1, a2); + } + /// Pops a cleanup scope off the stack. This should only be called /// by CodeGenFunction::PopCleanupBlock. void popCleanup(); @@ -542,7 +561,14 @@ public: /// process all branch fixups. void PopCleanupBlock(bool FallThroughIsBranchThrough = false); - void ActivateCleanup(EHScopeStack::stable_iterator Cleanup); + /// DeactivateCleanupBlock - Deactivates the given cleanup block. + /// The block cannot be reactivated. Pops it if it's the top of the + /// stack. + void DeactivateCleanupBlock(EHScopeStack::stable_iterator Cleanup); + + /// ActivateCleanupBlock - Activates an initially-inactive cleanup. + /// Cannot be used to resurrect a deactivated cleanup. + void ActivateCleanupBlock(EHScopeStack::stable_iterator Cleanup); /// \brief Enters a new scope for capturing cleanups, all of which /// will be executed once the scope is exited. diff --git a/test/CodeGenCXX/exceptions.cpp b/test/CodeGenCXX/exceptions.cpp index 4d8fb809ab..2337eee071 100644 --- a/test/CodeGenCXX/exceptions.cpp +++ b/test/CodeGenCXX/exceptions.cpp @@ -1,18 +1,210 @@ -// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - -fexceptions +// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - -fexceptions | FileCheck %s -struct allocator { - allocator(); - allocator(const allocator&); - ~allocator(); -}; +typedef typeof(sizeof(0)) size_t; -void f(); -void g(bool b, bool c) { - if (b) { - if (!c) - throw allocator(); +// This just shouldn't crash. +namespace test0 { + struct allocator { + allocator(); + allocator(const allocator&); + ~allocator(); + }; - return; + void f(); + void g(bool b, bool c) { + if (b) { + if (!c) + throw allocator(); + + return; + } + f(); + } +} + +namespace test1 { + struct A { A(int); A(int, int); ~A(); void *p; }; + + A *a() { + // CHECK: define [[A:%.*]]* @_ZN5test11aEv() + // CHECK: [[NEW:%.*]] = call noalias i8* @_Znwm(i64 8) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 5) + // CHECK: ret [[A]]* [[CAST]] + // CHECK: call void @_ZdlPv(i8* [[NEW]]) + return new A(5); + } + + A *b() { + // CHECK: define [[A:%.*]]* @_ZN5test11bEv() + // CHECK: [[NEW:%.*]] = call noalias i8* @_Znwm(i64 8) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: [[FOO:%.*]] = invoke i32 @_ZN5test13fooEv() + // CHECK: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 [[FOO]]) + // CHECK: ret [[A]]* [[CAST]] + // CHECK: call void @_ZdlPv(i8* [[NEW]]) + extern int foo(); + return new A(foo()); + } + + struct B { B(); ~B(); operator int(); int x; }; + B makeB(); + + A *c() { + // CHECK: define [[A:%.*]]* @_ZN5test11cEv() + // CHECK: [[ACTIVE:%.*]] = alloca i1 + // CHECK-NEXT: store i1 true, i1* [[ACTIVE]] + // CHECK-NEXT: [[NEW:%.*]] = call noalias i8* @_Znwm(i64 8) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: invoke void @_ZN5test11BC1Ev([[B:%.*]]* [[T0:%.*]]) + // CHECK: [[T1:%.*]] = getelementptr inbounds [[B]]* [[T0]], i32 0, i32 0 + // CHECK-NEXT: [[T2:%.*]] = load i32* [[T1]], align 4 + // CHECK-NEXT: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 [[T2]]) + // CHECK: store i1 false, i1* [[ACTIVE]] + // CHECK-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]]) + // CHECK: ret [[A]]* [[CAST]] + // CHECK: [[ISACTIVE:%.*]] = load i1* [[ACTIVE]] + // CHECK-NEXT: br i1 [[ISACTIVE]] + // CHECK: call void @_ZdlPv(i8* [[NEW]]) + return new A(B().x); + } + + A *d() { + // CHECK: define [[A:%.*]]* @_ZN5test11dEv() + // CHECK: [[ACTIVE:%.*]] = alloca i1 + // CHECK-NEXT: store i1 true, i1* [[ACTIVE]] + // CHECK-NEXT: [[NEW:%.*]] = call noalias i8* @_Znwm(i64 8) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: invoke void @_ZN5test11BC1Ev([[B:%.*]]* [[T0:%.*]]) + // CHECK: [[T1:%.*]] = invoke i32 @_ZN5test11BcviEv([[B]]* [[T0]]) + // CHECK: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 [[T1]]) + // CHECK: store i1 false, i1* [[ACTIVE]] + // CHECK-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]]) + // CHECK: ret [[A]]* [[CAST]] + // CHECK: [[ISACTIVE:%.*]] = load i1* [[ACTIVE]] + // CHECK-NEXT: br i1 [[ISACTIVE]] + // CHECK: call void @_ZdlPv(i8* [[NEW]]) + return new A(B()); + } + + A *e() { + // CHECK: define [[A:%.*]]* @_ZN5test11eEv() + // CHECK: [[ACTIVE:%.*]] = alloca i1 + // CHECK-NEXT: store i1 true, i1* [[ACTIVE]] + // CHECK-NEXT: [[NEW:%.*]] = call noalias i8* @_Znwm(i64 8) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: invoke void @_ZN5test11BC1Ev([[B:%.*]]* [[T0:%.*]]) + // CHECK: [[T1:%.*]] = invoke i32 @_ZN5test11BcviEv([[B]]* [[T0]]) + // CHECK: invoke void @_ZN5test11BC1Ev([[B]]* [[T2:%.*]]) + // CHECK: [[T3:%.*]] = invoke i32 @_ZN5test11BcviEv([[B]]* [[T2]]) + // CHECK: invoke void @_ZN5test11AC1Eii([[A]]* [[CAST]], i32 [[T1]], i32 [[T3]]) + // CHECK: store i1 false, i1* [[ACTIVE]] + // CHECK-NEXT: invoke void @_ZN5test11BD1Ev([[B]]* [[T2]]) + // CHECK: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]]) + // CHECK: ret [[A]]* [[CAST]] + // CHECK: [[ISACTIVE:%.*]] = load i1* [[ACTIVE]] + // CHECK-NEXT: br i1 [[ISACTIVE]] + // CHECK: call void @_ZdlPv(i8* [[NEW]]) + return new A(B(), B()); + } + A *f() { + return new A(makeB().x); + } + A *g() { + return new A(makeB()); + } + A *h() { + return new A(makeB(), makeB()); } - f(); + + A *i() { + // CHECK: define [[A:%.*]]* @_ZN5test11iEv() + // CHECK: [[X:%.*]] = alloca [[A]]*, align 8 + // CHECK: [[ACTIVE:%.*]] = alloca i1 + // CHECK: store i1 true, i1* [[ACTIVE]] + // CHECK-NEXT: [[NEW:%.*]] = call noalias i8* @_Znwm(i64 8) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: invoke void @_ZN5test15makeBEv([[B:%.*]]* sret [[T0:%.*]]) + // CHECK: [[T1:%.*]] = invoke i32 @_ZN5test11BcviEv([[B]]* [[T0]]) + // CHECK: invoke void @_ZN5test11AC1Ei([[A]]* [[CAST]], i32 [[T1]]) + // CHECK: store i1 false, i1* [[ACTIVE]] + // CHECK-NEXT: store [[A]]* [[CAST]], [[A]]** [[X]], align 8 + // CHECK: invoke void @_ZN5test15makeBEv([[B:%.*]]* sret [[T2:%.*]]) + // CHECK: [[RET:%.*]] = load [[A]]** [[X]], align 8 + // CHECK: invoke void @_ZN5test11BD1Ev([[B]]* [[T2]]) + // CHECK: invoke void @_ZN5test11BD1Ev([[B]]* [[T0]]) + // CHECK: ret [[A]]* [[RET]] + // CHECK: [[ISACTIVE:%.*]] = load i1* [[ACTIVE]] + // CHECK-NEXT: br i1 [[ISACTIVE]] + // CHECK: call void @_ZdlPv(i8* [[NEW]]) + A *x; + return (x = new A(makeB()), makeB(), x); + } +} + +namespace test2 { + struct A { + A(int); A(int, int); ~A(); + void *p; + void *operator new(size_t); + void operator delete(void*, size_t); + }; + + A *a() { + // CHECK: define [[A:%.*]]* @_ZN5test21aEv() + // CHECK: [[NEW:%.*]] = call i8* @_ZN5test21AnwEm(i64 8) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: invoke void @_ZN5test21AC1Ei([[A]]* [[CAST]], i32 5) + // CHECK: ret [[A]]* [[CAST]] + // CHECK: invoke void @_ZN5test21AdlEPvm(i8* [[NEW]], i64 8) + // CHECK: call void @_ZSt9terminatev() + return new A(5); + } +} + +namespace test3 { + struct A { + A(int); A(int, int); ~A(); + void *p; + void *operator new(size_t, void*, void*); + void operator delete(void*, void*, void*); + }; + + A *a() { + // CHECK: define [[A:%.*]]* @_ZN5test31aEv() + // CHECK: [[FOO:%.*]] = call i8* @_ZN5test33fooEv() + // CHECK: [[BAR:%.*]] = call i8* @_ZN5test33barEv() + // CHECK: [[NEW:%.*]] = call i8* @_ZN5test31AnwEmPvS1_(i64 8, i8* [[FOO]], i8* [[BAR]]) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: invoke void @_ZN5test31AC1Ei([[A]]* [[CAST]], i32 5) + // CHECK: ret [[A]]* [[CAST]] + // CHECK: invoke void @_ZN5test31AdlEPvS1_S1_(i8* [[NEW]], i8* [[FOO]], i8* [[BAR]]) + // CHECK: call void @_ZSt9terminatev() + extern void *foo(), *bar(); + + return new(foo(),bar()) A(5); + } +} + +namespace test4 { + struct A { + A(int); A(int, int); ~A(); + void *p; + void *operator new(size_t, void*, void*); + void operator delete(void*, size_t, void*, void*); // not a match + }; + + A *a() { + // CHECK: define [[A:%.*]]* @_ZN5test41aEv() + // CHECK: [[FOO:%.*]] = call i8* @_ZN5test43fooEv() + // CHECK-NEXT: [[BAR:%.*]] = call i8* @_ZN5test43barEv() + // CHECK-NEXT: [[NEW:%.*]] = call i8* @_ZN5test41AnwEmPvS1_(i64 8, i8* [[FOO]], i8* [[BAR]]) + // CHECK-NEXT: [[CAST:%.*]] = bitcast i8* [[NEW]] to [[A]]* + // CHECK-NEXT: call void @_ZN5test41AC1Ei([[A]]* [[CAST]], i32 5) + // CHECK-NEXT: ret [[A]]* [[CAST]] + extern void *foo(), *bar(); + + return new(foo(),bar()) A(5); + } + }