mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-05 09:31:03 +03:00
Introduce try-catch scopes abstraction
Single intended functional change/fix: check ALL active catch blocks in `isCatchingNonExceptions()`, not just the ones from the innermost try-catch scope. The catch bodies of a try-catch scope are now emitted in lexical order (used to be reversed, last catch block first).
This commit is contained in:
parent
a4eb01e4e4
commit
fffe86cd2e
6 changed files with 560 additions and 503 deletions
|
@ -24,7 +24,6 @@
|
|||
#include "gen/logger.h"
|
||||
#include "gen/runtime.h"
|
||||
#include "gen/tollvm.h"
|
||||
#include "gen/ms-cxx-helper.h"
|
||||
#include "ir/irfunction.h"
|
||||
#include "ir/irmodule.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
|
@ -795,89 +794,7 @@ public:
|
|||
irs->funcGen().scopes.popCleanups(cleanupBefore);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if LDC_LLVM_VER >= 308
|
||||
void emitBeginCatchMSVCEH(Catch *ctch, llvm::BasicBlock *endbb,
|
||||
llvm::CatchSwitchInst *catchSwitchInst) {
|
||||
VarDeclaration *var = ctch->var;
|
||||
// The MSVC/x86 build uses C++ exception handling
|
||||
// This needs a series of catch pads to match the exception
|
||||
// and the catch handler must be terminated by a catch return instruction
|
||||
LLValue *exnObj = nullptr;
|
||||
LLValue *cpyObj = nullptr;
|
||||
LLValue *typeDesc = nullptr;
|
||||
LLValue *clssInfo = nullptr;
|
||||
if (var) {
|
||||
// alloca storage for the variable, it always needs a place on the stack
|
||||
// do not initialize, this will be done by the C++ exception handler
|
||||
var->_init = nullptr;
|
||||
|
||||
// redirect scope to avoid the generation of debug info before the
|
||||
// catchpad
|
||||
IRScope save = irs->scope();
|
||||
irs->scope() = IRScope(gIR->topallocapoint()->getParent());
|
||||
irs->scope().builder.SetInsertPoint(gIR->topallocapoint());
|
||||
DtoDeclarationExp(var);
|
||||
|
||||
// catch handler will be outlined, so always treat as a nested reference
|
||||
exnObj = getIrValue(var);
|
||||
|
||||
if (var->nestedrefs.dim) {
|
||||
// if variable needed in a closure, use a stack temporary and copy it
|
||||
// when caught
|
||||
cpyObj = exnObj;
|
||||
exnObj = DtoAlloca(var->type, "exnObj");
|
||||
}
|
||||
irs->scope() = save;
|
||||
irs->DBuilder.EmitStopPoint(ctch->loc); // re-set debug loc after the
|
||||
// SetInsertPoint(allocaInst) call
|
||||
} else if (ctch->type) {
|
||||
// catch without var
|
||||
exnObj = DtoAlloca(ctch->type, "exnObj");
|
||||
} else {
|
||||
// catch all
|
||||
exnObj = LLConstant::getNullValue(getVoidPtrType());
|
||||
}
|
||||
|
||||
if (ctch->type) {
|
||||
ClassDeclaration *cd = ctch->type->toBasetype()->isClassHandle();
|
||||
typeDesc = getTypeDescriptor(*irs, cd);
|
||||
clssInfo = getIrAggr(cd)->getClassInfoSymbol();
|
||||
} else {
|
||||
// catch all
|
||||
typeDesc = LLConstant::getNullValue(getVoidPtrType());
|
||||
clssInfo = LLConstant::getNullValue(DtoType(Type::typeinfoclass->type));
|
||||
}
|
||||
|
||||
// "catchpad within %switch [TypeDescriptor, 0, &caughtObject]" must be
|
||||
// first instruction
|
||||
int flags = var ? 0 : 64; // just mimicking clang here
|
||||
LLValue *args[] = {typeDesc, DtoConstUint(flags), exnObj};
|
||||
auto catchpad = irs->ir->CreateCatchPad(
|
||||
catchSwitchInst, llvm::ArrayRef<LLValue *>(args), "");
|
||||
catchSwitchInst->addHandler(irs->scopebb());
|
||||
|
||||
if (cpyObj) {
|
||||
// assign the caught exception to the location in the closure
|
||||
auto val = irs->ir->CreateLoad(exnObj);
|
||||
irs->ir->CreateStore(val, cpyObj);
|
||||
exnObj = cpyObj;
|
||||
}
|
||||
|
||||
// Exceptions are never rethrown by D code (but thrown again), so
|
||||
// we can leave the catch handler right away and continue execution
|
||||
// outside the catch funclet
|
||||
llvm::BasicBlock *catchhandler = llvm::BasicBlock::Create(
|
||||
irs->context(), "catchhandler", irs->topfunc());
|
||||
llvm::CatchReturnInst::Create(catchpad, catchhandler, irs->scopebb());
|
||||
irs->scope() = IRScope(catchhandler);
|
||||
auto enterCatchFn =
|
||||
getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch");
|
||||
irs->CreateCallOrInvoke(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()),
|
||||
clssInfo);
|
||||
}
|
||||
#endif
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void visit(TryCatchStatement *stmt) LLVM_OVERRIDE {
|
||||
IF_LOG Logger::println("TryCatchStatement::toIR(): %s",
|
||||
|
@ -885,7 +802,6 @@ public:
|
|||
LOG_SCOPE;
|
||||
|
||||
auto &PGO = irs->funcGen().pgo;
|
||||
auto entryCount = PGO.setCurrentStmt(stmt);
|
||||
|
||||
// Emit dwarf stop point
|
||||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||||
|
@ -899,185 +815,26 @@ public:
|
|||
llvm::BasicBlock *endbb = llvm::BasicBlock::Create(
|
||||
irs->context(), "try.success.or.caught", irs->topfunc());
|
||||
|
||||
assert(stmt->catches);
|
||||
|
||||
struct CatchBlock {
|
||||
ClassDeclaration *classdecl;
|
||||
llvm::BasicBlock *BB;
|
||||
uint64_t catchcount;
|
||||
};
|
||||
|
||||
llvm::SmallVector<CatchBlock, 6> catchBlocks;
|
||||
catchBlocks.reserve(stmt->catches->dim);
|
||||
|
||||
#if LDC_LLVM_VER >= 308
|
||||
if (useMSVCEH()) {
|
||||
auto &scopes = irs->funcGen().scopes;
|
||||
auto catchSwitchBlock = llvm::BasicBlock::Create(
|
||||
irs->context(), "catch.dispatch", irs->topfunc());
|
||||
llvm::BasicBlock *unwindto =
|
||||
scopes.currentCleanupScope() > 0 ? scopes.getLandingPad() : nullptr;
|
||||
auto funclet = scopes.getFunclet();
|
||||
auto catchSwitchInst = llvm::CatchSwitchInst::Create(
|
||||
funclet ? funclet : llvm::ConstantTokenNone::get(irs->context()),
|
||||
unwindto, stmt->catches->dim, "", catchSwitchBlock);
|
||||
|
||||
for (auto c : *stmt->catches) {
|
||||
auto catchBB = llvm::BasicBlock::Create(
|
||||
irs->context(), llvm::Twine("catch.") + c->type->toChars(),
|
||||
irs->topfunc(), endbb);
|
||||
|
||||
irs->scope() = IRScope(catchBB);
|
||||
irs->DBuilder.EmitBlockStart(c->loc);
|
||||
PGO.emitCounterIncrement(c);
|
||||
|
||||
emitBeginCatchMSVCEH(c, endbb, catchSwitchInst);
|
||||
|
||||
// Emit handler, if there is one. The handler is zero, for instance,
|
||||
// when building 'catch { debug foo(); }' in non-debug mode.
|
||||
if (c->handler) {
|
||||
Statement_toIR(c->handler, irs);
|
||||
}
|
||||
|
||||
if (!irs->scopereturned()) {
|
||||
irs->ir->CreateBr(endbb);
|
||||
}
|
||||
|
||||
irs->DBuilder.EmitBlockEnd();
|
||||
}
|
||||
|
||||
// TODO: PGO has not yet been implemented for MSVC EH, set catchCount
|
||||
// temporarily to 0
|
||||
uint64_t catchCount = 0;
|
||||
|
||||
CatchBlock cb = {nullptr, catchSwitchBlock, catchCount};
|
||||
catchBlocks.push_back(cb); // just for cleanup
|
||||
scopes.pushCatch(nullptr, catchSwitchBlock);
|
||||
|
||||
// if no landing pad is created, the catch blocks are unused, but
|
||||
// the verifier complains if there are catchpads without personality
|
||||
// so we can just set it unconditionally
|
||||
if (!irs->func()->func->hasPersonalityFn()) {
|
||||
const char *personality = "__CxxFrameHandler3";
|
||||
LLFunction *personalityFn =
|
||||
getRuntimeFunction(Loc(), irs->module, personality);
|
||||
irs->func()->func->setPersonalityFn(personalityFn);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
for (auto it = stmt->catches->rbegin(), end = stmt->catches->rend();
|
||||
it != end; ++it) {
|
||||
auto catchBB = llvm::BasicBlock::Create(
|
||||
irs->context(), llvm::Twine("catch.") + (*it)->type->toChars(),
|
||||
irs->topfunc(), endbb);
|
||||
|
||||
irs->scope() = IRScope(catchBB);
|
||||
irs->DBuilder.EmitBlockStart((*it)->loc);
|
||||
PGO.emitCounterIncrement(*it);
|
||||
|
||||
const auto enterCatchFn =
|
||||
getRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch");
|
||||
auto ptr = DtoLoad(irs->funcGen().getOrCreateEhPtrSlot());
|
||||
auto throwableObj = irs->ir->CreateCall(enterCatchFn, ptr);
|
||||
|
||||
// For catches that use the Throwable object, create storage for it.
|
||||
// We will set it in the code that branches from the landing pads
|
||||
// (there might be more than one) to catchBB.
|
||||
auto var = (*it)->var;
|
||||
if (var) {
|
||||
// This will alloca if we haven't already and take care of nested refs
|
||||
// if there are any.
|
||||
DtoDeclarationExp(var);
|
||||
|
||||
// Copy the exception reference over from the _d_eh_enter_catch return
|
||||
// value.
|
||||
DtoStore(DtoBitCast(throwableObj, DtoType((*it)->var->type)),
|
||||
getIrLocal(var)->value);
|
||||
}
|
||||
|
||||
// Emit handler, if there is one. The handler is zero, for instance,
|
||||
// when building 'catch { debug foo(); }' in non-debug mode.
|
||||
if ((*it)->handler) {
|
||||
Statement_toIR((*it)->handler, irs);
|
||||
}
|
||||
|
||||
if (!irs->scopereturned()) {
|
||||
irs->ir->CreateBr(endbb);
|
||||
}
|
||||
|
||||
irs->DBuilder.EmitBlockEnd();
|
||||
|
||||
// PGO information, currently unused
|
||||
auto catchCount = PGO.getRegionCount(*it);
|
||||
|
||||
CatchBlock cb = {(*it)->type->toBasetype()->isClassHandle(), catchBB,
|
||||
catchCount};
|
||||
catchBlocks.push_back(cb);
|
||||
}
|
||||
|
||||
// Total number of uncaught exceptions is equal to the execution count at
|
||||
// the start of the try block minus the one after the continuation.
|
||||
// uncaughtCount keeps track of the exception type mismatch count while
|
||||
// iterating through the catchBlocks list.
|
||||
auto uncaughtCount = entryCount - PGO.getRegionCount(stmt);
|
||||
|
||||
// Only after emitting all the catch bodies, register the catch scopes.
|
||||
// This is so that (re)throwing inside a catch does not match later
|
||||
// catches.
|
||||
for (const auto &cb : catchBlocks) {
|
||||
auto matchWeights =
|
||||
PGO.createProfileWeights(cb.catchcount, uncaughtCount);
|
||||
// Add this exception type's match count to the uncaughtCount, because
|
||||
// these failed to match the exception types of the remaining
|
||||
// iterations.
|
||||
uncaughtCount += cb.catchcount;
|
||||
|
||||
DtoResolveClass(cb.classdecl);
|
||||
|
||||
irs->funcGen().scopes.pushCatch(
|
||||
getIrAggr(cb.classdecl)->getClassInfoSymbol(), cb.BB, matchWeights);
|
||||
}
|
||||
}
|
||||
irs->funcGen().scopes.pushTryCatch(stmt, endbb);
|
||||
|
||||
// Emit the try block.
|
||||
irs->scope() = IRScope(trybb);
|
||||
|
||||
const bool isCatchingNonExceptions =
|
||||
std::any_of(stmt->catches->begin(), stmt->catches->end(), [](Catch *c) {
|
||||
bool isException = false;
|
||||
for (auto cd = c->type->toBasetype()->isClassHandle(); cd;
|
||||
cd = cd->baseClass) {
|
||||
if (cd == ClassDeclaration::exception) {
|
||||
isException = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !isException;
|
||||
});
|
||||
|
||||
irs->func()->scopes->pushTryBlock(isCatchingNonExceptions);
|
||||
|
||||
assert(stmt->_body);
|
||||
irs->DBuilder.EmitBlockStart(stmt->_body->loc);
|
||||
stmt->_body->accept(this);
|
||||
irs->DBuilder.EmitBlockEnd();
|
||||
|
||||
irs->func()->scopes->popTryBlock();
|
||||
|
||||
if (!irs->scopereturned())
|
||||
llvm::BranchInst::Create(endbb, irs->scopebb());
|
||||
|
||||
// Now that we have done the try block, remove the catches and continue
|
||||
// codegen in the end block the try and all the catches branch to.
|
||||
for (size_t i = 0; i < catchBlocks.size(); ++i) {
|
||||
irs->funcGen().scopes.popCatch();
|
||||
}
|
||||
irs->funcGen().scopes.popTryCatch();
|
||||
|
||||
// Move end block after all generated blocks
|
||||
endbb->moveAfter(&irs->topfunc()->back());
|
||||
|
||||
irs->scope() = IRScope(endbb);
|
||||
|
||||
// PGO counter tracks the continuation of the try statement
|
||||
PGO.emitCounterIncrement(stmt);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue