//===--- CIRGenCleanup.cpp - Bookkeeping and code emission for cleanups ---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains code dealing with the IR generation for cleanups // and related information. // // A "cleanup" is a piece of code which needs to be executed whenever // control transfers out of a particular scope. This can be // conditionalized to occur only on exceptional control flow, only on // normal control flow, or both. // //===----------------------------------------------------------------------===// #include "CIRGenCleanup.h" #include "CIRGenFunction.h" #include "clang/CIR/MissingFeatures.h" using namespace clang; using namespace clang::CIRGen; //===----------------------------------------------------------------------===// // CIRGenFunction cleanup related //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // EHScopeStack //===----------------------------------------------------------------------===// void EHScopeStack::Cleanup::anchor() {} /// Push an entry of the given size onto this protected-scope stack. char *EHScopeStack::allocate(size_t size) { size = llvm::alignTo(size, ScopeStackAlignment); if (!startOfBuffer) { unsigned capacity = llvm::PowerOf2Ceil(std::max(size, 1024ul)); startOfBuffer = std::make_unique(capacity); startOfData = endOfBuffer = startOfBuffer.get() + capacity; } else if (static_cast(startOfData - startOfBuffer.get()) < size) { unsigned currentCapacity = endOfBuffer - startOfBuffer.get(); unsigned usedCapacity = currentCapacity - (startOfData - startOfBuffer.get()); unsigned requiredCapacity = usedCapacity + size; // We know from the 'else if' condition that requiredCapacity is greater // than currentCapacity. unsigned newCapacity = llvm::PowerOf2Ceil(requiredCapacity); std::unique_ptr newStartOfBuffer = std::make_unique(newCapacity); char *newEndOfBuffer = newStartOfBuffer.get() + newCapacity; char *newStartOfData = newEndOfBuffer - usedCapacity; memcpy(newStartOfData, startOfData, usedCapacity); startOfBuffer.swap(newStartOfBuffer); endOfBuffer = newEndOfBuffer; startOfData = newStartOfData; } assert(startOfBuffer.get() + size <= startOfData); startOfData -= size; return startOfData; } void EHScopeStack::deallocate(size_t size) { startOfData += llvm::alignTo(size, ScopeStackAlignment); } void *EHScopeStack::pushCleanup(CleanupKind kind, size_t size) { char *buffer = allocate(EHCleanupScope::getSizeForCleanupSize(size)); bool isEHCleanup = kind & EHCleanup; bool isLifetimeMarker = kind & LifetimeMarker; assert(!cir::MissingFeatures::innermostEHScope()); EHCleanupScope *scope = new (buffer) EHCleanupScope(size); if (isLifetimeMarker) cgf->cgm.errorNYI("push lifetime marker cleanup"); // With Windows -EHa, Invoke llvm.seh.scope.begin() for EHCleanup if (cgf->getLangOpts().EHAsynch && isEHCleanup && !isLifetimeMarker && cgf->getTarget().getCXXABI().isMicrosoft()) cgf->cgm.errorNYI("push seh cleanup"); return scope->getCleanupBuffer(); } void EHScopeStack::popCleanup() { assert(!empty() && "popping exception stack when not empty"); assert(isa(*begin())); EHCleanupScope &cleanup = cast(*begin()); deallocate(cleanup.getAllocatedSize()); // Destroy the cleanup. cleanup.destroy(); assert(!cir::MissingFeatures::ehCleanupBranchFixups()); } static void emitCleanup(CIRGenFunction &cgf, EHScopeStack::Cleanup *cleanup) { // Ask the cleanup to emit itself. assert(cgf.haveInsertPoint() && "expected insertion point"); assert(!cir::MissingFeatures::ehCleanupFlags()); cleanup->emit(cgf); assert(cgf.haveInsertPoint() && "cleanup ended with no insertion point?"); } /// Pops a cleanup block. If the block includes a normal cleanup, the /// current insertion point is threaded through the cleanup, as are /// any branch fixups on the cleanup. void CIRGenFunction::popCleanupBlock() { assert(!ehStack.empty() && "cleanup stack is empty!"); assert(isa(*ehStack.begin()) && "top not a cleanup!"); EHCleanupScope &scope = cast(*ehStack.begin()); // Remember activation information. bool isActive = scope.isActive(); assert(!cir::MissingFeatures::ehCleanupBranchFixups()); // - whether there's a fallthrough mlir::Block *fallthroughSource = builder.getInsertionBlock(); bool hasFallthrough = fallthroughSource != nullptr && isActive; bool requiresNormalCleanup = scope.isNormalCleanup() && hasFallthrough; // If we don't need the cleanup at all, we're done. assert(!cir::MissingFeatures::ehCleanupScopeRequiresEHCleanup()); if (!requiresNormalCleanup) { ehStack.popCleanup(); return; } // Copy the cleanup emission data out. This uses either a stack // array or malloc'd memory, depending on the size, which is // behavior that SmallVector would provide, if we could use it // here. Unfortunately, if you ask for a SmallVector, the // alignment isn't sufficient. auto *cleanupSource = reinterpret_cast(scope.getCleanupBuffer()); alignas(EHScopeStack::ScopeStackAlignment) char cleanupBufferStack[8 * sizeof(void *)]; std::unique_ptr cleanupBufferHeap; size_t cleanupSize = scope.getCleanupSize(); EHScopeStack::Cleanup *cleanup; // This is necessary because we are going to deallocate the cleanup // (in popCleanup) before we emit it. if (cleanupSize <= sizeof(cleanupBufferStack)) { memcpy(cleanupBufferStack, cleanupSource, cleanupSize); cleanup = reinterpret_cast(cleanupBufferStack); } else { cleanupBufferHeap.reset(new char[cleanupSize]); memcpy(cleanupBufferHeap.get(), cleanupSource, cleanupSize); cleanup = reinterpret_cast(cleanupBufferHeap.get()); } assert(!cir::MissingFeatures::ehCleanupFlags()); ehStack.popCleanup(); scope.markEmitted(); emitCleanup(*this, cleanup); } /// Pops cleanup blocks until the given savepoint is reached. void CIRGenFunction::popCleanupBlocks( EHScopeStack::stable_iterator oldCleanupStackDepth) { assert(!cir::MissingFeatures::ehstackBranches()); // Pop cleanup blocks until we reach the base stack depth for the // current scope. while (ehStack.stable_begin() != oldCleanupStackDepth) { popCleanupBlock(); } }