aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/CodeGenFunction.cpp
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2010-09-14 07:57:04 +0000
committerJohn McCall <rjmccall@apple.com>2010-09-14 07:57:04 +0000
commit7d8647f194ae4f2499e5bcd40dcfea34cd21ebc6 (patch)
treede0d80d76baee6e0550c50b08e95b2f3fc94c7f6 /lib/CodeGen/CodeGenFunction.cpp
parentf5ddcc0aca828f312a8151940be71743620d1f65 (diff)
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
Diffstat (limited to 'lib/CodeGen/CodeGenFunction.cpp')
-rw-r--r--lib/CodeGen/CodeGenFunction.cpp342
1 files changed, 260 insertions, 82 deletions
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() {