Mercurial > projects > ldc
view ir/irlandingpad.cpp @ 355:d8357f7004ca trunk
[svn r376] Fix bug with finally blocks and labels. The labels would get emitted multiple times and conflict.
It is now possible to add label scopes in IrFunction and all labels names will be prefixed accordingly.
Also disallow goto into finally blocks.
Fixes nocompile/finally_02 and others.
author | ChristianK |
---|---|
date | Mon, 14 Jul 2008 11:48:55 +0200 |
parents | d22d8d63c1fd |
children | 672eb4893b55 |
line wrap: on
line source
#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, if there is one // handler is zero for instance for 'catch { debug foo(); }' if(catchstmt->handler) catchstmt->handler->toIR(gIR); if (!gIR->scopereturned()) gIR->ir->CreateBr(end); assert(catchstmt->type); //TODO: Is toBasetype correct here? Should catch handlers with typedefed // classes behave differently? catchType = catchstmt->type->toBasetype()->isClassHandle(); assert(catchType); DtoForceDeclareDsymbol(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 and some catch allocated storage, store exception object if(catchToInt.size() && catch_var) { const LLType* objectTy = DtoType(ClassDeclaration::object->type); 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; // since this may be emitted multiple times // give the labels a new scope gIR->func()->pushUniqueLabelScope("finally"); it->finallyBody->toIR(gIR); gIR->func()->popLabelScope(); } // 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(DtoSize_t(), 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; }