blob: 4d4d10be400243e2ca65948c7ab565fcf2f2b41e [file] [log] [blame]
//===--- 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<char[]>(capacity);
startOfData = endOfBuffer = startOfBuffer.get() + capacity;
} else if (static_cast<size_t>(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<char[]> newStartOfBuffer =
std::make_unique<char[]>(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<EHCleanupScope>(*begin()));
EHCleanupScope &cleanup = cast<EHCleanupScope>(*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<EHCleanupScope>(*ehStack.begin()) && "top not a cleanup!");
EHCleanupScope &scope = cast<EHCleanupScope>(*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<char>, the
// alignment isn't sufficient.
auto *cleanupSource = reinterpret_cast<char *>(scope.getCleanupBuffer());
alignas(EHScopeStack::ScopeStackAlignment) char
cleanupBufferStack[8 * sizeof(void *)];
std::unique_ptr<char[]> 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<EHScopeStack::Cleanup *>(cleanupBufferStack);
} else {
cleanupBufferHeap.reset(new char[cleanupSize]);
memcpy(cleanupBufferHeap.get(), cleanupSource, cleanupSize);
cleanup =
reinterpret_cast<EHScopeStack::Cleanup *>(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();
}
}