mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-16 12:53:14 +01:00
Merge pull request #409 from AlexeyProkhin/chaining
New exception chaining implementation
This commit is contained in:
@@ -898,6 +898,14 @@ static void LLVM_D_BuildRuntimeModule()
|
||||
llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M);
|
||||
}
|
||||
|
||||
// void _d_eh_handle_collision(ptr exc_struct, ptr exc_struct)
|
||||
{
|
||||
llvm::StringRef fname("_d_eh_handle_collision");
|
||||
LLType *types[] = { voidPtrTy, voidPtrTy };
|
||||
LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false);
|
||||
llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -17,62 +17,56 @@
|
||||
#include "gen/tollvm.h"
|
||||
#include "ir/irlandingpad.h"
|
||||
|
||||
IRLandingPadInfo::IRLandingPadInfo(Catch* catchstmt_, llvm::BasicBlock* end_) :
|
||||
finallyBody(NULL), catchstmt(catchstmt_), end(end_)
|
||||
IRLandingPadCatchInfo::IRLandingPadCatchInfo(Catch* catchstmt_, llvm::BasicBlock* end_) :
|
||||
catchStmt(catchstmt_), end(end_)
|
||||
{
|
||||
target = llvm::BasicBlock::Create(gIR->context(), "catch", gIR->topfunc(), end);
|
||||
|
||||
assert(catchstmt->type);
|
||||
catchType = catchstmt->type->toBasetype()->isClassHandle();
|
||||
assert(catchStmt->type);
|
||||
catchType = catchStmt->type->toBasetype()->isClassHandle();
|
||||
assert(catchType);
|
||||
catchType->codegen(Type::sir);
|
||||
|
||||
if(catchstmt->var) {
|
||||
if(!catchstmt->var->nestedrefs.dim) {
|
||||
gIR->func()->gen->landingPadInfo.getExceptionStorage();
|
||||
}
|
||||
if (catchStmt->var) {
|
||||
if (!catchStmt->var->nestedrefs.dim) {
|
||||
gIR->func()->gen->landingPadInfo.getExceptionStorage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IRLandingPadInfo::IRLandingPadInfo(Statement* finallystmt) :
|
||||
target(NULL), finallyBody(finallystmt), catchstmt(NULL)
|
||||
void IRLandingPadCatchInfo::toIR()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IRLandingPadInfo::toIR()
|
||||
{
|
||||
if (!catchstmt)
|
||||
if (!catchStmt)
|
||||
return;
|
||||
|
||||
gIR->scope() = IRScope(target, target);
|
||||
DtoDwarfBlockStart(catchstmt->loc);
|
||||
DtoDwarfBlockStart(catchStmt->loc);
|
||||
|
||||
// assign storage to catch var
|
||||
if(catchstmt->var) {
|
||||
if (catchStmt->var) {
|
||||
// use the same storage for all exceptions that are not accessed in
|
||||
// nested functions
|
||||
if(!catchstmt->var->nestedrefs.dim) {
|
||||
assert(!catchstmt->var->ir.irLocal);
|
||||
catchstmt->var->ir.irLocal = new IrLocal(catchstmt->var);
|
||||
if (!catchStmt->var->nestedrefs.dim) {
|
||||
assert(!catchStmt->var->ir.irLocal);
|
||||
catchStmt->var->ir.irLocal = new IrLocal(catchStmt->var);
|
||||
LLValue* catch_var = gIR->func()->gen->landingPadInfo.getExceptionStorage();
|
||||
catchstmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchstmt->var->type)));
|
||||
catchStmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchStmt->var->type)));
|
||||
}
|
||||
|
||||
// this will alloca if we haven't already and take care of nested refs
|
||||
DtoDeclarationExp(catchstmt->var);
|
||||
DtoDeclarationExp(catchStmt->var);
|
||||
|
||||
// the exception will only be stored in catch_var. copy it over if necessary
|
||||
if(catchstmt->var->ir.irLocal->value != gIR->func()->gen->landingPadInfo.getExceptionStorage()) {
|
||||
LLValue* exc = gIR->ir->CreateBitCast(DtoLoad(gIR->func()->gen->landingPadInfo.getExceptionStorage()), DtoType(catchstmt->var->type));
|
||||
DtoStore(exc, catchstmt->var->ir.irLocal->value);
|
||||
if (catchStmt->var->ir.irLocal->value != gIR->func()->gen->landingPadInfo.getExceptionStorage()) {
|
||||
LLValue* exc = gIR->ir->CreateBitCast(DtoLoad(gIR->func()->gen->landingPadInfo.getExceptionStorage()), DtoType(catchStmt->var->type));
|
||||
DtoStore(exc, catchStmt->var->ir.irLocal->value);
|
||||
}
|
||||
}
|
||||
|
||||
// emit handler, if there is one
|
||||
// handler is zero for instance for 'catch { debug foo(); }'
|
||||
if(catchstmt->handler)
|
||||
catchstmt->handler->toIR(gIR);
|
||||
if (catchStmt->handler)
|
||||
catchStmt->handler->toIR(gIR);
|
||||
|
||||
if (!gIR->scopereturned())
|
||||
gIR->ir->CreateBr(end);
|
||||
@@ -80,89 +74,77 @@ void IRLandingPadInfo::toIR()
|
||||
DtoDwarfBlockEnd();
|
||||
}
|
||||
|
||||
|
||||
void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end)
|
||||
{
|
||||
unpushed_infos.push_front(IRLandingPadInfo(catchstmt, end));
|
||||
unpushedScope.catches.push_back(IRLandingPadCatchInfo(catchstmt, end));
|
||||
}
|
||||
|
||||
void IRLandingPad::addFinally(Statement* finallystmt)
|
||||
{
|
||||
unpushed_infos.push_front(IRLandingPadInfo(finallystmt));
|
||||
assert(unpushedScope.finallyBody == NULL && "only one finally per try-finally block");
|
||||
unpushedScope.finallyBody = finallystmt;
|
||||
}
|
||||
|
||||
void IRLandingPad::push(llvm::BasicBlock* inBB)
|
||||
{
|
||||
// store infos such that matches are right to left
|
||||
nInfos.push(infos.size());
|
||||
infos.insert(infos.end(), unpushed_infos.begin(), unpushed_infos.end());
|
||||
unpushed_infos.clear();
|
||||
|
||||
// store as invoke target
|
||||
padBBs.push(inBB);
|
||||
unpushedScope.target = inBB;
|
||||
scopeStack.push(unpushedScope);
|
||||
unpushedScope = IRLandingPadScope();
|
||||
gIR->func()->gen->landingPad = get();
|
||||
}
|
||||
|
||||
void IRLandingPad::pop()
|
||||
{
|
||||
llvm::BasicBlock *inBB = padBBs.top();
|
||||
padBBs.pop();
|
||||
IRLandingPadScope scope = scopeStack.top();
|
||||
scopeStack.pop();
|
||||
gIR->func()->gen->landingPad = get();
|
||||
|
||||
size_t n = nInfos.top();
|
||||
for (int i = n, c = infos.size(); i < c; ++i)
|
||||
infos.at(i).toIR();
|
||||
constructLandingPad(inBB);
|
||||
|
||||
infos.resize(n);
|
||||
nInfos.pop();
|
||||
std::deque<IRLandingPadCatchInfo>::iterator itr, end = scope.catches.end();
|
||||
for (itr = scope.catches.begin(); itr != end; ++itr)
|
||||
itr->toIR();
|
||||
constructLandingPad(scope);
|
||||
}
|
||||
|
||||
llvm::BasicBlock* IRLandingPad::get()
|
||||
{
|
||||
if(padBBs.size() == 0)
|
||||
if (scopeStack.size() == 0)
|
||||
return NULL;
|
||||
else
|
||||
return padBBs.top();
|
||||
return scopeStack.top().target;
|
||||
}
|
||||
|
||||
void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB)
|
||||
// creates new landing pad
|
||||
static llvm::LandingPadInst *createLandingPadInst()
|
||||
{
|
||||
llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
|
||||
LLType *retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()),
|
||||
LLType::getInt32Ty(gIR->context()),
|
||||
NULL);
|
||||
return gIR->ir->CreateLandingPad(retType, personality_fn, 0, "landing_pad");
|
||||
}
|
||||
|
||||
void IRLandingPad::constructLandingPad(IRLandingPadScope scope)
|
||||
{
|
||||
// save and rewrite scope
|
||||
IRScope savedscope = gIR->scope();
|
||||
gIR->scope() = IRScope(inBB,savedscope.end);
|
||||
IRScope savedIRScope = gIR->scope();
|
||||
gIR->scope() = IRScope(scope.target, savedIRScope.end);
|
||||
|
||||
// personality fn
|
||||
llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality");
|
||||
// create landingpad
|
||||
LLType *retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()), LLType::getInt32Ty(gIR->context()), NULL);
|
||||
llvm::LandingPadInst *landingPad = gIR->ir->CreateLandingPad(retType, personality_fn, 0);
|
||||
llvm::LandingPadInst *landingPad = createLandingPadInst();
|
||||
LLValue* eh_ptr = DtoExtractValue(landingPad, 0);
|
||||
LLValue* eh_sel = DtoExtractValue(landingPad, 1);
|
||||
|
||||
// add landingpad clauses, emit finallys and 'if' chain to catch the exception
|
||||
llvm::Function* eh_typeid_for_fn = GET_INTRINSIC_DECL(eh_typeid_for);
|
||||
std::deque<IRLandingPadInfo> infos = this->infos;
|
||||
std::stack<size_t> nInfos = this->nInfos;
|
||||
std::deque<IRLandingPadInfo>::reverse_iterator rit, rend = infos.rend();
|
||||
bool isFirstCatch = true;
|
||||
for(rit = infos.rbegin(); rit != rend; ++rit)
|
||||
{
|
||||
// if it's a finally, emit its code
|
||||
if(rit->finallyBody)
|
||||
{
|
||||
size_t n = this->nInfos.top();
|
||||
this->infos.resize(n);
|
||||
this->nInfos.pop();
|
||||
rit->finallyBody->toIR(gIR);
|
||||
landingPad->setCleanup(true);
|
||||
}
|
||||
// otherwise it's a catch and we'll add a if-statement
|
||||
else
|
||||
{
|
||||
std::stack<IRLandingPadScope> savedScopeStack = scopeStack;
|
||||
std::deque<IRLandingPadCatchInfo>::iterator catchItr, catchItrEnd;
|
||||
while (true) {
|
||||
catchItr = scope.catches.begin();
|
||||
catchItrEnd = scope.catches.end();
|
||||
for (; catchItr != catchItrEnd; ++catchItr) {
|
||||
// if it is a first catch and some catch allocated storage, store exception object
|
||||
if(isFirstCatch && catch_var)
|
||||
{
|
||||
if (isFirstCatch && catch_var) {
|
||||
// eh_ptr is a pointer to _d_exception, which has a reference
|
||||
// to the Throwable object at offset 0.
|
||||
LLType *objectPtrTy = DtoType(ClassDeclaration::object->type->pointerTo());
|
||||
@@ -173,21 +155,51 @@ void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB)
|
||||
// create next block
|
||||
llvm::BasicBlock *next = llvm::BasicBlock::Create(gIR->context(), "eh.next", gIR->topfunc(), gIR->scopeend());
|
||||
// get class info symbol
|
||||
LLValue *classInfo = rit->catchType->ir.irAggr->getClassInfoSymbol();
|
||||
LLValue *classInfo = catchItr->catchType->ir.irAggr->getClassInfoSymbol();
|
||||
// add that symbol as landing pad clause
|
||||
landingPad->addClause(classInfo);
|
||||
// call llvm.eh.typeid.for to get class info index in the exception table
|
||||
classInfo = DtoBitCast(classInfo, getPtrToType(DtoType(Type::tint8)));
|
||||
LLValue *eh_id = gIR->ir->CreateCall(eh_typeid_for_fn, classInfo);
|
||||
// check exception selector (eh_sel) against the class info index
|
||||
gIR->ir->CreateCondBr(gIR->ir->CreateICmpEQ(eh_sel, eh_id), rit->target, next);
|
||||
gIR->ir->CreateCondBr(gIR->ir->CreateICmpEQ(eh_sel, eh_id), catchItr->target, next);
|
||||
gIR->scope() = IRScope(next, gIR->scopeend());
|
||||
}
|
||||
|
||||
if (scope.finallyBody) {
|
||||
// create collision landing pad that handles exceptions thrown inside the finally block
|
||||
llvm::BasicBlock *collision = llvm::BasicBlock::Create(gIR->context(), "eh.collision", gIR->topfunc(), gIR->scopeend());
|
||||
llvm::BasicBlock *bb = gIR->scopebb();
|
||||
gIR->scope() = IRScope(collision, gIR->scopeend());
|
||||
llvm::LandingPadInst *collisionLandingPad = createLandingPadInst();
|
||||
LLValue* collision_eh_ptr = DtoExtractValue(collisionLandingPad, 0);
|
||||
collisionLandingPad->setCleanup(true);
|
||||
llvm::Function* collision_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_handle_collision");
|
||||
gIR->CreateCallOrInvoke2(collision_fn, collision_eh_ptr, eh_ptr);
|
||||
gIR->ir->CreateUnreachable();
|
||||
gIR->scope() = IRScope(bb, gIR->scopeend());
|
||||
|
||||
// set collision landing pad as unwind target and emit the body of the finally
|
||||
DtoDwarfBlockStart(scope.finallyBody->loc);
|
||||
scopeStack.push(IRLandingPadScope(collision));
|
||||
gIR->func()->gen->landingPad = collision;
|
||||
scope.finallyBody->toIR(gIR);
|
||||
scopeStack.pop();
|
||||
gIR->func()->gen->landingPad = get();
|
||||
DtoDwarfBlockEnd();
|
||||
landingPad->setCleanup(true);
|
||||
}
|
||||
|
||||
if (scopeStack.empty())
|
||||
break;
|
||||
scope = scopeStack.top();
|
||||
scopeStack.pop();
|
||||
gIR->func()->gen->landingPad = get();
|
||||
}
|
||||
|
||||
// restore landing pad infos
|
||||
this->infos = infos;
|
||||
this->nInfos = nInfos;
|
||||
scopeStack = savedScopeStack;
|
||||
gIR->func()->gen->landingPad = get();
|
||||
|
||||
// no catch matched and all finallys executed - resume unwind
|
||||
llvm::Function* unwind_resume_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_resume_unwind");
|
||||
@@ -195,13 +207,12 @@ void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB)
|
||||
gIR->ir->CreateUnreachable();
|
||||
|
||||
// restore scope
|
||||
gIR->scope() = savedscope;
|
||||
gIR->scope() = savedIRScope;
|
||||
}
|
||||
|
||||
LLValue* IRLandingPad::getExceptionStorage()
|
||||
{
|
||||
if(!catch_var)
|
||||
{
|
||||
if (!catch_var) {
|
||||
Logger::println("Making new catch var");
|
||||
catch_var = DtoAlloca(ClassDeclaration::object->type, "catchvar");
|
||||
}
|
||||
|
||||
@@ -25,37 +25,39 @@ namespace llvm {
|
||||
class Value;
|
||||
class BasicBlock;
|
||||
class Function;
|
||||
class LandingPadInst;
|
||||
}
|
||||
|
||||
// only to be used within IRLandingPad
|
||||
// holds information about a single catch or finally
|
||||
struct IRLandingPadInfo
|
||||
// holds information about a single catch
|
||||
struct IRLandingPadCatchInfo
|
||||
{
|
||||
// default constructor for being able to store in a vector
|
||||
IRLandingPadInfo() :
|
||||
target(NULL), finallyBody(NULL), catchstmt(NULL)
|
||||
IRLandingPadCatchInfo() :
|
||||
target(NULL), end(0), catchStmt(NULL), catchType(0)
|
||||
{}
|
||||
|
||||
// constructor for catch
|
||||
IRLandingPadInfo(Catch* catchstmt, llvm::BasicBlock* end);
|
||||
|
||||
// constructor for finally
|
||||
IRLandingPadInfo(Statement* finallystmt);
|
||||
IRLandingPadCatchInfo(Catch* catchStmt, llvm::BasicBlock* end);
|
||||
|
||||
// codegen the catch block
|
||||
void toIR();
|
||||
|
||||
// the target catch bb if this is a catch
|
||||
// or the target finally bb if this is a finally
|
||||
llvm::BasicBlock* target;
|
||||
llvm::BasicBlock *target;
|
||||
llvm::BasicBlock *end;
|
||||
Catch *catchStmt;
|
||||
ClassDeclaration *catchType;
|
||||
};
|
||||
|
||||
// nonzero if this is a finally
|
||||
Statement* finallyBody;
|
||||
// holds information about a single try-catch-inally block
|
||||
struct IRLandingPadScope
|
||||
{
|
||||
explicit IRLandingPadScope(llvm::BasicBlock *target_ = NULL) : target(target_), finallyBody(0) {}
|
||||
|
||||
// nonzero if this is a catch
|
||||
Catch* catchstmt;
|
||||
llvm::BasicBlock* end;
|
||||
ClassDeclaration* catchType;
|
||||
// the target for invokes
|
||||
llvm::BasicBlock *target;
|
||||
// information about catch blocks
|
||||
std::deque<IRLandingPadCatchInfo> catches;
|
||||
// the body of finally
|
||||
Statement *finallyBody;
|
||||
};
|
||||
|
||||
|
||||
@@ -79,25 +81,19 @@ struct IRLandingPad
|
||||
// and its infos
|
||||
void pop();
|
||||
|
||||
// gets the current landing pad
|
||||
llvm::BasicBlock* get();
|
||||
|
||||
// creates or gets storage for exception object
|
||||
llvm::Value* getExceptionStorage();
|
||||
|
||||
private:
|
||||
// constructs the landing pad from infos
|
||||
void constructLandingPad(llvm::BasicBlock* inBB);
|
||||
// gets the current landing pad
|
||||
llvm::BasicBlock* get();
|
||||
|
||||
// information needed to create landing pads
|
||||
std::deque<IRLandingPadInfo> infos;
|
||||
std::deque<IRLandingPadInfo> unpushed_infos;
|
||||
// constructs the landing pad
|
||||
void constructLandingPad(IRLandingPadScope scope);
|
||||
|
||||
// the number of infos we had before the push
|
||||
std::stack<size_t> nInfos;
|
||||
|
||||
// the target for invokes
|
||||
std::stack<llvm::BasicBlock*> padBBs;
|
||||
// information about try-catch-finally blocks
|
||||
std::stack<IRLandingPadScope> scopeStack;
|
||||
IRLandingPadScope unpushedScope;
|
||||
|
||||
// storage for the catch variable
|
||||
llvm::Value* catch_var;
|
||||
|
||||
Submodule runtime/druntime updated: adaeaf9e7b...a245e8d2f2
Submodule tests/d2/dmd-testsuite updated: 32aa48727a...dbb7b9fa18
Reference in New Issue
Block a user