Mercurial > projects > ldc
diff ir/irlandingpad.cpp @ 319:e9c93739bc4c trunk
[svn r340] Rework exception handling to work with nested tryfinally and trycatch.
author | ChristianK |
---|---|
date | Sat, 05 Jul 2008 10:22:56 +0200 |
parents | |
children | d772927ca496 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ir/irlandingpad.cpp Sat Jul 05 10:22:56 2008 +0200 @@ -0,0 +1,186 @@ +#include "gen/llvm.h" +#include "gen/tollvm.h" +#include "gen/irstate.h" +#include "gen/runtime.h" +#include "gen/logger.h" +#include "gen/classes.h" +#include "gen/llvmhelpers.h" +#include "ir/irlandingpad.h" + +IRLandingPadInfo::IRLandingPadInfo(Catch* catchstmt, llvm::BasicBlock* end) +: finallyBody(NULL) +{ + target = llvm::BasicBlock::Create("catch", gIR->topfunc(), end); + gIR->scope() = IRScope(target,end); + + // assign storage to catch var + if(catchstmt->var) { + assert(!catchstmt->var->ir.irLocal); + catchstmt->var->ir.irLocal = new IrLocal(catchstmt->var); + LLValue* catch_var = gIR->func()->landingPad.getExceptionStorage(); + catchstmt->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(catchstmt->var->type))); + } + + // emit handler + assert(catchstmt->handler); + catchstmt->handler->toIR(gIR); + + if (!gIR->scopereturned()) + gIR->ir->CreateBr(end); + + assert(catchstmt->type); + catchType = catchstmt->type->isClassHandle(); + DtoForceDeclareDsymbol(catchType); + assert(catchType); +} + +IRLandingPadInfo::IRLandingPadInfo(Statement* finallystmt) +: target(NULL), finallyBody(finallystmt), catchType(NULL) +{ + +} + + +void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end) +{ + unpushed_infos.push_front(IRLandingPadInfo(catchstmt, end)); +} + +void IRLandingPad::addFinally(Statement* finallystmt) +{ + unpushed_infos.push_front(IRLandingPadInfo(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(); + + constructLandingPad(inBB); + + // store as invoke target + padBBs.push(inBB); +} + +void IRLandingPad::pop() +{ + padBBs.pop(); + + size_t n = nInfos.top(); + infos.resize(n); + nInfos.pop(); +} + +llvm::BasicBlock* IRLandingPad::get() +{ + if(padBBs.size() == 0) + return NULL; + else + return padBBs.top(); +} + +void IRLandingPad::constructLandingPad(llvm::BasicBlock* inBB) +{ + // save and rewrite scope + IRScope savedscope = gIR->scope(); + gIR->scope() = IRScope(inBB,savedscope.end); + + // eh_ptr = llvm.eh.exception(); + llvm::Function* eh_exception_fn = GET_INTRINSIC_DECL(eh_exception); + LLValue* eh_ptr = gIR->ir->CreateCall(eh_exception_fn); + + // build selector arguments + LLSmallVector<LLValue*, 6> selectorargs; + selectorargs.push_back(eh_ptr); + + llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality"); + LLValue* personality_fn_arg = gIR->ir->CreateBitCast(personality_fn, getPtrToType(LLType::Int8Ty)); + selectorargs.push_back(personality_fn_arg); + + bool hasFinally = false; + // associate increasing ints with each unique classdecl encountered + std::map<ClassDeclaration*, int> catchToInt; + std::deque<IRLandingPadInfo>::reverse_iterator it = infos.rbegin(), end = infos.rend(); + for(size_t i = infos.size() - 1; it != end; ++it, --i) + { + if(it->finallyBody) + hasFinally = true; + else + { + if(catchToInt.find(it->catchType) == catchToInt.end()) + { + int newval = catchToInt.size(); + catchToInt[it->catchType] = newval; + } + assert(it->catchType); + assert(it->catchType->ir.irStruct); + selectorargs.push_back(it->catchType->ir.irStruct->classInfo); + } + } + // if there's a finally, the eh table has to have a 0 action + if(hasFinally) + selectorargs.push_back(llvm::ConstantInt::get(LLType::Int32Ty, 0)); + // if there is a catch, store exception object + if(catchToInt.size()) + { + const LLType* objectTy = DtoType(ClassDeclaration::object->type); + assert(catch_var); + gIR->ir->CreateStore(gIR->ir->CreateBitCast(eh_ptr, objectTy), catch_var); + } + + // eh_sel = llvm.eh.selector(eh_ptr, cast(byte*)&_d_eh_personality, <selectorargs>); + llvm::Function* eh_selector_fn; + if (global.params.is64bit) + eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i64); + else + eh_selector_fn = GET_INTRINSIC_DECL(eh_selector_i32); + LLValue* eh_sel = gIR->ir->CreateCall(eh_selector_fn, selectorargs.begin(), selectorargs.end()); + + // emit finallys and switches that branch to catches until there are no more catches + // then simply branch to the finally chain + llvm::SwitchInst* switchinst = NULL; + for(it = infos.rbegin(); it != end; ++it) + { + // if it's a finally, emit its code + if(it->finallyBody) + { + if(switchinst) + switchinst = NULL; + it->finallyBody->toIR(gIR); + } + // otherwise it's a catch and we'll add a switch case + else + { + if(!switchinst) + { + switchinst = gIR->ir->CreateSwitch(eh_sel, llvm::BasicBlock::Create("switchdefault", gIR->topfunc(), gIR->scopeend()), infos.size()); + gIR->scope() = IRScope(switchinst->getDefaultDest(), gIR->scopeend()); + } + // catches matched first get the largest switchval, so do size - unique int + llvm::ConstantInt* switchval = llvm::ConstantInt::get(LLType::Int32Ty, catchToInt.size() - catchToInt[it->catchType]); + // and make sure we don't add the same switchval twice, may happen with nested trys + if(!switchinst->findCaseValue(switchval)) + switchinst->addCase(switchval, it->target); + } + } + + // no catch matched and all finallys executed - resume unwind + llvm::Function* unwind_resume_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_resume_unwind"); + gIR->ir->CreateCall(unwind_resume_fn, eh_ptr); + gIR->ir->CreateUnreachable(); + + gIR->scope() = savedscope; +} + +LLValue* IRLandingPad::getExceptionStorage() +{ + if(!catch_var) + { + Logger::println("Making new catch var"); + const LLType* objectTy = DtoType(ClassDeclaration::object->type); + catch_var = new llvm::AllocaInst(objectTy,"catchvar",gIR->topallocapoint()); + } + return catch_var; +}