# HG changeset patch # User ChristianK # Date 1215246176 -7200 # Node ID e9c93739bc4ca546b7a7fb43d5dfa5e7dc8c9876 # Parent 8e570dbe4087fd7e8b58c160aa7ca5d61fcbe86c [svn r340] Rework exception handling to work with nested tryfinally and trycatch. diff -r 8e570dbe4087 -r e9c93739bc4c gen/irstate.h --- a/gen/irstate.h Fri Jul 04 09:00:49 2008 +0200 +++ b/gen/irstate.h Sat Jul 05 10:22:56 2008 +0200 @@ -172,10 +172,6 @@ llvm::BasicBlock* scopeend(); bool scopereturned(); - // landing pads for try statements - typedef std::vector BBVec; - BBVec landingPads; - // create a call or invoke, depending on the landing pad info // the template function is defined further down in this file template @@ -229,15 +225,16 @@ template CallOrInvoke* IRState::CreateCallOrInvoke(LLValue* Callee, InputIterator ArgBegin, InputIterator ArgEnd, const char* Name) { - if(landingPads.empty()) - return new CallOrInvoke_Call(ir->CreateCall(Callee, ArgBegin, ArgEnd, Name)); - else + llvm::BasicBlock* pad; + if(pad = func()->landingPad.get()) { llvm::BasicBlock* postinvoke = llvm::BasicBlock::Create("postinvoke", topfunc(), scopeend()); - llvm::InvokeInst* invoke = ir->CreateInvoke(Callee, postinvoke, *landingPads.rbegin(), ArgBegin, ArgEnd, Name); + llvm::InvokeInst* invoke = ir->CreateInvoke(Callee, postinvoke, pad, ArgBegin, ArgEnd, Name); scope() = IRScope(postinvoke, scopeend()); return new CallOrInvoke_Invoke(invoke); } + else + return new CallOrInvoke_Call(ir->CreateCall(Callee, ArgBegin, ArgEnd, Name)); } #endif // LLVMDC_GEN_IRSTATE_H diff -r 8e570dbe4087 -r e9c93739bc4c gen/statements.cpp --- a/gen/statements.cpp Fri Jul 04 09:00:49 2008 +0200 +++ b/gen/statements.cpp Sat Jul 05 10:22:56 2008 +0200 @@ -27,6 +27,7 @@ #include "gen/dvalue.h" #include "ir/irfunction.h" +#include "ir/irlandingpad.h" ////////////////////////////////////////////////////////////////////////////// @@ -497,8 +498,7 @@ llvm::BasicBlock* trybb = llvm::BasicBlock::Create("try", p->topfunc(), oldend); llvm::BasicBlock* finallybb = llvm::BasicBlock::Create("finally", p->topfunc(), oldend); // the landing pad for statements in the try block - // only reached via eh-unwinding, a call to resume unwinding is appended - llvm::BasicBlock* unwindfinallybb = llvm::BasicBlock::Create("unwindfinally", p->topfunc(), oldend); + llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create("landingpad", p->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create("endtryfinally", p->topfunc(), oldend); // pass the previous BB into this @@ -506,10 +506,17 @@ llvm::BranchInst::Create(trybb, p->scopebb()); // + // set up the landing pad + // + p->scope() = IRScope(landingpadbb, endbb); + + gIR->func()->landingPad.addFinally(finalbody); + gIR->func()->landingPad.push(landingpadbb); + + // // do the try block // p->scope() = IRScope(trybb,finallybb); - p->landingPads.push_back(unwindfinallybb); assert(body); body->toIR(p); @@ -518,12 +525,12 @@ if (!p->scopereturned()) llvm::BranchInst::Create(finallybb, p->scopebb()); - p->landingPads.pop_back(); + gIR->func()->landingPad.pop(); // // do finally block // - p->scope() = IRScope(finallybb,unwindfinallybb); + p->scope() = IRScope(finallybb,landingpadbb); assert(finalbody); finalbody->toIR(p); @@ -533,39 +540,6 @@ llvm::BranchInst::Create(endbb, p->scopebb()); } - // - // do landing pad - // - p->scope() = IRScope(unwindfinallybb,endbb); - - // eh_ptr = llvm.eh.exception(); - llvm::Function* eh_exception_fn = GET_INTRINSIC_DECL(eh_exception); - LLValue* eh_ptr = gIR->ir->CreateCall(eh_exception_fn); - - // eh_sel = llvm.eh.selector(eh_ptr, cast(byte*)&_d_eh_personality, 0); - 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); - llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality"); - LLValue* personality_fn_arg = gIR->ir->CreateBitCast(personality_fn, getPtrToType(LLType::Int8Ty)); - LLValue* eh_sel = gIR->ir->CreateCall3(eh_selector_fn, eh_ptr, personality_fn_arg, llvm::ConstantInt::get(LLType::Int32Ty, 0)); - - // emit finally code - finalbody->toIR(p); - - // finally code may not be terminated! - if (gIR->scopereturned()) { - error("finally blocks may not be terminated", loc.toChars()); - fatal(); - } - - 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(); - // rewrite the scope p->scope() = IRScope(endbb,oldend); } @@ -593,10 +567,23 @@ llvm::BranchInst::Create(trybb, p->scopebb()); // + // do catches and the landing pad + // + assert(catches); + gIR->scope() = IRScope(landingpadbb, endbb); + + for (int i = 0; i < catches->dim; i++) + { + Catch *c = (Catch *)catches->data[i]; + gIR->func()->landingPad.addCatch(c, endbb); + } + + gIR->func()->landingPad.push(landingpadbb); + + // // do the try block // p->scope() = IRScope(trybb,landingpadbb); - p->landingPads.push_back(landingpadbb); assert(body); body->toIR(p); @@ -604,94 +591,7 @@ if (!gIR->scopereturned()) llvm::BranchInst::Create(endbb, p->scopebb()); - p->landingPads.pop_back(); - - // - // do catches - // - assert(catches); - - // get storage for exception var - const LLType* objectTy = DtoType(ClassDeclaration::object->type); - llvm::AllocaInst* catch_var = new llvm::AllocaInst(objectTy,"catchvar",p->topallocapoint()); - - // for further reference in landing pad - LLSmallVector catch_bbs; - - for (int i = 0; i < catches->dim; i++) - { - Catch *c = (Catch *)catches->data[i]; - - llvm::BasicBlock* catchbb = llvm::BasicBlock::Create("catch", p->topfunc(), oldend); - catch_bbs.push_back(catchbb); - p->scope() = IRScope(catchbb,oldend); - - // assign storage to catch var - if(c->var) { - assert(!c->var->ir.irLocal); - c->var->ir.irLocal = new IrLocal(c->var); - c->var->ir.irLocal->value = gIR->ir->CreateBitCast(catch_var, getPtrToType(DtoType(c->var->type))); - } - - // emit handler - assert(c->handler); - c->handler->toIR(p); - - if (!gIR->scopereturned()) - llvm::BranchInst::Create(endbb, p->scopebb()); - } - - // - // do landing pad - // - p->scope() = IRScope(landingpadbb,endbb); - - // eh_ptr = llvm.eh.exception(); - llvm::Function* eh_exception_fn = GET_INTRINSIC_DECL(eh_exception); - LLValue* eh_ptr = gIR->ir->CreateCall(eh_exception_fn); - - // store eh_ptr in 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, ); - 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); - - LLSmallVector args; - args.push_back(eh_ptr); - llvm::Function* personality_fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_eh_personality"); - args.push_back(gIR->ir->CreateBitCast(personality_fn, getPtrToType(LLType::Int8Ty))); - for (int i = 0; i < catches->dim; i++) - { - Catch *c = (Catch *)catches->data[i]; - assert(c->type); - ClassDeclaration* cdecl = c->type->isClassHandle(); - assert(cdecl); - assert(cdecl->ir.irStruct); - args.push_back(cdecl->ir.irStruct->classInfo); - } - - LLValue* eh_sel = gIR->ir->CreateCall(eh_selector_fn, args.begin(), args.end()); - - // switch on eh_sel and branch to correct case - - // setup default target - llvm::BasicBlock* defaulttarget = llvm::BasicBlock::Create("default", p->topfunc(), oldend); - //TODO: Error handling? - new llvm::UnreachableInst(defaulttarget); - - llvm::SwitchInst* sw = p->ir->CreateSwitch(eh_sel, defaulttarget, catch_bbs.size()); - - // add all catches as cases - for(unsigned int c = 0; c < catch_bbs.size(); ++c) - { - llvm::BasicBlock* casebb = llvm::BasicBlock::Create("case", p->topfunc(), oldend); - llvm::BranchInst::Create(catch_bbs[c], casebb); - sw->addCase(llvm::ConstantInt::get(LLType::Int32Ty, c+1), casebb); - } + gIR->func()->landingPad.pop(); // rewrite the scope p->scope() = IRScope(endbb,oldend); diff -r 8e570dbe4087 -r e9c93739bc4c ir/irfunction.h --- a/ir/irfunction.h Fri Jul 04 09:00:49 2008 +0200 +++ b/ir/irfunction.h Sat Jul 05 10:22:56 2008 +0200 @@ -2,6 +2,7 @@ #define LLVMDC_IR_IRFUNCTION_H #include "ir/ir.h" +#include "ir/irlandingpad.h" #include @@ -25,6 +26,9 @@ llvm::AllocaInst* srcfileArg; llvm::AllocaInst* msgArg; + // landing pads for try statements + IRLandingPad landingPad; + IrFunction(FuncDeclaration* fd); }; diff -r 8e570dbe4087 -r e9c93739bc4c ir/irlandingpad.cpp --- /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 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 catchToInt; + std::deque::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, ); + 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; +} diff -r 8e570dbe4087 -r e9c93739bc4c ir/irlandingpad.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ir/irlandingpad.h Sat Jul 05 10:22:56 2008 +0200 @@ -0,0 +1,70 @@ +#ifndef LLVMDC_IR_IRLANDINGPADINFO_H +#define LLVMDC_IR_IRLANDINGPADINFO_H + +#include "ir/ir.h" +#include "statement.h" + +#include +#include + +struct IRLandingPadInfo +{ + // default constructor for being able to store in a vector + IRLandingPadInfo() + : target(NULL), finallyBody(NULL), catchType(NULL) + {} + + IRLandingPadInfo(Catch* catchstmt, llvm::BasicBlock* end); + IRLandingPadInfo(Statement* finallystmt); + + // the target catch bb if this is a catch + // or the target finally bb if this is a finally + llvm::BasicBlock* target; + + // nonzero if this is a finally + Statement* finallyBody; + + // nonzero if this is a catch + ClassDeclaration* catchType; +}; + +struct IRLandingPad +{ + IRLandingPad() : catch_var(NULL) {} + + // builds a new landing pad according to given infos + // and the ones on the stack. also stores it as invoke target + void push(llvm::BasicBlock* inBB); + + void addCatch(Catch* catchstmt, llvm::BasicBlock* end); + void addFinally(Statement* finallystmt); + + // pops the most recently constructed landing pad bb + // and its infos + void pop(); + + // gets the current landing pad + llvm::BasicBlock* get(); + + // creates or gets storage for exception object + LLValue* getExceptionStorage(); + +private: + // constructs the landing pad from infos + void constructLandingPad(llvm::BasicBlock* inBB); + + // information needed to create landing pads + std::deque infos; + std::deque unpushed_infos; + + // the number of infos we had before the push + std::stack nInfos; + + // the target for invokes + std::stack padBBs; + + // storage for the catch variable + LLValue* catch_var; +}; + +#endif diff -r 8e570dbe4087 -r e9c93739bc4c tango/lib/compiler/llvmdc/eh.d --- a/tango/lib/compiler/llvmdc/eh.d Fri Jul 04 09:00:49 2008 +0200 +++ b/tango/lib/compiler/llvmdc/eh.d Sat Jul 05 10:22:56 2008 +0200 @@ -4,7 +4,7 @@ import util.console; -//debug = EH_personality; +// debug = EH_personality; private extern(C) void abort(); private extern(C) int printf(char*, ...); @@ -251,6 +251,7 @@ else if(actions & _Unwind_Action.HANDLER_PHASE) { + debug(EH_personality) printf("Setting switch value to: %d!\n", switchval); _Unwind_SetGR(context, eh_exception_regno, cast(ulong)cast(void*)(exception_struct.exception_object)); _Unwind_SetGR(context, eh_selector_regno, switchval); _Unwind_SetIP(context, landing_pad); @@ -269,6 +270,7 @@ debug(EH_personality) printf("Calling cleanup routine...\n"); _Unwind_SetGR(context, eh_exception_regno, cast(ulong)exception_struct); + _Unwind_SetGR(context, eh_selector_regno, 0); _Unwind_SetIP(context, landing_pad); return _Unwind_Reason_Code.INSTALL_CONTEXT; }