brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.7 KiB · 33f2de9 Raw
415 lines · cpp
1//===--- CIRGenCleanup.cpp - Bookkeeping and code emission for cleanups ---===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file contains code dealing with the IR generation for cleanups10// and related information.11//12// A "cleanup" is a piece of code which needs to be executed whenever13// control transfers out of a particular scope.  This can be14// conditionalized to occur only on exceptional control flow, only on15// normal control flow, or both.16//17//===----------------------------------------------------------------------===//18 19#include "CIRGenCleanup.h"20#include "CIRGenFunction.h"21 22#include "clang/CIR/MissingFeatures.h"23 24using namespace clang;25using namespace clang::CIRGen;26 27//===----------------------------------------------------------------------===//28// CIRGenFunction cleanup related29//===----------------------------------------------------------------------===//30 31/// Build a unconditional branch to the lexical scope cleanup block32/// or with the labeled blocked if already solved.33///34/// Track on scope basis, goto's we need to fix later.35cir::BrOp CIRGenFunction::emitBranchThroughCleanup(mlir::Location loc,36                                                   JumpDest dest) {37  // Insert a branch: to the cleanup block (unsolved) or to the already38  // materialized label. Keep track of unsolved goto's.39  assert(dest.getBlock() && "assumes incoming valid dest");40  auto brOp = cir::BrOp::create(builder, loc, dest.getBlock());41 42  // Calculate the innermost active normal cleanup.43  EHScopeStack::stable_iterator topCleanup =44      ehStack.getInnermostActiveNormalCleanup();45 46  // If we're not in an active normal cleanup scope, or if the47  // destination scope is within the innermost active normal cleanup48  // scope, we don't need to worry about fixups.49  if (topCleanup == ehStack.stable_end() ||50      topCleanup.encloses(dest.getScopeDepth())) { // works for invalid51    // FIXME(cir): should we clear insertion point here?52    return brOp;53  }54 55  // If we can't resolve the destination cleanup scope, just add this56  // to the current cleanup scope as a branch fixup.57  if (!dest.getScopeDepth().isValid()) {58    BranchFixup &fixup = ehStack.addBranchFixup();59    fixup.destination = dest.getBlock();60    fixup.destinationIndex = dest.getDestIndex();61    fixup.initialBranch = brOp;62    fixup.optimisticBranchBlock = nullptr;63    // FIXME(cir): should we clear insertion point here?64    return brOp;65  }66 67  cgm.errorNYI(loc, "emitBranchThroughCleanup: valid destination scope depth");68  return brOp;69}70 71/// Emits all the code to cause the given temporary to be cleaned up.72void CIRGenFunction::emitCXXTemporary(const CXXTemporary *temporary,73                                      QualType tempType, Address ptr) {74  pushDestroy(NormalAndEHCleanup, ptr, tempType, destroyCXXObject);75}76 77//===----------------------------------------------------------------------===//78// EHScopeStack79//===----------------------------------------------------------------------===//80 81void EHScopeStack::Cleanup::anchor() {}82 83EHScopeStack::stable_iterator84EHScopeStack::getInnermostActiveNormalCleanup() const {85  stable_iterator si = getInnermostNormalCleanup();86  stable_iterator se = stable_end();87  while (si != se) {88    EHCleanupScope &cleanup = llvm::cast<EHCleanupScope>(*find(si));89    if (cleanup.isActive())90      return si;91    si = cleanup.getEnclosingNormalCleanup();92  }93  return stable_end();94}95 96/// Push an entry of the given size onto this protected-scope stack.97char *EHScopeStack::allocate(size_t size) {98  size = llvm::alignTo(size, ScopeStackAlignment);99  if (!startOfBuffer) {100    unsigned capacity = llvm::PowerOf2Ceil(std::max(size, 1024ul));101    startOfBuffer = std::make_unique<char[]>(capacity);102    startOfData = endOfBuffer = startOfBuffer.get() + capacity;103  } else if (static_cast<size_t>(startOfData - startOfBuffer.get()) < size) {104    unsigned currentCapacity = endOfBuffer - startOfBuffer.get();105    unsigned usedCapacity =106        currentCapacity - (startOfData - startOfBuffer.get());107    unsigned requiredCapacity = usedCapacity + size;108    // We know from the 'else if' condition that requiredCapacity is greater109    // than currentCapacity.110    unsigned newCapacity = llvm::PowerOf2Ceil(requiredCapacity);111 112    std::unique_ptr<char[]> newStartOfBuffer =113        std::make_unique<char[]>(newCapacity);114    char *newEndOfBuffer = newStartOfBuffer.get() + newCapacity;115    char *newStartOfData = newEndOfBuffer - usedCapacity;116    memcpy(newStartOfData, startOfData, usedCapacity);117    startOfBuffer.swap(newStartOfBuffer);118    endOfBuffer = newEndOfBuffer;119    startOfData = newStartOfData;120  }121 122  assert(startOfBuffer.get() + size <= startOfData);123  startOfData -= size;124  return startOfData;125}126 127void EHScopeStack::deallocate(size_t size) {128  startOfData += llvm::alignTo(size, ScopeStackAlignment);129}130 131/// Remove any 'null' fixups on the stack.  However, we can't pop more132/// fixups than the fixup depth on the innermost normal cleanup, or133/// else fixups that we try to add to that cleanup will end up in the134/// wrong place.  We *could* try to shrink fixup depths, but that's135/// actually a lot of work for little benefit.136void EHScopeStack::popNullFixups() {137  // We expect this to only be called when there's still an innermost138  // normal cleanup;  otherwise there really shouldn't be any fixups.139  cgf->cgm.errorNYI("popNullFixups");140}141 142void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) {143  char *buffer = allocate(EHCleanupScope::getSizeForCleanupSize(size));144  bool isNormalCleanup = kind & NormalCleanup;145  bool isEHCleanup = kind & EHCleanup;146  bool isLifetimeMarker = kind & LifetimeMarker;147 148  assert(!cir::MissingFeatures::innermostEHScope());149 150  EHCleanupScope *scope = new (buffer) EHCleanupScope(151      size, branchFixups.size(), innermostNormalCleanup, innermostEHScope);152 153  if (isNormalCleanup)154    innermostNormalCleanup = stable_begin();155 156  if (isLifetimeMarker)157    cgf->cgm.errorNYI("push lifetime marker cleanup");158 159  // With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup160  if (cgf->getLangOpts().EHAsynch && isEHCleanup && !isLifetimeMarker &&161      cgf->getTarget().getCXXABI().isMicrosoft())162    cgf->cgm.errorNYI("push seh cleanup");163 164  return scope->getCleanupBuffer();165}166 167void EHScopeStack::popCleanup() {168  assert(!empty() && "popping exception stack when not empty");169 170  assert(isa<EHCleanupScope>(*begin()));171  EHCleanupScope &cleanup = cast<EHCleanupScope>(*begin());172  innermostNormalCleanup = cleanup.getEnclosingNormalCleanup();173  deallocate(cleanup.getAllocatedSize());174 175  // Destroy the cleanup.176  cleanup.destroy();177 178  // Check whether we can shrink the branch-fixups stack.179  if (!branchFixups.empty()) {180    // If we no longer have any normal cleanups, all the fixups are181    // complete.182    if (!hasNormalCleanups()) {183      branchFixups.clear();184    } else {185      // Otherwise we can still trim out unnecessary nulls.186      popNullFixups();187    }188  }189}190 191bool EHScopeStack::requiresCatchOrCleanup() const {192  for (stable_iterator si = getInnermostEHScope(); si != stable_end();) {193    if (auto *cleanup = dyn_cast<EHCleanupScope>(&*find(si))) {194      if (cleanup->isLifetimeMarker()) {195        // Skip lifetime markers and continue from the enclosing EH scope196        assert(!cir::MissingFeatures::emitLifetimeMarkers());197        continue;198      }199    }200    return true;201  }202  return false;203}204 205EHCatchScope *EHScopeStack::pushCatch(unsigned numHandlers) {206  char *buffer = allocate(EHCatchScope::getSizeForNumHandlers(numHandlers));207  EHCatchScope *scope =208      new (buffer) EHCatchScope(numHandlers, innermostEHScope);209  innermostEHScope = stable_begin();210  return scope;211}212 213static void emitCleanup(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup) {214  // Ask the cleanup to emit itself.215  assert(cgf.haveInsertPoint() && "expected insertion point");216  assert(!cir::MissingFeatures::ehCleanupFlags());217  cleanup->emit(cgf);218  assert(cgf.haveInsertPoint() && "cleanup ended with no insertion point?");219}220 221static mlir::Block *createNormalEntry(CIRGenFunction &cgf,222                                      EHCleanupScope &scope) {223  assert(scope.isNormalCleanup());224  mlir::Block *entry = scope.getNormalBlock();225  if (!entry) {226    mlir::OpBuilder::InsertionGuard guard(cgf.getBuilder());227    entry = cgf.curLexScope->getOrCreateCleanupBlock(cgf.getBuilder());228    scope.setNormalBlock(entry);229  }230  return entry;231}232 233/// Pops a cleanup block. If the block includes a normal cleanup, the234/// current insertion point is threaded through the cleanup, as are235/// any branch fixups on the cleanup.236void CIRGenFunction::popCleanupBlock() {237  assert(!ehStack.empty() && "cleanup stack is empty!");238  assert(isa<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!");239  EHCleanupScope &scope = cast<EHCleanupScope>(*ehStack.begin());240  assert(scope.getFixupDepth() <= ehStack.getNumBranchFixups());241 242  // Remember activation information.243  bool isActive = scope.isActive();244 245  // - whether there are branch fix-ups through this cleanup246  unsigned fixupDepth = scope.getFixupDepth();247  bool hasFixups = ehStack.getNumBranchFixups() != fixupDepth;248 249  // - whether there's a fallthrough250  mlir::Block *fallthroughSource = builder.getInsertionBlock();251  bool hasFallthrough = fallthroughSource != nullptr && isActive;252 253  bool requiresNormalCleanup =254      scope.isNormalCleanup() && (hasFixups || hasFallthrough);255 256  // If we don't need the cleanup at all, we're done.257  assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup());258  if (!requiresNormalCleanup) {259    ehStack.popCleanup();260    return;261  }262 263  // Copy the cleanup emission data out.  This uses either a stack264  // array or malloc'd memory, depending on the size, which is265  // behavior that SmallVector would provide, if we could use it266  // here. Unfortunately, if you ask for a SmallVector<char>, the267  // alignment isn't sufficient.268  auto *cleanupSource = reinterpret_cast<char *>(scope.getCleanupBuffer());269  alignas(EHScopeStack::ScopeStackAlignment) char270      cleanupBufferStack[8 * sizeof(void *)];271  std::unique_ptr<char[]> cleanupBufferHeap;272  size_t cleanupSize = scope.getCleanupSize();273  EHScopeStack::Cleanup *cleanup;274 275  // This is necessary because we are going to deallocate the cleanup276  // (in popCleanup) before we emit it.277  if (cleanupSize <= sizeof(cleanupBufferStack)) {278    memcpy(cleanupBufferStack, cleanupSource, cleanupSize);279    cleanup = reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferStack);280  } else {281    cleanupBufferHeap.reset(new char[cleanupSize]);282    memcpy(cleanupBufferHeap.get(), cleanupSource, cleanupSize);283    cleanup =284        reinterpret_cast<EHScopeStack::Cleanup *>(cleanupBufferHeap.get());285  }286 287  assert(!cir::MissingFeatures::ehCleanupFlags());288 289  // If we have a fallthrough and no other need for the cleanup,290  // emit it directly.291  if (hasFallthrough && !hasFixups) {292    assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup());293    ehStack.popCleanup();294    scope.markEmitted();295    emitCleanup(*this, cleanup);296  } else {297    // Otherwise, the best approach is to thread everything through298    // the cleanup block and then try to clean up after ourselves.299 300    // Force the entry block to exist.301    mlir::Block *normalEntry = createNormalEntry(*this, scope);302 303    // I.  Set up the fallthrough edge in.304    mlir::OpBuilder::InsertPoint savedInactiveFallthroughIP;305 306    // If there's a fallthrough, we need to store the cleanup307    // destination index. For fall-throughs this is always zero.308    if (hasFallthrough) {309      assert(!cir::MissingFeatures::ehCleanupHasPrebranchedFallthrough());310 311    } else if (fallthroughSource) {312      // Otherwise, save and clear the IP if we don't have fallthrough313      // because the cleanup is inactive.314      assert(!isActive && "source without fallthrough for active cleanup");315      savedInactiveFallthroughIP = builder.saveInsertionPoint();316    }317 318    // II.  Emit the entry block.  This implicitly branches to it if319    // we have fallthrough.  All the fixups and existing branches320    // should already be branched to it.321    builder.setInsertionPointToEnd(normalEntry);322 323    // intercept normal cleanup to mark SEH scope end324    assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup());325 326    // III.  Figure out where we're going and build the cleanup327    // epilogue.328    bool hasEnclosingCleanups =329        (scope.getEnclosingNormalCleanup() != ehStack.stable_end());330 331    // Compute the branch-through dest if we need it:332    //   - if there are branch-throughs threaded through the scope333    //   - if fall-through is a branch-through334    //   - if there are fixups that will be optimistically forwarded335    //     to the enclosing cleanup336    assert(!cir::MissingFeatures::cleanupBranchThrough());337    if (hasFixups && hasEnclosingCleanups)338      cgm.errorNYI("cleanup branch-through dest");339 340    mlir::Block *fallthroughDest = nullptr;341 342    // If there's exactly one branch-after and no other threads,343    // we can route it without a switch.344    // Skip for SEH, since ExitSwitch is used to generate code to indicate345    // abnormal termination. (SEH: Except _leave and fall-through at346    // the end, all other exits in a _try (return/goto/continue/break)347    // are considered as abnormal terminations, using NormalCleanupDestSlot348    // to indicate abnormal termination)349    assert(!cir::MissingFeatures::cleanupBranchThrough());350    assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup());351 352    // IV.  Pop the cleanup and emit it.353    scope.markEmitted();354    ehStack.popCleanup();355    assert(ehStack.hasNormalCleanups() == hasEnclosingCleanups);356 357    emitCleanup(*this, cleanup);358 359    // Append the prepared cleanup prologue from above.360    assert(!cir::MissingFeatures::cleanupAppendInsts());361 362    // Optimistically hope that any fixups will continue falling through.363    if (fixupDepth != ehStack.getNumBranchFixups())364      cgm.errorNYI("cleanup fixup depth mismatch");365 366    // V.  Set up the fallthrough edge out.367 368    // Case 1: a fallthrough source exists but doesn't branch to the369    // cleanup because the cleanup is inactive.370    if (!hasFallthrough && fallthroughSource) {371      // Prebranched fallthrough was forwarded earlier.372      // Non-prebranched fallthrough doesn't need to be forwarded.373      // Either way, all we need to do is restore the IP we cleared before.374      assert(!isActive);375      cgm.errorNYI("cleanup inactive fallthrough");376 377      // Case 2: a fallthrough source exists and should branch to the378      // cleanup, but we're not supposed to branch through to the next379      // cleanup.380    } else if (hasFallthrough && fallthroughDest) {381      cgm.errorNYI("cleanup fallthrough destination");382 383      // Case 3: a fallthrough source exists and should branch to the384      // cleanup and then through to the next.385    } else if (hasFallthrough) {386      // Everything is already set up for this.387 388      // Case 4: no fallthrough source exists.389    } else {390      // FIXME(cir): should we clear insertion point here?391    }392 393    // VI.  Assorted cleaning.394 395    // Check whether we can merge NormalEntry into a single predecessor.396    // This might invalidate (non-IR) pointers to NormalEntry.397    //398    // If it did invalidate those pointers, and normalEntry was the same399    // as NormalExit, go back and patch up the fixups.400    assert(!cir::MissingFeatures::simplifyCleanupEntry());401  }402}403 404/// Pops cleanup blocks until the given savepoint is reached.405void CIRGenFunction::popCleanupBlocks(406    EHScopeStack::stable_iterator oldCleanupStackDepth) {407  assert(!cir::MissingFeatures::ehstackBranches());408 409  // Pop cleanup blocks until we reach the base stack depth for the410  // current scope.411  while (ehStack.stable_begin() != oldCleanupStackDepth) {412    popCleanupBlock();413  }414}415