diff --git a/gen/runtime.cpp b/gen/runtime.cpp index c6e4af3425a..3f72ace1f01 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -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); + } + ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irlandingpad.cpp b/ir/irlandingpad.cpp index b3ae23b6089..a3036f590a1 100644 --- a/ir/irlandingpad.cpp +++ b/ir/irlandingpad.cpp @@ -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 IRLandingPadInfo::toIR() +void IRLandingPadCatchInfo::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"); } diff --git a/ir/irlandingpad.h b/ir/irlandingpad.h index 904a5cffc2f..708bb2563be 100644 --- a/ir/irlandingpad.h +++ b/ir/irlandingpad.h @@ -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); - - // information needed to create landing pads - std::deque<IRLandingPadInfo> infos; - std::deque<IRLandingPadInfo> unpushed_infos; + // gets the current landing pad + llvm::BasicBlock* get(); - // the number of infos we had before the push - std::stack<size_t> nInfos; + // constructs the landing pad + void constructLandingPad(IRLandingPadScope scope); - // 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; diff --git a/runtime/druntime b/runtime/druntime index adaeaf9e7b3..a245e8d2f2f 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit adaeaf9e7b3151d2efb710dca3d79cb724bd30bb +Subproject commit a245e8d2f2f8edce0ead880b5e1d0632df1c2bb1 diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 32aa48727a4..dbb7b9fa186 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 32aa48727a4db7a28878e6f13af603aaa3e0e48d +Subproject commit dbb7b9fa186c46576fadf195d8da9117ee988a52