mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-05 17:43:35 +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
|
@ -25,12 +25,6 @@ GotoJump::GotoJump(Loc loc, llvm::BasicBlock *sourceBlock,
|
||||||
: sourceLoc(std::move(loc)), sourceBlock(sourceBlock),
|
: sourceLoc(std::move(loc)), sourceBlock(sourceBlock),
|
||||||
tentativeTarget(tentativeTarget), targetLabel(targetLabel) {}
|
tentativeTarget(tentativeTarget), targetLabel(targetLabel) {}
|
||||||
|
|
||||||
CatchScope::CatchScope(llvm::Constant *classInfoPtr,
|
|
||||||
llvm::BasicBlock *bodyBlock, CleanupCursor cleanupScope,
|
|
||||||
llvm::MDNode *branchWeights)
|
|
||||||
: classInfoPtr(classInfoPtr), bodyBlock(bodyBlock),
|
|
||||||
cleanupScope(cleanupScope), branchWeights(branchWeights) {}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 308
|
#if LDC_LLVM_VER >= 308
|
||||||
|
@ -348,59 +342,18 @@ void ScopeStack::popCleanups(CleanupCursor targetScope) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScopeStack::pushCatch(llvm::Constant *classInfoPtr,
|
void ScopeStack::pushTryCatch(TryCatchStatement *stmt,
|
||||||
llvm::BasicBlock *bodyBlock,
|
llvm::BasicBlock *endbb) {
|
||||||
llvm::MDNode *matchWeights) {
|
tryCatchScopes.push(stmt, endbb);
|
||||||
if (useMSVCEH()) {
|
if (!useMSVCEH())
|
||||||
#if LDC_LLVM_VER >= 308
|
|
||||||
assert(isCatchSwitchBlock(bodyBlock));
|
|
||||||
pushCleanup(bodyBlock, bodyBlock);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
catchScopes.emplace_back(classInfoPtr, bodyBlock, currentCleanupScope(),
|
|
||||||
matchWeights);
|
|
||||||
currentLandingPads().push_back(nullptr);
|
currentLandingPads().push_back(nullptr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ScopeStack::popCatch() {
|
void ScopeStack::popTryCatch() {
|
||||||
if (useMSVCEH()) {
|
tryCatchScopes.pop();
|
||||||
#if LDC_LLVM_VER >= 308
|
if (!useMSVCEH())
|
||||||
assert(isCatchSwitchBlock(cleanupScopes.back().beginBlock));
|
|
||||||
popCleanups(currentCleanupScope() - 1);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
catchScopes.pop_back();
|
|
||||||
currentLandingPads().pop_back();
|
currentLandingPads().pop_back();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ScopeStack::pushTryBlock(bool catchesNonExceptions) {
|
|
||||||
catchingNonExceptions.push_back(catchesNonExceptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScopeStack::popTryBlock() {
|
|
||||||
catchingNonExceptions.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ScopeStack::isCatchingNonExceptions() const {
|
|
||||||
bool hasCatchScopes = !catchScopes.empty();
|
|
||||||
if (useMSVCEH()) {
|
|
||||||
#if LDC_LLVM_VER >= 308
|
|
||||||
hasCatchScopes = std::any_of(
|
|
||||||
cleanupScopes.begin(), cleanupScopes.end(),
|
|
||||||
[](const CleanupScope &c) { return isCatchSwitchBlock(c.beginBlock); });
|
|
||||||
#else
|
|
||||||
hasCatchScopes = false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasCatchScopes)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
assert(!catchingNonExceptions.empty());
|
|
||||||
return catchingNonExceptions.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScopeStack::pushLoopTarget(Statement *loopStatement,
|
void ScopeStack::pushLoopTarget(Statement *loopStatement,
|
||||||
llvm::BasicBlock *continueTarget,
|
llvm::BasicBlock *continueTarget,
|
||||||
|
@ -499,142 +452,11 @@ llvm::BasicBlock *&ScopeStack::getLandingPadRef(CleanupCursor scope) {
|
||||||
|
|
||||||
llvm::BasicBlock *ScopeStack::getLandingPad() {
|
llvm::BasicBlock *ScopeStack::getLandingPad() {
|
||||||
llvm::BasicBlock *&landingPad = getLandingPadRef(currentCleanupScope() - 1);
|
llvm::BasicBlock *&landingPad = getLandingPadRef(currentCleanupScope() - 1);
|
||||||
if (!landingPad) {
|
if (!landingPad)
|
||||||
#if LDC_LLVM_VER >= 308
|
landingPad = tryCatchScopes.emitLandingPad();
|
||||||
if (useMSVCEH()) {
|
|
||||||
assert(currentCleanupScope() > 0);
|
|
||||||
landingPad = emitLandingPadMSVCEH(currentCleanupScope() - 1);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
landingPad = emitLandingPad();
|
|
||||||
}
|
|
||||||
return landingPad;
|
return landingPad;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
llvm::LandingPadInst *createLandingPadInst(IRState &irs) {
|
|
||||||
LLType *retType =
|
|
||||||
LLStructType::get(LLType::getInt8PtrTy(irs.context()),
|
|
||||||
LLType::getInt32Ty(irs.context()), nullptr);
|
|
||||||
#if LDC_LLVM_VER >= 307
|
|
||||||
LLFunction *currentFunction = irs.func()->func;
|
|
||||||
if (!currentFunction->hasPersonalityFn()) {
|
|
||||||
LLFunction *personalityFn =
|
|
||||||
getRuntimeFunction(Loc(), irs.module, "_d_eh_personality");
|
|
||||||
currentFunction->setPersonalityFn(personalityFn);
|
|
||||||
}
|
|
||||||
return irs.ir->CreateLandingPad(retType, 0);
|
|
||||||
#else
|
|
||||||
LLFunction *personalityFn =
|
|
||||||
getRuntimeFunction(Loc(), irs.module, "_d_eh_personality");
|
|
||||||
return irs.ir->CreateLandingPad(retType, personalityFn, 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 308
|
|
||||||
llvm::BasicBlock *ScopeStack::emitLandingPadMSVCEH(CleanupCursor scope) {
|
|
||||||
|
|
||||||
LLFunction *currentFunction = irs.func()->func;
|
|
||||||
if (!currentFunction->hasPersonalityFn()) {
|
|
||||||
const char *personality = "__CxxFrameHandler3";
|
|
||||||
LLFunction *personalityFn =
|
|
||||||
getRuntimeFunction(Loc(), irs.module, personality);
|
|
||||||
currentFunction->setPersonalityFn(personalityFn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope == 0)
|
|
||||||
return runCleanupPad(scope, nullptr);
|
|
||||||
|
|
||||||
llvm::BasicBlock *&pad = getLandingPadRef(scope - 1);
|
|
||||||
if (!pad)
|
|
||||||
pad = emitLandingPadMSVCEH(scope - 1);
|
|
||||||
|
|
||||||
return runCleanupPad(scope, pad);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
llvm::BasicBlock *ScopeStack::emitLandingPad() {
|
|
||||||
// save and rewrite scope
|
|
||||||
IRScope savedIRScope = irs.scope();
|
|
||||||
|
|
||||||
llvm::BasicBlock *beginBB =
|
|
||||||
llvm::BasicBlock::Create(irs.context(), "landingPad", irs.topfunc());
|
|
||||||
irs.scope() = IRScope(beginBB);
|
|
||||||
|
|
||||||
llvm::LandingPadInst *landingPad = createLandingPadInst(irs);
|
|
||||||
|
|
||||||
// Stash away the exception object pointer and selector value into their
|
|
||||||
// stack slots.
|
|
||||||
llvm::Value *ehPtr = DtoExtractValue(landingPad, 0);
|
|
||||||
irs.ir->CreateStore(ehPtr, irs.funcGen().getOrCreateEhPtrSlot());
|
|
||||||
|
|
||||||
llvm::Value *ehSelector = DtoExtractValue(landingPad, 1);
|
|
||||||
if (!irs.funcGen().ehSelectorSlot) {
|
|
||||||
irs.funcGen().ehSelectorSlot =
|
|
||||||
DtoRawAlloca(ehSelector->getType(), 0, "eh.selector");
|
|
||||||
}
|
|
||||||
irs.ir->CreateStore(ehSelector, irs.funcGen().ehSelectorSlot);
|
|
||||||
|
|
||||||
// Add landingpad clauses, emit finallys and 'if' chain to catch the
|
|
||||||
// exception.
|
|
||||||
CleanupCursor lastCleanup = currentCleanupScope();
|
|
||||||
for (auto it = catchScopes.rbegin(), end = catchScopes.rend(); it != end;
|
|
||||||
++it) {
|
|
||||||
// Insert any cleanups in between the last catch we ran (i.e. tested for
|
|
||||||
// and found that the type does not match) and this one.
|
|
||||||
assert(lastCleanup >= it->cleanupScope);
|
|
||||||
if (lastCleanup > it->cleanupScope) {
|
|
||||||
landingPad->setCleanup(true);
|
|
||||||
llvm::BasicBlock *afterCleanupBB = llvm::BasicBlock::Create(
|
|
||||||
irs.context(), beginBB->getName() + llvm::Twine(".after.cleanup"),
|
|
||||||
irs.topfunc());
|
|
||||||
runCleanups(lastCleanup, it->cleanupScope, afterCleanupBB);
|
|
||||||
irs.scope() = IRScope(afterCleanupBB);
|
|
||||||
lastCleanup = it->cleanupScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the ClassInfo reference to the landingpad instruction so it is
|
|
||||||
// emitted to the EH tables.
|
|
||||||
landingPad->addClause(it->classInfoPtr);
|
|
||||||
|
|
||||||
llvm::BasicBlock *mismatchBB = llvm::BasicBlock::Create(
|
|
||||||
irs.context(), beginBB->getName() + llvm::Twine(".mismatch"),
|
|
||||||
irs.topfunc());
|
|
||||||
|
|
||||||
// "Call" llvm.eh.typeid.for, which gives us the eh selector value to
|
|
||||||
// compare the landing pad selector value with.
|
|
||||||
llvm::Value *ehTypeId =
|
|
||||||
irs.ir->CreateCall(GET_INTRINSIC_DECL(eh_typeid_for),
|
|
||||||
DtoBitCast(it->classInfoPtr, getVoidPtrType()));
|
|
||||||
|
|
||||||
// Compare the selector value from the unwinder against the expected
|
|
||||||
// one and branch accordingly.
|
|
||||||
irs.ir->CreateCondBr(
|
|
||||||
irs.ir->CreateICmpEQ(irs.ir->CreateLoad(irs.funcGen().ehSelectorSlot),
|
|
||||||
ehTypeId),
|
|
||||||
it->bodyBlock, mismatchBB, it->branchWeights);
|
|
||||||
irs.scope() = IRScope(mismatchBB);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No catch matched. Execute all finallys and resume unwinding.
|
|
||||||
if (lastCleanup > 0) {
|
|
||||||
landingPad->setCleanup(true);
|
|
||||||
runCleanups(lastCleanup, 0, irs.funcGen().getOrCreateResumeUnwindBlock());
|
|
||||||
} else if (!catchScopes.empty()) {
|
|
||||||
// Directly convert the last mismatch branch into a branch to the
|
|
||||||
// unwind resume block.
|
|
||||||
irs.scopebb()->replaceAllUsesWith(
|
|
||||||
irs.funcGen().getOrCreateResumeUnwindBlock());
|
|
||||||
irs.scopebb()->eraseFromParent();
|
|
||||||
} else {
|
|
||||||
irs.ir->CreateBr(irs.funcGen().getOrCreateResumeUnwindBlock());
|
|
||||||
}
|
|
||||||
|
|
||||||
irs.scope() = savedIRScope;
|
|
||||||
return beginBB;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::BasicBlock *SwitchCaseTargets::get(Statement *stmt) {
|
llvm::BasicBlock *SwitchCaseTargets::get(Statement *stmt) {
|
||||||
auto it = targetBBs.find(stmt);
|
auto it = targetBBs.find(stmt);
|
||||||
assert(it != targetBBs.end());
|
assert(it != targetBBs.end());
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "gen/irstate.h"
|
#include "gen/irstate.h"
|
||||||
#include "gen/pgo.h"
|
#include "gen/pgo.h"
|
||||||
|
#include "gen/trycatch.h"
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/IR/CallSite.h"
|
#include "llvm/IR/CallSite.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -163,29 +164,6 @@ public:
|
||||||
std::vector<llvm::BasicBlock *> cleanupBlocks;
|
std::vector<llvm::BasicBlock *> cleanupBlocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Stores information to be able to branch to a catch clause if it matches.
|
|
||||||
///
|
|
||||||
/// Each catch body is emitted only once, but may be target from many landing
|
|
||||||
/// pads (in case of nested catch or cleanup scopes).
|
|
||||||
struct CatchScope {
|
|
||||||
/// The ClassInfo reference corresponding to the type to match the
|
|
||||||
/// exception object against.
|
|
||||||
llvm::Constant *classInfoPtr = nullptr;
|
|
||||||
|
|
||||||
/// The block to branch to if the exception type matches.
|
|
||||||
llvm::BasicBlock *bodyBlock = nullptr;
|
|
||||||
|
|
||||||
/// The cleanup scope stack level corresponding to this catch.
|
|
||||||
CleanupCursor cleanupScope;
|
|
||||||
|
|
||||||
// PGO branch weights for the exception type match branch.
|
|
||||||
// (first weight is for match, second is for mismatch)
|
|
||||||
llvm::MDNode *branchWeights = nullptr;
|
|
||||||
|
|
||||||
CatchScope(llvm::Constant *classInfoPtr, llvm::BasicBlock *bodyBlock,
|
|
||||||
CleanupCursor cleanupScope, llvm::MDNode *branchWeights = nullptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Keeps track of active (abstract) scopes in a function that influence code
|
/// Keeps track of active (abstract) scopes in a function that influence code
|
||||||
/// generation of their contents. This includes cleanups (finally blocks,
|
/// generation of their contents. This includes cleanups (finally blocks,
|
||||||
/// destructors), try/catch blocks and labels for goto/break/continue.
|
/// destructors), try/catch blocks and labels for goto/break/continue.
|
||||||
|
@ -203,8 +181,10 @@ struct CatchScope {
|
||||||
/// the rest of the ScopeStack API, as it (in contrast to goto) never requires
|
/// the rest of the ScopeStack API, as it (in contrast to goto) never requires
|
||||||
/// resolving forward references across cleanup scopes.
|
/// resolving forward references across cleanup scopes.
|
||||||
class ScopeStack {
|
class ScopeStack {
|
||||||
|
friend class TryCatchScopes;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ScopeStack(IRState &irs) : irs(irs) {}
|
explicit ScopeStack(IRState &irs) : irs(irs), tryCatchScopes(irs) {}
|
||||||
~ScopeStack();
|
~ScopeStack();
|
||||||
|
|
||||||
/// Registers a piece of cleanup code to be run.
|
/// Registers a piece of cleanup code to be run.
|
||||||
|
@ -246,30 +226,11 @@ public:
|
||||||
/// popped.
|
/// popped.
|
||||||
CleanupCursor currentCleanupScope() { return cleanupScopes.size(); }
|
CleanupCursor currentCleanupScope() { return cleanupScopes.size(); }
|
||||||
|
|
||||||
/// Registers a catch block to be taken into consideration when an exception
|
/// Registers a try-catch scope.
|
||||||
/// is thrown within the current scope.
|
void pushTryCatch(TryCatchStatement *stmt, llvm::BasicBlock *endbb);
|
||||||
///
|
|
||||||
/// When a potentially throwing function call is emitted, a landing pad will
|
|
||||||
/// be emitted to compare the dynamic type info of the exception against the
|
|
||||||
/// given ClassInfo constant and to branch to the given body block if it
|
|
||||||
/// matches. The registered catch blocks are maintained on a stack, with the
|
|
||||||
/// top-most (i.e. last pushed, innermost) taking precedence.
|
|
||||||
void pushCatch(llvm::Constant *classInfoPtr, llvm::BasicBlock *bodyBlock,
|
|
||||||
llvm::MDNode *matchWeights = nullptr);
|
|
||||||
|
|
||||||
/// Unregisters the last registered catch block.
|
/// Unregisters the last registered try-catch scope.
|
||||||
void popCatch();
|
void popTryCatch();
|
||||||
|
|
||||||
/// Registers a try block and the info whether non-Exceptions (Errors and
|
|
||||||
/// other Throwables) can be caught.
|
|
||||||
void pushTryBlock(bool catchingNonExceptions);
|
|
||||||
|
|
||||||
/// Unregisters the last registered try block.
|
|
||||||
void popTryBlock();
|
|
||||||
|
|
||||||
/// Indicates whether there are any registered catch blocks that handle
|
|
||||||
/// non-Exception Throwables.
|
|
||||||
bool isCatchingNonExceptions() const;
|
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 308
|
#if LDC_LLVM_VER >= 308
|
||||||
/// MSVC: catch and cleanup code is emitted as funclets and need
|
/// MSVC: catch and cleanup code is emitted as funclets and need
|
||||||
|
@ -362,13 +323,6 @@ private:
|
||||||
|
|
||||||
llvm::BasicBlock *&getLandingPadRef(CleanupCursor scope);
|
llvm::BasicBlock *&getLandingPadRef(CleanupCursor scope);
|
||||||
|
|
||||||
/// Emits a landing pad to honor all the active cleanups and catches.
|
|
||||||
llvm::BasicBlock *emitLandingPad();
|
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 308
|
|
||||||
llvm::BasicBlock *emitLandingPadMSVCEH(CleanupCursor scope);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Unified implementation for labeled break/continue.
|
/// Unified implementation for labeled break/continue.
|
||||||
void jumpToStatement(std::vector<JumpTarget> &targets,
|
void jumpToStatement(std::vector<JumpTarget> &targets,
|
||||||
Statement *loopOrSwitchStatement);
|
Statement *loopOrSwitchStatement);
|
||||||
|
@ -396,10 +350,7 @@ private:
|
||||||
std::vector<CleanupScope> cleanupScopes;
|
std::vector<CleanupScope> cleanupScopes;
|
||||||
|
|
||||||
///
|
///
|
||||||
std::vector<CatchScope> catchScopes;
|
TryCatchScopes tryCatchScopes;
|
||||||
|
|
||||||
///
|
|
||||||
std::vector<bool> catchingNonExceptions;
|
|
||||||
|
|
||||||
/// Gotos which we were not able to resolve to any cleanup scope, but which
|
/// Gotos which we were not able to resolve to any cleanup scope, but which
|
||||||
/// might still be defined later in the function at top level. If there are
|
/// might still be defined later in the function at top level. If there are
|
||||||
|
@ -423,10 +374,10 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args,
|
||||||
// to our advantage.
|
// to our advantage.
|
||||||
llvm::Function *calleeFn = llvm::dyn_cast<llvm::Function>(callee);
|
llvm::Function *calleeFn = llvm::dyn_cast<llvm::Function>(callee);
|
||||||
|
|
||||||
// Ignore 'nothrow' inside try-blocks with at least 1 catch block handling a
|
// Ignore 'nothrow' if there are active catch blocks handling non-Exception
|
||||||
// non-Exception Throwable.
|
// Throwables.
|
||||||
if (isNothrow)
|
if (isNothrow && tryCatchScopes.isCatchingNonExceptions())
|
||||||
isNothrow = !isCatchingNonExceptions();
|
isNothrow = false;
|
||||||
|
|
||||||
// Intrinsics don't support invoking and 'nounwind' functions don't need it.
|
// Intrinsics don't support invoking and 'nounwind' functions don't need it.
|
||||||
const bool doesNotThrow =
|
const bool doesNotThrow =
|
||||||
|
@ -440,7 +391,7 @@ llvm::CallSite ScopeStack::callOrInvoke(llvm::Value *callee, const T &args,
|
||||||
BundleList.push_back(llvm::OperandBundleDef("funclet", funclet));
|
BundleList.push_back(llvm::OperandBundleDef("funclet", funclet));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (doesNotThrow || (cleanupScopes.empty() && catchScopes.empty())) {
|
if (doesNotThrow || (cleanupScopes.empty() && tryCatchScopes.empty())) {
|
||||||
llvm::CallInst *call = irs.ir->CreateCall(callee, args,
|
llvm::CallInst *call = irs.ir->CreateCall(callee, args,
|
||||||
#if LDC_LLVM_VER >= 308
|
#if LDC_LLVM_VER >= 308
|
||||||
BundleList,
|
BundleList,
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "gen/logger.h"
|
#include "gen/logger.h"
|
||||||
#include "gen/runtime.h"
|
#include "gen/runtime.h"
|
||||||
#include "gen/tollvm.h"
|
#include "gen/tollvm.h"
|
||||||
#include "gen/ms-cxx-helper.h"
|
|
||||||
#include "ir/irfunction.h"
|
#include "ir/irfunction.h"
|
||||||
#include "ir/irmodule.h"
|
#include "ir/irmodule.h"
|
||||||
#include "llvm/IR/CFG.h"
|
#include "llvm/IR/CFG.h"
|
||||||
|
@ -797,95 +796,12 @@ public:
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#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 {
|
void visit(TryCatchStatement *stmt) LLVM_OVERRIDE {
|
||||||
IF_LOG Logger::println("TryCatchStatement::toIR(): %s",
|
IF_LOG Logger::println("TryCatchStatement::toIR(): %s",
|
||||||
stmt->loc.toChars());
|
stmt->loc.toChars());
|
||||||
LOG_SCOPE;
|
LOG_SCOPE;
|
||||||
|
|
||||||
auto &PGO = irs->funcGen().pgo;
|
auto &PGO = irs->funcGen().pgo;
|
||||||
auto entryCount = PGO.setCurrentStmt(stmt);
|
|
||||||
|
|
||||||
// Emit dwarf stop point
|
// Emit dwarf stop point
|
||||||
irs->DBuilder.EmitStopPoint(stmt->loc);
|
irs->DBuilder.EmitStopPoint(stmt->loc);
|
||||||
|
@ -899,185 +815,26 @@ public:
|
||||||
llvm::BasicBlock *endbb = llvm::BasicBlock::Create(
|
llvm::BasicBlock *endbb = llvm::BasicBlock::Create(
|
||||||
irs->context(), "try.success.or.caught", irs->topfunc());
|
irs->context(), "try.success.or.caught", irs->topfunc());
|
||||||
|
|
||||||
assert(stmt->catches);
|
irs->funcGen().scopes.pushTryCatch(stmt, endbb);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit the try block.
|
// Emit the try block.
|
||||||
irs->scope() = IRScope(trybb);
|
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);
|
assert(stmt->_body);
|
||||||
irs->DBuilder.EmitBlockStart(stmt->_body->loc);
|
irs->DBuilder.EmitBlockStart(stmt->_body->loc);
|
||||||
stmt->_body->accept(this);
|
stmt->_body->accept(this);
|
||||||
irs->DBuilder.EmitBlockEnd();
|
irs->DBuilder.EmitBlockEnd();
|
||||||
|
|
||||||
irs->func()->scopes->popTryBlock();
|
|
||||||
|
|
||||||
if (!irs->scopereturned())
|
if (!irs->scopereturned())
|
||||||
llvm::BranchInst::Create(endbb, irs->scopebb());
|
llvm::BranchInst::Create(endbb, irs->scopebb());
|
||||||
|
|
||||||
// Now that we have done the try block, remove the catches and continue
|
irs->funcGen().scopes.popTryCatch();
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move end block after all generated blocks
|
// Move end block after all generated blocks
|
||||||
endbb->moveAfter(&irs->topfunc()->back());
|
endbb->moveAfter(&irs->topfunc()->back());
|
||||||
|
|
||||||
irs->scope() = IRScope(endbb);
|
irs->scope() = IRScope(endbb);
|
||||||
|
|
||||||
// PGO counter tracks the continuation of the try statement
|
// PGO counter tracks the continuation of the try statement
|
||||||
PGO.emitCounterIncrement(stmt);
|
PGO.emitCounterIncrement(stmt);
|
||||||
}
|
}
|
||||||
|
|
438
gen/trycatch.cpp
Normal file
438
gen/trycatch.cpp
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
//===-- trycatch.cpp --------------------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// LDC – the LLVM D compiler
|
||||||
|
//
|
||||||
|
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||||||
|
// file for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "gen/trycatch.h"
|
||||||
|
|
||||||
|
#include "statement.h"
|
||||||
|
#include "gen/classes.h"
|
||||||
|
#include "gen/funcgenstate.h"
|
||||||
|
#include "gen/llvmhelpers.h"
|
||||||
|
#include "gen/ms-cxx-helper.h"
|
||||||
|
#include "gen/runtime.h"
|
||||||
|
#include "gen/tollvm.h"
|
||||||
|
#include "ir/irfunction.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if LDC_LLVM_VER >= 308
|
||||||
|
void emitBeginCatchMSVC(IRState &irs, 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
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
llvm::LandingPadInst *createLandingPadInst(IRState &irs) {
|
||||||
|
LLType *retType =
|
||||||
|
LLStructType::get(LLType::getInt8PtrTy(irs.context()),
|
||||||
|
LLType::getInt32Ty(irs.context()), nullptr);
|
||||||
|
#if LDC_LLVM_VER >= 307
|
||||||
|
LLFunction *currentFunction = irs.func()->func;
|
||||||
|
if (!currentFunction->hasPersonalityFn()) {
|
||||||
|
LLFunction *personalityFn =
|
||||||
|
getRuntimeFunction(Loc(), irs.module, "_d_eh_personality");
|
||||||
|
currentFunction->setPersonalityFn(personalityFn);
|
||||||
|
}
|
||||||
|
return irs.ir->CreateLandingPad(retType, 0);
|
||||||
|
#else
|
||||||
|
LLFunction *personalityFn =
|
||||||
|
getRuntimeFunction(Loc(), irs.module, "_d_eh_personality");
|
||||||
|
return irs.ir->CreateLandingPad(retType, personalityFn, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
TryCatchScope::TryCatchScope(TryCatchStatement *stmt, llvm::BasicBlock *endbb,
|
||||||
|
size_t cleanupScope)
|
||||||
|
: stmt(stmt), endbb(endbb), cleanupScope(cleanupScope) {
|
||||||
|
assert(stmt->catches);
|
||||||
|
catchesNonExceptions =
|
||||||
|
std::any_of(stmt->catches->begin(), stmt->catches->end(), [](Catch *c) {
|
||||||
|
for (auto cd = c->type->toBasetype()->isClassHandle(); cd;
|
||||||
|
cd = cd->baseClass) {
|
||||||
|
if (cd == ClassDeclaration::exception)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<TryCatchScope::CatchBlock> &
|
||||||
|
TryCatchScope::getCatchBlocks() const {
|
||||||
|
assert(!catchBlocks.empty());
|
||||||
|
return catchBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryCatchScope::emitCatchBodies(IRState &irs) {
|
||||||
|
assert(catchBlocks.empty());
|
||||||
|
|
||||||
|
#if LDC_LLVM_VER >= 308
|
||||||
|
if (useMSVCEH()) {
|
||||||
|
emitCatchBodiesMSVC(irs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto &PGO = irs.funcGen().pgo;
|
||||||
|
const auto entryCount = PGO.setCurrentStmt(stmt);
|
||||||
|
|
||||||
|
struct CBPrototype {
|
||||||
|
ClassDeclaration *cd;
|
||||||
|
llvm::BasicBlock *catchBB;
|
||||||
|
uint64_t catchCount;
|
||||||
|
uint64_t uncaughtCount;
|
||||||
|
};
|
||||||
|
llvm::SmallVector<CBPrototype, 8> cbPrototypes;
|
||||||
|
cbPrototypes.reserve(stmt->catches->dim);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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.
|
||||||
|
if (c->var) {
|
||||||
|
// This will alloca if we haven't already and take care of nested refs
|
||||||
|
// if there are any.
|
||||||
|
DtoDeclarationExp(c->var);
|
||||||
|
|
||||||
|
// Copy the exception reference over from the _d_eh_enter_catch return
|
||||||
|
// value.
|
||||||
|
DtoStore(DtoBitCast(throwableObj, DtoType(c->var->type)),
|
||||||
|
getIrLocal(c->var)->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
// PGO information, currently unused
|
||||||
|
auto catchCount = PGO.getRegionCount(c);
|
||||||
|
// uncaughtCount is handled in a separate pass below
|
||||||
|
|
||||||
|
auto cd = c->type->toBasetype()->isClassHandle();
|
||||||
|
cbPrototypes.push_back({cd, catchBB, catchCount, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 catch block prototypes in reversed order.
|
||||||
|
auto uncaughtCount = entryCount - PGO.getRegionCount(stmt);
|
||||||
|
for (auto it = cbPrototypes.rbegin(), end = cbPrototypes.rend(); it != end;
|
||||||
|
++it) {
|
||||||
|
it->uncaughtCount = uncaughtCount;
|
||||||
|
// Add this catch block's match count to the uncaughtCount, because these
|
||||||
|
// failed to match the remaining (lexically preceding) catch blocks.
|
||||||
|
uncaughtCount += it->catchCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
catchBlocks.reserve(stmt->catches->dim);
|
||||||
|
|
||||||
|
for (const auto &p : cbPrototypes) {
|
||||||
|
auto branchWeights =
|
||||||
|
PGO.createProfileWeights(p.catchCount, p.uncaughtCount);
|
||||||
|
DtoResolveClass(p.cd);
|
||||||
|
auto ci = getIrAggr(p.cd)->getClassInfoSymbol();
|
||||||
|
catchBlocks.push_back({ci, p.catchBB, branchWeights});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LDC_LLVM_VER >= 308
|
||||||
|
void TryCatchScope::emitCatchBodiesMSVC(IRState &irs) {
|
||||||
|
auto &PGO = irs.funcGen().pgo;
|
||||||
|
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);
|
||||||
|
|
||||||
|
emitBeginCatchMSVC(irs, 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes.pushCleanup(catchSwitchBlock, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void TryCatchScopes::push(TryCatchStatement *stmt, llvm::BasicBlock *endbb) {
|
||||||
|
TryCatchScope scope(stmt, endbb, irs.funcGen().scopes.currentCleanupScope());
|
||||||
|
// 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.
|
||||||
|
scope.emitCatchBodies(irs);
|
||||||
|
tryCatchScopes.push_back(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TryCatchScopes::pop() {
|
||||||
|
tryCatchScopes.pop_back();
|
||||||
|
if (useMSVCEH()) {
|
||||||
|
auto &scopes = irs.funcGen().scopes;
|
||||||
|
assert(isCatchSwitchBlock(scopes.cleanupScopes.back().beginBlock));
|
||||||
|
scopes.popCleanups(scopes.currentCleanupScope() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryCatchScopes::isCatchingNonExceptions() const {
|
||||||
|
return std::any_of(
|
||||||
|
tryCatchScopes.begin(), tryCatchScopes.end(),
|
||||||
|
[](const TryCatchScope &tc) { return tc.isCatchingNonExceptions(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::BasicBlock *TryCatchScopes::emitLandingPad() {
|
||||||
|
auto &scopes = irs.funcGen().scopes;
|
||||||
|
|
||||||
|
#if LDC_LLVM_VER >= 308
|
||||||
|
if (useMSVCEH()) {
|
||||||
|
auto currentCleanupScope = scopes.currentCleanupScope();
|
||||||
|
assert(currentCleanupScope > 0);
|
||||||
|
return emitLandingPadMSVC(currentCleanupScope - 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// save and rewrite scope
|
||||||
|
IRScope savedIRScope = irs.scope();
|
||||||
|
|
||||||
|
llvm::BasicBlock *beginBB =
|
||||||
|
llvm::BasicBlock::Create(irs.context(), "landingPad", irs.topfunc());
|
||||||
|
irs.scope() = IRScope(beginBB);
|
||||||
|
|
||||||
|
llvm::LandingPadInst *landingPad = createLandingPadInst(irs);
|
||||||
|
|
||||||
|
// Stash away the exception object pointer and selector value into their
|
||||||
|
// stack slots.
|
||||||
|
llvm::Value *ehPtr = DtoExtractValue(landingPad, 0);
|
||||||
|
irs.ir->CreateStore(ehPtr, irs.funcGen().getOrCreateEhPtrSlot());
|
||||||
|
|
||||||
|
llvm::Value *ehSelector = DtoExtractValue(landingPad, 1);
|
||||||
|
if (!irs.funcGen().ehSelectorSlot) {
|
||||||
|
irs.funcGen().ehSelectorSlot =
|
||||||
|
DtoRawAlloca(ehSelector->getType(), 0, "eh.selector");
|
||||||
|
}
|
||||||
|
irs.ir->CreateStore(ehSelector, irs.funcGen().ehSelectorSlot);
|
||||||
|
|
||||||
|
// Add landingpad clauses, emit finallys and 'if' chain to catch the
|
||||||
|
// exception.
|
||||||
|
size_t lastCleanup = scopes.currentCleanupScope();
|
||||||
|
for (auto it = tryCatchScopes.rbegin(), end = tryCatchScopes.rend();
|
||||||
|
it != end; ++it) {
|
||||||
|
const auto &tryCatchScope = *it;
|
||||||
|
|
||||||
|
// Insert any cleanups in between the previous (inner-more) try-catch scope
|
||||||
|
// and this one.
|
||||||
|
const auto newCleanup = tryCatchScope.getCleanupScope();
|
||||||
|
assert(lastCleanup >= newCleanup);
|
||||||
|
if (lastCleanup > newCleanup) {
|
||||||
|
landingPad->setCleanup(true);
|
||||||
|
llvm::BasicBlock *afterCleanupBB = llvm::BasicBlock::Create(
|
||||||
|
irs.context(), beginBB->getName() + llvm::Twine(".after.cleanup"),
|
||||||
|
irs.topfunc());
|
||||||
|
scopes.runCleanups(lastCleanup, newCleanup, afterCleanupBB);
|
||||||
|
irs.scope() = IRScope(afterCleanupBB);
|
||||||
|
lastCleanup = newCleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &cb : tryCatchScope.getCatchBlocks()) {
|
||||||
|
// Add the ClassInfo reference to the landingpad instruction so it is
|
||||||
|
// emitted to the EH tables.
|
||||||
|
landingPad->addClause(cb.classInfoPtr);
|
||||||
|
|
||||||
|
llvm::BasicBlock *mismatchBB = llvm::BasicBlock::Create(
|
||||||
|
irs.context(), beginBB->getName() + llvm::Twine(".mismatch"),
|
||||||
|
irs.topfunc());
|
||||||
|
|
||||||
|
// "Call" llvm.eh.typeid.for, which gives us the eh selector value to
|
||||||
|
// compare the landing pad selector value with.
|
||||||
|
llvm::Value *ehTypeId =
|
||||||
|
irs.ir->CreateCall(GET_INTRINSIC_DECL(eh_typeid_for),
|
||||||
|
DtoBitCast(cb.classInfoPtr, getVoidPtrType()));
|
||||||
|
|
||||||
|
// Compare the selector value from the unwinder against the expected
|
||||||
|
// one and branch accordingly.
|
||||||
|
irs.ir->CreateCondBr(
|
||||||
|
irs.ir->CreateICmpEQ(irs.ir->CreateLoad(irs.funcGen().ehSelectorSlot),
|
||||||
|
ehTypeId),
|
||||||
|
cb.bodyBB, mismatchBB, cb.branchWeights);
|
||||||
|
irs.scope() = IRScope(mismatchBB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No catch matched. Execute all finallys and resume unwinding.
|
||||||
|
if (lastCleanup > 0) {
|
||||||
|
landingPad->setCleanup(true);
|
||||||
|
scopes.runCleanups(lastCleanup, 0,
|
||||||
|
irs.funcGen().getOrCreateResumeUnwindBlock());
|
||||||
|
} else if (!tryCatchScopes.empty()) {
|
||||||
|
// Directly convert the last mismatch branch into a branch to the
|
||||||
|
// unwind resume block.
|
||||||
|
irs.scopebb()->replaceAllUsesWith(
|
||||||
|
irs.funcGen().getOrCreateResumeUnwindBlock());
|
||||||
|
irs.scopebb()->eraseFromParent();
|
||||||
|
} else {
|
||||||
|
irs.ir->CreateBr(irs.funcGen().getOrCreateResumeUnwindBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
irs.scope() = savedIRScope;
|
||||||
|
return beginBB;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LDC_LLVM_VER >= 308
|
||||||
|
llvm::BasicBlock *TryCatchScopes::emitLandingPadMSVC(size_t cleanupScope) {
|
||||||
|
auto &scopes = irs.funcGen().scopes;
|
||||||
|
|
||||||
|
LLFunction *currentFunction = irs.func()->func;
|
||||||
|
if (!currentFunction->hasPersonalityFn()) {
|
||||||
|
const char *personality = "__CxxFrameHandler3";
|
||||||
|
LLFunction *personalityFn =
|
||||||
|
getRuntimeFunction(Loc(), irs.module, personality);
|
||||||
|
currentFunction->setPersonalityFn(personalityFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanupScope == 0)
|
||||||
|
return scopes.runCleanupPad(cleanupScope, nullptr);
|
||||||
|
|
||||||
|
llvm::BasicBlock *&pad = scopes.getLandingPadRef(cleanupScope - 1);
|
||||||
|
if (!pad)
|
||||||
|
pad = emitLandingPadMSVC(cleanupScope - 1);
|
||||||
|
|
||||||
|
return scopes.runCleanupPad(cleanupScope, pad);
|
||||||
|
}
|
||||||
|
#endif
|
88
gen/trycatch.h
Normal file
88
gen/trycatch.h
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
//===-- gen/trycatch.h - Try-catch scopes -----------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// LDC – the LLVM D compiler
|
||||||
|
//
|
||||||
|
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||||||
|
// file for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LDC_GEN_TRYCATCH_H
|
||||||
|
#define LDC_GEN_TRYCATCH_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct IRState;
|
||||||
|
class TryCatchStatement;
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
class BasicBlock;
|
||||||
|
class GlobalVariable;
|
||||||
|
class MDNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class TryCatchScope {
|
||||||
|
public:
|
||||||
|
/// Stores information to be able to branch to a catch clause if it matches.
|
||||||
|
///
|
||||||
|
/// Each catch body is emitted only once, but may be target from many landing
|
||||||
|
/// pads (in case of nested catch or cleanup scopes).
|
||||||
|
struct CatchBlock {
|
||||||
|
/// The ClassInfo reference corresponding to the type to match the
|
||||||
|
/// exception object against.
|
||||||
|
llvm::GlobalVariable *classInfoPtr;
|
||||||
|
/// The block to branch to if the exception type matches.
|
||||||
|
llvm::BasicBlock *bodyBB;
|
||||||
|
// PGO branch weights for the exception type match branch.
|
||||||
|
// (first weight is for match, second is for mismatch)
|
||||||
|
llvm::MDNode *branchWeights;
|
||||||
|
};
|
||||||
|
|
||||||
|
TryCatchScope(TryCatchStatement *stmt, llvm::BasicBlock *endbb,
|
||||||
|
size_t cleanupScope);
|
||||||
|
|
||||||
|
size_t getCleanupScope() const { return cleanupScope; }
|
||||||
|
bool isCatchingNonExceptions() const { return catchesNonExceptions; }
|
||||||
|
|
||||||
|
void emitCatchBodies(IRState &irs);
|
||||||
|
const std::vector<CatchBlock> &getCatchBlocks() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TryCatchStatement *stmt;
|
||||||
|
llvm::BasicBlock *endbb;
|
||||||
|
size_t cleanupScope;
|
||||||
|
bool catchesNonExceptions;
|
||||||
|
|
||||||
|
std::vector<CatchBlock> catchBlocks;
|
||||||
|
|
||||||
|
void emitCatchBodiesMSVC(IRState &irs);
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class TryCatchScopes {
|
||||||
|
public:
|
||||||
|
TryCatchScopes(IRState &irs) : irs(irs) {}
|
||||||
|
|
||||||
|
void push(TryCatchStatement *stmt, llvm::BasicBlock *endbb);
|
||||||
|
void pop();
|
||||||
|
bool empty() const { return tryCatchScopes.empty(); }
|
||||||
|
|
||||||
|
/// Indicates whether there are any active catch blocks that handle
|
||||||
|
/// non-Exception Throwables.
|
||||||
|
bool isCatchingNonExceptions() const;
|
||||||
|
|
||||||
|
/// Emits a landing pad to honor all the active cleanups and catches.
|
||||||
|
llvm::BasicBlock *emitLandingPad();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IRState &irs;
|
||||||
|
std::vector<TryCatchScope> tryCatchScopes;
|
||||||
|
|
||||||
|
llvm::BasicBlock *emitLandingPadMSVC(size_t cleanupScope);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -78,13 +78,14 @@ void try_catch() {
|
||||||
if (i) {} // 1 : 1 (branch taken)
|
if (i) {} // 1 : 1 (branch taken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exception handlers (only the first BBs):
|
// ExceptionTwo 1st BB:
|
||||||
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 4
|
|
||||||
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 3
|
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 3
|
||||||
// ExceptionThree 2nd BB:
|
// ExceptionThree 1st BB:
|
||||||
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 8
|
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 4
|
||||||
// ExceptionTwo 2nd BB:
|
// ExceptionTwo 2nd BB: if(i)
|
||||||
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 7
|
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 7
|
||||||
|
// ExceptionThree 2nd BB: if(i)
|
||||||
|
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 8
|
||||||
// Try body 2nd BB: if(i < 2)
|
// Try body 2nd BB: if(i < 2)
|
||||||
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 5
|
// PROFGEN: store {{.*}} @[[TC]], i64 0, i64 5
|
||||||
// Landingpad stuff:
|
// Landingpad stuff:
|
||||||
|
@ -97,8 +98,8 @@ void try_catch() {
|
||||||
// Try body: if(i < 2)
|
// Try body: if(i < 2)
|
||||||
// PROFUSE: br {{.*}} !prof ![[TC5:[0-9]+]]
|
// PROFUSE: br {{.*}} !prof ![[TC5:[0-9]+]]
|
||||||
// Exception handlers: if(i){}
|
// Exception handlers: if(i){}
|
||||||
// PROFUSE: br {{.*}} !prof ![[TC8:[0-9]+]]
|
|
||||||
// PROFUSE: br {{.*}} !prof ![[TC7:[0-9]+]]
|
// PROFUSE: br {{.*}} !prof ![[TC7:[0-9]+]]
|
||||||
|
// PROFUSE: br {{.*}} !prof ![[TC8:[0-9]+]]
|
||||||
// More try body: if(i < 5)
|
// More try body: if(i < 5)
|
||||||
// PROFUSE: br {{.*}} !prof ![[TC6:[0-9]+]]
|
// PROFUSE: br {{.*}} !prof ![[TC6:[0-9]+]]
|
||||||
// Landingpad stuff:
|
// Landingpad stuff:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue